Подход, который я вывел для себя, работает и с первой и со второй версии yii. И так Yii для работы с данными использует паттерн ActiveRecord - одна запись в таблице - один экземпляр класса. Но в жизни (реальных проектах) все сложнее. Редко когда одна форма работает только с одной таблицей. Из последних проектов простой пример - есть форма редактирования клиента, у клиента есть множество телефонов. Пользователю удобнее сразу указать все телефоны клиента в форме. В такой ситуации нам на помощь приходит сервисный класс. В Yii1 это будет наследник от CFormModel, Yii2 - Model. "Ручками" добавляем ему все свойства - тут удобнее добавить getter/setter для каждого свойства и внутри обращаться к ActiveRecord объектам. Незабываем добавлять правила валидации. При вызове метода save у сервисного класса "раскладываем" все по таблицам используя ActiveRecord модели и транзакцию. Вместо тысячи слов несколько строк кода
Тут реализованы не все методы, но с этого скелета уже можно начать. В одном проекте (который я сейчас делаю) у меня только getter/setter уже 20 шт и форма будет управлять 6 классами и их массивами. До того как я нашел такой "кашерный" вариант на просторах сети, я бы все засунул в контроллер. Больше я так не делаю.
Еще один довольно часто встречающийся кейс - хранение даты или любого форматированного поля (например номер телефона). Тут вариантов миллион и все они неправильные :-) А правильный мой. В базе дату можно хранить или в виде целого числа секунд, прошедших с 1 января 1970 года (вариант быстрый и удобный, если только не нужно потом делать расследование прямо в базе) или в виде типа date/datetime (удобно смотреть прямо в базе, но затратнее при хранении и выводе). Но когда мы начинаем работать через форму, пользователь не очень хочет видеть 2016-04-21 или 1459880043 в поле ввода, им подавай 21.04.2016. И так вместо тысячи слов код
Была моделька
class Client extends CActiveRecord { public function tableName() { return 'client'; } public function rules() { return [['name', 'required'],]; } } class Phone extends CActiveRecord { public function tableName() { return 'phone'; } public function rules() { return [ ['number, client_id', 'required'], ['client_id', 'exist', 'className' => 'Client', 'attributeName' => 'id'], ]; } } class ClientForm extends CFormModel { /** @var Client */ private $client; /** @var Phone */ private $phones; public function rules() { return [['name', 'required'], ['phones', 'validatePhones']]; } public function getName() { return $this->client->name; } public function setName($value) { $this->client->name = $value; } public function save() { if (!$this->validate()) { return false; } $tr = Yii::app()->db->beginTransaction(); try { if (!$this->client->save(false)) { $this->addErrors($this->client->getErrors()); throw new Exception; } $tr->commit(); return true; } catch (Exception $e) { $tr->rollback(); } return false; } }
Тут реализованы не все методы, но с этого скелета уже можно начать. В одном проекте (который я сейчас делаю) у меня только getter/setter уже 20 шт и форма будет управлять 6 классами и их массивами. До того как я нашел такой "кашерный" вариант на просторах сети, я бы все засунул в контроллер. Больше я так не делаю.
Еще один довольно часто встречающийся кейс - хранение даты или любого форматированного поля (например номер телефона). Тут вариантов миллион и все они неправильные :-) А правильный мой. В базе дату можно хранить или в виде целого числа секунд, прошедших с 1 января 1970 года (вариант быстрый и удобный, если только не нужно потом делать расследование прямо в базе) или в виде типа date/datetime (удобно смотреть прямо в базе, но затратнее при хранении и выводе). Но когда мы начинаем работать через форму, пользователь не очень хочет видеть 2016-04-21 или 1459880043 в поле ввода, им подавай 21.04.2016. И так вместо тысячи слов код
Была моделька
class Client extends CActiveRecord { public function tableName() { return 'client'; } public function rules() { return [['name, birthday', 'required'],]; } }добавляем фиктивное поле через набор getter/setter, специально для формы
/** * Class Client * @property string $birthday */ class Client extends CActiveRecord { public function tableName() { return 'client'; } public function rules() { return [['name,birthdayDate', 'required'], ['birthdayDate', 'date', 'format' => 'dd.MM.yyyy'],]; } public function getBirthdayDate() { return Yii::app()->format->formatDate($this->birthday); } public function setBirthdayDate($value) { $this->birthday = date('Y-m-d', CDateTimeParser::parse($value, 'dd.MM.yyyy')); } }и в форме работаем с birthday через getter/setter
<div class="form-group"> <?= $form->labelEx($model, 'birthdayDate'); ?> <?= $form->dateField($model, 'birthdayDate', ['class' => 'input-sm']); ?> <?= $form->error($model, 'birthdayDate'); ?> </div>При правильной организации проекта - делается наследник от базовой модельки в отдельном модуле и эти getter/setter нигде больше не будут нам мешаться.