Модель ORM
Важно: это лишь предлагаемая мною примерная альфа-версия модели, для ознакомления с идеями, скорее всего она будет отличаться от реальной модели, котораябудет введена в LS.
Существует 4 типа отношений:
belongs_to — связь 1 к 1, или многие к 1. в таблице обязательно наличие foreign key вида relationalias_id. Примеры:
has_one — связь 1 к 1, обратная к belongs_to. При наличии описанных выше связей для Topic, можно добавить к сущности PluginBill_ModuleEvent_EntityEvent:
has_many — 1 ко многим: При установленном belongs_to для Topic, можно расширить, например, User:
acts_as_tree — возможность создания древовидной структуры. Пример: бесконечно вложенные категорий:
Разрабатываются типы has_and_belongs_to_many (с использованием join-таблиц) и acts_as_list для создания упорядоченных списков.
protected $_aData = array(); — Хранит слепок записи из таблицы.
protected $_aAdditionalData = array(); — Содержит дополнительные поля, такие как 'children', 'parent', а также ссылки на связанные объекты.
public function __construct($oObject)
— При инициализации сущности пробегает по массиву $aRelations и создаёт на его основе $aRelationsAliases (то, что будет доступно по get*()), например:
public function Save()
public function Add()
public function Update()
public function Delete()
— Вызывают соответствующие функции EntityAdd(), EntityUpdate(), EntityDelete() маппера ORM. Сбрасывают зависимые кэши.
public function getParent()
public function setParent($oEntityParent)
public function getChildren()
public function setChildren($oEntityItems)
public function getSiblings()
— Функции, доступные для деревьев, обозначенных в отношениях как «acts_as_tree».
public function __call($sName,$aArgs)
— Определяет тип запрашиваемого ключа и возвращает или записывает нужное значение или объект.
protected function GetRelationItems($sRelationAlias)
— Возвращает связанный объект или массив связанных объектов согласно типу отношений.
protected function DefineRelationType($sRelationAlias)
— Определяет тип отношений по заданному алиасу.
protected function HasRelationType($sRelationType)
— Проверяет наличие типа отношений.
static function TypeOfKey($oEntity,$sKey)
— Определяет тип ключа. Сначала пытается найти ключ в связанных отношениях, иначе считает что это сокращенное свойство (поле таблицы: `title` вместо `tablename_title`).
public function GetByFilter($aFilter=array())
— Обрабатывает кэширование и выполняет метод ModuleGetByFilter маппера ORM. Возвращает один объект сущности в случае успеха.
public function GetItemsByFilter($aFilter=array())
— Обрабатывает кэширование и выполняет метод ModuleGetItemsByFilter маппера ORM. Возвращает массив сущностей.
public function GetById($iId) — алиас для GetByFilter(array('tablename_id'=>$iId)).
public function GetAll($aFilter = array()) — алиас для GetByFilter(). Вернет массив из всех сущностей записей в таблице.
public function __call($sName,$aArgs)
— «Умное» определение фильтров к методу GetByFilter(). Пример замен:
public function EntityAdd()
public function EntityUpdate()
public function EntityDelete()
— Основые функции для операций с сущностями. Сохраняют содержимое $_aData в таблице.
public function ModuleGetByFilter($aFilter = array())
— Вызывает метод ModuleGetItemsByFilter($aFilter,1) c параметром $bSingle=true
public function ModuleGetItemsByFilter($aFilter = array(),$bSingle = false)
— Основной метод для запросов к таблице класса и получения сущностей объектов из её записей.
static function GetPluginName($oModule)
— Пример функционирования:
static function GetPluginPrefix($oModule)
— Пример функционирования:
static function GetModuleName($oModule)
— Пример функционирования:
static function GetEntityName($oEntity)
— Пример функционирования:
static function GetTableName($oModule)
— Пример функционирования:
static function PrefixField($oModule,$sFieldName)
— Пример функционирования:
Синтаксис отношений $aRelations.
Существует 4 типа отношений:
belongs_to — связь 1 к 1, или многие к 1. в таблице обязательно наличие foreign key вида relationalias_id. Примеры:
$aRelations = array('belongs_to' =>
array(
'User' => 'autor',
'Blog',
'PluginBill_ModuleEvent' => 'event'
)
);
// поля таблицы prefix_topic
topic_id | topic_title | ... | autor_id | blog_id | event_id
// доступные функции:
$oTopic()->getAutor(); $oTopic()->setAutor();
$oTopic()->getBlog(); $oTopic()->setBlog();
$oTopic->Save();
has_one — связь 1 к 1, обратная к belongs_to. При наличии описанных выше связей для Topic, можно добавить к сущности PluginBill_ModuleEvent_EntityEvent:
$aRelations = array('has_one' => array('Topic'));
// поля таблицы prefix_event не меняются!
// доступные функции:
$oEvent()->getTopic(); $oEvent()->setTopic();
$oEvent()->getTopic()->getBlog(); $oEvent()->getTopic()->setBlog();
$oEvent()->getTopic()->getAutor(); $oEvent()->getTopic()->setAutor();
$oEvent()->getTopic()->Save();
has_many — 1 ко многим: При установленном belongs_to для Topic, можно расширить, например, User:
$aRelations = array('has_many' => array('Topic' => 'owner')); // тут мы указываем алиас 'owner', потому foreign key в таблице prefix_topic был задан как `owner_id`
// поля таблицы prefix_user не меняются!
// доступный функционал:
foreach($oUser->getTopicItems() as $oTopic) {
$oTopic->getBlog()->setDescription('В этом блоге есть топики самого '.$oUser->getLogin());
$oTopic->getBlog()->Save();
}
acts_as_tree — возможность создания древовидной структуры. Пример: бесконечно вложенные категорий:
$aRelations = array('acts_as_tree');
// поля таблицы prefix_category
category_id | category_parent_id | category_title | category_url
// доступный функционал:
$oCategory->getParent(); $oCategory->setParent();
$oCategory->getChildren(); $oCategory->setChildren();
foreach($oCategory->getChildren() as $oChildCategory) {
$oChildCategory->setUrl($oCategory->getUrl().'_'.$oChildCategory->getUrl());
}
$oCategory->getSiblings(); // вывод всех "братьев".
Разрабатываются типы has_and_belongs_to_many (с использованием join-таблиц) и acts_as_list для создания упорядоченных списков.
Описание новых методов
1. Entity.class.php
protected $_aData = array(); — Хранит слепок записи из таблицы.
protected $_aAdditionalData = array(); — Содержит дополнительные поля, такие как 'children', 'parent', а также ссылки на связанные объекты.
public function __construct($oObject)
— При инициализации сущности пробегает по массиву $aRelations и создаёт на его основе $aRelationsAliases (то, что будет доступно по get*()), например:
protected $aRelations = array(
'belongs_to' => array('PluginAdvert_Advcategory' => 'cat', 'User' => 'autor'),
'has_one' => array('User'),
'has_many' => array('PluginAdvert_Photo' => 'album'),
);
=>
protected $aRelationsAliases = array(
'belongs_to' => array('Cat' => 'PluginAdvert_Advcategory', 'Autor' => 'User'),
'has_one' => array('User' => 'User'),
'has_many' => array('Photo' => 'PluginAdvert_Photo'),
);
public function Save()
public function Add()
public function Update()
public function Delete()
— Вызывают соответствующие функции EntityAdd(), EntityUpdate(), EntityDelete() маппера ORM. Сбрасывают зависимые кэши.
public function getParent()
public function setParent($oEntityParent)
public function getChildren()
public function setChildren($oEntityItems)
public function getSiblings()
— Функции, доступные для деревьев, обозначенных в отношениях как «acts_as_tree».
public function __call($sName,$aArgs)
— Определяет тип запрашиваемого ключа и возвращает или записывает нужное значение или объект.
protected function GetRelationItems($sRelationAlias)
— Возвращает связанный объект или массив связанных объектов согласно типу отношений.
protected function DefineRelationType($sRelationAlias)
— Определяет тип отношений по заданному алиасу.
protected function HasRelationType($sRelationType)
— Проверяет наличие типа отношений.
static function TypeOfKey($oEntity,$sKey)
— Определяет тип ключа. Сначала пытается найти ключ в связанных отношениях, иначе считает что это сокращенное свойство (поле таблицы: `title` вместо `tablename_title`).
2. Module.class.php
public function GetByFilter($aFilter=array())
— Обрабатывает кэширование и выполняет метод ModuleGetByFilter маппера ORM. Возвращает один объект сущности в случае успеха.
public function GetItemsByFilter($aFilter=array())
— Обрабатывает кэширование и выполняет метод ModuleGetItemsByFilter маппера ORM. Возвращает массив сущностей.
public function GetById($iId) — алиас для GetByFilter(array('tablename_id'=>$iId)).
public function GetAll($aFilter = array()) — алиас для GetByFilter(). Вернет массив из всех сущностей записей в таблице.
public function __call($sName,$aArgs)
— «Умное» определение фильтров к методу GetByFilter(). Пример замен:
Test_GetItemsByTitle('Test2') => Test_GetItemsByFilter(array ( 'test_title' => 'Test2' ) );
Test_GetTestItemsByTitle('Test Title')=> Test_GetItemsByFilter(array ( 'test_title' => 'Test Title' ) );
Test_GetItemsByCategoryId(5) => Test_GetItemsByFilter(array ( 'category_id' => 5 ) );
Test_GetByTitle('Test1') => Test_GetByFilter(array ( 'test_title' => 'Test1' ) );
Test_GetByUrl('my_url') => Test_GetByFilter(array ( 'test_url' => 'my_url' ) );
Test_GetTestById(10) => Test_GetByFilter(array ( 'test_id' => 10 ) );
3. ORM.class.php
public function EntityAdd()
public function EntityUpdate()
public function EntityDelete()
— Основые функции для операций с сущностями. Сохраняют содержимое $_aData в таблице.
public function ModuleGetByFilter($aFilter = array())
— Вызывает метод ModuleGetItemsByFilter($aFilter,1) c параметром $bSingle=true
public function ModuleGetItemsByFilter($aFilter = array(),$bSingle = false)
— Основной метод для запросов к таблице класса и получения сущностей объектов из её записей.
Служебные методы:
static function GetPluginName($oModule)
— Пример функционирования:
PluginBill_ModulePlace $oModule => 'Bill'
static function GetPluginPrefix($oModule)
— Пример функционирования:
PluginBill_ModulePlace $oModule => 'PluginBill_'
static function GetModuleName($oModule)
— Пример функционирования:
PluginBill_ModulePlace $oModule => 'Place'
static function GetEntityName($oEntity)
— Пример функционирования:
PluginBill_ModulePlace_EntityPlaceCity $oEntity => 'PlaceCity'
static function GetTableName($oModule)
— Пример функционирования:
PluginBill_ModulePlace $oModule => 'prefix_place'
static function PrefixField($oModule,$sFieldName)
— Пример функционирования:
PluginBill_ModulePlace $oModule, 'id' => 'place_id'
4. Engine.class.php
Алиасы для доступа к основным методам движка:/**
* Short aliases for Engine basic methods
*
*/
class E extends Engine {
public static function GI() {
return parent::GetInstance();
}
public function GM($sClassName,$sName=null,$oConnect=null) {
return parent::GetMapper($sClassName,$sName,$oConnect);
}
public function GE($sName,$aParams=array()) {
return parent::GetEntity($sName,$aParams);
}
public function __call($sName,$aArgs=array()) {
return parent::$sName($aArgs);
}
}
59 комментариев
codeigniter.com/wiki/IgnitedRecord/
это автоматизация 80% всех действий с моделями.
то есть в 80% случаев, у вас будут пустые файлы мапперов, модулей и сущностей.
вам нравится каждый раз вручную писать один и тот же код методов Save, Update, GetById(), GetByTitle(), GetDyUrl() и т.д.?
ORM и модель с мапперами — разные вещи, и предназначение у них разное, поэтому они совершенно не являются взаимозаменяемыми. Мапперы нужны, как вы правильно сказали, для отделения SQL от логики, а ORM — это модель, с помощью которой записи из таблицы приложения представляются как объекты в ООП, которые можно содавать, изменять и записывать. ORM — более высокий уровень абстракции, и если вы внимательно читали мои сообщения и код, я предлагаю ORM, как надстройку над существующей моделью.
И тем более, где вы видели у меня подобный код:
?
И что вообще он должен делать, в вашем понимании?
Предлаголось лишь автоматизировать в самом движке стандартные методы типа Blog_GetBlogById(), чтобы не прописывать их заново каждый раз при создании нового модуля. Вот и все, о чём разговор?
В последнее время создается пугающее ощущение, что аудитория этого сайта состоит из пользователей-старшекласников, которые хотят завести себе модный сайт а-ля Хабр и скачать побольше бесплатных хаков.
Ожидалось, что будет больше профессиональных разработчиков, которые рассматривают бы LiveStreet как перспективный фреймворк для создания web-приложений…
Грустно…
LS отличный фреймворк, и ORM, как дополнительная возможность, будет не лишним
Разработчик сам сможет выбрать, какой подход использовать. Причем можно использовать оба подхода одновременно.
Я уже второй раз слышу недовольство именно этим моментом, может кто-то объяснит чем обоснованы эти беспокойства?
ORM действительно упростила бы разработку.
архивчик с примером прилепить бы к топику
не очень удобно, было бы хорошо как-то сократить эту запись.
для переопределения нам нужно писать
а для поддержки ORM —
есть идеи?
1) для старых модулей всегда придется дописывать extends ModuleORM и прописывать parent::Init() в их Init-методе, чтобы заработали функции ModuleORM и MapperORM. Это плохо, лучше бы, чтобы все модули по умолчанию наследовали ORM функции. Или хотя бы, чтобы это можно было удобно настроить через переопределение в плагине.
2) на основании чего ты решил убрать формат полей с 'modulename_id', 'modulename_url' на 'id' и 'url' соответственно?
в том числе необходимо запрещать использовать собственные поля с таким же именем, как у связи, которая определена в aRelations, вот и все.
так почему не сделать правилом задавать полям любые имена(кроме регистра и пересечений в aRelations)?
Так как быть с примером выше? status может и не быть в aRelations, т.е. связь только на уровне MySQL InnoDB. Если не использовать префикс для полей, то таких проблем не возникнет.
мне кажется, все связи нужно установит, что все связи должны быть описаны в aRelations, чтобы работали функции ORM.
сама по себе идея с #with хорошая, если с помощью нее генерировать запросы с JOIN`ами для тех, кто хочет сэкономить количество запросов (или нет возможности пользоваться кэшированием).
тогда ее можно прикрутить и к GetByFilter, а не только для GetItemsByFilter.
кстати, только сейчас заметил, что у тебя в реализации кэширование пока не поддерживается.
КМС это что?
очевидно же, он имел в виду данную cms
Былая простота (версии 0.3) ушла, вместо этого пришло много интересного функционала, для разработчиков, но разработчиков нет, и плагинов тоже нет. Системе нужно множество простейших плагинов, реализующих элементарные вещи, для которых описываемый функционал не нужен, и нужен не будет.
Той же админки и той в комплекте нет, такие вещи как разграничение прав доступа только сняться. Вы говорите это 0.4? Ок, и сколько ждать версии 1.0? еще лет 6?
Вы скажете на ЛС множество сайтов? Хорошо, но что останется, если убрать все однодневки со стандартной темой? Есть на кого равняться?
Что касается моего ИМХО, то я бы так и сидел на 0.3, если бы не понадеялся на оптимизацию в 0.4 версии и не было так мучительно лень писать самому галерею.