#laravel #eloquent
#laravel #красноречивый
Вопрос:
У меня есть две таблицы с отношением один ко многим.
Первая таблица status
:
| id | product_id | status | created_at |
|----|------------|--------|---------------------|
| 1 | 1 | 0 | 2018-01-01 00:00:00 |
| 2 | 1 | 1 | 2018-02-01 00:00:00 |
| 3 | 2 | 1 | 2018-01-01 00:00:00 |
| 4 | 2 | 5 | 2018-02-01 00:00:00 |
Отношение
public function product()
{
return $this->belongsTo('AppProduct');
}
Второе product
:
| id | name |
|----|-----------|
| 1 | product_1 |
| 2 | product_2 |
Отношение:
public function status()
{
return $this->hasMany('AppProductStatus');
}
Я пытаюсь получить все product
s, где они последние status
1
, но я получаю также product
s, даже если оно последнее status
!= 1
, но в истории оно было status 1
.
Комментарии:
1. Какой запрос / красноречивый запрос вы используете? Что вы пробовали до сих пор? Является
status
ли таблица «статусом истории», потому что, если ваш ответ отрицательный, нет смысла иметь 2 или более статусов для одного и того жеproduct
. Пожалуйста, дайте больше информации.2. да, таблица statue — это таблица состояния истории
Ответ №1:
В неидеальном мире вы можете фильтровать после факта:
Добавьте отношение:
public function latestStatus()
{
return $this->hasOne('AppProductStatus')->latest();
}
Это предполагает, что существует отношение один к одному, и поэтому будет получен только один статус
Затем вы можете сделать:
Product::has('latestStatus')->with('latestStatus')->get()->filter(function (Product $product) {
return $product->latestStatus->status === 1;
});
И я говорю, что это не идеально, потому что сначала будут получены все продукты, а затем отфильтрованы по последнему статусу.
Проблема в том, что вам нужны вложенные запросы, чтобы получить то, что вам нужно в SQL. В руководстве описано решение этой проблемы с использованием объединений, которое выглядит как:
$latestStatus = ProductStatus::latest()->take(1);
$productsWithLatestStatusOne = Product::joinSub($latestStatus, 'latest_status', function ($join) {
$join->on('products.id', '=', 'latest_status.product_id');
})
->select('products.*')
->where('latest_status.status', 1)
->get();
но, конечно, при этом используется соединение с подзапросом, которое может быть медленнее.
Комментарии:
1. Это работает нормально, но, как вы говорите, это не идеальное решение для получения продуктов и их фильтрации!! есть идеи, как использовать обратное отношение и начать работу с lastStatus, чтобы получить все продукты?
2. Насколько мне известно, соединение подзапроса с
$latestStatus
запросом немного неизбежно. Проблема не красноречива, просто, насколько я знаю, именно так вы обычно решаете эту проблему в SQL. Однако я никоим образом не эксперт по SQL, поэтому, возможно, кто-то, кто лучше знает SQL-часть, может меня поправить
Ответ №2:
Попробуйте метод whereHas()
Product::wherehas('status', function(IlluminateDatabaseEloquentBuilder $query) {
$query->where('status', 1);
})->get();
Комментарии:
1. вы получите продукты, даже если его последний статус равен != 1, но в истории у него был статус 1