Выдано 110027 лицензий

ORM

В HostCMS реализован быстрый и гибкий ORM на основе паттерна Active Record с поддержкой ленивой загрузки.

ORM поддерживает следующие виды связей: hasOne (в том числе через промежуточную таблицу), hasMany (в том числе через промежуточную таблицу) и belongsTo.

В системе управления ORM реализован классом Core_ORM, от которого порожден класс Core_Entity с реализацией дополнительной логики работы, например, генерации XML.

Все модели сущностей порождены от Core_Entity, сам Core_ORM явно не используется.

Имена таблиц для моделей преобразуются во множественное число по правилам английского языка, например, для модели Book_Model используется таблица books, для Property_Model таблица properties.

Что такое ORM?

ORM является технологией, которая связывает БД с концепцией ООП. Реализовываться может с помощью шаблонов программирования, мы выбрали Active Record.

Что мне это дает?

Простую и безопасную работу с объектами БД через объекты PHP.

Примеры работы с ORM

Создадим таблицу `books` со столбцами:

id INT(11) PK
name VA(255)
value VA(100)
deleted tinyint(1)

Создадим модель Book_Model, которую поместим в modules/book/model.php со следующим кодом:

class Book_Model extends Core_Entity
{
}

Получение существующего элемента или создание нового осуществляется через фабрику (более подробно о фабриках можно прочитать в сети по запросу "Порождающий шаблон проектирования Фабричный метод").

Далее идут несколько примеров работы с ORM, настоятельно рекомендуем поработать с ними.

Атрибут deleted

Используются два механизма - пометка на удаление markDeleted() и окончательное удаление delete(), все стандартные выборки с использованием findAll() выбирают не помеченные на удаление объекты. Окончательно удалить или восстановить объект можно через модуль "Корзина".

В случае, если вы не хотите использовать для модели пометку на удаление, в файл модели добавьте свойство $_marksDeleted:

	/**
	 * Disable markDeleted()
	 * @var mixed
	 */
	protected $_marksDeleted = NULL;

Создание объектов

Создание нового объекта Book_Model

$oBook = Core_Entity::factory('Book');

Создание объекта Book_Model и загрузка в объект данных из таблицы, где первичный ключ равен 1

$oBook = Core_Entity::factory('Book', 1);

Изменение и сохранение объекта

Изменение свойства объекта возможно двумя способами: установкой значения свойства или вызовом метода с названием свойства и передачей ему аргумента в качестве нового значения:

$oBook = Core_Entity::factory('Book', 1);
$oBook->value = 123;
$oBook->save();

Второй способ с method chaining:

$oBook = Core_Entity::factory('Book', 1);
$oBook->value(123)->save();
При установке значения свойства через вызов метода с названием свойства убедитесь, что в модели не переопределен метод с именем свойства!

Поиск объектов

Получим все объекты модели Book_Model с использованием метода findAll(). Метод принимает необязательный параметр кэширования, по умолчанию TRUE (кэширование включено).

$aBooks = Core_Entity::factory('Book')->findAll();

foreach ($aBooks as $oBook)
{
   // do something
   echo "<p>" . $oBook->name . "</p>";
}

Поиск объектов с заданием дополнительного условия через QueryBuilder и отключением кэширования результатов запроса

$oBooks = Core_Entity::factory('Book');
$oBooks->queryBuilder()
   ->where('value', '=', 99);
$aBooks = $oBooks->findAll(FALSE);

foreach ($aBooks as $oBook)
{
   // do something
echo "<p>" . $oBook->name . "</p>"; }

Связи с использованием ORM

Связи указываются в модели и позволяют получать быстрый доступ к связанным и зависимым объектам.

Связь один-ко-многим

Используется, когда у объекта текущей модели есть множество зависимых элементов, в примере одна книга имеет несколько комментариев.

В файл моделиmodules/book/model.php добавим описание связи:

// Relation one-to-many for Book-Comments:
protected $_hasMany = array('comment' => array());

Аналогичная связь один-ко-многим с подробным заполнением параметров связи

// Equivalence relation one-to-many for Book-Comments with detailed conditions:
// comment - Model name
// foreign_key - Foreign key
protected $_hasMany = array('comment' => array(
         'foreign_key' => 'book_id'
   ));

Для получения элементов из связи 1:М указывается название связи с большой буквы и во множественной форме, например, для comment будет Comments:

$oBook = Core_Entity::factory('Book', 1);
$aComments = $oBook->Comments->findAll();

Связь многие-ко-многим

Множеству объектов текущей модели могут соответствовать множество элементов другой, в примере один и тот же автор может быть у разных книг, и у одной книги может быть несколько авторов.

В файл моделиmodules/book/model.php добавим описание связи:

// Relation many-to-many for Book-Authorss through model 'books_author':
protected $_hasMany = array('author' => array(
         'through' => 'books_author'
   ));

Аналогичная связь многие-ко-многим с подробным заполнением параметров связи

// Relation many-to-many for Book-Authorss through model 'books_author' with detailed conditions:
protected $_hasMany = array('author' => array(
         'foreign_key' => 'book_id,
         'through' => 'books_author',
         'dependent_key' => 'author_id'
   ));

Для получения элементов из связи М:М указывается название связи с большой буквы и во множественной форме, например:

// Получение авторов книги c ID 17
$oBook = Core_Entity::factory('Book', 17); $aAuthors = $oBook->Authors->findAll();
// Получение книг автора с ID 19
$oAuthor = Core_Entity::factory('Author', 19); $aBooks = $oAuthor->Books->findAll();

Связь один-к-одному

В файл моделиmodules/book/model.php добавим описание связи:

// Relation one-to-one for Book-Comment:
protected $_hasOne = array('comment' => array());

Аналогичная связь один-к-одному с подробным заполнением параметров связи

// Equivalence relation one-to-one for Book-Comment with detailed conditions:
// comment - Model name
// foreign_key - Foreign key
protected $_hasOne = array('comment' => array(
                  'foreign_key' => 'book_id'
   ));

Для получения элементов из связи 1:1 указывается название связи с большой буквы, например:

$oBook = Core_Entity::factory('Book', 1);
$oComment = $oBook->Comment;

Связь "belongs to"

Объект зависим от другого объекта и содержит в себе внешний ключ.

В файл моделиmodules/book/model.php добавим описание связи с таблицей sections (в таблице books должен быть внешний ключ section_id):

// Belongs to relation for Section-Book:
protected $_belongsTo = array('section' => array());

Аналогичная связь "belongs to" с подробным заполнением параметров связи

// Equivalence belongs to relation for Section-Book with detailed conditions:
// section - Model name
// foreign_key - Foreign key
// primary_key - Primary key in the parent table
protected $_belongsTo = array('section' => array(
                  'foreign_key' => 'section_id',
                  'primary_key' => 'id'
   ))

Для получения элементов из связи указывается название связи с большой буквы, например:

$oBook = Core_Entity::factory('Book', 1);
$oSection = $oBook->Section;

Методы-перехватчики

Core_Entity поддерживает удобные методы-перехватчики (где «FieldName» — имя столбца в таблице, «$value» — значение):

  • getByFieldName($value, $bCache = TRUE, $compare = '=') — возвращает один найденный объект или NULL;
  • getAllByFieldName($value, $bCache = TRUE, $compare = '=') — возвращает массив с элементами или пустой массив;
  • getCountByFieldName($value, $bCache = TRUE, $compare = '=') — возвращает количество найденных элементов без выборки самих элементов.
//Implement methods like getByXXX($value, $cache = TRUE) where XXX is the field name
$object = Core_Entity::factory('Book', 1)->getByName('The Catcher in the Rye');

//Implement methods like getAllByXXX($value, $cache = TRUE) where XXX is the field name
$aObject = Core_Entity::factory('Book', 1)->getAllByName('The Catcher in the Rye');

Методы-перехватчики можно также использовать для связи. Получим активные комментарии книги с использование QueryBuilder:

// С использованием QueryBuilder
$oComments = Core_Entity::factory('Book', 1)->Comments;
$oComments->queryBuilder()
	->where('active', '=', 1);
$aComments = $oComments->findAll();

и с использованием метода-перехватчика:

// С использованием метода-перехватчика
$aComments = Core_Entity::factory('Book', 1)->Comments->getAllByActive(1);

Получение количества объектов

Метод getCount() используется для получения количества найденных элементов без выбора самих элементов. Дополнительный метод-перехватчик getCountByFieldName($value) используется для получения количества найденных объектов с применением дополнительного ограничения по столбцу и значению.

// Количество комментариев к книге
$iCount = Core_Entity::factory('Book', 1)->Comments->getCount();

и с использованием метода-перехватчика:

// Количество активных комментариев к книге
$iCount = Core_Entity::factory('Book', 1)->Comments->getCountByActive(1);

Подсчет количества найденных объектов

Для подсчета количества найденных элементов при использовании ограничений выборки limit()/offset() используется метод sqlCalcFoundRows(). До вызова findAll() необходимо загрузить структуру модели через getTableColums(), в противном случае запрос на получение структуры модели может стать между запросом на выбору и FOUND_ROWS()

$oBooks = Core_Entity::factory('Book');

// Загружаем структуру модели до FOUND_ROWS()
Core_Entity::factory('Book')->getTableColums();

// Включаем подсчет количества всех объектов, подходящих под ограничения, ограничиваем выборку
$oBooks->queryBuilder()
    ->sqlCalcFoundRows()
    ->offset(0)
    ->limit(10);

// Выбираем элементы
$aBooks = $oBooks->findAll();

// Подсчет количества найденных элементов
$row = Core_QueryBuilder::select(array('FOUND_ROWS()', 'count'))->execute()->asAssoc()->current();
echo "<p>Всего найдено: " . $row['count'];

// Вывод найденных объектов
foreach ($aBooks as $oBook)
{
   // do something
   echo "<p>" . $oBook->name . "</p>";
}

Кэширование

Метод findAll(), getCount() и методы-перехватчики имеют встроенный кэш, исключающий повторное выполнение запросов при указании одних и тех же условий выборки. В случае, если Вы произвели вставку или удаление объекта, содержащего в предыдущей выборке, может понадобится игнорирование кэша. Для этого в метод findAll() первым параметром, а в методы-перехватчики вторым параметром передайте значение FALSE.

$aBooks = Core_Entity::factory('Book')->findAll(FALSE);

//Implement methods like getByXXX($value, $cache = TRUE) where XXX is the field name
$object = Core_Entity::factory('Book')->getByName('The Catcher in the Rye', FALSE);

//Implement methods like getAllByXXX($value, $cache = TRUE) where XXX is the field name
$aObject = Core_Entity::factory('Book')->getAllByName('The Catcher in the Rye', FALSE);

Ленивая загрузка / Lazy Load

Использование ленивой загрузки позволяет не загружать данные из таблицы до тех пор, пока они не понадобятся на самом деле. Создание объекта с указанием его идентификатора не приводит к возникновению запроса на выборку данных.

$oEntity = Core_Entity::factory('Entity', 123);

// Покажет 123, эти даныне уже заданы и не требуется обращаться к таблице
var_dump($oEntity->id);

// Выполнит запрос к таблице, если данные в объект не были загружены ранее
// Отобразит значение или NULL в случае, если объекта с указанным ID не существует
// При этом если объекта не существует, то ID также будет сброшен в NULL
var_dump($oEntity->name);

Создать объект с загрузкой данных из таблицы можно через метод find():

// Выполнит запрос к таблице
$oEntity = Core_Entity::factory('Entity')->find(123);

// Покажет 123, если объект с указанным ID существует, в противном случае ID будет сброшен в NULL
var_dump($oEntity->id);
Не злоупотребляйте использованием метода find(), используется только в случае прямой необходимости

Комментарии

  • krapivam

    Без темы

    Поправьте если не так понял документацию. Думаю, что для получения значений массива $aComments должен использоваться метод getAllByActive(1) а не getByActive(1) и код должен выглядеть так // С использованием метода-перехватчика $aComments = Core_Entity::factory('Book')->Comments->getAllByActive(1);

    17.03.2013 19:38:57 krapivam
    Levsha

    Спасибо!

    Согласен, в статье ошибка, я бы долго искал причину, если бы не Ваш комментарий.

    14.04.2015 13:56:07 Levsha
  • Без темы

    Дак система для опытных програмистов

    10.12.2012 13:41:29 Сергей
  • Подробности

    Как начсет того, чтобы составить самоучитель по системе, что-то вроде: «HostCMS от А до Я», начиная с понятия переменной и т.д., иначе нужно иметь достаточно высокий уровень знаний в програмировании, чтобы все это понимать. Я бы приобрел такую книгу.

    15.11.2012 23:03:53 sersh