将两个数组与模型合并

将两个数组与模型合并

问题描述:

So I have a simple table:

messages:
 - id
 - user_id - user that "sent" the message (the author)
 - to_user_id - user that "received" the message
 - body
 - created_at
 - updated_at

I'm using Laravel 5.2, I have a model that looks like this:

class Message extends Model
{
    public function user()
    {
        return $this->belongsTo('App\Models\User'); //This should return only the sender
    }

    public function scopeSentByUser($query, $user_id)
    {
        return $query->where('user_id', '=', $user_id);
    }

    public function scopeReceivedByUser($query, $user_id)
    {
        return $query->orWhere('to_user_id', '=', $user_id);
    }

    public function scopeNewestFirst($query)
    {
        return $query->orderBy('created_at', 'DESC');
    }
}

In my controller I have two variables that call few model methods:

$this->sent = Message::sentByUser(Auth::user()->id)->newestFirst()->get();
$this->received = Message::receivedByUser(Auth::user()->id)->newestFirst()->get();

I was wondering how can I merge them into threads. Basically I don't have anything like threads in my app. Users can write messages between and it's a single thread. Like Zuckerberg's social network, or Google's chat client.

I want to be able to keep them in order (or I can order them after grouping) and have something like:

$threads = [
    [
        'participants' => [1, 2], //those are users' ids
        'messages' => [
            (Model)Message,
            (Model)Message,
            (Model)Message,
            (Model)Message,
            (Model)Message
        ]
    ] 
];

Edit:

Based on the accepted answer I ended up having the following code:

$query = self::where('user_id', $logged_user_id)->orWhere('to_user_id', $logged_user_id)->get();

$inUserId = $query->lists('user_id')->toArray();
$inToUserId = $query->lists('to_user_id')->toArray();

$mergedIds = array_merge($inUserId, $inToUserId);
$uniqueIds = array_unique($mergedIds);

unset($uniqueIds[array_search($logged_user_id, $uniqueIds)]); //Remove logged in user ID

$combinations = [];
foreach ($uniqueIds as $id) {
    $combinations[] = [$id, $logged_user_id];
}
$threads = [];
foreach ($combinations as $key => $combo) {
    $threads[] = [
        'receiver' => $combo[0] == $logged_user_id ? User::find($combo[1]) : User::find($combo[0]),
        'messages' => self::where(function ($query) use ($combo) {
            $query->where('user_id', $combo[0])->where('to_user_id', $combo[1]);
        })->orWhere(function ($query) use ($combo) {
            $query->where('user_id', $combo[1])->where('to_user_id', $combo[0]);
        })->orderBy('created_at', 'ASC')->get()
    ];
}

return $threads;

The differences are: pluck() is replaced with lists()->toArray()

After merging the arrays (array_merge) and picking the unique values only (array_unique) I unset the logged in user's id. Because I don't need it.

Also, removed that method for getting the distinct pairs, since it's not applicable. I believe it should be working now properly.

Will update after some more complex testing is done.

Posted this as update, since I cannot edit answers. And my corrections are too few to post my own answer.

Something like this should work. I've written it like a model method, but you can also add it to a controller by changing $this to the correct model name.

public function getThreads($id) {
    $query = $this->where('user_id', $id)->orWhere('to_user_id', $id)->get();
    // get the unique user id's
    $user_ids = array_unique (array_merge ($query->pluck('user_id'), $query->pluck('to_user_id')));

    // get the distinct pairs
    // taken from here: http://*.com/a/3770452/485418
    $num_ids = count($user_ids);
    for ($i = 0; $i < $num_ids; $i++)
    {
        for ($j = $i+1; $j < $num_ids; $j++)
        {
            $combinations[] = array($user_ids[$i], $user_ids[$j]);
        }
    }

    $threads = array();
    $tmp = array();
    // build the threads array
    foreach ($combinations as $key => $combo) {
        $tmp = array();
        $tmp['participants'] = $combo;
        $tmp['messages'] = $this->where(function($query) {
                                        $query->where('user_id', $combo[0])->where('to_user_id', $combo[1]);
                                    })->orWhere(function($query) {
                                        $query->where('user_id', $combo[1])->where('to_user_id', $combo[0]);
                                    })->orderBy('created_at', 'DESC')->get();
        $threads[] = $tmp;
    }

    return $threads
}

Maybe something like that:

$messages = array();
foreach ($this->sent as $message) {
    $messages[] = $message;
}
foreach ($this->received as $message) {
    $messages[] = $message;
}
$messages = collect($messages);
$participants = $messages->keyBy('user_id')->keys();

If you have collection $messages you can use all this methods: https://laravel.com/docs/master/collections