mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-04 15:24:07 +01:00
646 lines
37 KiB
Plaintext
646 lines
37 KiB
Plaintext
Active Record
|
||
=============
|
||
|
||
Хотя Yii DAO справляется практически с любыми задачами, касающимися работы с БД, почти наверняка
|
||
90% времени уйдёт на написание SQL-выражений,
|
||
реализующих общие операции CRUD (создание, чтение, обновление и удаление). Кроме того,
|
||
код, перемешанный с SQL-выражениями, поддерживать проблематично. Для решения этих
|
||
проблем мы можем воспользоваться Active Record.
|
||
|
||
Active Record реализует популярный подход объектно-реляционного проецирования (ORM).
|
||
Каждый класс AR отражает таблицу (или представление) базы данных, экземпляр AR — строку в этой таблице,
|
||
а общие операции CRUD реализованы как методы AR. В результате, мы можем работать с большей
|
||
объектно-ориентированностью. Например, используя следующий код, можно вставить новую строку в
|
||
таблицу `tbl_post`.
|
||
|
||
~~~
|
||
[php]
|
||
$post=new Post;
|
||
$post->title='тестовая запись';
|
||
$post->content='содержимое записи';
|
||
$post->save();
|
||
~~~
|
||
|
||
Ниже мы покажем, как настроить и использовать AR для реализации CRUD-операций,
|
||
а в следующем разделе — как использовать AR для работы со связанными таблицами.
|
||
Для примеров в этом разделе мы будем использовать следующую таблицу. Обратите внимание,
|
||
что при использовании БД MySQL в SQL-выражении ниже `AUTOINCREMENT` следует заменить
|
||
на `AUTO_INCREMENT`.
|
||
|
||
~~~
|
||
[sql]
|
||
CREATE TABLE tbl_post (
|
||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||
title VARCHAR(128) NOT NULL,
|
||
content TEXT NOT NULL,
|
||
create_time INTEGER NOT NULL
|
||
);
|
||
~~~
|
||
|
||
> Note|Примечание: AR не дает решения для всех задач, касающихся работы с базами данных.
|
||
Лучше всего его использовать для моделирования таблиц в конструкциях PHP и для
|
||
несложных SQL-запросов. Для сложных случаев следует использовать Yii DAO.
|
||
|
||
|
||
Соединение с базой данных
|
||
--------------------------
|
||
|
||
Для работы AR требуется подключение к базе данных. По умолчанию, предполагается, что
|
||
компонент приложения `db` предоставляет необходимый экземпляр класса [CDbConnection],
|
||
который отвечает за подключение к базе. Ниже приведен пример конфигурации приложения:
|
||
|
||
~~~
|
||
[php]
|
||
return array(
|
||
'components'=>array(
|
||
'db'=>array(
|
||
'class'=>'system.db.CDbConnection',
|
||
'connectionString'=>'sqlite:path/to/dbfile',
|
||
// включить кэширование схем для улучшения производительности
|
||
// 'schemaCachingDuration'=>3600,
|
||
),
|
||
),
|
||
);
|
||
~~~
|
||
|
||
> Tip|Подсказка: Поскольку для получения информации о полях таблицы AR использует метаданные,
|
||
требуется некоторое время для их чтения и анализа. Если не предполагается, что схема базы данных будет меняться, то
|
||
следует включить кэширование схемы установив для атрибута [CDbConnection::schemaCachingDuration]
|
||
любое значение больше нуля.
|
||
|
||
В настоящий момент AR поддерживает следующие СУБД:
|
||
|
||
- [MySQL 4.1 и выше](http://www.mysql.com)
|
||
- [PostgreSQL 7.3 и выше](http://www.postgres.com)
|
||
- [SQLite 2 и 3](http://www.sqlite.org)
|
||
- [Microsoft SQL Server 2000 и выше](http://www.microsoft.com/sqlserver/)
|
||
- [Oracle](http://www.oracle.com)
|
||
|
||
> Note|Примечание: Microsoft SQL Server поддерживается начиная с версии 1.0.4;
|
||
> Oracle поддерживается начиная с версии 1.0.5.
|
||
|
||
Если вы хотите использовать другой компонент нежели `db` или предполагаете,
|
||
используя AR, работать с несколькими БД, то следует переопределить метод
|
||
[CActiveRecord::getDbConnection()]. Класс [CActiveRecord] является базовым классом
|
||
для всех классов AR.
|
||
|
||
> Tip|Подсказка: Есть несколько способов для работы AR с несколькими БД. Если
|
||
схемы используемых баз различаются, то можно создать разные базовые классы AR с
|
||
различной реализацией метода [getDbConnection()|CActiveRecord::getDbConnection].
|
||
В противном случае, проще будет динамически менять статическую переменную [CActiveRecord::db].
|
||
|
||
Определение AR-класса
|
||
-----------------
|
||
|
||
Для доступа к таблице БД нам прежде всего требуется определить класс AR путем наследования
|
||
класса [CActiveRecord]. Каждый класс AR представляет одну таблицу базы данных, а экземпляр класса —
|
||
строку в этой таблице. Ниже приведен минимальный код, требуемый для определения класса AR,
|
||
представляющего таблицу `tbl_post`.
|
||
|
||
~~~
|
||
[php]
|
||
class Post extends CActiveRecord
|
||
{
|
||
public static function model($className=__CLASS__)
|
||
{
|
||
return parent::model($className);
|
||
}
|
||
|
||
public function tableName()
|
||
{
|
||
return 'tbl_post';
|
||
}
|
||
}
|
||
~~~
|
||
|
||
> Tip|Подсказка: Поскольку классы AR часто появляются во многих местах кода,
|
||
> мы можем вместо включения классов по одному, добавить всю папку с AR-классами.
|
||
> К примеру, если AR-классы находятся в папке `protected/models`,
|
||
> мы можем сконфигурировать приложение следующим образом:
|
||
> ~~~
|
||
> [php]
|
||
> return array(
|
||
> 'import'=>array(
|
||
> 'application.models.*',
|
||
> ),
|
||
> );
|
||
> ~~~
|
||
|
||
По умолчанию имя AR-класса совпадает с названием таблицы в базе данных.
|
||
Если они различаются, потребуется переопределить метод [tableName()|CActiveRecord::tableName].
|
||
Метод [model()|CActiveRecord::model] объявляется для каждого AR-класса.
|
||
|
||
> Info|Информация: Для использования [префиксов таблиц](/doc/guide/database.dao#using-table-prefix),
|
||
> появившихся в версии 1.1.0, метод [tableName()|CActiveRecord::tableName] AR-класса
|
||
> может быть переопределен как показано ниже:
|
||
> ~~~
|
||
> [php]
|
||
> public function tableName()
|
||
> {
|
||
> return '{{post}}';
|
||
> }
|
||
> ~~~
|
||
> Вместо того, чтобы возвращать полное имя таблицы, мы возвращаем
|
||
> имя таблицы без префикса и заключаем его в двойные фигурные скобки.
|
||
|
||
Значения полей в строке таблицы доступны как атрибуты соответствующего экземпляра AR-класса.
|
||
Например, код ниже устанавливает значение для атрибута `title`:
|
||
|
||
~~~
|
||
[php]
|
||
$post=new Post;
|
||
$post->title='тестовая запись';
|
||
~~~
|
||
|
||
Хотя мы никогда не объявляем заранее свойство `title` класса `Post`,
|
||
мы, тем не менее, можем обратиться к нему как в коде выше. Это возможно по причине того, что
|
||
`title` является полем в таблице `tbl_post` и [CActiveRecord] делает его доступным в качестве свойства
|
||
благодаря волшебному методу PHP `__get()`. Если аналогичным образом обратиться к несуществующему полю,
|
||
будет вызвано исключение.
|
||
|
||
> Info|Информация: В данном руководстве мы называем столбцы и таблицы в нижнем регистре так
|
||
как различные СУБД работают с регистрозависимыми именами по-разному.
|
||
Например, PostgreSQL считает имя столбцов
|
||
регистронезависимыми по умолчанию, и мы должны заключать имя столбца в кавычки
|
||
в условиях запроса, если имя столбца имеет заглавные буквы. Использование
|
||
нижнего регистра помогает избежать данной проблемы.
|
||
|
||
AR опирается на правильно определённые первичные ключи таблиц БД. Если в таблице
|
||
нет первичного ключа, требуется указать в соответствующем классе AR столбцы,
|
||
которые будут использоваться как первичный ключ. Сделать это можно путём
|
||
перекрытия метода `primaryKey()`:
|
||
|
||
~~~
|
||
[php]
|
||
public function primaryKey()
|
||
{
|
||
return 'id';
|
||
// Для составного первичного ключа следует использовать массив:
|
||
// return array('pk1', 'pk2');
|
||
}
|
||
~~~
|
||
|
||
Создание записи
|
||
---------------
|
||
|
||
Для добавления новой строки в таблицу БД, нам необходимо создать новый экземпляр
|
||
соответствующего класса, присвоить значения атрибутам, ассоциированным с полями таблицы,
|
||
и вызвать метод [save()|CActiveRecord::save] для завершения добавления.
|
||
|
||
~~~
|
||
[php]
|
||
$post=new Post;
|
||
$post->title='тестовая запись';
|
||
$post->content='содержимое тестовой записи';
|
||
$post->create_time=time();
|
||
$post->save();
|
||
~~~
|
||
|
||
Если первичный ключ таблицы автоинкрементный, то после добавления экземпляр AR
|
||
будет содержать обновленное значение первичного ключа. В примере выше, свойство
|
||
`id` всегда будет содержать первичный ключ для новой записи.
|
||
|
||
Если поле задано в схеме таблицы с некоторым статическим значением по умолчанию
|
||
(например, строка или число), то после создания экземпляра соответствующее свойство
|
||
экземпляра AR будет автоматически содержать это значение. Один из способов
|
||
поменять это значение — прописать его в AR-классе.
|
||
|
||
~~~
|
||
[php]
|
||
class Post extends CActiveRecord
|
||
{
|
||
public $title='пожалуйста, введите заголовок';
|
||
…
|
||
}
|
||
|
||
$post=new Post;
|
||
echo $post->title; // отобразится: пожалуйста, введите заголовок
|
||
~~~
|
||
|
||
Начиная с версии 1.0.2, до сохранения записи (добавления или обновления) атрибуту
|
||
может быть присвоено значение типа [CDbExpression]. Например, для сохранения текущей даты,
|
||
возвращаемой функцией MySQL `NOW()`, можно использовать следующий код:
|
||
|
||
~~~
|
||
[php]
|
||
$post=new Post;
|
||
$post->create_time=new CDbExpression('NOW()');
|
||
// $post->create_time='NOW()'; этот вариант работать не будет
|
||
// т.к. значение 'NOW()' будет воспринято как строка
|
||
$post->save();
|
||
~~~
|
||
|
||
> Tip|Подсказка: Несмотря на то, что AR позволяет производить различные операции без
|
||
написания громоздкого SQL, часто необходимо знать, какой SQL выполняется на самом деле.
|
||
Этого можно добиться, включив [журналирование](/doc/guide/topics.logging).
|
||
К примеру, чтобы вывести запросы SQL в конце каждой страницы, мы можем включить
|
||
[CWebLogRoute] в настройках приложения.
|
||
Начиная с версии 1.0.5, можно задать параметр [CDbConnection::enableParamLogging] в true
|
||
и получить также значения параметров запросов.
|
||
|
||
Чтение записи
|
||
--------------
|
||
|
||
Для чтения данных из таблицы базы данных нужно вызвать метод `find`:
|
||
|
||
~~~
|
||
[php]
|
||
// найти первую строку, удовлетворяющую условию
|
||
$post=Post::model()->find($condition,$params);
|
||
// найти строку с указанным значением первичного ключа
|
||
$post=Post::model()->findByPk($postID,$condition,$params);
|
||
// найти строку с указанными значениями атрибута
|
||
$post=Post::model()->findByAttributes($attributes,$condition,$params);
|
||
// найти первую строку, используя некоторое выражение SQL
|
||
$post=Post::model()->findBySql($sql,$params);
|
||
~~~
|
||
|
||
*Выше мы вызываем метод `find` через `Post::model()`. Запомните, что статический метод `model()`
|
||
обязателен для каждого AR-класса. Этот метод возвращает экземпляр AR, используемый для
|
||
доступа к методам уровня класса (что-то схожее со статическими методами класса) в контексте объекта.*
|
||
|
||
Если метод `find` находит строку, соответствующую условиям запроса, он возвращает экземпляр
|
||
класса `Post`, свойства которого содержат значения соответствующих полей строки таблицы. Далее мы
|
||
можем читать загруженные значения аналогично обычным свойствам объектам, например, `echo $post->title;`.
|
||
|
||
В случае, если в базе нет данных, соответствующих условиям запроса, метод `find` вернет значение null.
|
||
|
||
Параметры `$condition` и `$params` используются для уточнения запроса. В данном случае `$condition`
|
||
может быть строкой, соответствующей оператору `WHERE` в SQL-выражении, а `$params` — массивом параметров, значения
|
||
которых должны быть привязаны к маркерам, указанным в `$condition`. Например:
|
||
|
||
~~~
|
||
[php]
|
||
// найдем строку, где postID=10
|
||
$post=Post::model()->find('postID=:postID', array(':postID'=>10));
|
||
~~~
|
||
|
||
> Note|Примечание: В примере выше, нам может понадобиться заключить в кавычки
|
||
> обращение к столбцу `postID` для некоторых СУБД. Например, если мы используем
|
||
> СУБД PostgreSQL, нам следует писать условие как `"postID"=:postID`, потому
|
||
> что PostgreSQL по умолчанию считает имя столбца регистронезависимым.
|
||
|
||
Кроме того, можно использовать `$condition` для указания более сложных условий запроса. Вместо строки параметр
|
||
`$condition` может быть экземпляром класса [CDbCriteria], который позволяет указать иные условия
|
||
нежели `WHERE` выражение. К примеру:
|
||
|
||
~~~
|
||
[php]
|
||
$criteria=new CDbCriteria;
|
||
$criteria->select='title'; // выбираем только поле 'title'
|
||
$criteria->condition='postID=:postID';
|
||
$criteria->params=array(':postID'=>10);
|
||
$post=Post::model()->find($criteria); // $params не требуется
|
||
~~~
|
||
|
||
Обратите внимание, если в качестве условия запроса используется [CDbCriteria], то параметр
|
||
`$params` уже не нужен, поскольку его можно указать непосредственно в [CDbCriteria], как показано выше.
|
||
|
||
Помимо использования [CDbCriteria], есть другой способ указать условие — передать методу
|
||
массив ключей и значений, соответствующих именам и значениям свойств критерия. Пример выше можно переписать
|
||
следующим образом:
|
||
|
||
~~~
|
||
[php]
|
||
$post=Post::model()->find(array(
|
||
'select'=>'title',
|
||
'condition'=>'postID=:postID',
|
||
'params'=>array(':postID'=>10),
|
||
));
|
||
~~~
|
||
|
||
> Info|Информация: В случае, когда условие заключается в соответствии значениям некоторых полей, можно воспользоваться
|
||
методом [findByAttributes()|CActiveRecord::findByAttributes], где параметр `$attributes` представляет собой
|
||
массив значений, проиндексированных по имени поля. В некоторых фреймворках эта задача решается путем использования методов
|
||
типа `findByNameAndTitle`. Хотя такой способ и выглядит привлекательно, часто он вызывает путаницу и проблемы, связанные с чувствительностью
|
||
имен полей к регистру.
|
||
|
||
В случае, если условию запроса отвечает множество строк, мы можем получить их все, используя
|
||
методы `findAll`, приведенные ниже. Как мы отметили ранее, каждый из этих методов `findAll` имеет `find` аналог.
|
||
|
||
~~~
|
||
[php]
|
||
// найдем все строки, удовлетворяющие условию
|
||
$posts=Post::model()->findAll($condition,$params);
|
||
// найдем все строки с указанными значениями первичного ключа
|
||
$posts=Post::model()->findAllByPk($postIDs,$condition,$params);
|
||
// найдем все строки с указанными значениями атрибута
|
||
$posts=Post::model()->findAllByAttributes($attributes,$condition,$params);
|
||
// найдем все строки, используя SQL-выражение
|
||
$posts=Post::model()->findAllBySql($sql,$params);
|
||
~~~
|
||
|
||
В отличие от `find`, метод `findAll` в случае, если нет ни одной строки, удовлетворяющей запросу,
|
||
возвращает не null, а пустой массив.
|
||
|
||
Помимо методов `find` и `findAll` описанных выше, для удобства также доступны следующие методы:
|
||
|
||
~~~
|
||
[php]
|
||
// получим количество строк, удовлетворяющих условию
|
||
$n=Post::model()->count($condition,$params);
|
||
// получим количество строк с использованием указанного SQL-выражения
|
||
$n=Post::model()->countBySql($sql,$params);
|
||
// проверим, есть ли хотя бы одна строка, удовлетворяющая условию
|
||
$exists=Post::model()->exists($condition,$params);
|
||
~~~
|
||
|
||
Обновление записи
|
||
---------------
|
||
|
||
Заполнив экземпляр AR значениями полей, мы изменяем эти значения и сохраняем их обратно в БД.
|
||
|
||
~~~
|
||
[php]
|
||
$post=Post::model()->findByPk(10);
|
||
$post->title='new post title';
|
||
$post->save(); // сохраняем изменения в базу данных
|
||
~~~
|
||
|
||
Как можно было заметить, мы используем метод [save()|CActiveRecord::save] для добавления и обновления записей.
|
||
Если экземпляр AR создан с использованием оператора `new`, то вызов метода [save()|CActiveRecord::save]
|
||
приведет к добавлению новой строки в базу данных. В случае же, если экземпляр AR создан как результат вызова методов
|
||
`find` и `findAll`, вызов метода [save()|CActiveRecord::save] обновит данные существующей строки в таблице.
|
||
На самом деле, можно использовать свойство [CActiveRecord::isNewRecord] для указания, является экземпляр AR новым или нет.
|
||
|
||
Кроме того, можно обновить одну или несколько строк в таблице без их предварительной загрузки. Для этого в
|
||
AR существуют следующие методы уровня класса:
|
||
|
||
~~~
|
||
[php]
|
||
// обновим строки, отвечающие заданному условию
|
||
Post::model()->updateAll($attributes,$condition,$params);
|
||
// обновим строки, удовлетворяющие заданному условию и первичному ключу (или нескольким ключам)
|
||
Post::model()->updateByPk($pk,$attributes,$condition,$params);
|
||
// обновим поля-счетчики в строках, удовлетворяющих заданным условиям
|
||
Post::model()->updateCounters($counters,$condition,$params);
|
||
~~~
|
||
|
||
Здесь `$attributes` — это массив значений полей, проиндексированных по имени поля,
|
||
`$counters` — массив инкрементных значений, проиндексированных по имени поля,
|
||
`$condition` и `$params` аналогично описанию выше.
|
||
|
||
Удаление записи
|
||
---------------
|
||
|
||
Мы можем удалить строку, если экземпляр AR был заполнен значениями этой строки.
|
||
|
||
~~~
|
||
[php]
|
||
$post=Post::model()->findByPk(10); // предполагаем, что запись с ID=10 существует
|
||
$post->delete(); // удаляем строку из таблицы
|
||
~~~
|
||
|
||
Обратите внимание, что после удаления экземпляр AR не меняется, но соответствующей записи в
|
||
таблице уже нет.
|
||
|
||
Следующие методы используются для удаления строк без их предварительной загрузки:
|
||
|
||
~~~
|
||
[php]
|
||
// удалим строки, соответствующие указанному условию
|
||
Post::model()->deleteAll($condition,$params);
|
||
// удалим строки, соответствующие указанному условию и первичному ключу (или нескольким ключам)
|
||
Post::model()->deleteByPk($pk,$condition,$params);
|
||
~~~
|
||
|
||
Проверка данных
|
||
---------------
|
||
|
||
Часто во время добавления или обновления строки, нам требуется проверить, соответствуют ли
|
||
значения полей некоторым правилам. Особенно это важно в случае данных, поступающих со стороны клиента, —
|
||
в подавляющем большинстве случаев этим данным доверять нельзя.
|
||
|
||
AR осуществляет проверку данных автоматически в момент вызова метода [save()|CActiveRecord::save].
|
||
Проверка основана на правилах, заданных в методе AR-класса [rules()|CModel::rules]. Детально ознакомиться с
|
||
тем, как задаются правила проверки, можно в разделе [Определение правил проверки](/doc/guide/form.model#declaring-validation-rules).
|
||
Ниже приведем типичный порядок обработки в момент сохранения записи:
|
||
|
||
~~~
|
||
[php]
|
||
if($post->save())
|
||
{
|
||
// данные корректны и успешно добавлены\обновлены
|
||
}
|
||
else
|
||
{
|
||
// данные некорректны, сообщения об ошибках могут быть получены путем вызова метода getErrors()
|
||
}
|
||
~~~
|
||
|
||
В момент, когда данные для добавления или обновления отправляются пользователем через форму ввода, нам требуется
|
||
присвоить их соответствующим свойствам AR. Это можно проделать следующим образом:
|
||
|
||
~~~
|
||
[php]
|
||
$post->title=$_POST['title'];
|
||
$post->content=$_POST['content'];
|
||
$post->save();
|
||
~~~
|
||
|
||
Если полей будет много, мы получим простыню из подобных присваиваний. Этого можно избежать, если
|
||
использовать свойство [attributes|CActiveRecord::attributes] как показано ниже. Подробности можно найти в
|
||
разделах [Безопасное присваивание значений атрибутам](/doc/guide/form.model#securing-attribute-assignments) и
|
||
[Создание действия](/doc/guide/form.action).
|
||
|
||
~~~
|
||
[php]
|
||
// предполагаем, что $_POST['Post'] является массивом значений полей, проиндексированных по имени поля
|
||
$post->attributes=$_POST['Post'];
|
||
$post->save();
|
||
~~~
|
||
|
||
|
||
Сравнение записей
|
||
-----------------
|
||
|
||
Экземпляры AR идентифицируются уникальным образом по значениям первичного ключа, аналогично строкам таблицы, поэтому
|
||
для сравнения двух экземпляров нам нужно просто сравнить значения их первичных ключей, предполагая, что оба экземпляра одного AR-класса.
|
||
Однако можно проделать это еще проще, вызвав метод [CActiveRecord::equals()].
|
||
|
||
> Info|Информация: В отличие от реализации AR в других фреймворках, Yii поддерживает в AR составные первичные ключи.
|
||
Составной первичный ключ состоит из двух и более полей таблицы. Соответственно,
|
||
первичный ключ в Yii представлен как массив, а свойство [primaryKey|CActiveRecord::primaryKey]
|
||
содержит значение первичного ключа для экземпляра AR.
|
||
|
||
Тонкая настройка
|
||
-------------
|
||
|
||
Класс [CActiveRecord] предоставляет несколько методов, которые могут быть переопределены в дочерних классах
|
||
для тонкой настройки работы AR.
|
||
|
||
- [beforeValidate|CModel::beforeValidate] и [afterValidate|CModel::afterValidate]:
|
||
методы вызываются до и после осуществления проверки;
|
||
|
||
- [beforeSave|CActiveRecord::beforeSave] и [afterSave|CActiveRecord::afterSave]:
|
||
методы вызываются до и после сохранения экземпляра AR;
|
||
|
||
- [beforeDelete|CActiveRecord::beforeDelete] и [afterDelete|CActiveRecord::afterDelete]:
|
||
методы вызываются до и после удаления экземпляра AR;
|
||
|
||
- [afterConstruct|CActiveRecord::afterConstruct]: метод вызывается для каждого экземпляра AR,
|
||
созданного с использованием оператора `new`;
|
||
|
||
- [beforeFind|CActiveRecord::beforeFind]: метод вызывается перед тем, как
|
||
finder AR выполнит запрос (например, `find()`, `findAll()`). Данный метод
|
||
доступен с версии 1.0.9.
|
||
|
||
- [afterFind|CActiveRecord::afterFind]: метод вызывается для каждого экземпляра AR,
|
||
созданного в результате выполнения запроса.
|
||
|
||
Использование транзакций с AR
|
||
-------------------------
|
||
|
||
Каждый экземпляр AR содержит свойство [dbConnection|CActiveRecord::dbConnection], которое является экземпляром класса [CDbConnection].
|
||
Соответственно, в случае необходимости можно использовать возможность [транзакций](/doc/guide/database.dao#using-transactions),
|
||
предоставляемую Yii DAO:
|
||
|
||
~~~
|
||
[php]
|
||
$model=Post::model();
|
||
$transaction=$model->dbConnection->beginTransaction();
|
||
try
|
||
{
|
||
// поиск и сохранение — шаги, которые можно разбить третьим запросом
|
||
// соответственно, мы используем транзакцию, чтобы удостовериться в целостности
|
||
$post=$model->findByPk(10);
|
||
$post->title='new post title';
|
||
$post->save();
|
||
$transaction->commit();
|
||
}
|
||
catch(Exception $e)
|
||
{
|
||
$transaction->rollBack();
|
||
}
|
||
~~~
|
||
|
||
Именованные группы условий
|
||
--------------------------
|
||
|
||
> Note|Примечание: именованные группы условий поддерживаются, начиная с версии 1.0.5.
|
||
> Идея групп условий позаимствована у Ruby on Rails.
|
||
|
||
*Именованная группа условий* представляет собой *именованный* критерий запроса,
|
||
который можно использовать с другими группами и применять к запросам AR.
|
||
|
||
Именованные группы чаще всего описываются в методе [CActiveRecord::scopes()]
|
||
парами имя-условие. Приведённый ниже код описывает две именованные группы
|
||
условий для модели `Post`: `published` и `recently`:
|
||
|
||
~~~
|
||
[php]
|
||
class Post extends CActiveRecord
|
||
{
|
||
…
|
||
public function scopes()
|
||
{
|
||
return array(
|
||
'published'=>array(
|
||
'condition'=>'status=1',
|
||
),
|
||
'recently'=>array(
|
||
'order'=>'create_time DESC',
|
||
'limit'=>5,
|
||
),
|
||
);
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Каждая группа описывается массивом, который может быть использован для инициализации
|
||
экземпляра [CDbCriteria]. К примеру, `recently` определяет, что условие `order`
|
||
будет `create_time DESC`, а `limit` будет равен 5. Вместе эти условия означают, что
|
||
будут выбраны 5 последних публикаций.
|
||
|
||
Именованные группы условий обычно используются в качестве модификаторов для
|
||
метода `find`. Можно использовать несколько групп для получения более специфичного
|
||
результата. К примеру, чтобы найти последние опубликованные записи можно использовать
|
||
следующий код:
|
||
|
||
~~~
|
||
[php]
|
||
$posts=Post::model()->published()->recently()->findAll();
|
||
~~~
|
||
|
||
В общем случае, именованные группы условий должны располагаться левее вызова `find`.
|
||
Каждая группа определяет критерий запроса, который совмещается с остальными
|
||
критериями, включая переданные непосредственно методу `find`. Конечный результат
|
||
получается применением к запросу набора фильтров.
|
||
|
||
Начиная с версии 1.0.6, именованные группы условий могут быть использованы в
|
||
методах `update` и `delete`. К примеру, следующий код удалит все недавно
|
||
опубликованные записи:
|
||
|
||
~~~
|
||
[php]
|
||
Post::model()->published()->recently()->delete();
|
||
~~~
|
||
|
||
> Note|Примечание: Именованные группы могут быть использованы только с методами
|
||
класса. Таким образом, метод должен вызываться при помощи `ClassName::model()`.
|
||
|
||
|
||
### Именованные группы условий с параметрами
|
||
|
||
Именованные группы условий могут быть параметризованы. К примеру, нам понадобилось
|
||
задать число публикаций для группы `recently`. Чтобы это сделать,
|
||
вместо того, чтобы описывать группу в методе [CActiveRecord::scopes], нам необходимо
|
||
описать новый метод с таким же именем, как у группы условий:
|
||
|
||
~~~
|
||
[php]
|
||
public function recently($limit=5)
|
||
{
|
||
$this->getDbCriteria()->mergeWith(array(
|
||
'order'=>'create_time DESC',
|
||
'limit'=>$limit,
|
||
));
|
||
return $this;
|
||
}
|
||
~~~
|
||
|
||
После этого, для того, чтобы получить 3 последних опубликованных записи, можно
|
||
использовать следующий код:
|
||
|
||
~~~
|
||
[php]
|
||
$posts=Post::model()->published()->recently(3)->findAll();
|
||
~~~
|
||
|
||
Если не передать параметром 3, то по умолчанию будут выбраны 5 последних опубликованных записей.
|
||
|
||
|
||
### Именованная группа условий по умолчанию
|
||
|
||
Класс модели может содержать именованную группу условий по умолчанию, которая будет
|
||
применяться ко всем запросам (включая реляционные). К примеру, на сайте реализована
|
||
поддержка нескольких языков и содержимое отображается на языке, выбранном пользователем.
|
||
Так как запросов, связанных с получением даннных скорее всего достаточно много,
|
||
для решения этой задачи мы можем определить группу условий по умолчанию.
|
||
Для этого мы перекрываем метод [CActiveRecord::defaultScope] следующим образом:
|
||
|
||
~~~
|
||
[php]
|
||
class Content extends CActiveRecord
|
||
{
|
||
public function defaultScope()
|
||
{
|
||
return array(
|
||
'condition'=>"language='".Yii::app()->language."'",
|
||
);
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Теперь к показанному ниже вызову будет автоматически применены наши условия:
|
||
|
||
~~~
|
||
[php]
|
||
$contents=Content::model()->findAll();
|
||
~~~
|
||
|
||
Заметим, что именованная группа условий по умолчанию применяется только к
|
||
`SELECT` запросам. Она игнорируется в `INSERT`, `UPDATE` и `DELETE` запросах.
|
||
|
||
<div class="revision">$Id: database.ar.txt 2771 2010-12-24 14:48:44Z alexander.makarow $</div>
|