Самопальная ORM для Битрикс

    Не смотря на то, что модуль с поддержкой ORM разработчики битрикса анонсировали ещё в конце прошлого года, и того, что этот модуль заявлен в списке доступных в практически всех редакциях, реально его пока что нет в комплекте, как нет по нему и документации. Оставим за кадром неэтичность включения отсутствующих фич в описание продукта в разделе покупки, вернемся к ORM. Раз его нет — почему бы не реализовать нечто подобное самостоятельно?





    Для нетерпеливых — сразу дам ссылку на исходники модуля, на GitHub'е. Это как-бы прототип — потому что имеет некоторые проблемы, да и не все моменты решены красиво, но тем не менее, он уже работает. Так что всё-таки наш продукт. Хоть и прототип. На написание кода ушло менее 4х часов, со всеми вытекающими.

    Полезности


    • Реализован механизм создания объектов нужных классов через фабрику.
      $item=new ORM(5); //создаем объект инфоблока с номером 5
      $item=ORM::Factory(5); //аналогично
      
      $item=new ORM("news"); //создаем объект инфоблока с CODE=news
      
      $item=ORM::Factory("news"); //сначала будет попытка создать объект класса newsBitrixOrm или NewsBitrixOrm, если таковой не будет найдет - попытка создать объект инфоблока с CODE=news
      

    • Есть поддержка сеттеров и геттеров, автоматически применяемых при работе с полями.
      class NewsBitrixORM extends ORM{
          protected $IBlockID=1;
          protected $auto_getters = array("NAME"=>"GetNameValue");
          protected $auto_setters = array("NAME"=>"SetNameValue");
          
          public function GetNameValue($value){
              return str_replace(" (имя новости!)", "", $this->_data["NAME"]);
          }
          public function SetNameValue($value){
              $this->_data["NAME"]=$value." (имя новости!)";
              $this->_changed_fields["NAME"]="NAME";
              return true;
          }
      }
      

    • Реализованы все базовые действия (создание, удаление, изменение, поиск по различным фильтрам).
      $ormNews = ORM::Factory(4);
      $list=$ormNews->Where("WIDTH","=","140")->FindAll();
      foreach ($list as $_news){
          $_news->NAME="Новость с номером {$_news->ID}";
          $_news->WIDTH=24;
          echo "<pre>".print_r($_news->AsArray(true),true)."</pre>";
          if ($_news->ID%2==0) $_news->Delete(); else {
              $_news->NAME.=" [обновлена!]";
              $_news->Save();
          }
      }
      

    • Работа как со стандартными полями, так и с пользовательскими свойствами идет совершенно одинаково.
    • Есть своя особенность при работе с полями типа «список». Например, у нас есть свойство COLOR. При работе с ним будет видно два поля — COLOR, содержащее текстовое значение выбранного элемента списка, и COLOR__ID (два подчеркивания), содержащее ID выбранного варианта. Для изменения значения нужно назначить ему ID другого варианта списка (например $item->ID=5), при этом автоматически COLOR__ID получит это значение, а COLOR — изменит текстовое значение.


    Недостатки


    • некоторое уменьшение скорости. Работает с уже существующим API для работы с данными инфоблоков, а не напрямую с базой. С другой стороны, эти части можно и переписать, после чего весь код, завязанный на данную ORM продолжит работать
    • работает исключительно с элементами (не с папками)
    • отсутствуют нормальные механизмы для сложных запросов со вложениями запросами, с OR-запросами.
    • отсутствует возможность наполнять свойства-массивы (множественные свойства) запросом вида $obj->item[]=«значение»; Вместо него пока приходится использовать AddToArrayValue(«item»,«значение»). Как обойти этот момент, пока что не придумал.
    • отсутствует решения для создания зависимостей между инфоблоками (один к одному, один ко многим и т.п.)

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 11

      +8
      Посмотрел исходники. Тесты? SOLID? Вобщем как минимум рефакторить вам надо сие творение.
      Плюсану однако, делиться кодом это хорошо.
        0
        Спасибо, я с удовольствием приму любую критику по делу, вы мне как минимум полезные мысли уже подсказали (конкретно разобраться в SOLID-принципах, поиграться с автотестами). Если не делиться своими наработками — полезную критику получить гораздо сложнее. Плюс, имхо, даже в таком виде модуль уже может приносить пользу не только мне.
          0
          Тоже плюсанул за старания, но не вижу смысла в этой штуке учитывая это:
          Работает с уже существующим API для работы с данными инфоблоков, а не напрямую с базой.

          Смысл Highload iblocks как раз в том, что бы обойти тормозное iblock-api. А вы просто сделали (не доделали) новое АПИ. В чём смысл?
            0
            Смысл в
            0) именно новом API :)
            1) удобстве работы, написании новых компонентов например
            2) можно заюзать ORM, понравится код писать в таком ключе — уже тогда переписать ту часть, что отвечает за работу с базой.
            3) ORM не сломается, если в очередном патче поменяется внутренняя логика работы с базой
            4) Highload iblocks, судя по доступной информации, будет работать «с отдельным NoSQL хранилищем». Т.е., это будет отдельная структура, параллельная инфоблокам. Для существующих сайтов без существенной переделки профита не достичь.
          +1
          Мы тоже написали свою ORM для битрикса, но немного в не в таком ключе как у вас. Наши сущностные классы не привязаны напрямую к api битрикса, а работа с api идет через мепперы.
          Таким образом, мы легко можем и юнит тесты писать, да и вообще делать много интересных штук (например, дублирование и синхронизацию данных ИБ в mongodb, и поиск на фронте делать по монге, что дает существенные бонусы, а если еще взять и работу с геоданными, то восторг программистов не описать словами :) )
          Из минусов — естественный оверхед в dto и т.п. Но на больших проектах преимущества побеждают геморрои :)
            +1
            Эх, любопытно было бы в коде вашем покопаться… :)
              0
              К сожалению выложить в паблик не могу.
              Вообще, архитектурно ничего сверхъестественного нету, решения вполне стандартные и описанные в любом источнике по DDD.
              Изначально у нас был вариант а-ля Active Records, был базовый класс сущности, который мог сохранять элемент в ИБ, от этого класса наследовались другие сущностные классы. Все свойства у классов были описаны в phpdoc нотациях, поэтому в модулях и компонентах оперировали только объектами, с нормальной поддержкой от IDE.
              Но при росте сложности AR модель начинает показывать свои недостатки, вот, и поэтому переписали работу с ИБ.
              Мепперы — это прослойка, которая берет сущностные классы или контексты данных и сохраняет в БД. В этом случае получается бизнес уровень полностью независимый от субд и прочего. Это дает довольно хорошие бонусы, по сути, мы можем нашу логику перенести под что угодно, например, переписать все под symfony (ну а мы, например, сделали свои high load iblock, не дождались так сказать :)). Ну, а недостаток, как я уже написал — это увеличение по сути бесполезного кода в мепперах.

              Вопрос, тут на самом деле в другом, на какой черт надо городить все это в битре, потому что для сайта визитки или типового магазина данный функционал будет больше вредить. А на проектах, где реально начинают проявляться бонусы DDD, использовать битру как минимум сомнительно.
                0
                primepix Насчет кода понимаю, но в данный момент я пытаюсь понять общую вашу концепцию, методику встраивания. Как вы интегрировались с битриксовской админкой? Вы целиком отказались от обычных инфоблоков? Или дублировали инфоблочную структуру в отдельных таблицах? Извините за назойливость, но уж больно понять идею хочется. :)
                  0
                  Нет, совсем от ИБ мы не отказывались. В нашем случае ИБ это как бы база данных. Поэтому все родное (админка и другие модули) работают нормально, без изменений.
                  ИБ структуру так же нигде для этих целей не дублировали (кроме как в монго для кеширования и поиска). Поиск по API битры возвращает только ID'шники, а потом по ним из монго тянется весь объект. Где удобнее — ищем в монге. Тут суть в том, что битрикс делает доп. запросы, чтоб собраться все свойства элемента и сами запросы могут быть сложные за счет пачки join'ов. В монго же хранится «плоская» структура, т.е. объект сразу со всеми свойствами.
                  Когда администраторы системы меняют что-то в админке, то после сохранения срабатывает битровый хук и данные обновляются в монго.

            0
            Что еще заявлено, но не существует в битриксе?
              0
              Вопрос к авторам битрикса, их маркетологам и т.п. ;-)

            Only users with full accounts can post comments. Log in, please.