312 lines
16 KiB
Markdown
312 lines
16 KiB
Markdown
|
# 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');
|