Выход версии 0.5 для меня было нечто большим, чем добавление страницы активности и ленты топиков из подписанных блогов. В новой версии реализованы ORM и ActiveRecord. Вместе они дают мощнейший инструментарий для разработчика, избавляя того от кучи однотипного кода, который приходилось писать каждый раз при разработке плагина. Тот-же форум, о котором будет идти речь в статье, после обновления похудел на 2177 строк кода. В этой статье я хочу углубиться в ORM и AR на примере создания плагина для LiveStreet.
Почти год назад пользователь runawayed опубликовал статью о новшествах в транковой версии.
Отталкивался я от той статьи, но она всего-лишь вводная часть того инструментария. Там нет ни слова о кеше, постраничности, древовидного вида массива. Я постараюсь подать как можно больше информации читателю не нагружая его лишним. Начнем?
Я буду рассматривать все на примере плагина forum. Для начала нам надо создать скелет плагина:

Исходники файлов можно посмотреть на гитхабе, на описании плагина я останавливаться не буду.
Теперь создадим модуль с именем forum.

Нам надо описать модуль как ModuleORM, и, в методе Init нам надо наследовать родителя метода Init у обычного модуля:
Прежде чем описывать сущности, надо обдумать все связи между ними, чтобы было легче манипулировать через геттеры:

И описать созданные сущности:
Category.entity.class.php
Forum.entity.class.php
Topic.entity.class.php
Post.entity.class.php
Read.entity.class.php
Теперь всем этим можно манипулировать без описания классов в модуле плагина, например:
Фильтр #cache также может принимать следующиме параметры:
Есть несколько типов связей:
После описания сущностей, нам будут доступные следующие методы:
Таблицы в базе данных должны называться следующим образом: prefix_<module-name>_<entity-name> (или прописать их названия в конфиге), а если же название сущности совпадает с названием модуля, то достаточно назвать таблицу так: prefix_<module-name>. Поля тоже имеют свои стандарты: <entity-name>_<field-name>, или просто <field-name>.
Вот и все, господа, спасибо за уделенное внимание. Напоминаю, что свежие исходники форума можно посмотреть в git'е.
UPD: Сделал корректировки в статье, спасибо ort'у за его замечания.
Почти год назад пользователь runawayed опубликовал статью о новшествах в транковой версии.
Отталкивался я от той статьи, но она всего-лишь вводная часть того инструментария. Там нет ни слова о кеше, постраничности, древовидного вида массива. Я постараюсь подать как можно больше информации читателю не нагружая его лишним. Начнем?
Я буду рассматривать все на примере плагина forum. Для начала нам надо создать скелет плагина:

Исходники файлов можно посмотреть на гитхабе, на описании плагина я останавливаться не буду.
Теперь создадим модуль с именем forum.

Нам надо описать модуль как ModuleORM, и, в методе Init нам надо наследовать родителя метода Init у обычного модуля:
class PluginForum_ModuleForum extends ModuleORM { public function Init() { parent::Init(); } }
Прежде чем описывать сущности, надо обдумать все связи между ними, чтобы было легче манипулировать через геттеры:

И описать созданные сущности:
Category.entity.class.php
class PluginForum_ModuleForum_EntityCategory extends EntityORM {}
Forum.entity.class.php
class PluginForum_ModuleForum_EntityForum extends EntityORM { protected $aRelations = array( 'category'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'PluginForum_ModuleForum_EntityCategory','category_id'), 'user'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'ModuleUser_EntityUser','user_id'), 'topic'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'PluginForum_ModuleForum_EntityTopic','topic_id'), 'post'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'PluginForum_ModuleForum_EntityPost','post_id'), ); }
Topic.entity.class.php
class PluginForum_ModuleForum_EntityTopic extends EntityORM { protected $aRelations = array( 'user'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'ModuleUser_EntityUser','user_id'), 'post'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'PluginForum_ModuleForum_EntityPost','post_id'), 'forum'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'PluginForum_ModuleForum_EntityForum','forum_id'), ); }
Post.entity.class.php
class PluginForum_ModuleForum_EntityPost extends EntityORM { protected $aRelations = array( 'user'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'ModuleUser_EntityUser','user_id'), 'topic'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'PluginForum_ModuleForum_EntityTopic','topic_id'), 'forum'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'PluginForum_ModuleForum_EntityForum','forum_id'), ); }
Read.entity.class.php
class PluginForum_ModuleForum_EntityRead extends EntityORM {}
Теперь всем этим можно манипулировать без описания классов в модуле плагина, например:
$this->PluginForum_ModuleForum_GetCategoryItemsAll(); // получим все категории
$this->PluginForum_ModuleForum_GetForumItemsByCategoryId($oCategory->getId()); // получим все форумы по ID категории
$this->PluginForum_ModuleForum_GetTopicItemsByForumId($oForum->getId(),array('#page' => array(1,15), '#cache'=>'')); /** * Получим массив для вывода с постраничностью и без кеширования запроса вида: * array( * 'collection' => array of objects * 'count' => integer * ); * Где array('#page' => array('номер страницы', 'элементов на страницу')) * Где array('#cache' => '') отказ от кеширования запроса */
Фильтр #cache также может принимать следующиме параметры:
array( '#cache' => array( 'keys', 'tags', 'time' ) );
Есть несколько типов связей:
protected $aRelations = array( 'entity'=>array(EntityORM::RELATION_TYPE_BELONGS_TO,'ModuleSome_EntitySome','field_id'), // Получаем один элемент по одному элементу. Например, в таблице prefix_forum_topic есть поле с ID'шником пользователя, тогда мы получим юзера по ID с этого поля 'entity'=>array(EntityORM::RELATION_TYPE_HAS_MANY,'ModuleSome_EntitySome','field_id'), // Получает массив элементов 'entity'=>array(EntityORM::RELATION_TYPE_HAS_ONE,'ModuleSome_EntitySome','field_id'), // Это, например, когда у пользователя (основная сущность) есть сессия, и она хранится в отдельной таблице (с) ort 'entity'=>array(EntityORM::RELATION_TYPE_MANY_TO_MANY,'ModuleSome_EntitySome','field_id'), // Получает массив элементов по массиву EntityORM::RELATION_TYPE_TREE // Строит дерево, необходимо наличие поля parent_id в таблице );
После описания сущностей, нам будут доступные следующие методы:
/** * Конструкции вида [name] сделаны для примера */ /** * Работа с объектами */ Add(); Update(); Save(); // Совмещает и Update() и Add(), первый выполняется если сущность уже есть в бд, второй когда ее еще нет (с) ort Delete(); Reload(); // пример: $oParam->setTitle('Пара-пам-пам'); $oParam->Update(); /** * Запросы на получение объекта/массива объектов */ ShowColumnsFrom[Table](); LoadTreeOf[Entity](); Get[Entity]ItemsBy[Filter, Array, JoinTable, Gte, Lte, Gt, Lt, Like, In](); Get[Entity]All(); Get[Entity]ItemsAll(); /** * Методы, доступные только для типа tree */ GetChildrenOf[Entity](); GetParentOf[Entity](); GetDescendantsOf[Entity](); GetAncestorsOf[Entity](); // пример: $this->ModuleHabr_GetTopicItemsAll(); /** * А так-же сгенерированные через singleton Engine, работа с самим объектом */ get[Column](); // пример: $oParam->getTitle();
Таблицы в базе данных должны называться следующим образом: prefix_<module-name>_<entity-name> (или прописать их названия в конфиге), а если же название сущности совпадает с названием модуля, то достаточно назвать таблицу так: prefix_<module-name>. Поля тоже имеют свои стандарты: <entity-name>_<field-name>, или просто <field-name>.
Вот и все, господа, спасибо за уделенное внимание. Напоминаю, что свежие исходники форума можно посмотреть в git'е.
UPD: Сделал корректировки в статье, спасибо ort'у за его замечания.