Устройство поведений Behaviors в Yii2

Спасибо участникам! Было круто! Провели очередной вебинар по мотивам статьи о поведениях. В перерыве поговорили о жизни, о программировании, о блогах:

А какая тема дальше? Поразмышлял и об этом в эфире. В любом случае сразу вам сообщу в рассылке по вебинарам. Запишитесь, если ещё не с нами:

P.S. Если вдруг назрели интересные темы для следующих вебинаров, то можете предложить их в комментариях здесь или в первой статье. Спасибо за внимание!

Другие статьи

Недавно в обратную связь поступил вопрос: куда пойти работать неопытному молодому программисту? А именно, продолжить искать заказы на фрилансе или всё-таки устроиться удалённо в крупную студию или другую компанию и работать там?

После добавления личного кабинета пользователя и редактора профиля в прошлых частях сегодня начнём делать модуль администратора. А именно, создадим модуль и CRUD для управления пользователями с помощью генератора Gii и доработаем его вид и маршрутизацию под свои требования.

Давным-давно была опубликована статья про события в Yii. В дополнение на этот раз попробовали выполнить несколько примеров в прямом эфире. Начали с событий в JavaScript, пощёлкали по кнопкам, потом перешли в PHP и изучили принципы объявления, навешивания обработчиков и запуска события в Yii2 Framework.

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

Комментарии

 

Евгений

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

Ответить

 

Дмитрий Елисеев

Уже думал о выкладывании расшифровки. Попробую сделать.

Ответить

 

Евгений Левачев

Присоединяюсь к комменту выше. Я часто летаю и нет возможности везде смотреть видео, а текст очень бы хотелось читать.

Ответить

 

Денис

Здравствуйте, сделайте пожалуйста на сайте текстовые версии примеров скриптов используемых в вебинарах.

Ответить

 

Дмитрий Елисеев

Да, сделаю.

Ответить

 

Игорь

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

Ответить

 

Тимофей

Здравствуйте, Дмитрий. По тестированию что-нибудь планируется? Интересует неразбериха вокруг юнит тестов. Кто-то разделяет их собственно на юнит и интеграционные тесты, а кто-то не разделяет. К примеру здесь тестируется метод LoginForm::login(), а также неявно вызывается и соответственно тестируется LoginForm::getUser() и User::findByUsername($login). Я пытался спросить на тостере и некоторые стали писать, что это не юнит-тест из-за того, что он неявно тестирует другие методы, что в тесте нужно было мокать LoginForm::getUser(). Но лично я не увидел в этом целесообразности, так как в таком случае нельзя проверить, действительно ли получается залогиниться с верными данными. Смысла в таком тесте будет мало (или даже вовсе не будет) и всё равно нужно будет писать так называемый "интеграционный тест" на этот метод.

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

Ответить

 

Web design Dubai

Дмитрий, а есть архив с вебинарами, чтобы скачать и смотреть оффлайн в дороге?

Ответить

 

Дмитрий Елисеев

Можете попробовать загрузить с YouTube.

Ответить

 

Александр

Спасибо за хороший урок по Поведениям, очень хорошо и подробно все рассказали!

Ответить

 

Spirit Absolute

Дмитрий, пожалуйста, посоветуй готовое поведение для загрузки множества изображений.

Ответить

 

Дмитрий Елисеев

Обычно для множества я завожу отдельную таблицу с моделью PostImage и навешиваю обычное одинарное поведение уже на неё.

Ответить

 

Spirit Absolute

Понял, спасибо!

Ответить

 

Тимур

Здравствуйте, Дмитрий. Как поступить с вынесением метода beforeValidate() в поведение? Проблема возникает в том, что метод возвращает false, а валидация всё-равно проходит успешно. Но если выносить код в саму модель в beforeValidate() то все поля проходят/не проходят валидацию. Вот код самого поведения.

Ответить

 

Дмитрий Елисеев

В методах beforeValidate, beforeSave и прочих before* используется экземпляр ModelEvent с дополнительным полем isValid, значение которого в итоге возвращается из соответствующих методов.

Следовательно, для прерываения действия в обработчике события нужно установить isValid в false при ошибке:

if (!...) {
    $event->isValid = false;
}

А касательно реализации поведения - непосредственная работа в самой модели или в её поведении с $request->post() сильно связывает код.

Ответить

 

Тимур

Спасибо! По поводу последнего замечания, так буде правильнее или есть еще какие либо варианты:

public function behaviors() {
    return [
      'metaBehavior' => [
        'class'     => MetaBehavior::className(),
        'attribute' => 'meta_json',
        'data'      => Yii::$app->request->post()
      ]
    ];
  }
Ответить

 

Дмитрий Елисеев

Нет, Вы просто перенесли это из поведения обратно в модель. В итоге без POST-запроса значение request->post() будет пустым, что сломает валидацию и сохранение.

Логичнее помимо геттера getMetaTags добавить сеттер setMetaTags, принимающий массив из формы, и действия по загрузке производить уже в нём.

Ответить

 

Andrewkha

Дмитрий, добрый день!
Вопрос не совсем про поведения, но связан с вебинаром про них :)
Для закрепления материала решил написать поведение, которое рассматривалось в качестве примера на уроке - загрузка файлов. Почему-то у меня в приложении Yii::getAlias('@web') выдает пустую строку. Что-то пропущено в конфигурации?

Ответить

 

Дмитрий Елисеев

Ничего не пропущено. Это путь от корня сайта. Если у Вас сайт открывается не из поддиректории, то этот путь пустой.

Ответить

 

Andrewkha

Дмитрий, большое спасибо за быстрые ответы и здесь и на yiiframework форуме

Ответить

 

Andrewkha

Дмитрий,

теперь вопрос чисто по поведениям. Допустим, я в поведении объявляю некий статический метод
public static method1...

Это поведение присоединяю к модели AR model1.

Вопрос. Должен ли этот статичский метод быть доступен извне по model1::method1? Что-то у меня ругается, что метод недоступен... Хотя, по идее, методы поведения должны быть доступны его owner'у. Или есть особенности для статических методов?

Ответить

 

Дмитрий Елисеев

Для статических методов вместо __call используется магический метод __callStatic. Он в Yii2 не реализован. Но если бы это было, то по логике класс модели не смог бы ничего узнать о подключенных к нему поведениях. И наоборот, статический метод поведения ничего бы не знал о классе модели. Так что смысла использования статических методов в поведениях нет никакого.

Ответить

 

Andrewkha

Дмитрий, а можно поподробнее об этом:
" Но если бы это было, то по логике класс модели не смог бы ничего узнать о подключенных к нему поведениях. И наоборот, статический метод поведения ничего бы не знал о классе модели."

Что-то не уловил смылса... Почему класс модели ничего бы не узнал о поведениях?

Ответить

 

Дмитрий Елисеев

Взаимообратные задачи:

  • Модель знает список привязанных к нему поведений;
  • Каждое поведение хранит ссылку на модель.

Со статическими методами нужно делать то же самое:

  • из User::__callStatic получить список классов поведений;
  • в SomeBehavior::method узнать, откуда его вызывают.

Ну если пойти на хитрость и из __callStatic вызывать self::behaviors() статически, брать оттуда имена классов поведений и искать у них статический метод, то первое реализовать можно.

Но обратно из статического метода поведения явно узнать класс вызывающей его модели будет невозможно (если не передавать имя класса ещё одним параметром в сам метод или не парсить стек вызовов).

А вообще с практической точки зрения вызывать статические методы из поведения смысла не вижу. Они же ненастраиваемые и к модели никак не привязаны. Не проще ли их вызывать напрямую или, в крайнем случае, подключать через трейты?

Ответить

 

Andrewkha

Кажется, начинаю понимать, спасибо.
Вопрос на самом деле встал вот из-за чего. в вашем примере поведения для загрузки файлов есть метод getDir. Я подумал, что его было бы логично сделать статическим, поскольку он не привязан к экземпляру, будет одинаковым для всех экземпляров этого класса.

Ответить

 

Alex

Привет, когда следующий вебинар? Куда пропал? :-)

Ответить

 

Дмитрий Елисеев

Работу работаю :) Надо бы провести на днях...

Ответить

 

Alex

Понятно. Если много времени приходится готовиться к вебинарам, может быть можно было бы сделать что-нибудь в формате подкаст-вебкаст? Например, обсуждать какую-нибудь проблему или например, расширение или паттерн какой или что что-то интересное узнал или best practices на примерах. Я думаю многим было бы интересно!

Ответить

 

Alex

То есть выбрать какую-то менее глобальную тему, чем тестирование или разбить тестирование на части или вообще по-говорить например, как разбивать приложение на модули или еще чего.

Ответить

 

Василий

Здравствуйте Дмитрий
Помогите настроить на сайте оплату товаров через WebMoney в автоматизированном режиме
начиная с выбора товара.
Я не программист но готовые коды куда нужно вставлю, просто нет нигде конкретной пошаговой информации для чайников, с подробными инструкциями.
Спасибо
Ваш подписчик

Ответить

 

Тимур

Здравствуйте. Скажите, а нельзя ли из поведения добавить правила валидации в основную модель?
Пробовал сделать через $this->owner->validators[] = [...] в методе init() поведения, но ничего не вышло.

Ответить

 

Игорь

Добрый день!

Прошу пояснить - в чем преимущество использования TimestampBehavior перед реализацией данного функционала на стороне базы данных при помощи триггеров и значений полей по умолчанию?

Ответить

 

Дмитрий Елисеев

CURRENT_TIMESTAMP можно установить только у одного поля в таблице, так что одновременно для created_at и updated_at это сделать не получится.

Ответить

 

Игорь

Это можно реализовать средствами БД при помощи триггера.
Получается, что преимущество в удобстве - просто, вся логика в приложении, повторное использование кода. Да и когда много подобных таблиц придется соответственно много триггеров создавать.

Ответить

 

Игорь

Ответ на вопрос также прояснили исходники. В шаблоне yii2-app-advanced в миграции для создания таблицы user поля created_at и updated_at имеют тип integer и в них сохраняется значение, возвращаемое PHP функцией time(). Видим, что при помощи TimestampBehavior реализован нестандартный (в отличие от типов полей - DATETIME, TIMESTAMP) способ хранения даты/времени в базе.

Ответить

 

Дмитрий Елисеев

Да, так как результаты DATETIME и TIMESTAMP неудобно конвертировать в текущую временную зону.

Ответить

 

Максим Тимофеев

Использую OmgDef/yii2-multilingual-behavior
Все отлично работало до появления в моделе метода afterFind
Скажите, это криво написанное behavior или нельзя использовать метод afterFind если он используется в behavior?

Ответить

 

Дмитрий Елисеев

Не забывайте дёргать parent::afterFind

Ответить

 

Akulenok

При добавление поста мне надо записать и айди юзера и его имя. как мне лучше использовать?
Я сделал так, но не уверен что это правильно или оптимально

[
    'class' => 'yii\behaviors\AttributeBehavior',
    'attributes' => [
        ActiveRecord::EVENT_BEFORE_INSERT => 'username',
    ],
    'value' => function ($event) {
        return Yii::$app->user->identity->username;
    },
],
[
    'class' => 'yii\behaviors\BlameableBehavior',
    'createdByAttribute' => 'user_id',
    'updatedByAttribute' => false,
],
Ответить

 

Дмитрий Елисеев

А есть ли смысл в дополнительном поле username, если уже есть поле user_id, с которого можно выводить имя по связи $post->user->username?

Ответить

Оставить комментарий

Войти | Завести аккаунт


(никто не увидит)



Можно использовать теги <p> <ul> <li> <b> <i> <a> <pre>