Организация иерархического представления (HMVC)

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Аватара пользователя
denisbondar
Сообщения: 31
Зарегистрирован: 2015.10.12, 19:34

Организация иерархического представления (HMVC)

Сообщение denisbondar »

Добрый день, сообщество.

Имеется представление, которое содержит большое количество виджетов (внутри которых GridView, форма для добавления новых записей и для редактирования существующих). Каждый виджет связан со своей сущностью из агрегата.
Для простоты понимания представим следующий агрегат:
Изображение

На странице информации о клиенте содержатся виджеты непосредственно для отображения и редактирования модели Customer, а также по одному виджету для каждой сущности CustomerEmail, CustomerPhone и т.д.

То, как это примерно выглядит, можно посмотреть по ссылке.

Сейчас всё это всё ещё работает, но с ростом количества сущностей агрегата (их уже больше 10) приобретает очень страшный вид в действии контроллера.
Весь этот интерфейс рендерится одним действием контроллера. В этом действии создаются провайдеры данных и объекты классов фильтрации для всех сущностей и затем передаются в рендер.
Чтобы переключить страницу или отсортировать какой нибудь один из множества GridView, происходит инстанцирование всех остальных провайдеров данных и классов фильтрации, которые не нужны конкретно для сортировки какого-то одного GridView.
Это все вызывает дикий оверхед, который хотелось бы оптимизировать.

Было рассмотрено решение HMVC, но, к сожалению, не совсем понятно, как это использовать в Yii2.
Может быть есть какие-то рекомендации или практики по организации подобных иерархических интерфейсов?
Я хочу добиться такой организации кода, при которой для работы с одной сущностью (например, пагинация таблицы телефонов или добавление нового телефона) не придется инстанцировать десяток совершенно не нужных провайдеров и фильтров.

На ум приходит только организация отдельных контроллеров для каждого виджета, но я не нашел, как это можно реализовать в Yii2.
Аватара пользователя
denisbondar
Сообщения: 31
Зарегистрирован: 2015.10.12, 19:34

Re: Организация иерархического представления (HMVC)

Сообщение denisbondar »

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

Re: Организация иерархического представления (HMVC)

Сообщение ElisDN »

В контроллере просмотра клиента ищем только его:

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

class SiteController extends Controller
{
    public function actionClient($id)
    {
        $client = $this->findModel($id);

        $this->render('index', [
            'client' => $client,
        ]);
    }
}
В представлении выводим все виджеты:

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

<?= Html::encode($client->name) ?>

<?= ClientAddressesWidget::widget(['client' => $client]) ?>
<?= ClientPhonesWidget::widget(['client' => $client]) ?>
<?= ClientEmailsWidget::widget(['client' => $client]) ?>
Каждый виджет формирует вывод и форму, подставляя свои route:

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

class ClientAddressesWidget extends Widget
{
    public $client;
    public $model;

    public function run()
    {
        $search = new AddressSearch();
        $provider = $search->search($this->client->id, Yii::$app->request->queryParams);
        
        $provider->sort->route = 'client/addresses',
        $provider->pagination->route = 'client/addresses',
        
        $model = $this->model ?: new Address();

        $this->render('addresses', [
            'search' => $search,
            'provider' => $provider,
            'model' => $model,
            'client' => $this->client,
        ]);
    }
}
в своём представлении с action:

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

<?= Pjax::begin(['id' => 'client-addresses']) ?>
<?= GridView::widget([
    'dataProvider' => $provider,
    ...
]) ?>
<?php $form = ActiveForm::begin([
    'action' => ['addresses', 'id' => $client->id],
    'options' => ['data-pjax' => true],
]) ?>   
    ...
<?php ActiveForm::end() ?>
<?php Pjax::end() ?>
И для всех вещей делаем отдельные экшны:

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

class SiteController extends Controller
{
    public function actionAddresses($id)
    {
        $client = $this->findModel($id);

        $model = new Address();
        $model->client_id = $client->id;

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['client', 'id' => $id]);
        }

        return $this->render('addresses', [
            'client' => $client,
            'model' => $model,
        ]);
    }
}
с простым выводом одного виджета:

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

<?= ClientAddressesWidget::widget(['client' => $client,  'model' => $model]) ?>
В итоге всё выводится на одной странице, но все запросы из Pjax и форм летят в свои отдельные экшны, перегружающие только свой Pjax-блок.
Аватара пользователя
denisbondar
Сообщения: 31
Зарегистрирован: 2015.10.12, 19:34

Re: Организация иерархического представления (HMVC)

Сообщение denisbondar »

Огромное спасибо, Дмитрий!
Кажется, это именно то, чего я пытался добиться. Все оказалось намного проще. Я залез в дебри, совсем не в ту степь, и уже не мог самостоятельно вернуться назад.
Большое спасибо!

На всякий случай, вдруг кому будет полезно, код можно посмотреть здесь: https://github.com/denisbondar/yii2-hmvc-test
Понемногу дополню пример различной функциональностью.
Ответить