Итак, узнал сегодня небольшую фитчу Laravel/Eloquent, которая практически не описана и лишь вскользь упомянута в документации фреймовика.
Перейдите к TL;DR, если вы просто хотите увидеть эту фичу.
Возможно, вы уже знаете, что можно добавлять статические boot()-методы к Eloquent-модели, которые будут выполнены при ее загрузке. Это можно использовать, например, привязываясь к событиям модели, если это необходимо. Например, вы можете организовать email-уведомление, каждый раз, когда будет создан новый пользователь(неудачный пример), но вы могли бы описать это следующим образом:
Но что если вы хотите поместить это с trait?
Рассмотрим такой сценарий; необходимо организовать поиск в диапазоне моделей. Конечно, можно создать новый класс SearchableModel, унаследовав его от Eloquent\Model, но хотелось бы написать таким образом, чтобы функционал был легко переносим в другие проекты, не привязываясь к конкретному приложению. Трейт здесь подойдет как-никак лучше, потому что он легко переносим и относительно ненавязчив, подключая который вы получаете дополнительный функционал, который может быть легко переопределен.
Итак, я создал SearchableTrait.
Довольно просто пока, мы вызываем метод $model->search('query'), возвращающий модели, соответствующие критериям запроса.
Тем не менее, вместо непосредственного поиска по базе данных я планирую использовать сторонний поиск по приложению(если кому интересно, elasticsearch), использующий собственную индексацию, которая требует настройки индекса каждой строки вручную.
Но куда мне вставить этот код?
Я мог бы поместить его в boot()-метод модели, но там он может быть затерт, тем самым нарушая мой поиск и уничтожая любой шанс быть повторно использованным.
TL; DR
Ребята из Laravel явно предусмотрели, каким образом подгружать ваши трейты, определяя специальное метод, как в Eloquent-модели, к примеру:
Таким образом, здесь присутствует небольшая классическая магия Eloquent. Если у вас есть статический метод в трейте, который называется по принципу boot[NameOfTrait], то он будет выполнен также, как статический boot()-метод модели. Какое удобное место для регистрации событий своей модели.
Пример в документации по Laravel, реализует этот трюк при регистрации глобального скоупа, для перехвата всех запущенных запросов, при мягком удаление моделей.
Конечно же, кроме этой фичи, есть и другие хорошие способы для достижения того же результата, но всегда приятно держать плохо документированные тонкости в своем арсенале, чтобы воспользоваться ими в случае необходимости.
Перейдите к TL;DR, если вы просто хотите увидеть эту фичу.
Возможно, вы уже знаете, что можно добавлять статические boot()-методы к Eloquent-модели, которые будут выполнены при ее загрузке. Это можно использовать, например, привязываясь к событиям модели, если это необходимо. Например, вы можете организовать email-уведомление, каждый раз, когда будет создан новый пользователь(неудачный пример), но вы могли бы описать это следующим образом:
class User extends Eloquent {
public static function boot()
{
parent::boot();
static::created(function($user) {
// Send a mailing…
});
}
}
Но что если вы хотите поместить это с trait?
Рассмотрим такой сценарий; необходимо организовать поиск в диапазоне моделей. Конечно, можно создать новый класс SearchableModel, унаследовав его от Eloquent\Model, но хотелось бы написать таким образом, чтобы функционал был легко переносим в другие проекты, не привязываясь к конкретному приложению. Трейт здесь подойдет как-никак лучше, потому что он легко переносим и относительно ненавязчив, подключая который вы получаете дополнительный функционал, который может быть легко переопределен.
Итак, я создал SearchableTrait.
trait SearchableTrait {
public function search($query)
{
// ...
}
}
Довольно просто пока, мы вызываем метод $model->search('query'), возвращающий модели, соответствующие критериям запроса.
Тем не менее, вместо непосредственного поиска по базе данных я планирую использовать сторонний поиск по приложению(если кому интересно, elasticsearch), использующий собственную индексацию, которая требует настройки индекса каждой строки вручную.
Но куда мне вставить этот код?
Я мог бы поместить его в boot()-метод модели, но там он может быть затерт, тем самым нарушая мой поиск и уничтожая любой шанс быть повторно использованным.
TL; DR
Ребята из Laravel явно предусмотрели, каким образом подгружать ваши трейты, определяя специальное метод, как в Eloquent-модели, к примеру:
trait SearchableTrait {
public function search($query)
{
// ...
}
public static function bootSearchableTrait()
{
static::created(function($item){
// Index the item
});
}
}
Таким образом, здесь присутствует небольшая классическая магия Eloquent. Если у вас есть статический метод в трейте, который называется по принципу boot[NameOfTrait], то он будет выполнен также, как статический boot()-метод модели. Какое удобное место для регистрации событий своей модели.
Пример в документации по Laravel, реализует этот трюк при регистрации глобального скоупа, для перехвата всех запущенных запросов, при мягком удаление моделей.
Конечно же, кроме этой фичи, есть и другие хорошие способы для достижения того же результата, но всегда приятно держать плохо документированные тонкости в своем арсенале, чтобы воспользоваться ими в случае необходимости.
mark_ablov
> Eloquent-модели, которые будут выполнены при ее загрузке
Это не совсем верно, boot-методы (обычный и для трейтов) вызываются только один раз для модели, а не при каждом инстанциировании.
SerafimArts
> Это не совсем верно, boot-методы (обычный и для трейтов) вызываются только один раз для модели
В тестовом окружении метод boot вызывается только для самого первого юнит-теста. В остальных — просто игнорируется. Так что предлагаю быть с ними (с методами boot) аккуратнее.
mark_ablov
> В остальных — просто игнорируется
1. Поведение этого механизма не зависит от окружения.
2. Вызывается не для первого юнит-теста, а для первого созданного объекта модели.
По крайней мере в 4 версии это так:
SerafimArts
А внутри TestCase setUp метод релоада приложения, который уничтожает все данные (в т.ч. создаёт новый объект эвент листнера, если в boot прописан, например `static::updated` — его не будет в следующем тесте), но оставляет булев `booted`. Так что во втором+ тесте вызов не производится.
Верное замечание, что не зависит от окружения. Просто тестирование реализовано так, что boot нормально не работает и можно получить себе граблями в лоб.
SerafimArts
*промахнулся веточкой*
greabock
Эм… работа моделей в ларе — это повод для дискуссии, но вряд ли для поста на хабр. Да еще и такой короткий… Да еще и перевод… плохой выбор, в общем.