В Laravel Eloquent как ссылаться на первичный запрос в подзапросе - PullRequest
0 голосов
/ 24 декабря 2018

У меня есть модель Пользователь, у которого много заказов.У заказов есть много продуктов, с сводной таблицей order-product.Я не хочу предварительно загружать и повторять заказы, если это вообще возможно.

Мне нужно вернуть пользователей, у которых

  1. signature_date === true для пользователя
  2. order_date для заказа после подписанной даты для пользователя
  3. order-product показывает, что продукт не был оплачен

Мне не удалось набрать номер 2. В следующем коде первый запрос вгде не так.Я не знаю, как ссылаться на дату подписания пользователя изнутри где.Если бы я перебирал пользователей в коллекции, я мог бы сделать что-то вроде ($ query), используя $ user, но как мне сделать это без предварительной загрузки всех пользователей?

return User::whereNotNull('signed_date')
           ->whereHas('orders', function ($query) {
               $query->where('order_date', '<=', 'user.signed_date');
               $query->whereHas('products', function ($q) {
                   $q->where('paid', false);
               });
           })
           ->get(['id','fname','lname', 'title', 'signed_date']);

Я хотел бы использовать eloquent, есливозможный.Если это невозможно, я буду рад советам по решению этой проблемы с помощью построителя запросов / sql.

1 Ответ

0 голосов
/ 26 декабря 2018

В построителе запросов Eloquent есть специальная функция whereColumn('a', '<=', 'b'), позволяющая сравнивать столбцы вместо столбца со значением.Использование этой функции вместо обычной where() необходимо из-за способа, которым построитель запросов создает фактический запрос.Вам нужно сообщить разработчику запросов, что вы будете передавать имя столбца вместо значения для правильного экранирования и форматирования строки запроса.

В любом случае, вы также можете передать имена столбцов с префиксомимя таблицы для функции, позволяющей сравнивать столбцы между таблицами:

$query->whereColumn('orders.order_date', '<=', 'users.signed_date')

Это работает, потому что вы используете whereHas() в своем запросе.Ваш запрос в основном переводится на:

SELECT id, fname, lname, title, signed_date
FROM users
WHERE signed_date NOT NULL
  AND EXISTS (
    SELECT 1
    FROM orders
    WHERE orders.order_date <= users.signed_date
      AND EXISTS (
        SELECT 1
        FROM products
        WHERE paid = 0
      )
  )

На самом деле может не быть необходимости использовать имя таблицы вместе с именем столбца в whereColumn().Но в случае, если вы когда-нибудь добавите столбец с таким же именем в другую таблицу, запрос может прерваться - поэтому, IMHO, рекомендуется использовать имя таблицы в пользовательских запросах.


Кстати,причина, по которой это не будет работать вместе с with('relationship'), заключается в том, что эта функция приводит к дополнительному запросу, и вы, очевидно, не можете сравнивать столбцы между запросами.Представьте себе следующее:

Order::with('user')->take(5)->get();

Будет переведено следующее:

SELECT *
FROM orders
LIMIT 5

SELECT *
FROM users
WHERE id IN (?, ?, ?, ?, ?)

, где пять ? будут user_id с ордерами.Если первый запрос возвращает несколько строк с одинаковым user_id, количество строк, извлекаемых из таблицы пользователей, конечно, уменьшается.

Примечание. Все запросы являются только примерами.Возможно, построитель запросов создает разные запросы в зависимости от типа базы данных и / или по-разному экранирует их (например, имена столбцов в обратных галочках).

...