Laravel 5 рвется с лимитом

У меня есть две таблицы, скажем «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');
}
}

Теперь я хочу получить список из всех пользователи с их последние действия.

Я видел, что делает 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 = ‘что-то’

Я пытался с помощью

$actions->whereHas('action', function($query) {
$query->where('id_action', 1);
});

но это дает мне ВСЕХ пользователей, у которых было действие = 1, и, поскольку это журнал, каждый прошел этот шаг.

Изменить 2:

Благодаря @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

5

Решение

Мое решение, связанное с @berbayk — это круто, если вы хотите легко получить последнюю версию hasMany родственная модель.

Тем не менее, он не может решить другую часть того, что вы просите, так как запрос этого отношения с where предложение приведет к тому же самому, что вы уже испытали — все строки будут возвращены, только latest на самом деле не будет последним (но последним, соответствующим ограничению where).

Итак, поехали:

легкий путьполучить все и отфильтровать коллекцию:

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 () {..}), поэтому оба пути могут быть потенциально медленными на большом столе.

6

Другие решения

Я думаю, что решение, которое вы запрашиваете, объясняется здесь http://softonsofa.com/tweaking-eloquent-relations-how-to-get-latest-related-model/

Определите это отношение в пользовательской модели,

public function latestAction()
{
return $this->hasOne('Action')->latest();
}

И получить результаты с

User::with('latestAction')->get();
10

Я создал пакет для этого: https://github.com/staudenmeir/eloquent-eager-limit

Использовать HasEagerLimit черта в родительской и связанной модели.

class User extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}

class Action extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}

Тогда вы можете подать заявку ->limit(1) к вашим отношениям, и вы получите последнее действие для каждого пользователя.

2

Давайте немного изменим код @ berkayk.

Определите это отношение в Users модель,

public function latestAction()
{
return $this->hasOne('Action')->latest();
}

А также

Users::with(['latestAction' => function ($query) {
$query->where('id_action', 1);
}])->get();
0

Laravel использует функцию take (), а не Limit

Попробуйте приведенный ниже код, я надеюсь, что он работает нормально для вас

Users::with(['action' => function ($query) {
$query->orderBy('created_at', 'desc')->take(1);
}])->get();

или просто добавьте метод take в ваши отношения, как показано ниже

 return $this->hasMany('Action', 'user_id')->orderBy('created_at', 'desc')->take(1);
0

Чтобы загрузить последние связанные данные для каждого пользователя, вы можете получить их, используя метод самостоятельного соединения в таблице действий, что-то вроде

select u.*, a.*
from users u
join actions a on u.id = a.user_id
left join actions a1 on a.user_id = a1.user_id
and a.created_at < a1.created_at
where a1.user_id is null
a.id_action = 1 // id_action filter on related latest record

Чтобы сделать это с помощью конструктора запросов, вы можете написать это как

DB::table('users as u')
->select('u.*', 'a.*')
->join('actions as a', 'u.id', '=', 'a.user_id')
->leftJoin('actions as a1', function ($join) {
$join->on('a.user_id', '=', 'a1.user_id')
->whereRaw(DB::raw('a.created_at < a1.created_at'));
})
->whereNull('a1.user_id')
->where('aid_action', 1) // id_action filter on related latest record
->get();

Чтобы стремиться к последнему отношению для пользователя, вы можете определить его как hasOne отношение к вашей модели, как

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class User extends Model
{
public function latest_action()
{
return $this->hasOne(\App\Models\Action::class, 'user_id')
->leftJoin('actions as a1', function ($join) {
$join->on('actions.user_id', '=', 'a1.user_id')
->whereRaw(DB::raw('actions.created_at < a1.created_at'));
})->whereNull('a1.user_id')
->select('actions.*');
}
}

Нет необходимости в зависимом подзапросе, просто примените обычный фильтр внутри whereHas

User::with('latest_action')
->whereHas('latest_action', function ($query) {
$query->where('id_action', 1);
})
->get();
0
По вопросам рекламы [email protected]