Как запросить сводную таблицу с несколькими внешними ключами Laravel 5 Eloquent

У меня есть следующая структура таблицы:

| products | product_options | product_option_values | product_option_to_product | +----------+-----------------+-----------------------+---------------------------+ | id | id | id | id | | | | | product_id | | | | | product_option_id | | | | | product_option_value_id | 

Мои отношения выглядят так:

 // Product.php public function options() { return $this->belongsToMany('App\ProductOption', 'product_option_to_product', 'product_id', 'product_option_id')->withPivot('product_option_value_id', 'product_id'); } // ProductOption.php public function values() { return $this->belongsToMany('App\ProductOptionValue', 'product_option_to_product', 'product_option_id', 'product_option_value_id')->withPivot('product_id'); } 

Теперь, когда я пробую это:

 $products = Product::with('options')->with('options.values')->first(); 

Или

 $products = Product::with('options.values')->first(); 

Я получаю все возможные варианты, потому что он не использует идентификатор продукта для загрузки значений параметров.

Кто-нибудь знает, как я могу убедиться, что он загружает только значения, принадлежащие этому продукту?

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

Редактировать:

Я попробовал это с объединением следующим образом:

 $products = Product::with(['options.values' => function($q) { $q->join('products', 'products.id', '=', 'product_option_to_product.product_id'); }])->first(); 

Но он дает те же результаты.

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

 @if($value->pivot->product_id == $product->id) <div class="checkbox"> <label> <input type="checkbox" name="{{ $option->id }}" value="{{ $value->id }}"> {{ $value->language->name }} </label> </div> @endif 

Однако это мне не нравится.

Ваша проблема заключается в том, что записи из таблицы product_options не знают продукт, который вы извлекаете.

Основываясь на структуре базы данных, о которой вы говорили в вопросе, я написал следующие строки:

 $product = App\Product::with('options')->with('options.values')->first(); foreach ($product->options as $option) { foreach ($option->values as $value) { printf( "Product: %s, Option: %d, Value: %d\n", $product->id, $option->id, $value->id ); } } 

В приведенном выше коде просто печатаются возможные комбинации опций и значений для определенного продукта. Я использую те же отношения, которые вы создали в своем приложении.

Когда я загружаю этот код, Eloquent (ORM Laravel) запускает следующие запросы в базе данных:

 -- Retrieves the first product select * from `products` limit 1; -- Retrieves all options using the product_id as condition select `product_options`.*, `product_option_to_product`.`product_id` as `pivot_product_id`, `product_option_to_product`.`product_option_id` as `pivot_product_option_id`, `product_option_to_product`.`product_option_value_id` as `pivot_product_option_value_id` from `product_options` inner join `product_option_to_product` on `product_options`.`id` = `product_option_to_product`.`product_option_id` where `product_option_to_product`.`product_id` in ('1') -- Retrieves all values using the option_id as condition select `product_option_values`.*, `product_option_to_product`.`product_option_id` as `pivot_product_option_id`, `product_option_to_product`.`product_option_value_id` as `pivot_product_option_value_id`, `product_option_to_product`.`product_id` as `pivot_product_id` from `product_option_values` inner join `product_option_to_product` on `product_option_values`.`id` = `product_option_to_product`.`product_option_value_id` where `product_option_to_product`.`product_option_id` in ('1', '2') 

Как вы можете видеть, последний запрос не использует product_id как условие для получения значений, потому что в таблице product_options не содержится ссылка на таблицу products .

Наконец, я достиг того же результата, что и вы. Все значения, связанные с опцией, несмотря на то, что я ищу.

Самый простой способ исправить это – добавить ограничение запроса при вызове вашего отношения. Как это:

 $product = App\Product::with('options')->with('options.values')->first(); foreach ($product->options as $option) { foreach ($option->values()->where('product_id', $product->id)->get() as $value) { printf("PRO: %s, OPT: %d, VAL: %d<br>\n", $product->id, $option->id, $value->id); } } 

Обратите внимание, что вместо вызова $option->values в качестве свойства я использовал метод $option->values() в качестве метода. При вызове метода отношений вы можете использовать его так же, как вы используете построитель запросов.

Если вы не хотите использовать это обходное решение, вам, вероятно, потребуется изменить структуру базы данных и классы моделей.

редактировать

Альтернативным решением может быть использование построителя запросов для получения всех значений для определенного продукта.

Вы можете сделать это со следующими строками в вашем контроллере:

 $values = ProductOptionValue::select([ 'product_option_values.*', 'product_option_to_product.option_id', ]) ->join('product_option_to_product', 'product_option_to_product.product_option_value_id', '=', 'product_option_values.id') ->where('product_id', $product->id) ->get(); 

Таким образом вы получаете все значения для определенного продукта (заданного его $product->id ) с его соответствующим параметром option_id .

Чтобы использовать результат из запроса выше в вашем шаблоне, вы должны сделать только небольшое изменение в имени флажка:

 <div class="checkbox"> <label> <input type="checkbox" name="{{ $value->option_id }}" value="{{ $value->id }}"> {{ $value->language->name }} </label> </div>