Обращение к модели из View ? Грех ?

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

Обращение к модели из View ? Грех ?

Сообщение cr1gger »

Здравствуйте форумчане, такой вопрос.
Изучаю фреймворк, сам всю голову уже сломал как сделать правильно, помогите вы (
Имеется контроллер site и экшн index

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

public function actionIndex()
    {

        $posts = News::find()->orderBy(['date'=> SORT_DESC]);
        $countQuery = clone $posts;
        $pages = new Pagination(['totalCount' => $countQuery->count(), 'pageSize' => 5] );
        $pages->pageSizeParam = false;
        $models = $posts->offset($pages->offset)
        ->limit($pages->limit)
        ->all();
        

        $all_views = Views::find()->asArray()->all(); // на этом я остановился...
        


        return $this->render('index', compact('models', 'pages', 'all_views'));
    }
Я в нем получаю из модели News новости и передаю из во вью, а далее с помощью foreach вывожу их по 5 новостей на страницу.
Мне нужно сделать чтобы на каждой из новостей был счетчик просмотров(кол-во).
Проблема заключается в том что просмотры записываются в другую таблицу под названием Views и структура такая у неё:
(id, id_news, session_id)

Прямо в контроллере я не могу узнать какой id_news и куда отнести.
Я хотел сделать так: получить все записи из таблицы Views и перебрать их. в массив виде [id_news, кол-во строк с этим id]
но а если этих views записей миллион. Это нагрузка большая и будет долго все это перебирать.

Остается один вариант. Обратится из вью news к модели Views и там уже делать запросы к каждой новости.
Правильно это будет или нет ?
Если нет то как решить по другому проблему мою ?

Спасибо заранее :)
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Обращение к модели из View ? Грех ?

Сообщение samdark »

но а если этих views записей миллион
Миллион записей на одной странице? Это не будет работать, что бы вы там ни делали.
Обратится из вью news к модели Views и там уже делать запросы к каждой новости.
И это тоже нормально работать не будет. На каждый просмотр страницы из 100 новостей делать 100 запросов в базу?
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Обращение к модели из View ? Грех ?

Сообщение samdark »

Что такое id в таблице Views?
yiiliveext
Сообщения: 910
Зарегистрирован: 2019.08.13, 01:49

Re: Обращение к модели из View ? Грех ?

Сообщение yiiliveext »

Обращение к модели из View ? Грех ?
Здесь подразумевается обращение к БД, а это грех и большой. Из представления не должно быть запросов к БД.
Хотя, надо сказать, что Yii 2 не способствует такой чистой архитектуре. Те же виджеты созданы только как некие отрисовщики внутренних представлений, а не независимые полноценные куски кода. Поэтому для превращения в независимые полноценные куски кода с виджетами обычно делают всякие извращения, что приводит к обращению к БД из представления. Вот, например, как это сделано в одном из известных пакетов для Yii 2:

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

//controller
public function actionAssignments($id)
    {
        /** @var User $user */
        $user = $this->userQuery->where(['id' => $id])->one();

        return $this->render(
            '_assignments',
            [
                'user' => $user,
                'params' => Yii::$app->request->post(),
            ]
        );
    }
    

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

//view
<?= AssignmentsWidget::widget(['userId' => $user->id, 'params' => $params]) ?>

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

//widget view
<?php $form = ActiveForm::begin(
    [
        'enableClientValidation' => false,
        'enableAjaxValidation' => false,
    ]
) ?>

<?= Html::activeHiddenInput($model, 'user_id') ?>

<?= $form->field($model, 'items')->widget(
    SelectizeDropDownList::class,
    [
        'items' => $availableItems,
        'options' => [
            'id' => 'children',
            'multiple' => true,
        ],
    ]
) ?>

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

//widget
public function run()
    {
        $model = $this->make(Assignment::class, [], ['user_id' => $this->userId]);

        if ($model->load($this->params)) {
            $this->make(UpdateAuthAssignmentsService::class, [$model])->run();
        }

        return $this->render(
            '/widgets/assignments/form',
            [
                'model' => $model,
                'form' => $this->form,
                'availableItems' => $this->getAvailableItems(),
            ]
        );
    }
Как видите, здесь контроллер просто передает POST-запрос обратно в представление, где он передается параметром в виджет и уже виджет производит операции с моделью/БД и потом отрисовывает результат, что приводит к обращениям у БД из представления. Чтобы не заниматься такими извращениями, лучше сделать свой класс Widget, где создания/запуск и рендеринг будут разделены. Тогда мы можем конфигурировать виджеты в контроллере и выполнять их, рендерить уже в представлении. Выглядеть это будет примерно так:

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

public function actionIndex()
    {
        $widgetManager = new WidgetManager();
        $widgetManager->addWidget(MyFirstWidget::class, [
            'class' => MyFirstWidget::class,
            //... config
        ])->addWidget('second-widget-alias', [
            'class' => MySecondWidget::class,
            //... config
        ])->addWidget(MyThirdWidget::class, [
            'class' => MyThirdWidget::class,
            //... config
        ], 'top')->build()->run(['request' => Yii::$app->getRequest(), 'user' => Yii::$app->getUser()]);
        $widgets = $widgetManager->getWidgets();
        $topWidgets = $widgetManager->getWidgets('top');
        return $this->render('index', [
            'widgets' => $widgets,
            'topWidgets' => $topWidgets
        ]);
    }
Выводим в представлении

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

//отдетные виджеты
$widgets->render('second-widget-alias');
$widgets->render(MyFirstWidget::class);
//группу виджетов
$widgets->getGroup('top')->renderAll();
//тоже самое другим способом
$topWidgets->renderAll();
Также можно хранит конфиг виджетов в файле

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

public function actionIndex()
    {
        $widgetManager = new WidgetManager();
        $widgetManager->loadFromFile('my-file.php')->build()->run(['request' => Yii::$app->getRequest(), 'user' => Yii::$app->getUser()]);
        $widgets = $widgetManager->getWidgets();
        $topWidgets = $widgetManager->getWidgets('top');
        return $this->render('index', [
            'widgets' => $widgets,
            'topWidgets' => $topWidgets
        ]);
    }
или в базе данных (полезно, когда виджеты настраиваются в админке)

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

public function actionIndex()
    {
        $widgetManager = new WidgetManager();
        $widgetManager->loadFromDb('post/index')->build()->run(['request' => Yii::$app->getRequest(), 'user' => Yii::$app->getUser()]);
        $widgets = $widgetManager->getWidgets();
        $topWidgets = $widgetManager->getWidgets('top');
        return $this->render('index', [
            'widgets' => $widgets,
            'topWidgets' => $topWidgets
        ]);
    }
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Обращение к модели из View ? Грех ?

Сообщение unknownby »

Для подсчета количества просмотров конкретной новости

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

$model->updateCounters(['news_counter' => 1]);
При этом $model - это выбранная новость, news_counter - поле integer в модели новости, при создании новости по-умолчанию 0
Единица - это интервал на сколько увеличивать переменную.

Зачем выносить счетчик за модель новости не понятно.
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Обращение к модели из View ? Грех ?

Сообщение samdark »

yiiliveext, речь вроде не про виджеты была. Виджеты — как мелкие контроллеры. Как по мне, внутри них получать данные — это нормально, а вот использовать их в контроллере — не очень.
Аватара пользователя
proctoleha
Сообщения: 298
Зарегистрирован: 2016.07.10, 19:00

Re: Обращение к модели из View ? Грех ?

Сообщение proctoleha »

Так то понятно, что обращаться напрямую к модели во вьюхе - это грех. Но тут возникает старая тема - как передать данные в layout, который не относится ни к одному контроллеру.

Например, в главном меню есть запись вида
В наличии на складе (value)
Где value - это динамически меняющееся значение

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

Re: Обращение к модели из View ? Грех ?

Сообщение ElisDN »

proctoleha писал(а): 2020.02.19, 05:41Как не согрешить в таком случае? Как правильно передать данные из модели в layout?
Виджетами.
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Обращение к модели из View ? Грех ?

Сообщение unknownby »

proctoleha писал(а): 2020.02.19, 05:41 Так то понятно, что обращаться напрямую к модели во вьюхе - это грех. Но тут возникает старая тема - как передать данные в layout, который не относится ни к одному контроллеру.
Отошли от сути вопроса. Человеку надо увеличить счетчик просмотров новости и вывести значение в представлении, а не передать значение в layout. ;)
yiiliveext
Сообщения: 910
Зарегистрирован: 2019.08.13, 01:49

Re: Обращение к модели из View ? Грех ?

Сообщение yiiliveext »

proctoleha писал(а): 2020.02.19, 05:41 Как не согрешить в таком случае? Как правильно передать данные из модели в layout?

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

Event::on(\yii\web\View::class,\yii\web\View::EVENT_BEFORE_RENDER, function ($event) use ($model) {
            $event->sender->params['itemsStock'] = $model->getItemsStock();
        });
yiiliveext
Сообщения: 910
Зарегистрирован: 2019.08.13, 01:49

Re: Обращение к модели из View ? Грех ?

Сообщение yiiliveext »

samdark писал(а): 2020.02.18, 19:30 yiiliveext, речь вроде не про виджеты была. Виджеты — как мелкие контроллеры. Как по мне, внутри них получать данные — это нормально, а вот использовать их в контроллере — не очень.
Ну так если они контроллеры, тогда почему контроллеры вызываются в процессе рендеринга представления? Это же не нормально.
Тогда уж будет правильней делать виджеты модулем и чтобы при отрисовке виджет делал ajax запрос своему контроллеру на получение данных. Показанный мной подход имеет массу преимуществ, а при разработке CMS вообще незаменим, поскольку позволяет динамически собирать внутренние макеты страниц с настройкой их из админки. В Yii 3 вроде отделили создание виджета от его рендеринга и это хорошо.
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Обращение к модели из View ? Грех ?

Сообщение samdark »

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

AJAX-запрос — накладные расходы на HTTP, ухудшение индексации роботами и вот это всё.
В Yii 3 вроде отделили создание виджета от его рендеринга и это хорошо.
В Yii 2 они никогда одним целым и не были.
yiiliveext
Сообщения: 910
Зарегистрирован: 2019.08.13, 01:49

Re: Обращение к модели из View ? Грех ?

Сообщение yiiliveext »

samdark писал(а): 2020.02.20, 15:45 В Yii 2 они никогда одним целым и не были.
Типичный вывод виджета https://github.com/yiisoft/yii2/blob/ma ... t.php#L130
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Обращение к модели из View ? Грех ?

Сообщение samdark »

А, да. Я уже начал забывать :)

https://github.com/yiisoft/widget/blob/ ... et.php#L85
Ответить