Laravel, Как использовать условия для столбца отношения?
Я использую Laravel и имею небольшую проблему с Eloquent ORM.. Я могу заставить это работать просто с SQL-запросом с помощью JOIN, но я не могу заставить его работать с Eloquent!
Это то, что я хочу, у меня есть две таблицы. один - "Рестораны", а другой - "Restaurant_Facilities".
Таблицы являются простыми.. и отношениями "один-к-одному". например, есть таблица restaurant
с id
, name
, slug
и т.д. и другая таблица под названием restaurant_facilities
с id
, restaurant_id
, wifi
, parking
и т.д.
Теперь то, что я хочу сделать, - это загрузить все рестораны с wifi = 1 или wifi = 0..
Как я могу сделать это с помощью Eloquent? Я старался загружать, сводные таблицы с помощью(), collections(), и ничего не работает!
Та же проблема, что и для отношения "Много-ко-многим" для cuisines
!
У меня та же таблица restaurant
и таблица cuisine
и таблица restaurant_cuisine_connection
.
но как я могу загрузить все рестораны в определенной кухне с помощью ID?
Это работает.
Cuisine::find(6)->restaurants()->get();
но я хочу загрузить это из ресторана:: модель не из кухонь.. потому что у меня много условий, связанных друг с другом.. ее для поиска и фильтрации/просмотра страницы.
Любые идеи или способы? Я боролся с этим в течение 3 дней и до сих пор не отвечаю.
Примеры моделей:
class Restaurant extends Eloquent {
protected $table = 'restaurants';
public function facilities() {
return $this->hasOne('Facilities');
}
}
class Facilities extends Eloquent {
protected $table = 'restaurants_facilities';
public function restaurant() {
return $this->belongsTo('Restaurant');
}
}
PS:
Кажется, это работает... но это не правильно? Правильно?
Restaurant::leftJoin(
'cuisine_restaurant',
'cuisine_restaurant.restaurant_id',
'=', 'restaurants.id'
)
->where('cuisine_id', 16)
->get();
И какой лучший способ найти количество ресторанов, которые имеют конкретное значение столбца без другого запроса? как.. я должен найти общее количество ресторанов, у которых есть парковка = 1 и wifi = 1?
Пожалуйста, помогите нам в этом.
Спасибо.
Ответы
Ответ 1
Я не вижу ничего плохого в том, чтобы делать левое соединение здесь, если вам нужно загрузить из модели ресторана. Я мог бы отвлечь его до метода на моей модели ресторана, например:
class Restaurant extends Eloquent {
protected $table = 'restaurants'; // will be default in latest L4 beta
public function facility()
{
return $this->hasOne('Facility');
}
// Or, better, make public, and inject instance to controller.
public static function withWifi()
{
return static::leftJoin(
'restaurant_facilities',
'restaurants.id', '=', 'restaurant_facilities.restaurant_id'
)->where('wifi', '=', 1);
}
}
И затем, с ваших маршрутов:
Route::get('/', function()
{
return Restaurant::withWifi()->get();
});
В пути - не проверял этот код, но я думаю, он должен работать. Вместо этого вы можете использовать активную загрузку с ограничением, но это будет указывать только, является ли объект объекта нулевым или нет. Он все равно вернет все рестораны, если вы не укажете предложение where.
(P.S. Я придерживаюсь единственной формы Facility. Обратите внимание, что hasOne('Facilities')
не читается правильно?)
Ответ 2
Я наткнулся на это сообщение, пытаясь улучшить методологию REST API при создании новой парадигмы обмена. Вы хотите использовать Eager Loading Constraints. Скажем, у вас есть маршрут api, где вы загружаете общий элемент и его коллекцию подэлементов, например:
/api/shared/{share_id}/subitem/{subitem_id}
При нажатии этого маршрута с запросом GET вы хотите загрузить этот конкретный подпункт. Конечно, вы можете просто загрузить эту модель с помощью этого идентификатора, но что, если нам нужно проверить, имеет ли пользователь доступ к этому общему элементу в первую очередь? Один ответ рекомендовал загрузить обратные отношения, но это может привести к запутанному и запутанному контроллеру очень быстро. Использование ограничений для нетерпеливой нагрузки - более "красноречивый" подход. Поэтому мы будем загружать его следующим образом:
$shared = Shared::where('id', $share_id)
->with([ 'subitems' => function($query) use ($subitem_id) {
$query->where('subitem_id', $subitem_id)
}]);
Итак, где нужен только тот подтип, который имеет этот идентификатор. Теперь мы можем проверить, было ли это обнаружено или нет, выполнив что-то вроде этого:
if ($shared->subitems->isEmpty())
Так как подматрицы представляют собой набор (массив подэлементов), мы возвращаем подпункт [0] следующим образом:
return $shared->subitems[0];
Ответ 3
Используйте whereHas
для фильтрации по любым отношениям. Он не присоединится к отношению, но он будет фильтровать текущую модель связанным свойством. Также просмотрите локальные области, чтобы помочь с такими ситуациями https://laravel.com/docs/5.3/eloquent#local-scopes
Ваш пример:
Restaurant::whereHas('facilities', function($query) {
return $query->where('wifi', true);
})->get();
Restaurant::whereHas('cuisines', function($query) use ($cuisineId) {
return $query->where('id', $cuisineId);
})->get();
Чтобы добиться того же результата с локальными областями:
class Restaurant extends Eloquent
{
// Relations here
public function scopeHasWifi($query)
{
return $query->whereHas('facilities', function($query) {
return $query->where('wifi', true);
});
}
public function scopeHasCuisine($query, $cuisineId)
{
return $query->whereHas('cuisines', function($query) use ($cuisineId) {
return $query->where('id', $cuisineId);
});
}
}
Для локальных областей вы НЕ хотите определять их как статические методы в своей модели, так как это создает новый экземпляр построителя запросов и мешает вам связывать методы. Использование локальной области будет вводить и возвращает текущий экземпляр построителя запросов, чтобы вы могли связывать столько областей, сколько хотите:
Restaurant::hasWifi()->hasCuisine(6)->get();
Локальные области определяются с префиксом scope
в имени метода и вызываются без scope
в имени метода, как в примере abover.
Ответ 4
Другое решение в главной роли whereHas()
:
$with_wifi = function ($query) {
$query->where('wifi', 1);
};
Facilities::whereHas('restaurant', $with_wifi)
Приятный и аккуратный.
Ответ 5
Вам абсолютно необходимо загрузить его из модели ресторана? Чтобы решить проблему, я обычно подхожу к ней обратно.
Facilities::with('restaurant')->where('wifi' ,'=', 0)->get();
Это позволит получить все рестораны, которые соответствуют вашим условиям, и с нетерпением загружать ресторан.
Вы можете связать больше условий и подсчитать общее количество, как это.
Facilities::with('restaurant')
->where('wifi' ,'=', 1)
->where('parking','=', 1)
->count();
Это также будет работать с кухней
Cuisine::with('restaurant')->where('id','=',1)->get();
Это захватывает объект кухни с идентификатором 1, загруженным всеми ресторанами, у которых есть эта кухня.