Связь через промежуточную таблицу

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Аватара пользователя
theorist
Сообщения: 70
Зарегистрирован: 2012.12.24, 00:30
Откуда: Киев

Связь через промежуточную таблицу

Сообщение theorist » 2016.01.28, 11:23

Приветствую, коллеги.

Имеется 3 таблицы:
- product (id, title),
- category (id, title),
- product_has_category (id, product_id, category_id)

Подразумевается, что товар может находиться сразу в нескольких категориях, что понятно из существования промежуточной таблицы.
Никак у меня не получается настроить связь у модели категории, которая бы возвращала список продуктов из промежуточной таблицы по id категории. Пробовал так (в модели категории):

Код: Выделить всё

    public function getProducts() {
        return $this->hasMany(Product::className(), ['id'=>'category_id'])
            ->viaTable('sk_product_has_category', ['category_id'=>'product_id']);
    }
Коючи в связях пробовал разные, я, видимо, не до конца понимаю, как работает viaTable.
Буду очень благодарен за помощь.

andrei.obuhovski
Сообщения: 610
Зарегистрирован: 2015.07.16, 10:50

Re: Связь через промежуточную таблицу

Сообщение andrei.obuhovski » 2016.01.28, 11:25

а так?

Код: Выделить всё

    public function getProducts() {
        return $this->hasMany(Product::className(), ['id'=>'product_id'])
            ->viaTable('sk_product_has_category', ['category_id'=>'id']);
    } 

Аватара пользователя
theorist
Сообщения: 70
Зарегистрирован: 2012.12.24, 00:30
Откуда: Киев

Re: Связь через промежуточную таблицу

Сообщение theorist » 2016.01.28, 11:27

SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in where clause is ambiguous

А запрос генерится такой: SELECT `sk_category`.* FROM `sk_category` LEFT JOIN `sk_product_has_category` ON `sk_category`.`id` = `sk_product_has_category`.`category_id` LEFT JOIN `sk_product` ON `sk_product_has_category`.`product_id` = `sk_product`.`id` WHERE `id`=1

Я попробовал в условии в конце явно задать, из какой таблицы проверочный id (указал sk_category.id вместо id) - результат - возвращает 7 раз категорию, в которой есть товары (кстати, у этой категории как раз и должно быть 7 товаров).

lynicidn
Сообщения: 2221
Зарегистрирован: 2014.05.24, 15:12

Re: Связь через промежуточную таблицу

Сообщение lynicidn » 2016.01.28, 11:37

алиасы нужны обязательно

Аватара пользователя
theorist
Сообщения: 70
Зарегистрирован: 2012.12.24, 00:30
Откуда: Киев

Re: Связь через промежуточную таблицу

Сообщение theorist » 2016.01.28, 11:40

Даже с ними (когда пробую прямой SQL-запрос выполнить) возвращается 7 категорий вместо 7 товаров этой категории.

lynicidn
Сообщения: 2221
Зарегистрирован: 2014.05.24, 15:12

Re: Связь через промежуточную таблицу

Сообщение lynicidn » 2016.01.28, 11:43

так ты выбираешь сам категории - в запрос свой смотри - селект категори

Аватара пользователя
theorist
Сообщения: 70
Зарегистрирован: 2012.12.24, 00:30
Откуда: Киев

Re: Связь через промежуточную таблицу

Сообщение theorist » 2016.01.28, 11:46

Ага, как тогда связь прописать, чтобы продукты доставались?
В этом и был вопрос.

lynicidn
Сообщения: 2221
Зарегистрирован: 2014.05.24, 15:12

Re: Связь через промежуточную таблицу

Сообщение lynicidn » 2016.01.28, 11:49

select `products`.* from `products` inner join `category` on `products`.`category_id` = `category`.`id`

Аватара пользователя
theorist
Сообщения: 70
Зарегистрирован: 2012.12.24, 00:30
Откуда: Киев

Re: Связь через промежуточную таблицу

Сообщение theorist » 2016.01.28, 11:53

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

lynicidn
Сообщения: 2221
Зарегистрирован: 2014.05.24, 15:12

Re: Связь через промежуточную таблицу

Сообщение lynicidn » 2016.01.28, 11:57

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

Аватара пользователя
theorist
Сообщения: 70
Зарегистрирован: 2012.12.24, 00:30
Откуда: Киев

Re: Связь через промежуточную таблицу

Сообщение theorist » 2016.01.28, 12:13

Запросом я может кое-как и достану, хотелось связь красиво прописать.
Но спасибо в любом случае.

andrei.obuhovski
Сообщения: 610
Зарегистрирован: 2015.07.16, 10:50

Re: Связь через промежуточную таблицу

Сообщение andrei.obuhovski » 2016.01.28, 12:15

theorist писал(а): Я попробовал в условии в конце явно задать, из какой таблицы проверочный id (указал sk_category.id вместо id) - результат - возвращает 7 раз категорию, в которой есть товары (кстати, у этой категории как раз и должно быть 7 товаров).
distinct ?

Аватара пользователя
magicoder
Сообщения: 133
Зарегистрирован: 2015.12.16, 23:33
Контактная информация:

Re: Связь через промежуточную таблицу

Сообщение magicoder » 2016.01.29, 00:47

Для работы со связными данными посредством Active Record вы прежде всего должны объявить связи в классе Active Record

При проектировании баз данных, когда между двумя таблицами имеется кратность связи many-to-many, обычно вводится промежуточная таблица. Например, таблицы order и item могут быть связаны посредством промежуточной таблицы с названием order_item. Один заказ будет соотносится с несколькими товарами, в то время как один товар будет также соотноситься с несколькими заказами.

При объявлении подобных связей вы можете пользоваться методом [[yii\db\ActiveQuery::via()|via()]] или методом [[yii\db\ActiveQuery::viaTable()|viaTable()]] для указания промежуточной таблицы. Разница между методами [[yii\db\ActiveQuery::via()|via()]] и [[yii\db\ActiveQuery::viaTable()|viaTable()]] заключается в том, что первый метод указывает промежуточную таблицу с помощью названия связи, в то время как второй метод непосредственно указывает промежуточную таблицу. Например:

Код: Выделить всё

class Order extends ActiveRecord
{
    public function getItems()
    {
        return $this->hasMany(Item::className(), ['id' => 'item_id'])
            ->viaTable('order_item', ['order_id' => 'id']);
    }
} 
или по-другому:

Код: Выделить всё

class Order extends ActiveRecord
{
    public function getOrderItems()
    {
        return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
    }

    public function getItems()
    {
        return $this->hasMany(Item::className(), ['id' => 'item_id'])
            ->via('orderItems');
    }
} 
Использовать связи, объявленные с помощью промежуточных таблиц, можно точно также, как и обычные связи. Например:

Код: Выделить всё

// SELECT * FROM `order` WHERE `id` = 100
$order = Order::findOne(100);

// SELECT * FROM `order_item` WHERE `order_id` = 100
// SELECT * FROM `item` WHERE `item_id` IN (...)
// возвращает массив объектов Item
$items = $order->items; 
Разработка на yii2 или чистом php.| email: site4coder@gmail.com | skype: for-web1

dymsonn
Сообщения: 60
Зарегистрирован: 2018.06.13, 15:37

Re: Связь через промежуточную таблицу

Сообщение dymsonn » 2018.08.29, 14:13

У меня сейчас похожая проблема. Как при подобном описании функции
magicoder писал(а):
2016.01.29, 00:47

Код: Выделить всё

class Order extends ActiveRecord
{
    public function getItems()
    {
        return $this->hasMany(Item::className(), ['id' => 'item_id'])
            ->viaTable('order_item', ['order_id' => 'id']);
    }
} 
использовать ее на форме добавления нового элемента?
Допустим на странице order/create.php форма

Код: Выделить всё

<?= $form->field($model->items, 'name')->textInput() ?>
не работает.

andku83
Сообщения: 988
Зарегистрирован: 2016.07.01, 10:24
Откуда: Харьков

Re: Связь через промежуточную таблицу

Сообщение andku83 » 2018.08.31, 12:49

Код: Выделить всё

foreach($model->items as $item) {
	echo $form->field($item, 'name['.$item->id.']')->textInput();
}

Ответить