Устройство поведений Behaviors в Yii2
Спасибо участникам! Было круто! Провели очередной вебинар по мотивам статьи о поведениях. В перерыве поговорили о жизни, о программировании, о блогах:
А какая тема дальше? Поразмышлял и об этом в эфире. В любом случае сразу вам сообщу в рассылке по вебинарам. Запишитесь, если ещё не с нами:
P.S. Если вдруг назрели интересные темы для следующих вебинаров, то можете предложить их в комментариях здесь или в первой статье. Спасибо за внимание!
ЕвгенийЗдравствуйте. Жаль, что перестали публиковать статьи в текстовом виде. Не всегда удобно просматривать видео. А так пробежал по тексту, и что-нибудь для себя взял.
Дмитрий ЕлисеевУже думал о выкладывании расшифровки. Попробую сделать.
Евгений ЛевачевПрисоединяюсь к комменту выше. Я часто летаю и нет возможности везде смотреть видео, а текст очень бы хотелось читать.
Денис – rybinden.ruЗдравствуйте, сделайте пожалуйста на сайте текстовые версии примеров скриптов используемых в вебинарах.
Дмитрий ЕлисеевДа, сделаю.
ИгорьДмитрий, спасибо за вебинар!
Было бы очень хорошо, если бы к записи вебинара (этого и других) приложить файл презентации (ссылку на презентацию). Мне кажется это оптимальное решение.
ТимофейЗдравствуйте, Дмитрий. По тестированию что-нибудь планируется? Интересует неразбериха вокруг юнит тестов. Кто-то разделяет их собственно на юнит и интеграционные тесты, а кто-то не разделяет. К примеру здесь тестируется метод LoginForm::login(), а также неявно вызывается и соответственно тестируется LoginForm::getUser() и User::findByUsername($login). Я пытался спросить на тостере и некоторые стали писать, что это не юнит-тест из-за того, что он неявно тестирует другие методы, что в тесте нужно было мокать LoginForm::getUser(). Но лично я не увидел в этом целесообразности, так как в таком случае нельзя проверить, действительно ли получается залогиниться с верными данными. Смысла в таком тесте будет мало (или даже вовсе не будет) и всё равно нужно будет писать так называемый "интеграционный тест" на этот метод.
Я же хотел выяснить грань, когда при тестировании одного метода допускается неявное тестирование другиих методов, а когда это категорически не следует делать. И есть ли вообще эта грань. Но к сожалению, так и не смог найти ответ.
Web design Dubai – betterweb.aeДмитрий, а есть архив с вебинарами, чтобы скачать и смотреть оффлайн в дороге?
Дмитрий ЕлисеевМожете попробовать загрузить с YouTube.
АлександрСпасибо за хороший урок по Поведениям, очень хорошо и подробно все рассказали!
Spirit AbsoluteДмитрий, пожалуйста, посоветуй готовое поведение для загрузки множества изображений.
Дмитрий ЕлисеевОбычно для множества я завожу отдельную таблицу с моделью PostImage и навешиваю обычное одинарное поведение уже на неё.
Spirit AbsoluteПонял, спасибо!
Тимур – www.crimeatrips.comЗдравствуйте, Дмитрий. Как поступить с вынесением метода beforeValidate() в поведение? Проблема возникает в том, что метод возвращает false, а валидация всё-равно проходит успешно. Но если выносить код в саму модель в beforeValidate() то все поля проходят/не проходят валидацию. Вот код самого поведения.
Дмитрий ЕлисеевВ методах beforeValidate, beforeSave и прочих before* используется экземпляр ModelEvent с дополнительным полем isValid, значение которого в итоге возвращается из соответствующих методов.
Следовательно, для прерываения действия в обработчике события нужно установить isValid в false при ошибке:
if (!...) { $event->isValid = false; }А касательно реализации поведения - непосредственная работа в самой модели или в её поведении с $request->post() сильно связывает код.
Тимур – www.crimeatrips.comСпасибо! По поводу последнего замечания, так буде правильнее или есть еще какие либо варианты:
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Дмитрий, а можно поподробнее об этом:
" Но если бы это было, то по логике класс модели не смог бы ничего узнать о подключенных к нему поведениях. И наоборот, статический метод поведения ничего бы не знал о классе модели."
Что-то не уловил смылса... Почему класс модели ничего бы не узнал о поведениях?
Дмитрий ЕлисеевВзаимообратные задачи:
Со статическими методами нужно делать то же самое:
Ну если пойти на хитрость и из __callStatic вызывать self::behaviors() статически, брать оттуда имена классов поведений и искать у них статический метод, то первое реализовать можно.
Но обратно из статического метода поведения явно узнать класс вызывающей его модели будет невозможно (если не передавать имя класса ещё одним параметром в сам метод или не парсить стек вызовов).
А вообще с практической точки зрения вызывать статические методы из поведения смысла не вижу. Они же ненастраиваемые и к модели никак не привязаны. Не проще ли их вызывать напрямую или, в крайнем случае, подключать через трейты?
AndrewkhaКажется, начинаю понимать, спасибо.
Вопрос на самом деле встал вот из-за чего. в вашем примере поведения для загрузки файлов есть метод getDir. Я подумал, что его было бы логично сделать статическим, поскольку он не привязан к экземпляру, будет одинаковым для всех экземпляров этого класса.
AlexПривет, когда следующий вебинар? Куда пропал? :-)
Дмитрий ЕлисеевРаботу работаю :) Надо бы провести на днях...
AlexПонятно. Если много времени приходится готовиться к вебинарам, может быть можно было бы сделать что-нибудь в формате подкаст-вебкаст? Например, обсуждать какую-нибудь проблему или например, расширение или паттерн какой или что что-то интересное узнал или best practices на примерах. Я думаю многим было бы интересно!
AlexТо есть выбрать какую-то менее глобальную тему, чем тестирование или разбить тестирование на части или вообще по-говорить например, как разбивать приложение на модули или еще чего.
Василий – samdelkin.ruЗдравствуйте Дмитрий
Помогите настроить на сайте оплату товаров через WebMoney в автоматизированном режиме
начиная с выбора товара.
Я не программист но готовые коды куда нужно вставлю, просто нет нигде конкретной пошаговой информации для чайников, с подробными инструкциями.
Спасибо
Ваш подписчик
Тимур – www.crimeatrips.comЗдравствуйте. Скажите, а нельзя ли из поведения добавить правила валидации в основную модель?
Пробовал сделать через $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?
ДжониНе мешало бы к каждому видео писать полноценную статью, тогда как конспект к лекционному материалу получится.