===== Active Record ===== Хотя 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 не предоставляет решения всех задач, касающихся работы с базами данных. Лучше всего использовать AR для моделирования таблиц в конструкциях PHP и для несложных SQL-запросов. В сложных случаях следует использовать Yii DAO. ===== 1. Соединение с базой данных ===== Для работы AR требуется подключение к базе данных. По умолчанию предполагается, что компонент приложения ''db'' предоставляет необходимый экземпляр класса [[https://www.yiiframework.com/doc/api/1.1/CDbConnection|CDbConnection]], который отвечает за подключение к базе. Ниже приведён пример конфигурации приложения: return array( 'components'=>array( 'db'=>array( 'class'=>'system.db.CDbConnection', 'connectionString'=>'sqlite:path/to/dbfile', // включить кэширование схем для улучшения производительности // 'schemaCachingDuration'=>3600, ), ), ); **Подсказка:** Поскольку для получения информации о полях таблицы AR использует метаданные, требуется некоторое время для их чтения и анализа. Если не предполагается, что схема базы данных будет меняться, то следует включить кэширование схемы, установив для атрибута [[https://www.yiiframework.com/doc/api/1.1/CDbConnection#schemaCachingDuration|CDbConnection::schemaCachingDuration]] любое значение больше нуля. В настоящий момент AR поддерживает следующие СУБД: * [[http://www.mysql.com|MySQL 4.1 и выше]] * [[https://mariadb.com|MariaDB]] * [[http://www.postgres.com|PostgreSQL 7.3 и выше]] * [[http://www.sqlite.org|SQLite 2 и 3]] * [[http://www.microsoft.com/sqlserver/|Microsoft SQL Server 2000 и выше]] * [[http://www.oracle.com|Oracle]] Если вы хотите использовать другой компонент вместо ''db'' или предполагаете, используя AR, работать с несколькими БД, то следует переопределить метод [[https://www.yiiframework.com/doc/api/1.1/CActiveRecord#getDbConnection|CActiveRecord::getDbConnection()]]. Класс [[https://www.yiiframework.com/doc/api/1.1/CActiveRecord|CActiveRecord]] является базовым классом для всех классов AR. **Подсказка:** Есть несколько способов для работы AR с несколькими БД. Если схемы используемых баз различаются, то можно создать разные базовые классы AR с различной реализацией метода [[https://www.yiiframework.com/doc/api/1.1/CActiveRecord#getDbConnection|getDbConnection()]]. В противном случае проще будет динамически менять статическую переменную [[https://www.yiiframework.com/doc/api/1.1/CActiveRecord#db|CActiveRecord::db]]. ===== 2. Определение AR-класса ===== Для доступа к таблице БД, прежде всего, требуется определить класс AR путём наследования класса [[https://www.yiiframework.com/doc/api/1.1/CActiveRecord|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'; } } **Подсказка:** Поскольку классы AR часто появляются во многих местах кода, мы можем вместо включения классов по одному, подключить всю директорию с AR-классами. Например, если AR-классы находятся в директории ''protected/models'', мы можем сконфигурировать приложение следующим образом: return array( 'import'=>array( 'application.models.*', ), ); По умолчанию имя AR-класса совпадает с названием таблицы в базе данных. Если они различаются, потребуется переопределить метод [[https://www.yiiframework.com/doc/api/1.1/CActiveRecord#tableName|tableName()]]. Метод [[https://www.yiiframework.com/doc/api/1.1/CActiveRecord#model|model()]] объявляется для каждого AR-класса. **Информация:** В случае использования [[https://www.yiiframework.com/doc/guide/1.1/ru/database.dao#using-table-prefix|префиксов таблиц]] метод [[https://www.yiiframework.com/doc/api/1.1/CActiveRecord#tableName|tableName()]] AR-класса может быть переопределён следующим образом: public function tableName() { return '{{post}}'; } Вместо того чтобы возвращать полное имя таблицы, мы возвращаем имя таблицы без префикса и заключаем его в двойные фигурные скобки.\\ \\ Значения полей в строке таблицы доступны как атрибуты соответствующего экземпляра AR-класса. Например, код ниже устанавливает значение для атрибута ''title'': $post=new Post; $post->title='тестовая запись'; Хотя мы никогда не объявляем заранее свойство ''title'' класса ''Post'', мы, тем не менее, можем обратиться к нему как в коде выше. Это возможно потому, что ''title'' является полем таблицы ''tbl_post'', и [[https://www.yiiframework.com/doc/api/1.1/CActiveRecord|CActiveRecord]] делает его доступным в качестве свойства благодаря магическому методу PHP ''__get()''. Если аналогичным образом обратиться к несуществующему полю, будет выброшено исключение. \\ **Информация:** В данном руководстве мы именуем столбцы и таблицы в нижнем регистре, так как различные СУБД работают с регистрозависимыми именами по-разному. Например, PostgreSQL считает имена столбцов регистронезависимыми по умолчанию, и мы должны заключать их в кавычки в условиях запроса, если имена содержат заглавные буквы. Использование нижнего регистра помогает избежать подобных проблем. \\ AR опирается на правильно определённые первичные ключи таблиц БД. Если в таблице нет первичного ключа, то требуется указать в соответствующем классе AR столбцы, которые будут использоваться как первичный ключ. Сделать это можно путём перекрытия метода primaryKey(): public function primaryKey() { return 'id'; // Для составного первичного ключа следует использовать массив: // return array('pk1', 'pk2'); } ===== 3. Создание записи ===== Для добавления новой строки в таблицу БД нам необходимо создать новый экземпляр соответствующего класса, присвоить значения атрибутам, ассоциированным с полями таблицы, и вызвать метод 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(); Подсказка: Несмотря на то что AR позволяет производить различные операции без написания громоздкого SQL, часто необходимо знать, какой SQL выполняется на самом деле. Для этого необходимо включить журналирование. Например, чтобы выводить выполненные SQL-запросы в конце каждой страницы, мы можем включить [[https://www.yiiframework.com/doc/api/1.1/CWebLogRoute|CWebLogRoute]] в настройках приложения. Можно установить значение параметра [[https://www.yiiframework.com/doc/api/1.1/CDbConnection#enableParamLogging|CDbConnection::enableParamLogging]] в true для отображения значений параметров запросов. ===== 4. Чтение записи ===== Для чтения данных из таблицы базы данных можно использовать методы 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)); Примечание: В примере выше нам может понадобиться заключить в кавычки обращение к столбцу postID для некоторых СУБД. Например, если мы используем СУБД PostgreSQL, нам следует писать условие как "postID"=:postID, потому что PostgreSQL по умолчанию считает имя столбца регистронезависимым. Кроме того, можно использовать $condition для указания более сложных условий запроса. Вместо строки параметр $condition может быть экземпляром класса [[yii:yii1:cdbcriteria:примеры_использования|CDbCriteria]], который позволяет указать иные условия помимо выражения WHERE. Например: $criteria=new CDbCriteria; $criteria->select='title'; // выбираем только поле 'title' $criteria->condition='postID=:postID'; $criteria->params=array(':postID'=>10); $post=Post::model()->find($criteria); // $params не требуется Обратите внимание, если в качестве условия запроса используется [[yii:yii1:cdbcriteria:примеры_использования|CDbCriteria]], то параметр $params уже не нужен, поскольку его можно указать непосредственно в [[yii:yii1:cdbcriteria:примеры_использования|CDbCriteria]], как показано выше.\\ \\ Помимо использования [[yii:yii1:cdbcriteria:примеры_использования|CDbCriteria]], есть другой способ указать условие — передать методу массив ключей и значений, соответствующих именам и значениям свойств критерия. Пример выше можно переписать следующим образом:\\ $post=Post::model()->find(array( 'select'=>'title', 'condition'=>'postID=:postID', 'params'=>array(':postID'=>10), ));