Defining many-to-many bidirectional relations more eloquently

天涯浪子 提交于 2019-12-24 08:59:12

问题


I have implemented the relationship without Eloquent but I was wondering is there was a way to define this relationship in Eloquent so that my application can have more consistency.

table User
    -id
    -other user attributes

table friend_requests:
    -id
    -sender_id
    -reciever_id

table friends
    -id
    -first
    -second

The friendRequest relation has been easily implemented in the Eloquent but the problem lies in Friends.

If I do this in the User model class:

public function friends(){
    return $this->belongsToMany(User::class,'friends','first','second');
}

This wouldn't work as you would have noticed. Let me explain with example:

Table: friends 
 id    |   first    | second  
 1     |   1        | 2  
 2     |   3        | 1  

you see that user_1 is friends with user_2 as well as user_3 as the relationship is bi-directional. But Eloquent will naturally return that user_1 is friends with user_2 only. After thinking for a while I tweaked the statement but made little progress'

public function friends(){
    return $this->belongsToMany(User::class,'friends','first','second')
                ->orWhere('second',$this->id);
}

That is because now it selects both rows but the Users it returns are those whose id = second which means that in the second case it will return the user itself.

I implemented the relations with my own methods in User model class which use DB::table('friends')-> to addFriend(User $user), removeFriend(user $user) and returns list of friends(), but I'm disappointed that this isn't as eloquent as Eloquent relationships.

Perhaps some more experienced developers here would have come across this kind of problem and would have dealt better than I did. How do you propose I deal with this problem. Should I stick with my approach or is there a better way to deal with it?


回答1:


A more manageable way to implement bidirectional relations would be to create two entries for each confirmed friendship.

So a user would make a friend request to another user. When the second user confirms the friend request, two friendships are created.

Example Controller

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\User;
use App\FriendRequest;
use App\Friendship;

class FriendshipController extends Controller
{
    public function friendRequest(Request $request)
    {
        $receiver_id = $request->input('receiver_id');

        $request->user()->friend_offers()->create([
            'receiver_id' => $receiver_id
        ]);
    }

    public function friendshipConfirmation(Request $request)
    {
        $friend_request_id = $request->input('friend_request_id');

        $friend_request = FriendRequest::find($friend_request_id);

        $friend_request->receiver->friendships()->create([
            'user_2_id' => $friend_request->sender->id
        ]);

        $friend_request->sender->friendships()->create([
            'user_2_id' => $friend_request->receiver->id
        ]);
    }
}

Database Tables

(Note the proper spelling of receiver and plural users table)

table users
    - id
    - other user attributes

table friend_requests:
    - id
    - sender_id
    - receiver_id

table friendships
    - id
    - user_1_id
    - user_2_id

User Model

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Authenticatable
{
    use SoftDeletes;

    public $timestamps = true;

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = ['created_at', 'updated_at', 'deleted_at'];

    /**
     * The attributes that aren't mass assignable.
     *
     * @var array
     */
    protected $guarded = ['id'];

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        //
    ];

    /**
     * Return friend requests from other users
     * 
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function friend_requests()
    {
        return $this->hasMany(FriendRequest::class, 'receiver_id');
    }

    /**
     * Return friend requests sent to other users
     * 
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function friend_offers()
    {
        return $this->hasMany(FriendRequest::class, 'sender_id');
    }

    /**
     * Return friendships with other users
     * 
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function friendships()
    {
        return $this->hasMany(Friendship::class, 'user_1_id');
    }
}

FriendRequest Model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class FriendRequest extends Model
{
    use SoftDeletes;

    public $timestamps = true;

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = ['created_at', 'updated_at', 'deleted_at'];

    /**
     * The attributes that aren't mass assignable.
     *
     * @var array
     */
    protected $guarded = ['id'];

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'sender_id',
        'receiver_id'
    ];

    /**
     * Return the requesting user
     * 
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function sender()
    {
        return $this->belongsTo(User::class, 'sender_id');
    }

    /**
     * Return the receiving user
     * 
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function receiver()
    {
        return $this->belongsTo(User::class, 'receiver_id');
    }
}

Friendship Model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Friendship extends Model
{
    use SoftDeletes;

    public $timestamps = true;

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = ['created_at', 'updated_at', 'deleted_at'];

    /**
     * The attributes that aren't mass assignable.
     *
     * @var array
     */
    protected $guarded = ['id'];

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'user_1_id',
        'user_2_id'
    ];

    /**
     * Return user_1
     * 
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function first()
    {
        return $this->belongsTo(User::class, 'user_1_id');
    }

    /**
     * Return user_2
     * 
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function second()
    {
        return $this->belongsTo(User::class, 'user_2_id');
    }
}


来源:https://stackoverflow.com/questions/39186350/defining-many-to-many-bidirectional-relations-more-eloquently

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!