войти зарегистрироваться

PHP whois

индекс
169,28

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

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

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

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

Особенности:
  • Лёгкая: ~20kb вменяемого ООП кода.
  • Быстрая и нетребовательная к системным ресурсам.
  • Не требует никакой конфигурации, вы можете моментально начать использовать её на уже существующей схеме базы.
  • Очень лаконичная, позволяет в одну читабельную строку кода выполнить несколько различных операций.
  • Имеет полноценные средства для генерирования(экранирования) SQL запросов по аналогии с DbSimple.
  • Поддерживает псевдо-JOIN таблиц на базе соответствия имён таблиц и полей-индексов
  • Поддерживает создание специфицированных моделей данных с конфигурацией правил самовалидации полей
В ближайших планах:
  • Поддержка других БД (помимо MySQL) на базе PDO драйвера
  • Модуль автоматического кеширования
  • Инициализация структуры таблиц из лаконичного XML c поддержкой FOREIGN KEYS
  • Умные JOIN-ы с авторазбором на вложенные объекты
  • Вложенные логические условия для фильтров и группировки

SVN: http://dabase.googlecode.com/svn/trunk
Архив релиза: http://dabase.googlecode.com/files/DaBase_1.0.zip
Руководство с примерами: http://code.google.com/p/dabase/wiki/UserGuide
Страница проекта на Google Code: http://code.google.com/p/dabase

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

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

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

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

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

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

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

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

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

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

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

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

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

                    class a
                    {
                    const ORLY = 'lol';
                    }

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

                    echo b::ORLY;

                    и с комментом и без оно работает… чудеса :-)
                    • Не вижу в Вашем примере 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);

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

                            конкретно против пхп ничего не имею — он очень клёвый и программирование на нём занимает процентов 95 моего программирования. но уж больно в нём много неочевидных вещей :-)
                            • > много неочевидных вещей
                              С этим, к большому сожалению, полностью согласен :)
                      • с рефлексией можно и без статик в 5-ке старой. короче без LSB
            • > Если Вы можете узнать имя таблицы тут, вызвав $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().

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

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

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

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

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

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

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

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

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

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

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

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

        Пробежался по документации этого 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;
        }
        }


        А где элементарное экранирование? Не стал бы я таким разработчикам свою базу доверять.
        • полагаю, $this->adapter->escape($type) — экранирование, а $ids — числа
          • Ну как всё просто, вот только они там в документации чайникам красненьким не подсветили, чтобы те были осторожны с данными, которые на вход этого метода отсылают. Бедные чайники…
        • Я не говорил, что он лучше всех прочих 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                              $db->user->…

                              $db->user_gold->…

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

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

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

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

                                    public $isGold = 1;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Если бы в заголовке не было слова 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)
Только авторизованные пользователи могут оставлять комментарии. Авторизуйтесь, пожалуйста.