Как создать Behavior для CakePHP
Как я уже писал, я люблю выносить обработку данных в модели. При этом у меня часто получаются модели со схожими функциями, как в одном проекте так и в разных. А меня очень удручает, когда надо писать один и тот же код по несколько раз, а от копирования кода меня просто передёргивает, потому что я уже вижу как через полгода буду себя проклинать. До CakePHP я в таких случаях просто создавал общего потомка и там реализовал общую функциональность. Недостаток этого метода - это то, что в PHP можно наследовать можно только один класс.
К счастью, разработчики CakePHP придумали как удобнее выносить одинаковую функциональность из моделей. Эта возможность появилась в CakePHP 1.2 и называется behaviors.
Возьмём, нашу модель Word. У меня часто бывают такие таблицы, которые состоят из id и name. Я покажу как сделать так, чтобы к другим моделям такого типа можно было применять общую функциональность. Но при этом мы можем указать в каждой конкретной модели, какие behaviors к ней подключать. Самое удобное, что можно подключать их несколько, а не только одну.
Модель выглядела так
app/models/word.php
-
<?php
-
class Word extends AppModel {
-
var $name = 'Word';
-
'id' => VALID_NUMBER,
-
'name' => VALID_NOT_EMPTY,
-
);
-
-
/**
-
* Adds a word and returns id
-
*
-
* @param string $name
-
* @return integer If failed to save returns false
-
*/
-
function add($name) {
-
'name' => $name,
-
);
-
$this->create();
-
if ($this->save($item)) {
-
return $this->getInsertId();
-
} else {
-
return false;
-
}
-
}
-
-
/**
-
* Generates list of lowercase names and its ids
-
*
-
* @return array (a(name=>id), ...)
-
*/
-
function generateListFlipLow(&$model) {
-
$items = $this->generateList();
-
foreach ($items as $id=>$name) {
-
$items[$id] = low($name);
-
}
-
}
-
}
-
?>
С применением behavior этот же класс будет выглядеть так
И всё. Через запятую можно указать несколько behaviors. Если надо передать настройки в behavior, то надо писать
Сами behaviors находятся в app/models/behaviors. Для нашей модели - это
app/models/behaviors/name_list.php
-
<?
-
-
class NameListBehavior extends ModelBehavior {
-
-
-
$this->settings = $config;
-
}
-
-
/**
-
* Adds a word and returns id
-
*
-
* @param string $name
-
* @return integer If failed to save returns false
-
*/
-
function add(&$model, $name) {
-
'name' => $name,
-
);
-
$model->create();
-
if ($model->save($item)) {
-
return $model->getInsertId();
-
} else {
-
return false;
-
}
-
}
-
-
/**
-
* Generates list of lowercase names and its ids
-
*
-
* @return array (a(name=>id), ...)
-
*/
-
function generateListFlipLow(&$model) {
-
$items = $model->generateList();
-
foreach ($items as $id=>$name) {
-
$items[$id] = low($name);
-
}
-
-
}
-
-
}
-
-
?>
В методе setup мы настраиваем behavior. В $config передаются настройки, которые можно указать в модели.
Во всех методах добавляется первый параметр $model. Если вы переносите методы из модели, то надо не забыть все $this поменять на $model.
Как видите, создавать behaviors очень просто. Особенно, если мы заранее создали тест и можем автоматически убедиться, что всё по-прежнему работает
Владимир, несколько замечаний.
1) Методы с общими именами типа add в поведениях лучше не использовать. Это связано с тем, что кейк будет вызовет метод с таким именем из первого по списку поведения.
2) $this->settings = $config;
На самом деле нужно хранить конфигурации вех моделей, так как в реестре классов будет храниться только один экземпляр.
Поэтому используем $this->settings[$model->alias]. Причем именно $model->alias а не $model->name, поскольку разработка ядра это рекомендует.
Евгений, огромное спасибо, второй пункт для меня - открытие.
А add нигде в Cake не используется - у меня таких Behavior для однотипных объектов много и везде называется одинаково, я уже привык, что можно чуть проще создавать записи для простых таблиц. Я вот доведу до ума и выложу свой ExtTreeBehavior, который работает в связке с ExtJs (поддерживается json вывод записей с подгрузкой только нужных, переименование, правильное удаление и перетягивание с учётом порядка).
Хотя, действительно, стоило бы придумать менее распространённое название вроде addItem. У меня есть негативный опыт создания в моделях метода getId(), который перекрывает метод предка - Model. Самое обидное, что это уже несколько раз случалось и каждый раз тратил по 15 минут с дебаггером, чтобы найти причину.