16 KiB
ORM
Kohana 3.0 включает мощный модуль ORM, который использует паттерн Active Record и автоопределение информации о списке полей БД модели.
Модуль ORM включен в дистрибутив Kohana 3.0, но нуждается в подключении перед его использованием. В файле application/bootstrap.php
отредактируйте вызов [Kohana::modules] и добавьте модуль ORM:
Kohana::modules(array(
...
'orm' => MODPATH.'orm',
...
));
Настройка
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
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');