Что делать, если Вам потребовалось внедрить стандартный функционал (к примеру кэширование данных) к некоторым стандартным моделям? Мне в данном случае, на помощь пришел behavior и его великолепные возможности.
Если вкратце, то behavior (поведение), в фреймворке yii2, позволяет расширять контроллеры и модели (или все то что было наследовано от класса «Component») своими дополнительными методами. Работает по аналогии с трейтами в PHP, но не то же самое. Если вас интересует более детальное определение и возможности, для этого загляните на официальный сайт фреймворка.
В данной же статье, я опишу свой небольшой опыт совместной работы кэширования, моделей и поведений.
История. На определенном этапе разработки, потребовалось внедрить кэширование к четырем стандартным моделям:
- Рубрикам
- Категориям
- Регионам
- Городам
Все они имеют стандартную структуру таблиц, за исключением таблиц «категорий» и «городов». В них присутствует родительский ID (рубрики / региона).
Само наше поведение имеет следующий код:
class CachedBehavior extends Behavior { public $cache_pref; public $from = 'id'; public $to = 'title'; public $group; public function events() { return [ ActiveRecord::EVENT_AFTER_INSERT => 'deleteCache', ActiveRecord::EVENT_AFTER_UPDATE => 'deleteCache', ActiveRecord::EVENT_AFTER_DELETE => 'deleteCache', ]; } public function deleteCache() { Yii::$app -> cache -> delete($this -> cache_pref); } public function getCacheAsTree() { return ArrayHelper::map($this -> getCache(), $this -> from, $this -> to, $this -> group); } public function getCacheList() { return ArrayHelper::map($this -> getCache(), $this -> from, $this -> to); } public function getCacheById($id) { $a = $this -> getCache(); if(isset($a[$id])) { return $a[$id]; } return []; } public function getCache() { $Cache = Yii::$app -> cache; $a = $Cache -> get($this -> cache_pref); if($a === false) { $a_ = (new Query()) -> select('*') -> from($this -> owner -> tableName()) -> orderBy('`order` ASC, `title` ASC') -> all(); $a = []; foreach ($a_ as $v) { $a[$v[$this -> from]] = $v; } $Cache -> set($this -> cache_pref, $a, HelperDate::WEEK); } return $a; } }
Где:
public $cache_pref; public $from = 'id'; public $to = 'title'; public $group;
параметры нашего поведения, некоторые (стандартные для всех случаев) установленные по умолчанию.
Каждая модель имеет свой префикс для $cache_pref кэша (но можно использовать и названия таблицы, обратившись к ней как к «$this -> owner -> tableName()»)
Через события, указываем когда нам нужно удалить данные кэша:
public function events() { return [ ActiveRecord::EVENT_AFTER_INSERT => 'deleteCache', ActiveRecord::EVENT_AFTER_UPDATE => 'deleteCache', ActiveRecord::EVENT_AFTER_DELETE => 'deleteCache', ]; }
а именно при создании новой записи, редактировании существующий или удалении какой-то.
Все что начинается с «get» ниже по коду, идут методы работы с кэшем. И их можно использовать обратившись к методу модели.
К примеру. Подключаем наше поведение к модели городов:
class TerritoryCityModel extends ActiveRecord { public function behaviors() { return [ 'CachedBehavior' => [ 'class' => CachedBehavior::className(), 'cache_pref' => 'territory-city', 'group' => 'territory_region_id', ], ]; } public static function tableName() { return '{{%territory_city}}'; } }
В коде, к методам нашего поведения можно будет обратиться следующим образом:
//Отобразить деревом (new TerritoryCityModel()) -> getCacheAsTree(); //Или отобразить списком (new TerritoryCityModel()) -> getCacheList() //Или как-то иначе...
Еще одна замечательная идея использования поведений, это вынесение в них не стандартных методов валидации.