Хотя DAO Yii справляется практически с любыми задачами, касающимися работы с БД, почти наверняка 90% времени уйдёт на написание SQL-запросов, реализующих общие операции CRUD (создание, чтение, обновление и удаление). Кроме того, код, перемешанный с SQL-выражениями, поддерживать проблематично. Для решения этих проблем мы можем воспользоваться Active Record.
Active Record реализует популярный подход объектно-реляционного проецирования (ORM). Каждый класс AR отражает таблицу (или представление) базы данных, экземпляр AR — строку в этой таблице, а общие операции CRUD реализованы как методы AR. В результате мы можем использовать более объектно-ориентированный подход доступа к данным. Например, используя следующий код, можно вставить новую строку в таблицу tbl_post:
$post=new Post; $post->title='тестовая запись'; $post->content='содержимое записи'; $post->save();
Ниже мы покажем, как настроить и использовать AR для реализации CRUD-операций, а в следующем разделе — как использовать AR для работы со связанными таблицами. Для примеров в этом разделе мы будем использовать следующую таблицу. Обратите внимание, что при использовании БД MySQL в SQL-выражении ниже AUTOINCREMENT следует заменить на AUTO_INCREMENT.
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 );
Для работы AR требуется подключение к базе данных. По умолчанию предполагается, что компонент приложения db предоставляет необходимый экземпляр класса CDbConnection, который отвечает за подключение к базе. Ниже приведён пример конфигурации приложения:
return array( 'components'=>array( 'db'=>array( 'class'=>'system.db.CDbConnection', 'connectionString'=>'sqlite:path/to/dbfile', // включить кэширование схем для улучшения производительности // 'schemaCachingDuration'=>3600, ), ), );
В настоящий момент AR поддерживает следующие СУБД:
Если вы хотите использовать другой компонент вместо db или предполагаете, используя AR, работать с несколькими БД, то следует переопределить метод CActiveRecord::getDbConnection(). Класс CActiveRecord является базовым классом для всех классов AR.
Для доступа к таблице БД, прежде всего, требуется определить класс AR путём наследования класса CActiveRecord. Каждый класс AR представляет одну таблицу базы данных, а экземпляр класса — строку в этой таблице. Ниже приведён минимальный код, требуемый для определения класса AR, представляющего таблицу tbl_post.
class Post extends CActiveRecord { public static function model($className=__CLASS__) { return parent::model($className); } public function tableName() { return 'tbl_post'; } }
protected/models, мы можем сконфигурировать приложение следующим образом:
return array( 'import'=>array( 'application.models.*', ), );
По умолчанию имя AR-класса совпадает с названием таблицы в базе данных. Если они различаются, потребуется переопределить метод tableName(). Метод model() объявляется для каждого AR-класса.
public function tableName()
{
return '{{post}}';
}
Вместо того чтобы возвращать полное имя таблицы, мы возвращаем имя таблицы без префикса и заключаем его в двойные фигурные скобки.
Значения полей в строке таблицы доступны как атрибуты соответствующего экземпляра AR-класса. Например, код ниже устанавливает значение для атрибута title:
$post=new Post; $post->title='тестовая запись';
Хотя мы никогда не объявляем заранее свойство title класса Post, мы, тем не менее, можем обратиться к нему как в коде выше. Это возможно потому, что title является полем таблицы tbl_post, и CActiveRecord делает его доступным в качестве свойства благодаря магическому методу PHP __get(). Если аналогичным образом обратиться к несуществующему полю, будет выброшено исключение.
AR опирается на правильно определённые первичные ключи таблиц БД. Если в таблице нет первичного ключа, то требуется указать в соответствующем классе AR столбцы, которые будут использоваться как первичный ключ. Сделать это можно путём перекрытия метода primaryKey():
public function primaryKey() { return 'id'; // Для составного первичного ключа следует использовать массив: // return array('pk1', 'pk2'); }
Для добавления новой строки в таблицу БД нам необходимо создать новый экземпляр соответствующего класса, присвоить значения атрибутам, ассоциированным с полями таблицы, и вызвать метод save() для завершения добавления.
$post=new Post; $post->title='тестовая запись'; $post->content='содержимое тестовой записи'; $post->create_time=time(); $post->save();
Если первичный ключ таблицы автоинкрементный, то после добавления экземпляр AR будет содержать обновленное значение первичного ключа. В примере выше свойство id всегда будет содержать значение первичного ключа новой записи.
Если поле задано в схеме таблицы с некоторым статическим значением по умолчанию (например, строка или число), то после создания экземпляра соответствующее свойство экземпляра AR будет автоматически содержать это значение. Один из способов поменять это значение — прописать его в AR-классе.
class Post extends CActiveRecord { public $title='пожалуйста, введите заголовок'; … } $post=new Post; echo $post->title; // отобразится: пожалуйста, введите заголовок
До сохранения записи (добавления или обновления) атрибуту может быть присвоено значение типа CDbExpression. Например, для сохранения текущей даты, возвращаемой функцией MySQL NOW(), можно использовать следующий код:
$post=new Post; $post->create_time=new CDbExpression('NOW()'); // $post->create_time='NOW()'; этот вариант работать не будет // т.к. значение 'NOW()' будет воспринято как строка $post->save();
Для чтения данных из таблицы базы данных можно использовать методы find:
// найти первую строку, удовлетворяющую условию $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. Например:
// найдём строку, для которой postID равен 10 $post=Post::model()->find('postID=:postID', array(':postID'=>10));
Кроме того, можно использовать $condition для указания более сложных условий запроса. Вместо строки параметр $condition может быть экземпляром класса CDbCriteria, который позволяет указать иные условия помимо выражения WHERE. Например:
$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, есть другой способ указать условие — передать методу массив ключей и значений, соответствующих именам и значениям свойств критерия. Пример выше можно переписать следующим образом:
$post=Post::model()->find(array( 'select'=>'title', 'condition'=>'postID=:postID', 'params'=>array(':postID'=>10), ));