# ORM {#top} Kohana 3.0 включает мощный модуль ORM, который использует паттерн Active Record и автоопределение информации о списке полей БД модели. Модуль ORM включен в дистрибутив Kohana 3.0, но нуждается в подключении перед его использованием. В файле `application/bootstrap.php` отредактируйте вызов [Kohana::modules] и добавьте модуль ORM: Kohana::modules(array( ... 'orm' => MODPATH.'orm', ... )); ## Настройка {#configuration} ORM требует небольшую настройку перед использованием. Наследуйте Вашу модуль от ORM: class Model_User extends ORM { ... } В примере выше, модель будет искать таблицу `users` в БД по умолчанию. ### Свойства модели, отвечающие за конфигурацию Следующие свойства используются для настройки каждой модели: Тип | Название | Описание | Значение по умолчанию ----------|---------------------|-------------------------------------|--------------------------------- `string` | _table_name | Используемая таблица БД | `имя модели в единственном числе` `string` | _db | Название БД | `default` `string` | _primary_key | Поле - первичный ключ | `id` `string` | _primary_val | Титульное поле | `name` `bool` | _table_names_plural | Имя таблицы во множественном числе | `TRUE` `array` | _sorting | Сортировка (столбец => направление) | `primary key => ASC` `string` | _foreign_key_suffix | Суффикс внешнего ключа | `_id` ## Использование ORM ### Загрузка записи Для создания экземпляра модели используйте метод [ORM::factory] или конструктор [ORM::__construct]: $user = ORM::factory('user'); // или $user = new Model_User(); Конструктор и фабричный метод также поддерживают значение первичного ключа для загрузки конкретной записи: // Загружаем пользователя с ID 5 $user = ORM::factory('user', 5); // Проверяем успешность загрузки объекта пользователя if ($user->loaded()) { ... } Опционально, Вы можете передать массив с парами ключ => значение для загрузки данных объекта по совпадающим критериям, указанным в массиве: // Загрузка пользователя с email joe@example.com $user = ORM::factory('user', array('email' => 'joe@example.com')); ### Поиск записи ORM поддерживает большинство методов класса [Database] для полноценного поиска данных модели. В свойстве `_db_methods` перечислен полный список поддерживаемых методов. Записи извлекаются после вызовов [ORM::find] или [ORM::find_all]. // Извлекаем первого активного пользователя по имени Bob $user = ORM::factory('user') ->where('active', '=', TRUE) ->where('name', '=', 'Bob') ->find(); // Ищем всех активных пользователей по имени Bob $users = ORM::factory('user') ... ->find_all(); Когда Вы запрашиваете список моделей через [ORM::find_all], перебирать его можно аналогично обычным выборкам из БД: foreach ($users as $user) { ... } Мощным инструментом ORM является метод [ORM::as_array], который возвращает полученные записи в виде массива. При использовании совместно с [ORM::find_all], будет возвращён массив всех записей. Хороший пример использование этого метода - когда необходимо передать значения для выпадающего списка: // Отображается выпадающий список пользователей // (используется id в качестве значения select option) form::select('user', ORM::factory('user')->find_all()->as_array('id', 'username') ... ### Подсчёт записей Для получения количества записей для данного запроса, используйте [ORM::count_all]. // Число активных пользователей $count = ORM::factory('user')->where('active', '=', TRUE)->count_all(); Если требуется подсчитать общее количество пользователей для данного запроса при лимитировании количества возвращаемых записей, используйте метод [ORM::reset] с параметром `FALSE` перед использованием `count_all`: $user = ORM::factory('user'); // Общее число пользователей (reset FALSE предотвращает объект от очистки перез запросом) $count = $user->where('active', '=', TRUE)->reset(FALSE)->count_all(); // Получаем только первые 10 результатов $users = $user->limit(10)->find_all(); ### Доступ к свойствам модели Все свойства модели доступны через "магические" методы `__get` и `__set`. $user = ORM::factory('user', 5); // Выводит имя пользователя echo $user->name; // Изменяет имя пользователя $user->name = 'Bob'; Для хранения данных/свойств, которые отсутствуют в таблице модели, надо использовать атрибут `_ignored_columns`. class Model_User extends ORM { ... protected $_ignored_columns = array('field1', 'field2', ...) ... } Множественные пары ключ => значение могут быть заданы с использованием метода [ORM::values]: $user->values(array('username' => 'Joe', 'password' => 'bob')); ### Создаем и сохраняем записи Метод [ORM::save] используется как для создания новых записей, так и для обновления существующих. // Создаем запись $user = ORM::factory('user'); $user->name = 'New user'; $user->save(); // Редактируем запись $user = ORM::factory('user', 5); $user->name = 'User 2'; $user->save(); // Проверяем, сохранена ли запись if ($user->saved()) { ... } Вы можете обновить множество записей с помощью метода [ORM::save_all]: $user = ORM::factory('user'); $user->name = 'Bob'; // Все активные пользователи получат имя 'Bob' $user->where('active', '=', TRUE)->save_all(); #### Использование `Updated` и `Created` для столбцов БД Свойства `_updated_column` и `_created_column` позволяют производить автоматическое обновление модели при её обновлении и сохранении. Это используется не по-умолчанию. Чтобы использовать эти свойства, следует их указать: // date_created является столбцом таблицы, в котором хранится дата создания. // Для сохранения метки времени, используем TRUE protected $_created_column = array('date_created' => TRUE); // date_modified является столбцом таблицы, в котором хранится дата изменения. // В этом случае используется строка, определяющая формат для функции date() protected $_updated_column = array('date_modified' => 'm/d/Y'); ### Удаление записей Записи удаляются методами [ORM::delete] и [ORM::delete_all]. Эти методы работают аналогично описанному выше сохранению, за исключением того, что [ORM::delete] принимает необязательный параметр `id` для удаляемой записи. ### Отношения ORM предоставляет мощную поддержку отношений таблиц. Прочитать про отношения можно в [справочнике по Ruby](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html) #### Belongs-To и Has-Many Допустим, мы работаем со школой, которая имеет много учеников (has many). Каждый студент приписан к одной школе (принадлежит - belongs to). Необходимо определить отношения моделей следующим образом: // В модели school protected $_has_many = array('students' => array()); // В модели student protected $_belongs_to = array('school' => array()); Получаем информацию о школе студента: $school = $student->school; Ищем всех студентов школы: // Учтите, что после students следует вызвать метод find_all $students = $school->students->find_all(); // Чтобы сузить результаты поиска: $students = $school->students->where('active', '=', TRUE)->find_all(); По-умолчанию, ORM будет искать поле `school_id` в таблице модели student. Это можно изменить, используя аттрибут `foreign_key`: protected $_belongs_to = array('school' => array('foreign_key' => 'schoolID')); Внешний ключ будет перегружен как в модели student, так и в school. #### Has-One Has-One - это частный случай Has-Many, единственным отличаем которого является то, что в отношении участвует только одна запись. В дополнении к приведённому выше примеру школы, каждая школа будет иметь (has-one) только одного директора, который принадлежит (belongs-to) школе. // Inside the school model protected $_has_one = array('principal' => array()); Как и для Belongs-To, Вам не нужно использовать метод `find` для получение объекта, ссылающегося на Has-One объект - это будет сделано автоматически. #### Has-Many "через" (through) Отношение Has-Many "through" (так же известное как Has-And-Belongs-To-Many) оспользуется в случае если объект связан с несколькими объектами другого типа и наоборот. Например, студент записан на многие занятия и на занятие ходит много студентов. В этом случаеи используется `промежуточная` таблица. Используем для нашего примера промежуточную таблицу и модель - журнал (`enrollment`). // В модели student protected $_has_many = array('classes' => array('through' => 'enrollment')); // В модели class protected $_has_many = array('students' => array('through' => 'enrollment')); Таблица enrollment должна содержать 2 внешних ключа: для занятий `class_id` и для студентов `student_id`. Наименование внешних и дальних ключей (`foreign_key` и `far_key`) могут быть переопределены при определении отношений. Например: // В модели student (внешний ключ ссылается на модель student, // в то время, как дальний ключ - на class) protected $_has_many = array( 'classes' => array( 'through' => 'enrollment', 'foreign_key' => 'studentID', 'far_key' => 'classID' )); // В модели class protected $_has_many = array( 'students' => array( 'through' => 'enrollment', 'foreign_key' => 'classID', 'far_key' => 'studentID' )); Определяем в модели enrollment: // Журнал принадлежит как студенту, так и занятию protected $_belongs_to = array('student' => array(), 'class' => array()); Для доступа к связанным объектам: // Для получение занятий студента $student->classes->find_all(); // Для получения студентов, записанных на занятие $class->students->find_all(); ### Валидация ORM тесно взимодействует с [Validate] библиотекой, позволяя использовать возможности этого класса в следующих свойствах: * _rules * _callbacks * _filters * _labels #### `_rules` protected $_rules = array ( 'username' => array('not_empty' => array()), 'email' => array('not_empty' => array(), 'email' => array()), ); `username` будет проверяться на то, что значение этого поля не является пустым. `email` поле будет проверено на соответствие значения валидному email адресу. Ввиду возможности передачи дополнительных опций для правил, значения правил задаются как пустые массивы. #### `_callbacks` protected $_callbacks = array ( 'username' => array('username_unique'), ); Значение поля `username` будет передано методы `username_unique`. Если метод не существует в текущей модели, то будет вызвана глобальная функция. Вот пример описания этого метода: public function username_unique(Validate $data, $field) { // Логика, проверяющая уникальность имени пользователя ... } #### `_filters` protected $_filters = array ( TRUE => array('trim' => array()), 'username' => array('stripslashes' => array()), ); `TRUE` Указывает на то, что фильтр `trim` будет применён ко всем полям. Значение поля `username` будет отфильтровано с помощью функции `stripslashes` перед процессом валидации. Ввиду возможности передачи дополнительных опций для фильтров, значения фильтра задаются как пустые массивы. #### Проверка объекта Для проверки объекта, используйте [ORM::check]: // Задание значений объекта и дальнейшая их валидация if ($user->values($_POST)->check()) { $user->save(); } Для доступа к объекту валидации данной модели, можно использовать метод `validate()`: // Ручное добавление дополнительного фильтра $user->validate()->filter('username', 'trim');