Laravel 4.2合并两个关联关系时抛出未捕获异常求助
咱们先拆解下你遇到的问题,再一步步解决:
问题根源
错误的关联类型选择:你用了
hasOne来定义following_friend和follower_friend,但一个用户显然可以关注多人,也会被多人关注,hasOne只会返回匹配的第一条记录,而且它返回的是单个Friend模型实例,不是集合(Collection)。而merge()是集合的方法,直接在单个模型上调用它,自然会抛出「未定义方法」的异常。预加载不支持自定义方法:你尝试用
with('mutual_friends')预加载这个方法,但Eloquent的预加载只认返回关联关系实例(比如hasMany、belongsTo这类)的方法,你的mutual_friends直接返回合并结果,不是关联对象,所以预加载会失败,这也是异常的诱因。
分步解决方案
第一步:修正关联关系
先把hasOne改成hasMany,符合用户和好友记录的一对多关系:
// 当前用户关注的所有好友记录 public function following_friends() { return $this->hasMany('Friend', 'following_id', 'id'); } // 所有关注当前用户的好友记录 public function follower_friends() { return $this->hasMany('Friend', 'follower_id', 'id'); }
第二步:根据需求实现mutual_friends
这里分两种常见场景,你可以根据业务需求选:
场景1:获取互相关注的用户模型
如果你想拿到「你关注的人里,同时也关注你的用户」,把mutual_friends改成关联方法,这样就能支持预加载:
public function mutual_friends() { return $this->belongsToMany('User', 'friends', 'following_id', 'follower_id') ->whereExists(function($query) { $query->select(DB::raw(1)) ->from('friends as reciprocal') ->whereRaw('reciprocal.following_id = friends.follower_id') ->whereRaw('reciprocal.follower_id = friends.following_id') ->where('reciprocal.follower_id', $this->id); }); }
之后你原来的with('mutual_friends')就能正常工作了。
场景2:合并双向的Friend记录
如果你只是想把当前用户作为「关注者」和「被关注者」的所有Friend记录合并成一个集合,可以用访问器的方式实现:
public function getMutualFriendsAttribute() { // 合并两个hasMany返回的集合 return $this->following_friends->merge($this->follower_friends); }
注意这种情况不能用with('mutual_friends')预加载,要改成预加载两个关联,之后再合并:
public static function get_users_infomation_by_ids($login_id, $users_arr = array()) { $users = User::whereIn('id', $users_arr) ->with('following_friends', 'follower_friends') ->get(array('id', 'username', 'full_name', 'is_live', 'message_privacy', 'picture')); // 给每个用户手动添加合并后的属性 $users->each(function($user) { $user->mutual_friends = $user->following_friends->merge($user->follower_friends); }); return $users->isNotEmpty() ? $users->toArray() : array(); }
最后提醒
一定要明确mutual_friends的实际业务含义:是要合并Friend表的记录,还是要获取互相关注的用户模型?选对实现方式才能避免异常。
内容的提问来源于stack exchange,提问作者Rizwan Saleem

