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.
    Поделиться публикацией

    Похожие публикации

    Комментарии 102

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

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

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

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

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

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

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

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

                                Это исключительно имхо.
                                  0
                                  Про дыры можно подробнее?
                  +2
                  «Она», как я понял — объект $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
                    конкретно тут константа будет не очень удобной — её нельзя будет переопределить в наследнике класса.
                      0
                      Конечно. Этот код не «как бы я сделал», а ответ на:
                      > (в том случае если определён класс User с константой table = 'some_users_tablename')
                        0
                        С чего это в наследниках нельзя константы переопределять? Очень даже можно!
                          0
                          хехехе, клёвый очередной unobviously behaviour в php
                            0
                            Как я понял, zerkms имел ввиду сложности с доступом к переопределенным константам из не переопределенных методов. До 5.3 late static binding не было.
                              0
                              кстати, что удивительно, и без LSB в php 5.2 есть «переопределение» констант.
                              очень удивило.

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

                              class a
                              {
                              const ORLY = 'lol';
                              }

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

                              echo b::ORLY;

                              и с комментом и без оно работает… чудеса :-)
                                0
                                Не вижу в Вашем примере 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
                                  я знаю, что такое LSB.
                                  я о том, что меня удивило, что b::ORLY будет существовать.
                                    0
                                    а почему нет? в каком языке будет не так?
                                      0
                                      во всех известных мне ЯП будет так. я просто не ожидал этого конкретно в пхп :-)

                                      конкретно против пхп ничего не имею — он очень клёвый и программирование на нём занимает процентов 95 моего программирования. но уж больно в нём много неочевидных вещей :-)
                                        0
                                        > много неочевидных вещей
                                        С этим, к большому сожалению, полностью согласен :)
                                    0
                                    с рефлексией можно и без статик в 5-ке старой. короче без LSB
                            0
                            > Если Вы можете узнать имя таблицы тут, вызвав $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
                              > это слишком избыточный код
                              Согласен… но для простых критериев можно писать, напрмер:
                              $db->users->getByCriteria(array('isActive' => $isActive));
                              что может в каком-то смысле и избыточно, но не избыточнее кастомного запроса однозначно (собственно какова цель продукта — не писать SQL и работать с объектами, т.е. ORM, или что-то другое?).

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

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

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

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

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

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

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

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

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

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

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

                              Пробежался по документации этого 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
                                  Ну как всё просто, вот только они там в документации чайникам красненьким не подсветили, чтобы те были осторожны с данными, которые на вход этого метода отсылают. Бедные чайники…
                                  0
                                  Я не говорил, что он лучше всех прочих 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
                                    > Для RedBean так же как и у вас, не нужно писать конфигов или схему БД. Достаточно списка классов, которым на ходу можно присваивать любые поля.

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

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

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

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

                                  DaBase — Lightweight pseudo-ORM for PHP

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                                              $db->user->…

                                                              $db->user_gold->…

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

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

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

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

                                                                    public $isGold = 1;

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

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

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

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

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

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

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

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

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

                                                                          B потом уже делайте с ним что хотите.
                                                                        0
                                                                        Добавил возможность создания кастомных 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
                                              И если у вас уже так всё лихо закручено с геттерами, которые сопоставляют объект таблице, зачем в таком случае вводит зависимость объектов от DataBase_Object?

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

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

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

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

                                                    API должно абстрагировать бизнес логику от ORM, а ORM в свою очередь в идеале должно абстрагировать способ хранения и доступа к данным.
                                                +2
                                                В документации, кстати, очень скудно (никак) освящена проблема работы с relations. Их нет или это недостаток документации?
                                                0
                                                И если у вас уже так всё лихо закручено с геттерами, которые сопоставляют объект таблице, зачем в таком случае вводит зависимость объектов от DataBase_Object?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                                            Вы предложили что-то революционное? Что-то удобнее существующего? Удачно совместили плюсы других ORM и не повторили минусов? Ответы на все вопросы: нет. Единственный «аргумент» — легкость, который отнюдь не является плюсом и являться им не может. Ибо не кол-вом кода мерится качество и удобство продукта.
                                                              –2
                                                              > Не надо давать способ выбрать объект 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
                                                                Спорный вопрос. Properl & Doctrine & etc это полноценные и тяжелые ORM, они требуют полной спецификации базы и соответствующей конкретики в обращении с ней
                                                                В том же самом пропеле я в одном XML конфиге получаю структуру базы. Легко, доступно и в одном месте.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                                          • НЛО прилетело и опубликовало эту надпись здесь

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

                                                            Самое читаемое