...

суббота, 15 февраля 2014 г.

Yii — обмен опытом: модели




Материал в данной статье предназначен для разработчиков, которые уже имеют навыки работы с YII фреймворком. Опытным программистам материал может показаться неинтересным.

Статья написана для фреймворка версии YII 1.1.14. Под катом много текста с фрагментами кода.


Начнем




Как правило начинать построение моделей следует с проектирования таблиц исходя из поставленной задачи. Это увлекательное занятие мы оставим за рамками статьи, предположим что у нас уже есть требования к таблице. Модель строится уже по готовой таблице, поэтому начинать следует с миграции.
Миграции



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

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


Создание класса модели



Это занятие можно доверить генератору gii, он выполнит для Вас это с радостью. Однако после генерации модели ее, как правило, надо прилично доработать.

Первое что делаем — меняем класс от которого наследуется модель. Если у Вас в каталоге application.components еще не существует файл ActiveRecord.php, то самое время его создать, это будет базовый класс для всех наших моделей.



class ActiveRecord extends CActiveRecord
{
const SCENARIO_CREATE = 'insert';
const SCENARIO_UPDATE = 'update';
const SCENARIO_SEARCH = "search";

/** Return model ID
*
* @return integer
*/
public function getId()
{
return $this->id;
}

/**
* Returns the static ActiveRecord model.
* Please note that you should have this exact method in all your CActiveRecord descendants!
* @param string $className active record class name.
* @return ActiveRecord the static model class
*/
public static function model($className=__CLASS__)
{
return parent::model($className);
}
}


В этот класс следует вынести все повторяющиеся методы из наследованных моделей для соблюдения DRY принципа. Не забываем оставлять комментарии к методам с указанием директивы return, это не только для PHPDoc но и для IDE.


Также для удобства написания кода в IDE созданы константы с названиями сценариев, в этом случае автодополнение помогает избежать опечаток, особенно когда сценариев становится сильно много (используется как правило в валидации, но и не только).


Базовые модели



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

Например цепочка наследования может иметь вид:

User -> BaseUser -> ActiveRecord -> CActiveRecord

При этом в базовую модель выносятся методы, необходимые для работы YII (реляционные связи, правила проверок, метки атрибутов), а свои методы помещаем в основной класс модели.

В базовом и основном классе модели обязательно выполнить переопределение статического метода model() как показано ниже. В комментарии к методу в теге return нужно не забыть указать свой класс.

/**
* Returns the static BaseUser model
*
* @param string $className active record class name.
* @return BaseUser
*/
public static function model($className=__CLASS__)
{
return parent::model($className);
}


Перевод названий атрибутов



Хорошая практика — привыкать к разработке с учетом интернационального интерфейса, для этого используем метод Yii::t(). В качестве категории можно указывать класс модуля или компонента к которому принадлежит модель, к сожалению переопределять этот класс в модели нежелательно, так как консольный парсер при построении словарей не будет знать категории (однако это возможно, но тогда придется вызывать консольную команду с дополнительной конфигурацией).

/**
* @return array customized attribute labels (name=>label)
*/
public function attributeLabels()
{
return array(
'id' => Yii::t('BaseUser','ID'),
'firstName' => Yii::t('BaseUser','First Name'),
'middleName' => Yii::t('BaseUser','Middle Name'),
'lastName' => Yii::t('BaseUser','Last Name'),
'phone' => Yii::t('BaseUser','Phone Number'),
'email' => Yii::t('BaseUser','E-mail'),
// and more...
);
}


Аналогично методу attributeLabels() нужно создавать и свои, например для полей статуса statusLabels. Это пригодится при построении выпадающих списков на форме.


Константы в моделях



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

const STATUS_DRAFT = 1;
const STATUS_PUBLIC = 1;
const STATUS_TRASH = 1;

const FLAG_HAS_CAR = 1;
const FLAG_HAS_HOUSE = 2;
const FLAG_HAS_CREDIT = 4;
const FLAG_HAS_DEPOSIT = 8;


Очевидно, что встречая такие константы в исходниках они нам сразу говорят с чем мы имеем дело.


Методы доступа к данным



При разработке моделей, необходимо использовать геттеры и сеттеры и не использовать чтение атрибутов и присвоение им значений напрямую. Это даст разработчикам больший контроль над расширением проекта. Сеттер может делать тихую фильтрацию при установке значения, или приведение типа данных, а геттер может форматировать выходные данные в удобном формате. Сеттер должен только присваивать значение атрибуту, но не должен делать сохранение модели. Если сеттер будет возвращать $this, то таким образом можно строить текучий интерфейс для модели, для геттера это естественно неприменимо.

/**
* Set user status
*
* @param int $status
* @return \User
*/
public function setStatus(int $status){
$this->status = $status;
return $this;
}

/**
* Return First Name
*
* @return string
*/
public function getFirstName()
{
return (string)$this->upperFirst($this->firstName);
}


Мелкие операции



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

/**
* Make user active
*
* @return bool
*/
public function makeActive(){
if($this->hasEventHandler('onBeforeCount')){
$this->onBeforeCount(new CEvent($this));
}

$result = $this->setStatus(self::STATUS_ACTIVE)
->save();

if($this->hasEventHandler('onAfterMakeActive')){
$this->onAfterMakeActive(new CEvent($this));
}
return $result;
}



На этом пожалуй прервусь, чтобы узнать мнение аудитории.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

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