У меня есть две таблицы, например «users» и «users_actions», где «users_actions» имеет отношение hasMany с пользователями:
пользователи
id | name | surname | email...
действия
id | id_action | id_user | log | created_at
Модель Users.php
class Users { public function action() { return $this->hasMany('Action', 'user_id')->orderBy('created_at', 'desc'); } }
Теперь я хочу получить список всех пользователей с их действием LAST .
Я видел, что выполнение Users::with('action')->get();
может легко дать мне последнее действие, просто выбрав только первый результат отношения:
foreach ($users as $user) { echo $user->action[0]->description; }
но я хотел избежать этого, конечно, и просто выбрать ТОЛЬКО ПОСЛЕДНИЕ действия для каждого пользователя.
Я попытался использовать ограничение, например
Users::with(['action' => function ($query) { $query->orderBy('created_at', 'desc') ->limit(1); }]) ->get();
но это дает мне неправильный результат, так как Laravel выполняет этот запрос:
SELECT * FROM users_actions WHERE user_id IN (1,2,3,4,5) ORDER BY created_at LIMIT 1
что, конечно, неправильно. Есть ли возможность получить это без выполнения запроса для каждой записи с использованием Eloquent? Я делаю какую-то очевидную ошибку, которую я не вижу? Я совершенно новичок в использовании Eloquent, и иногда отношения меня беспокоят.
Редактировать:
Часть из репрезентативной цели, мне также нужна эта функция для поиска внутри отношения , например, например, я хочу искать пользователей, где LAST ACTION = 'something'
Я попытался использовать
$actions->whereHas('action', function($query) { $query->where('id_action', 1); });
но это дает мне ВСЕ пользователи, у которых было действие = 1, и поскольку это журнал, каждый прошел этот шаг.
Благодаря @berkayk, похоже, я решил первую часть моей проблемы, но все же я не могу искать в этом отношении.
Actions::whereHas('latestAction', function($query) { $query->where('id_action', 1); });
все еще не выполняет правильный запрос, он генерирует что-то вроде:
select * from `users` where (select count(*) from `users_action` where `users_action`.`user_id` = `users`.`id` and `id_action` in ('1') ) >= 1 order by `created_at` desc
Мне нужно получить запись, где последнее действие – 1
Мое решение, связанное с @berbayk, классно, если вы хотите легко получить последнюю hasMany
связанную модель.
Тем не менее, он не мог решить другую часть того, о чем вы просите, поскольку запрос этого отношения с предложением where
приведет к тому, что вы уже испытали, – все строки будут возвращены, только latest
не будут последними на самом деле (но последнее совпадение с тем, где ограничение).
Итак, вот вы идете:
простой способ – получить все и фильтровать коллекцию :
User::has('actions')->with('latestAction')->get()->filter(function ($user) { return $user->latestAction->id_action == 1; });
или сложный путь – сделайте это в sql (предполагая MySQL):
User::whereHas('actions', function ($q) { // where id = (..subquery..) $q->where('id', function ($q) { $q->from('actions as sub') ->selectRaw('max(id)') ->whereRaw('actions.user_id = sub.user_id'); })->where('id_action', 1); })->with('latestAction')->get();
Выбирайте одно из этих решений, сравнивая производительность – первый возвращает все строки и фильтрует, возможно, большую коллекцию .
Последний будет запускать подзапрос ( whereHas
) с вложенным подзапросом ( where('id', function () {..}
) , поэтому оба пути могут быть потенциально медленными на большой таблице.
Я думаю, что решение, о котором вы просите, объясняется здесь http://softonsofa.com/tweaking-eloquent-relations-how-to-get-latest-related-model/
Определите это отношение в модели пользователя,
public function latestAction() { return $this->hasOne('Action')->latest(); }
И получить результаты с помощью
User::with('latestAction')->get();
Давайте немного изменим код @ berkayk.
Определите это отношение в модели Users
,
public function latestAction() { return $this->hasOne('Action')->latest(); }
А также
Users::with(['latestAction' => function ($query) { $query->where('id_action', 1); }])->get();
Laravel Использует функцию take () not Limit
Попробуйте приведенный ниже код. Надеюсь, он отлично работает для u
Users::with(['action' => function ($query) { $query->orderBy('created_at', 'desc')->take(1); }])->get();
или просто добавьте метод принятия к вашим отношениям, как показано ниже.
return $this->hasMany('Action', 'user_id')->orderBy('created_at', 'desc')->take(1);