简体   繁体   中英

Laravel / Eloquent - hasManyThrough - belongsToMany

I try to create a messenger where users are able to join threads and therefore I have four tables:

users
    id - integer
    name - string

threads
    id - integer
    name - string
    created_at - dateTime
    updated_at - dateTime

participants
    user_id - integer
    threads_id - integer
    created_at - dateTime
    updated_at - dateTime
    deleted_at - dateTime

messages
    id - integer
    user_id - integer
    thread_id - integer
    text - string
    created_at - dateTime
    updated_at - dateTime
    deleted_at - dateTime

The User has:

threads(){
    return $this->belongsToMany(
        Thread::class,
        'participants',
        'user_id',
        'thread_id'
    )
    ->withTimestamps()
    ->withPivot('thread_id', 'user_id', 'deleted_at')
    ->orderBy('created_at', 'desc');
}

Now I try to get a relationship to the messages, only the messages which are after joining the thread should be visible:

public function messagesThroughThreads()
{
    $relation = $this->hasManyThrough(
        Message::class,
        $this->threads(),
        'user_id',
        'thread_id',
        'id',
        'id'
    )
        ->orderBy('updated_at', 'desc');

    return $relation;
}

actually, this is not working like that and I get the Models via:

public function messagesThroughThreads(bool $show_deleted = false)
{
    $messages = collect([]);

    foreach ($this->threads as $thread){
        if($thread->pivot->deleted_at){
            $m = $thread->messages_model()->whereBetween('messages.created_at', [$thread->pivot->deleted_at, $thread->pivot->created_at])->get();
        }else{
            $m = $thread->messages_model()->where('messages.created_at', '>=', $thread->pivot->created_at)->get();
        }
        $messages = $messages->merge($m->toArray());
    }

    return $messages->unique();
}

Any Idea how I could get a relationship instance with the same conditions?

There's no (native) relationship to access the messages directly.

Depending on your data it might be faster to get all messages and then remove some of them:

class Thread
{
    public function messages()
    {
        return $this->hasMany(Message::class);
    }
}

public function messagesThroughThreads(bool $show_deleted = false)
{
    $this->load('threads.messages');

    foreach ($this->threads as $thread) {
          $m = $thread->messages->where('created_at', '<=', $thread->pivot->deleted_at)
             ->where('created_at', '>=', $thread->pivot->created_at);
    }

You would be working on collections, so methods like whereBetween() aren't available.

I created a HasManyThrough relationship with unlimited levels: Repository on GitHub

After the installation, you can use it like this:

class User extends Model {
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function messagesThroughThreads() {
        return $this->hasManyDeep(Message::class, ['participants', Thread::class])
            ->whereColumn('messages.created_at', '>=', 'participants.created_at')
            ->where(function($query) {
                $query->whereNull('participants.deleted_at')
                    ->orWhereColumn('messages.created_at', '<=', 'participants.deleted_at');
            })->orderByDesc('messages.updated_at');
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM