PHP

индекс
206,76

DaBase — не очередная ORM для PHP

DaBase - не очередная ORM для PHP

Прошла первые боевые испытания и теперь представляется на милость хабрасообщества.

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

Особенности:
  • Поддержка MySQL и PostgreSQL
  • Лёгкая: ~50kb лаконичного ООП кода
  • Быстрая и нетребовательная к системным ресурсам
  • Не требует никакой конфигурации, вы можете моментально начать использовать её на уже существующей схеме базы.
  • Очень лаконичная, позволяет в одну читабельную строку кода выполнить несколько различных операций.
  • Имеет полноценные средства для генерирования(экранирования) SQL запросов по аналогии с DbSimple.
  • Поддерживает псевдо-JOIN таблиц на базе соответствия имён таблиц и полей-индексов
  • Поддерживает создание специфицированных моделей данных с конфигурацией правил валидации свойств
  • Есть реализация работы с древовдиными Nested Sets коллекциями
  • Есть поддержка кеширования (пока только в памяти процесса) с авто-очисткой


SVN: http://dabase.googlecode.com/svn/trunk
Скачать можно тут: http://code.google.com/p/dabase/downloads/list
Страница проекта на Google Code: http://code.google.com/p/dabase

Всем желающим присоединиться к проекту — очень рад :)
Всем спасибо за конструктивную критику и отзывы!

UPD: Народ, я просил чтобы критика была конструктивная, а тут большинство претензий исходит либо из-за того что руководство не полностью прочитали, либа из-за банального недопонимания. Я понимаю, что руководство на английском и тема холиварная, но давайте будем внимательнее и терпимее.
UPD: При всей лаконичности DaBase, уважаемый хабраюзер, если ты работаешь над крупным проектом с базой данных включающей множественные связи таблиц и есть необходимость полноценной спецификации их отношений, то лучше обратить внимание на такие системы как Propel и Doctrine.
+33
18 октября 2009, 18:24
37

комментарии (102)

+7
zerkms #
$users = $db->users->getByQuery('SELECT * FROM # WHERE `isActive`=?', $db->users->getTable(), $isActive); // get array of all rows like objects

гхм… почему $db->users не знает откуда нужно выбирать данные?
–4
liaren #
Потому что ей надо знать какого типа объекты генерить на базе результата.
К тому же $db->users не обязательно указывает на таблицу `users` (в том случае если определён класс User с константой table = 'some_users_tablename')
+1
zw0rk #
а сама она не может $this->getTable()?
–2
liaren #
Она это кто, таблица?

Тут таблицы могут вообще не описываться, если не надо. А если и надо, то они описываются посредством классов конечных объектов этих таблиц.
0
zw0rk #
«Она» это софтина :)

Я тогда не понимаю, зачем нужен $db->users? Чем это круче чем $db->getByQuery('… from User ...', 'User_Class', $isActive) [то есть некий map-query-to-object]?
–2
liaren #
Если бы было просто $db->getByQuery('… from User ...') то сам $db (DaBase_Db) понятия не имеет на какие классы какие таблицы мапить, и как вообще это делать.

DaBase_Db — исключительно гибкая работа с запросами с маппингом через $db->someTable на DaBase_Getter

DaBase_Getter разруливает всё что касается обработки результатов запросов в контексте соответствующих объектов классов (если они объявлены) или DaBase_Object (если не объявлены)
0
zerkms #
$db не знает, но ведь $db->users знает. так зачем указывать ему ещё раз то, что и так он знает?
–2
liaren #
Кто-то кого-то чего-то не понял?

О чём речь вообще, зачем в $db->users->getByQuery имя таблицы прописывалось? Затем чтобы кастомные запросы формировать. Если кастомные не надо, то и указывать ничего не надо.
0
zw0rk #
Именно для того, чтобы он знал куда маппить там и указан 'User_Class'. Работа примерно такая:
foreach ($result as $k => $v) {
$obj = new $param;
$this->fill_fields($obj, $v);
}
–3
liaren #
Это вообще о чём и откуда?
Давайте по делу, конструктивно. Если что непонятно, то сперва спрашивайте.

В DaBase не совсем заурядная архитектура и это специально было сделано, с практичной точки зрения.
0
zw0rk #
Когда я писал этот комментарий, не было вашего комментария о том, что это нужно «чтобы кастомные запросы формировать». Создавалось ощущение, что получается какая-то избыточность: и $db->users и в самом обращнии к $db->users стоит ...->getTable().
+1
zerkms #
даже если запросы кастомные — ничего не мешало сделать магическую подстановку, чтобы не выносить знание об имени таблицы на прикладной уровень.
0
zw0rk #
Ну я так понимаю, там ишеться баланс между абстрактностью и легковесностью. Видимо, это результаты найденного баланса :)
–2
liaren #
Спасибо, хоть в чём-то меня начинают понимать :)
+1
zw0rk #
Просто это странная полумера, достаточно. Так или иначе, вводиться слой абстракции — ОРМ и тут же в нём проделывают столько дыр, что непонятно уже зачем его вводили :)

Это исключительно имхо.
0
liaren #
Про дыры можно подробнее?
+2
aubt #
«Она», как я понял — объект $db->users.
Конструкция
db->users->getByQuery('SELECT * FROM # WHERE `isActive`=?', $db->users->getTable(), $isActive);
выглядит, как минимум странно. Если Вы можете узнать имя таблицы тут, вызвав $db->users->getTable(), то, почему Вы не можете сделать тоже самое внутри getByQuery(), метода того же объекта $db->users — совершенно не ясно.

Логичнее выглядело бы, наверное, так:
$db->users->getByCriteria( new DaBase_Criteria(array('isActive' => $isActive)) );

Также, не ясно, зачем реализован доступ к моделям через $db->? Вы пишете:

> К тому же $db->users не обязательно указывает на таблицу `users` (в том случае если определён класс User с константой table = 'some_users_tablename')

Т.о., собственно, mapping осуществляется классами? Почему не сделать доступ к моделям через объекты этих классов?
class User extends DaBase_Model
{
const DABASE_TABLE = «some_users_tablename»;
}
$user = new User();
$user = User::getByCriteria();
и т.д.

В любом случае, необходимость писать SQL для такой простой выборки, как приведена выше, как-то сразу ставит под сомнение весь продукт.
–1
zerkms #
конкретно тут константа будет не очень удобной — её нельзя будет переопределить в наследнике класса.
0
aubt #
Конечно. Этот код не «как бы я сделал», а ответ на:
> (в том случае если определён класс User с константой table = 'some_users_tablename')
0
liaren #
С чего это в наследниках нельзя константы переопределять? Очень даже можно!
0
zerkms #
хехехе, клёвый очередной unobviously behaviour в php
0
aubt #
Как я понял, zerkms имел ввиду сложности с доступом к переопределенным константам из не переопределенных методов. До 5.3 late static binding не было.
0
zerkms #
кстати, что удивительно, и без LSB в php 5.2 есть «переопределение» констант.
очень удивило.

я ожидал:
или фатала (переопределение константы) или отсутствия константы в наследнике (угу, из-за отсутствия LSB), но на гора имеем вполне рабочий код:

class a
{
const ORLY = 'lol';
}

class b extends a
{
//const ORLY = 'lol2';
}

echo b::ORLY;

и с комментом и без оно работает… чудеса :-)
0
aubt #
Не вижу в Вашем примере LSB. Дело в другом:

class A
{

const CNST = «this is A»;

public function getConst()
{
echo self::CNST;
}

}

class B extends A
{

const CNST = «this is B»;

}

$obj = new B;
$obj->getConst(); // выведет «this is A»;

Соответственно, в 5.3 можно self:: заменить на static:: и тогда будет выведено «this is B»;

До 5.3 такое тоже реализовывалось:

$constName = get_class($this). "::CNST";
echo constant($constName);

Но это анальный доступ к данным.
0
zerkms #
я знаю, что такое LSB.
я о том, что меня удивило, что b::ORLY будет существовать.
0
aubt #
а почему нет? в каком языке будет не так?
0
zerkms #
во всех известных мне ЯП будет так. я просто не ожидал этого конкретно в пхп :-)

конкретно против пхп ничего не имею — он очень клёвый и программирование на нём занимает процентов 95 моего программирования. но уж больно в нём много неочевидных вещей :-)
0
aubt #
> много неочевидных вещей
С этим, к большому сожалению, полностью согласен :)
0
developer #
с рефлексией можно и без статик в 5-ке старой. короче без LSB
0
liaren #
> Если Вы можете узнать имя таблицы тут, вызвав $db->users->getTable(), то, почему Вы не можете сделать тоже самое внутри getByQuery()

Вы не поняли, $db->users->getByQuery() нужен для формирования кастомных запросов по конкретным таблицам, а куда там название таблицы автоматом подставлять когда эти кастомные запросы на то и кастомные чтобы вы сами прописывали что куда надо.

> $db->users->getByCriteria( new DaBase_Criteria(array('isActive' => $isActive)) );

А вот от таких схем я умышленно ушёл с самого начала т.к. ДЛЯ МЕНЯ это слишком избыточный код, избыточная нагрузка на систему… ну и вообще с таким подходом уж лучше на голом SQL писать.

> В любом случае, необходимость писать SQL для такой простой выборки, как приведена выше, как-то сразу ставит под сомнение весь продукт.

Пример с $db->users->getByQuery() был приведён для примера. Понятно что в DaBase тот же самый запрос можно написать куда короче: $db->users->limit(5)->get().

0
aubt #
> это слишком избыточный код
Согласен… но для простых критериев можно писать, напрмер:
$db->users->getByCriteria(array('isActive' => $isActive));
что может в каком-то смысле и избыточно, но не избыточнее кастомного запроса однозначно (собственно какова цель продукта — не писать SQL и работать с объектами, т.е. ORM, или что-то другое?).

> $db->users->limit(5)->get();
Такая выборка не учитывает isActive. Получается для любой выборки с критерием/фильтром приходится писать кастомный запрос?
0
liaren #
Ну читайте вы внимательней руководство, там ведь всё есть даже с примерами))

Критерии есть, но они только логического типа AND, по типу фильтров:
$db->users->isActive(true)->isModerator(true)->get(); // вот и все критерии

Можно ещё так:
$db->users->posts(50, '>')->get();

Это кстати примеры из руководства :)
+3
zerkms #
имхо на то он и ORM, что сам должен знать, какие классы на какие таблицы отображать (и наоборот).
зачем нужно указывать таблицу явно и даже запрос (это вообще работа сугубо ОРМа, как получить, как обработать запрос итд).

нужны другой тип пользователей — отнаследуйте класс, определите в нём другую таблицу

class Yet_Another_Users extends Users
{
private $table = 'ya_users';

как-то так.
0
zw0rk #
Ну вот тут не совсем правда. ОРМ-мы могут отображать произвольные запросы на таблицы, тогда запрос такой надо прописывать руками. Но в этом конкретном случае, конечно, профит от орма не особо ясен, раз ему надо всё явно указывать.
0
zerkms #
хзхз… для меня ОРМ это инструмент для отражения рсубд (в виде таблиц/записей) на классы (в виде объектов) и обратно. это лично мой взгляд :-)
согласно ему же — один класс отражается ровно на одну таблицу. и наоборот (бывают исключения, но это не тот случай)
0
liaren #
В DaBase так же, один класс — одна таблица. Но опять же, не всегда актуально все таблицы «классифицировать».
0
zerkms #
тогда я не понимаю кода совершенно.
0
zw0rk #
А для всего прогрессивного человечества ОРМ это, в частности:
ORM (англ. Object-relational mapping, русск. Объектно-реляционная проекция) — запись объектов программы в реляционную базу данных, отображение объекта и его представления в виде набора таблиц.

:)
Я к тому, что многие проблемы широкораспространённых ОРМ-ов как раз в ограничении отображения объект<->таблица.
0
zerkms #
читал я эту статью, конечно же :-) более того — свой орм я писал, и писал не раз (результат, кстати, вполне клёвый и используется не один год)
в силу вышесказанного: очень слабо представляю метаописание объекта, который будет отражаться на набор таблиц.
0
liaren #
Так он и знает, если название таблицы вписывается в определённый name convention.
И на то он и DaBase, чтобы не надо было для каждой таблицы отдельный класс создавать т.к. это не всегда критично.

И насчёт:
class Yet_Another_Users extends Users

В DaBase это всё есть, см. руководство.
0
zerkms #
я понимаю, что всё есть. своим кодом я комментировал фразу: «К тому же $db->users не обязательно указывает на таблицу `users` (в том случае если определён класс User с константой table = 'some_users_tablename')»
немного уточню: в моём понимании 1 класс конкретно отражает одну таблицу бд. обратное верно

существуют исключения, но с ними всё не так просто и это всё не имеет отношения к текущему разговору.
–4
nayjest #
Чем RedBean не устроил?
+1
nayjest #
Нет, ну действительно чем? RedBean — такой же недо-Orm, только намного лаконичней и архитектурно продуманней (я не говорю сейчас о классических ORM системах типа Doctrine и Propel, заминусовали видимо потому что увидели первый раз «какую-то малоизвестную библиотеку»). Чем ваш лучше?
0
liaren #
Да мне то чего минусовать, сам в минусе :)

Пробежался по документации этого RedBean http://www.redbeanphp.com/#/beans. Не знаю чем он лучше всех прочих ORM, хотя кстати всем прочим я предпочитаю DaBase (хоть она и не совсем и ORM). Не аргумент?

Тогда вот, пробежался по исходникам… помимо претензий к архитектуре, вот конкретный пример:

public function selectRecord($type, $ids) {
$type=$this->adapter->escape($type);
$rows = $this->adapter->get( "SELECT * FROM `$type` WHERE id IN ( ".implode(",",$ids)." )");
if ($rows && is_array($rows) && count($rows)>0) {
return $rows;
}
else {
return null;
}
}


А где элементарное экранирование? Не стал бы я таким разработчикам свою базу доверять.
0
w999d #
полагаю, $this->adapter->escape($type) — экранирование, а $ids — числа
0
liaren #
Ну как всё просто, вот только они там в документации чайникам красненьким не подсветили, чтобы те были осторожны с данными, которые на вход этого метода отсылают. Бедные чайники…
0
nayjest #
Я не говорил, что он лучше всех прочих ORM, он выполняет примерно те же задачи, что и ваш DaBase + немного больше.

Для RedBean так же как и у вас, не нужно писать конфигов или схему БД. Достаточно списка классов, которым на ходу можно присваивать любые поля.

require('oodb.php');

RedBean_Setup::kickstartDev('', 'mysql:host=localhost;dbname=mydatabase' );
//В режиме разработки по ходу того что мы будем делать далее создадутся соответствующие таблицы в БД, так же их структура может меняться налету.
Есть и режим, в котором изменение структуры БД запрещено — уже для продакшена.

Redbean_OODB::Gen('Post,Comment');//теперь у нас есть классы Page,Post,Comment

$p = new Post;
$p->caption='bla-bla-bla';
$p->setRating(1500);//можно и методами
$p->text=$some_text;
$c = new Comment;
$c->text='qweqweqwe';
$p->add($c);
$id = $p->save();

Доступ к сохранённому экземпляру:
$p2 = new Post( $id );
echo $p2->caption;
$p2->author='Valera';
$p2->save(); — вот теперь в структуру таблицы будет добавлено новое поле!

Можно импортировать поля из массива:
$employee->import( array('name'=>'John','badge'=>123) );

Или экспортировать в массив:
$arr = $bean->exportAsArr();

Есть поиск по SQL:
$persons = Person::where(' name LIKE {nm} and age > {ag} order by customerid asc ',
array('name'=>'Geor%','age'=>50))->getBeans();

Просто получить все записи из таблицы (массив массивов) можно через:
$persons = Person::findAll();

Можно также хранить древовидные структуры, используется Ad.List

И многое другое
0
liaren #
> Для RedBean так же как и у вас, не нужно писать конфигов или схему БД. Достаточно списка классов, которым на ходу можно присваивать любые поля.

Стоп. Опять кто-то не внимательно руководство читал. Для DaBase не нужен никакой список классов!

Вы конечно извинете, но я уже устал тут разглагольствовать. Чисто субъективно этот Redbeans местами чепушной какой-то. И не надо говорить, что он удобней чем DaBase т.к. банально судя по примерам которые вы привели это не так.

Короче, если вам так нравится этот ORM, то напишите о нём полноценную статью на хабре, лично мне будет очень интересно почитать. А критика, будьте уверены, народ вам ещё много чего «хорошего» про него расскажет.
+5
nikelin #
ORM по определению должна абстрагировать бизнес-логику от SQL. Тут этого не наблюдается.

Да и вообще… это сложно назвать ORM'ом.
0
k0rn #
полностью согласен, ORM для того чтобы не видеть и знать про SQL, а тут кругом один SQL запросы. Само собой надо оставлять место для того чтобы рисовать кастомные запросы.
–7
liaren #
Во-первых я нигде и не писал, что DaBase это ORM в чистом виде. Даже на сайте проекта сказано, что:

DaBase — Lightweight pseudo-ORM for PHP

Потому, что если это был в чистом виде ORM, то его исходник весил бы соответственно в 50-100 раз тяжелее, и синтаксис был бы также обременён всякими излишествами.

Принципиальная особенность DaBase в его лаконичности.
И насчёт того, что там почти везде SQL — далеко не везде. Работа с чистым SQL там приводится в основном для примера работы со своими кастомными запросами, от которых ни в одной ORM вам не уйти (и слава богу), и всё чем вам может помочь ORM в этом вопросе это предоставить удобный инструмент для генерации этих самых кастомных запросов.

Вот как один из аспектов DaBase (DaBase_Db в частности) это и есть — удобный интерфейс для генерации кастомных запросов.

Но помимо этого есть и DaBase_Getter и DaBase_Db которые как раз то и делают, что пытаются максимально абстрагировать вас от написания регулярных запросов.

Надеюсь, что понятно объяснил.
–1
k0rn #
да, я понял, просто я ожидал что это будет реализовано как в RubyOnRails, вот там всё просто идеально, само делает таблицы, поля, само всё мапит, само всё создает, я только указываю действия: сохранить, найти по ид, показать все, добавить какой то кастомный WHERE и т.д.
И ещё, какая нафик разница сколько весит серверсайд приложение? Главное это сколько он памяти жрет, а не места.
–5
liaren #
Оно обычно очень взаимосвязано. Я по опыту уже не один проект видел, который отказался от Propel или Doctrine в виду их тормозов.
0
k0rn #
В моей компании, для клиентов которые не хотят Руби, используется свой самопальный ORM на РНР который очень похож на Руби, только вот таблицы сами создаем. И пока не нашли ни одного большого проекта, который бы тормозил. Кеширование, ngnix, нормальный код для генерации запросов и всё получается хорошо.
0
nikelin #
Обычно тормоза не из-за ORM, а из-за ошибок проектирования во время разработки включая отсутствие нормального подхода к кешированию. Даже если дело действительно в ORM, что тоже бывает часто, особенно в случае с Doctrine, вам ничто не мешает переписать особенно тяжеловесные запросы с каскадом JOIN'ов на PDO.
+1
nikelin #
PDO встроенный, в таком случае, вам чем не угодил?)
–1
liaren #
1. PDO умеет только по значению экранировать.
2. Если не ошибаюсь он работает с базой через свой драйвер, а в PHP 5.3 используется нативная библиотека MySQL, которой он уступает в скорости.
+1
nikelin #
1. Да, это действительно причина, для создания форка.
2. Нет: forge.mysql.com/wiki/PHP_PDO_MYSQLND
0
nikelin #
Плюс, в PDO относительно легко можно перейти на другую БД, там хотя бы есть возможность этого (изменяя значения драйвера поддерживающего текущее подключение), у вас же mysql строго защита в объект Database_Db, что по сути своей ужасно.
0
liaren #
Это первая версия библиотеки, расширить её на поддержку того же PDO и тем более адаптеров для других БД задача элементарная, но пока не столь актуальная.

Мне изначально хочется понять, кроме негативной критики будут ли позитивные причины причины заниматься её развитием.
0
nikelin #
1. Расширять её в текущей версии не позволяют ошибки проектирования, т. к. даже подключение к базе не абстрагировано.

2. Сразу скажу, использовать её я не буду :-)
–1
liaren #
Помимо отсутствия адаптера подключения к БД, в чём ещё ошибки проектирования?
0
nikelin #
Я уже написал об основных:
1. Явно ненужное Dependency Injection ввиде DataBase_Object
2. Создание DataBase_Getter'а каждый раз, когда идёт обращение к DataBase_Db::getGetter(), что конечно же очень «оптимально» для памяти.
3. Отсутствие адаптера для подключения.
4. Отсутствие программной поддержки блокировок ( см. «Пессимистическая блокировка», «Оптимистическая блокировка»), т.е. оно есть но я никак не смогу узнать заблокирована ли таблица в настоящий момент, и т.д.
0
liaren #
1. В данному случае от этого никак не уйти, и в конце концов ничего архи-плохого в этом не вижу.

2. Для памяти это ерунда т.к. сам по себе DaBase_Getter почти ничего не весит, к тому же по одному крохотному объекту на выборку это тем более ни о чём по сравнению с 10-20 объектами на выборку у тех же Propel/Doctrine/etc.

Вообще странная претензия т.к. сам результат из 100 объектов выборки будет по сути в 100 раз тяжелее объекта запроса выборки, но и всё равно при этом очень мало памяти задействуется.

3. Адаптер для подключения будет, и вполне возможно что на базе PDO

4. Про блокировки пока не думал даже, но буду учитывать. Пока обходимся транзакциями и блокировками на уровне mysql.
0
nikelin #
1. Это очень плохо, потому что это не нужно, ибо сам объект User нигде явно не используется.

2. Зачем это допускать, если этого можно избежать? См. Фабрика объектов.

P.S Ещё подумайте в сторону наследования.
–1
liaren #
1. В каком-то смысле согласен, но там есть некоторые вилки. Буду думать как их разрешить.

2. Я знаю, что такое фабрика объектов, но так или иначе невозможно избежать создания отдельного объекта DaBase_Getter на каждый отдельный запрос. Надо понимать, что это объект, который хранит конфигурацию запроса: фильтры, сортировки, join-ы.

Что же теперь, сделать один глобальный Getter и в нём стеком хранить все текущие конфигурации запросов? Там по накладкам памяти ещё больше уйдёт.

P.S. В сторону наследования чего от чего? Там всего 3 базовых класса. Может имеется ввиду создание абстрактных прородителей?
0
nikelin #
Наследования модели A от модели B.

$db->user->…

$db->user_gold->…

Мне, допустим, нужно чтобы user и user_gold — были одной и той же таблицой.
0
liaren #
Ну и в чём проблема?
На DaBase это просто делается

наследуем User от DaBase_Object и прописываем const table = 'users'

наследуем GoldUser от User и… и всё

$db->users и $db->usersGold будут выдавать объекты одной таблицы, но разных классов
0
aubt #
Допустим в таблице users есть поле isGold.
Можно ли сделать так, чтобы $db->usersGold выдавал только объекты с isGold = 1?
0
liaren #
ну конечно, объявить для класса GoldUser

public $isGold = 1;

и для верности можно ещё валидацию на него повесить, типа

$validator->add(new Validator_Rule_Regexp('/^1$/'))

оно по умолчанию будет автоматом при всех insert-ах и update-ах срабатывать
0
liaren #
поправка:

$validator->add('isGold', new Validator_Rule_Regexp('/^1$/'))
0
aubt #
я имел ввиду выборку.

$db->usersGold->limit(10)->get() == select * from users where isGold = 1 limit 10

или нет?
0
liaren #
В этом плане нет, не получится. Хотя я вот сейчас всё больше убеждаюсь, что на самом деле объекты должны выступать в качестве factory для геттеров, тогда можно было бы просто для $db->usersGold выдавать Getter у которого уже проставлено $getter->isGold(true);

Но просто в этом случае теряется некая прозрачность логики работы объектов. Я вовсе не считаю правильным возлагать на инструмент абстрагирования с базой функции контроля бизнес процессов, потому как в этом случае всем прочим разработчикам придётся каждый раз обращаясь к методам библиотеки учитывать что они могли бы быть 15 раз переопределны.

В вашем случае мне кажется проще всего было бы ограничиться одной моделью User, при этом добавив в своё API метод возвращаемый Getter с фильтром по isGold. Т.е.:

public function getGoldUsersGetter() {
return $this->db->users->isGold(true);
}

B потом уже делайте с ним что хотите.
0
liaren #
Добавил возможность создания кастомных Getter-ов унаследованных от DaBase_Getter. Тогда вот этот ваш случай вообще просто решается:

создаёте класс

class GoldUsers extends DaBase_Getter {

protected $tableName = 'users';
protected $objectsClass = 'User';

protected function postInit() {
$this->isGold(true);
}
}


И всё, теперь обращаясь: $db->goldUsers->get() всегда получаете геттер класса GoldUsers c предустановленным фильтром.
0
nikelin #
И если у вас уже так всё лихо закручено с геттерами, которые сопоставляют объект таблице, зачем в таком случае вводит зависимость объектов от DataBase_Object?

Database_Object вполне себе может включать в себя экземпляр связуемого класса, и связывать его поля с полями таблицы через Reflection.
0
nikelin #
Не в ту ветку написал
0
liaren #
DaBase_Getter у нас один, а наследников DaBase_Object может быть много, они как раз и могут переопределять названия таблиц, на которые завязывается Getter.

> Database_Object вполне себе может включать в себя экземпляр связуемого класса, и связывать его поля с полями таблицы через Reflection.

Не совсем понял как это, но до Reflection точно не хочется опускаться т.к. это на мой взгляд уже изврат.
0
liaren #
А вы считаете правильным завязывать бизнес логику на ORM? Я сражён.
0
nikelin #
Я считаю правильнеым завязывать бизнес логику на модели и контроллеры, да. ORM просто предоставляет средства. О нём в идеальном случае я и знать-то не должен, как это проиходит в случае с Java Persistence Objects.
0
liaren #
Т.е. ORM на бизнес логику завязывать нехорошо, вы согласны?

А к чему тогда эти ваши слова:
> ORM по определению должна абстрагировать бизнес-логику от SQL

API должно абстрагировать бизнес логику от ORM, а ORM в свою очередь в идеале должно абстрагировать способ хранения и доступа к данным.
+2
zerkms #
В документации, кстати, очень скудно (никак) освящена проблема работы с relations. Их нет или это недостаток документации?
–2
liaren #
В данной версии реализации relations ограничиваются реализацией вот этого: http://code.google.com/p/dabase/wiki/UserGuide#appenders%28pseudo-JOINS%29
0
nikelin #
И если у вас уже так всё лихо закручено с геттерами, которые сопоставляют объект таблице, зачем в таком случае вводит зависимость объектов от DataBase_Object?

Database_Object вполне себе может включать в себя экземпляр связуемого класса, и связывать его поля с полями таблицы через Reflection.
0
liaren #
DaBase_Getter у нас один, а наследников DaBase_Object может быть много, они как раз и могут переопределять названия таблиц, на которые завязывается Getter.

> Database_Object вполне себе может включать в себя экземпляр связуемого класса, и связывать его поля с полями таблицы через Reflection.

Не совсем понял как это, но до Reflection точно не хочется опускаться т.к. это на мой взгляд уже изврат.
–1
nikelin #
Изврат делать так, как делается сейчас.

Вы лучше посмотрите как это реализовано в концептуально плане в Hibernate и в iBatis (в ней как раз всё очень хорошо с кастомными запросами и user defined связывателями полей таблицы с классом ). Пусть они и на Java, но это не важно для понимания основополагающих принципов.
0
liaren #
С Hibernate я как раз знаком, но писать что-то похожее не захотелось, равно как не захотелось использовать его на практике(есть пара неполноценных адаптеров под PHP). Тяжёлый он, хотя конечно очень функциональный.

Грубо так сравнивать, Hibernate больше подходит для Enterprise-решений, и при том если с нуля только на нём и разрабатывать. А попробуйте действующий проект на Hibernate зарефакторить, непросто это очень.
0
ArtemS #
Всегда в таблице primary key должен называться id, или можно назвать user_id?
–2
liaren #
Пока только `id`, но в планах зарефакторить на кастомные варинаты. Поймите меня правильно, на разрботку этой библиотеки, в том числе на написание документации ушло всего 4 дня.

На на самом деле зарефакторить на поддержку кастомных PK-полей дело плёвое, просто в константу вынести название PK-поля. Дальше начинаются вариации с составными PK-полями, но их уже как-то совсем избыточно учитывать.
0
ArtemS #
Не увидел и не нашел в документации отложенной загрузки.
$comment->getUser()->getName() — Так можно?

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

0
liaren #
Отложенной загрузки нету, вот сколько работаю острой необходимости в ней как-то не возникало.

Насчёт двух объектов одной и той же строки: момент с одной стороны важный, но с другой стороны не совсем т.к. у вас ведь если подумать в один момент времени один и тот же объект может быть задействован в разных скриптах… а это уж точно почти никак не проконтролировать.

Но меня в микро-тасках есть этот вопрос, хочу унаследовать решение у LightOrm for PHP.
–1
haskel #
Конструктивная критика:
C DaBase все ясно, но почему Николай Воронов?
–3
capt_Rimmer #
Нас минусуют, а мы кодим :) Народ, есть критика к коду — джойнимся к проекту и делаем изменения. Есть критика по доке — джойнимся и делаем доку. Или вы хотите бесплатно, своё, узконаправленное, но безупречно описанное?)… и манную кашу с неба вёдрами :)
0
remal #
Автор, узнайте, наконец, про PDO!

Я не верю, что после просмотра кода Propel'a или Doctrine можно написать такое. Неужели вы не смотрели другие ORM перед тем как писать свою?
–2
liaren #
) смешно

Знаю я про Propel, а Doctrine так лучше бы и не знал вовсе. Ещё раз: DaBase — это не ORM в буквальном смысле. Вы документацию вообще видели, на страницу проекта хотя бы заходили, читали что там написано?

И про PDO я тоже знаю, но бонусы его пока не актуальны были. Единственное в чём он действительно может быть полезен так это как адаптер к другим БД, как-нибудь прикручу на досуге.

А вы не понимаете, что для ряда проектов Doctrine и Propel просто неприлично тяжелы и большинство просто не возьмётся их осваивать? Хотя я вот освоил, но всё равно порой не актуально мне на тракторе ковёр пылесосить. Вот для таких целей и использую DaBase — просто зная структуру базы работаю с ней как с объектной моделью, а где критично добавляю спецификацию моделей с набором правил валидации.

+3
remal #
Читал я документацию. Каша. Не надо давать способ выбрать объект 20 способами. Надо дать один. Причем очевидный. То, что добавить поддержку других баз невозможно принципиально — дикий фейл. Вы вообще про полиморфизм слышали что-нибудь?

PDO, в отличие от mysql_ позволяет нормально вставлять константные значения в запросы. А вы ваше экранирование где-нибудь обязательно забудете и получите дыру в безопасности. Да и кроме этого плюсы есть, которые очевидны если почитать документацию.

Я не понимаю почему надо использовать велосипед сомнительного качества с крайне низким кол-вом функциональности ради «легкости». Освоить тот же Propel туда как проще чем вашу ORM. Документация в разы лучше и сам код логичнее. У меня на это ушло ну максимум час.

Вы предложили что-то революционное? Что-то удобнее существующего? Удачно совместили плюсы других ORM и не повторили минусов? Ответы на все вопросы: нет. Единственный «аргумент» — легкость, который отнюдь не является плюсом и являться им не может. Ибо не кол-вом кода мерится качество и удобство продукта.
–2
liaren #
> Не надо давать способ выбрать объект 20 способами. Надо дать один. Причем очевидный.

Ну один самый очевидный способ и так все знают — чистый SQL, всё что далее — оптимизация способов его генерации, и тут уж не так много методов, чтобы их запомнить. Штук 5 всего, а ключевых так вообще 2-3.

> PDO, в отличие от mysql_ позволяет нормально вставлять константные значения в запросы. А вы ваше экранирование где-нибудь обязательно забудете и получите дыру в безопасности.

Ну и DaBase нормально с константами работает, в руководстве ведь написано. И какая разница, что вы забудете в PDO значение переменной без prepare вставить, или в DaBase так же? Если человек дурак…

> То, что добавить поддержку других баз невозможно принципиально — дикий фейл. Вы вообще про полиморфизм слышали что-нибудь?

Слышал, только пока необходимости не было, не думал, что для большинства этот вопрос так остро стоит. На днях прикручу PDO с его драйверами под прочие БД и зарефакторю генерацию соответствующего SQL.

> Освоить тот же Propel туда как проще чем вашу ORM. Документация в разы лучше и сам код логичнее. У меня на это ушло ну максимум час.

Спорный вопрос. Properl & Doctrine & etc это полноценные и тяжелые ORM, они требуют полной спецификации базы и соответствующей конкретики в обращении с ней. Вы высоко нагруженные проекты на Propel когда-нибудь профайлили? Случается, что в конечном счёте приходится или Propel откручивать, или вторую серверную стойку на Xeon-ах прикручивать.

> Вы предложили что-то революционное? Удачно совместили плюсы других ORM и не повторили минусов?

Я предложил альтернативу, и ни в коем случае не конкуренцию тяжеловесным ORM пакетам. Главная задача серьёзных ORM пакетов — универсальность, за что приходится платить производительностью и удобством использования. Я преследовал главный принцип практичности, т.к. именно этого мне часто и не хватает в других библиотеках. А вдруг не я один такой?

> Что-то удобнее существующего?

Удобнее? Я считаю да, во многих моментах удобнее. Надо будет в топике подправить, что сие не есть Enterprise-решение.

> Удачно совместили плюсы других ORM и не повторили минусов?

А это вообще возможно? Простите, но как вы себе представляете пылесос и фен в одном флаконе?

> Единственный «аргумент» — легкость, который отнюдь не является плюсом и являться им не может.

Скажите это тем, кто использует Twitter вместо Livejournal. Вы слишком критичны. Я не спорю, что есть широкий ряд задач, для которых актуальнее использовать Properl или Doctrine, но всё-таки считаю, что спектр адекватного применения DaBase также достаточно широк.
0
remal #
Спорный вопрос. Properl & Doctrine & etc это полноценные и тяжелые ORM, они требуют полной спецификации базы и соответствующей конкретики в обращении с ней
В том же самом пропеле я в одном XML конфиге получаю структуру базы. Легко, доступно и в одном месте.

Вы высоко нагруженные проекты на Propel когда-нибудь профайлили? Случается, что в конечном счёте приходится или Propel откручивать, или вторую серверную стойку на Xeon-ах прикручивать.
Вы хотите сказать, что Propel был узким местом вашего проекта? В чем именно? Кроме кривой системы кеширования, которую можно всегда заменить, я не могу представить что там может тормозить. Быть может, стоило, наконец, освоить какой-то другой язык кроме PHP, если ORM библиотека является узки местом?

Удобнее? Я считаю да, во многих моментах удобнее. Надо будет в топике подправить, что сие не есть Enterprise-решение
Расскажите как мне работать с колонкой order, если она есть в таблице? Как мне сделать условие «IS NOT NULL»?

А это вообще возможно? Простите, но как вы себе представляете пылесос и фен в одном флаконе?
Видел пару раз удачные экземпляры, но, к сожалению, ссылок найти сейчас не смог.

Скажите это тем, кто использует Twitter вместо Livejournal
Я бы не стал сравнивать клиентскую часть и серверный код.
0
liaren #
> Вы хотите сказать, что Propel был узким местом вашего проекта? В чем именно?

В том, что при создании запроса генерится слишком много левых (Propel-овских объектов), в том, что сам код перевешивает код всей системы и т.д.

> В том же самом пропеле я в одном XML конфиге получаю структуру базы. Легко, доступно и в одном месте.

А мне вот не нравится их синтексис XML-конфига, я в SQLYog быстрее базу заделаю. А вообще это всё клёво если с нуля систему проектировать, а если готовая — пилим гири?

> Расскажите как мне работать с колонкой order, если она есть в таблице? Как мне сделать условие «IS NOT NULL»?

$db->users->order('order')->get(); // :)

> Я бы не стал сравнивать клиентскую часть и серверный код.

Тут вопрос не в клиентской части и серверной, а в том какими граблями волосы расчёсывать :)
0
VolCh #
>А мне вот не нравится их синтексис XML-конфига

YAML не пробовали?
0
remal #
В том, что при создании запроса генерится слишком много левых (Propel-овских объектов), в том, что сам код перевешивает код всей системы и т.д.
Можно конкретики? Памяти слишком много отжиралось, слишком долго выполнялось? Что именно?

Я правильно понимаю, что проект был настолько оптимизирован (и база тоже), что ORM стала реально узким местом?

$db->users->order('order')->get(); // :)
Я криво написал, а вы не поняли. Как сделать фильтр по этой колонке. Сейчас фильтрация у вас идет методами с таким же названием как и колонка. Метод order же уже существует, а, значит, по значению эту колонку будет не отфильтровать.
0
liaren #
> YAML не пробовали?

Нет, хотя наверное стоило.

> Можно конкретики? Памяти слишком много отжиралось, слишком долго выполнялось? Что именно?

Памяти и процессорного времени. В проекте, в котором очень высокая нагрузка на фронтенд, более 500kb Propel-овского кода начинают давать о себе знать. Даже использование opcode-ного кеширование не спасало.

> Как сделать фильтр по этой колонке.

$db->users->filter('order', 5, '>')->get();

+4
w999d #
Если бы в заголовке не было слова ORM…

С моей точки зрения главное — ORM должна обеспечить разработчика таким функционалом, чтобы ему не пришлось написать ни строчки SQL (оставив такую возможность).

На самом деле «DaBase» до ORM допилить не так сложно, у меня был подобный прототип — code.google.com/p/zenmysql/ Вторая версия, которую вылизываю последние пару недель теперь полностью поддерживает CRUD и умеет возвращать нужные типы объектов. В сочетании с генератором классов — теперь объект сам знает свои PK, список полей и дефолтных значений, связи с другими таблицами, поля можно оборачивать в другие классы с настройкой свойств. Но это не набор функционала любой ORM.

Наворачивать можно до бесконечности, но такие обертки для баз, таблиц, рядов в основе, как DaBase — очень помогают при написании собственно, самой ORM. Также, в моем случае, основной задачей первой версии zenmysql было получение ссылок (mysql link resource) на 3-4 базы данных одновременно, и абстракция mysql_query($sql, $dblink) при каждом запросе в виде $table->q($sql)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.