Обновление модели со связанными таблицами

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
GHopper
Сообщения: 83
Зарегистрирован: 2017.06.05, 10:53

Обновление модели со связанными таблицами

Сообщение GHopper »

Здравствуйте.
Есть БД со следующей структурой

Item -> ItemCategory <- Category

Т.е. у Item может быть сколько угодно Category и связываются они через таблицу ItemCategory.
Создал миграцию, создал модель Item с методом getCategories():

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

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getCategories()
    {
        return $this->hasMany(Category::className(), ['id' => 'category_id'])
            ->viaTable('item_category', ['item_id' => 'id']);
    }
Теперь в представлениях могу вызывать метод

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

$model->categories
и получать все связанные с элементом категории. Все отлично, пока я не задумался над сохранением/обновлением модели Item. Как написать сеттер для

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

$model->categories = []
? У меня даже стандартный код

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

        $model = new Item();
        if ($model->load(Yii::$app->request->post())) {
            ...
        }
вызовет ошибку. Как правильно обрабатывать такие ситуации?
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Обновление модели со связанными таблицами

Сообщение samdark »

foreach-ем.
GHopper
Сообщения: 83
Зарегистрирован: 2017.06.05, 10:53

Re: Обновление модели со связанными таблицами

Сообщение GHopper »

Сложно все. Непонятно...

По итогу имеем слудующее:
Модель Item

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

    private $_categories;
    ...
    /**
     * @return \yii\db\ActiveQuery
     */
    public function getCategories()
    {
        return $this->hasMany(Category::className(), ['id' => 'category_id'])
            ->viaTable('item_category', ['item_id' => 'id']);
    }

    public function setCategories($categories)
    {
        if (!empty($categories)) {
            if (!is_array($categories)) {
                $categories = array($categories);
            }
            foreach ($categories as $category) {
                if ($category instanceof Category) {
                    $this->_categories[] = $category;
                } else {
                    $this->_categories[] = Category::findOne(['id' => $category]);
                }
            }
        }
    }
    
    public function afterSave($insert, $changedAttributes)
    {
        ItemCategory::deleteAll(['item_id' => $this->id]);

        if (is_array($this->_categories)) {
            foreach ($this->_categories as $category) {
                $this->link('categories', $category);
            }
        }

        return parent::afterSave($insert, $changedAttributes);
    }    
Т.е. во время загрузки модели через метод

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

$model->load()
все данные загружаются в модель, при этом категории грузятся в приватный член. Это удобно, что можно передать POST-данные скопом, а не парсить их в контроллере. После успешного сохранения, когда появляется id модели, мы в afterSave связываем модель Category с Item через метод link().

Все работает, но вот осадок какой-то присутствует. Есть подозрение, что я что-то делаю не так как планировали разработчики ядра.
Почему-то в setCategories передаются то объекты Category, то массив с id из формы. Где и как правильно транзакцию начинать, чтобы в случае ошибки в afterSave можно было откатить все обратно? Как правильно проводить валидацию массива _categories?
Ответить