SaveRelationsBehavior

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

SaveRelationsBehavior

Сообщение nexus »

Всем привет, подскажите один момент.
У меня есть класс Project у которого есть hasMany связь с Page

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

class Project extends ActiveRecord
{
    public function addPage(Page $page)
    {
        $pages = $this->pages;
        $pages[] = $page;
        $this->pages = $pages;
    }
    
    public function updatePage(Page $newPage)
    {
        $pages = $this->pages;
        foreach ($pages as $i => $page) {
            if ($page->id == $newPage->id) {
                array_splice($pages, $i, 0, [$newPage]);
                $this->pages = $pages;
                return;
            }
        }

        throw new \DomainException('Page is not found.');
    }

    public function findPage($pageId)
    {
        foreach ($this->pages as $page) {
            if ($page->id == $pageId) {
                return $page;
            }
        }
        return null;
    }
 
    public function getPages()
    {
        return $this->hasMany(Page::class, ['projectId' => 'id']);
    }
    
    public static function tableName()
    {
        return '{{%projects}}';
    }

    public function behaviors()
    {
        return [
            [
                'class'     => SaveRelationsBehavior::class,
                'relations' => [
                    'pages'
                ],
            ],
        ];
    }

    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => self::OP_ALL,
        ];
    }
}
У класса Page есть также hasMany связь с Item

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

class Page extends ActiveRecord
{
    public function addItem(Item $item)
    {
        $items = $this->items;
        array_splice($items, $item->sortOrder-1, 0, [$item]);
        
        $sortOrder = 1;
        array_map(function(Item $element) use (&$sortOrder) {
            $element->sortOrder = $sortOrder++;
        }, $items);
        
        $this->items = $items;
    }
   
    public function getItems()
    {
        return $this->hasMany(Item::class, ['pageId' => 'id']);
    }

    public static function tableName()
    {
        return '{{%project_page}}';
    }

    public function behaviors()
    {
        return [
            [
                'class'     => SaveRelationsBehavior::class,
                'relations' => [
                    'items'
                ],
            ],
        ];
    }

    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => self::OP_ALL,
        ];
    }
}
Когда пытаюсь добавить Item в существующую страницу проекта, то данные не сохраняются

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

$project = Project::findOne(1);
$page = $project->findPage(1);
$page->addItem(new Item());
$project->updatePage($page);
$project->save();
Так как я по сути не меняю никакие атрибуты в Page, а лишь работаю со связью items, то у Page не срабатывает метод save(), а следовательно и не срабатывает поведение SaveRelationsBehavior, для сохранения связи items. Если в методе addPage пометить какой-то атрибут как измененный, то все работает

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

public function addItem(Item $item)
    {
        $items = $this->items;
        array_splice($items, $item->sortOrder-1, 0, [$item]);
        
        $sortOrder = 1;
        array_map(function(Item $element) use (&$sortOrder) {
            $element->sortOrder = $sortOrder++;
        }, $items);
        
        $this->items = $items;
        $this->markAttributeDirty('updatedOn');
    }
Подскажите, что я делаю не так и как правильно сохранять такие связи?
lgXenos
Сообщения: 243
Зарегистрирован: 2015.11.30, 15:42

Re: SaveRelationsBehavior

Сообщение lgXenos »

Я не работал с этим поведением, но скажите: а что значит "у Page не срабатывает метод save()"?
Обычно save возвращает булево значение: true || false
В случае если это false - значит есть ошибки, которые надо исправить
М.б. там что-то пошло не так, и поэтому не сохраняет?

+ у AR есть метод refresh, но кажется он тут не нужен. это больше просто как информация
mkramer
Сообщения: 531
Зарегистрирован: 2014.12.14, 13:02

Re: SaveRelationsBehavior

Сообщение mkramer »

nexus, залезь отладчиком в это поведение и посмотри, что там и как работает. Тоже не пользовал, но надо посмотреть код просто
nexus
Сообщения: 10
Зарегистрирован: 2017.03.23, 14:43

Re: SaveRelationsBehavior

Сообщение nexus »

lgXenos писал(а): 2018.05.28, 10:45 Я не работал с этим поведением, но скажите: а что значит "у Page не срабатывает метод save()"?
Обычно save возвращает булево значение: true || false
В случае если это false - значит есть ошибки, которые надо исправить
М.б. там что-то пошло не так, и поэтому не сохраняет?

+ у AR есть метод refresh, но кажется он тут не нужен. это больше просто как информация
Это поведение проверяет dirtyAttributes в связях и если ничего не поменялось, то не вызывает save() в связанных моделях. Похоже использовать

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

$this->markAttributeDirty('updatedOn');
это единственный способ сохранить связи, если не меняются атрибуты самой связи.
mkramer
Сообщения: 531
Зарегистрирован: 2014.12.14, 13:02

Re: SaveRelationsBehavior

Сообщение mkramer »

А написать

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

$page = $project->findPage(1);
$page->addItem(new Item());
$page->save();
что мешает?
nexus
Сообщения: 10
Зарегистрирован: 2017.03.23, 14:43

Re: SaveRelationsBehavior

Сообщение nexus »

Project - это агрегат и все управление идет через него. То что я скинул это очень упрощенный вариант.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: SaveRelationsBehavior

Сообщение ElisDN »

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

public function editPage($id, $title, $text)
{
    $pages = $this->pages;
    foreach ($pages as $page) {
        if ($page->id == $id) {
            $page->edit($title, $text);
            $this->pages = $pages;
            $this->updatedOn = time();
            return;
        }
    }
    throw new \DomainException('Page is not found.');
}
nexus
Сообщения: 10
Зарегистрирован: 2017.03.23, 14:43

Re: SaveRelationsBehavior

Сообщение nexus »

Спасибо, именно так и сделал. А не подскажите еще один вопрос, нужно уметь переносить элементы с одной страницы на другую, я сделал в сервисе примерно такой метод:

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

$items = $project->getItemsList();
$pages = [];
foreach ($sortingForm->sortOrder as $pageId => $itemIds) {
    $page = $project->findPage($pageId);
    $page->updateItems(array_map(function($itemId) use ($items) {
          return $items[$itemId];
    }, $itemIds));
    $pages[] = $page;
}
$project->updatePages($pages);
Т.е. я формирую новый список элементов и обновляю их для страницы, но так как поведение SaveRelationsBehavior видит, что элемент исчез из списка элементов в старой странице, то он его автоматически удаляет! Как с этим бороться?
Сейчас сделал отдельный репозиторий и вручную (через Query) сохраняю все зависимости при сортировки.
Ответить