Правильная интеграция Doctrine в CodeIgniter

    Здравствуйте, на днях занялся интеграцией популярного PHP-ORM Doctrine с не менее популярным PHP-фрэймворком CodeIgniter и обнаружил, что официальный способ интеграции, озвученный в кукбуке Doctrine и на вики CodeIgniter вызывает у меня, по меньшей мере, негодование.

    Почему? Потому что в инструкции предлагается:
    1. Изменять содержимое файла index.php;
    2. Использовать конфигурационный файл application/config/database.php для инициализации соединения и подключения CI.

    Что в этом плохого? То, что при обновлении фрэймворка, нам из раза в раз придется лезть в файл index.php и вносить изменения. То, что в конфигурационных файлах не должно выполняться системных действий. А также то, что в итоге мы подключаем конфигурационный файл database.php через index.php, полностью игнорируя гибкие средства фрэймворка.

    Я предлагаю на суд хабраюзеров свой способ подключения Doctrine к CodeIgniter, лишеннный вышеуказанных недостатков. Итак, начнем:
    1. Первым делом скачиваем последнюю версию Doctrine отсюда;
    2. Копируем из скачанного архива содержимое директории lib в директорию system/database/doctrine/;
    3. Я стараюсь всегда мыслить логично, поэтому, для подключения ORM библиотеки, мы используем механизм библиотек в CI. В директорию application/libraries мы помещаем файл doctrineORM.php со следующим кодом:

      1. <?php if (!defined('BASEPATH')) exit('No direct script access allowed');
      2.  
      3. /**
      4. * Doctrine initialization class
      5. */
      6. class DoctrineORM
      7. {
      8.  function __construct() {
      9.     // Получаем конфиг базы данных
      10.     require_once(APPPATH . 'config/database.php');
      11.  
      12.     // Создаем DSN из полученной инфы
      13.     $db['default']['dsn'] = $db['default']['dbdriver'] .
      14.                             '://' . $db['default']['username'] .
      15.                             ':' . $db['default']['password'].
      16.                             '@' . $db['default']['hostname'] .
      17.                             '/' . $db['default']['database'];
      18.  
      19.     // Подключаем Doctrine.php
      20.     require_once(BASEPATH . 'database/doctrine/Doctrine.php');
      21.  
      22.     // Устанавливаем autoloader
      23.     spl_autoload_register(array('Doctrine', 'autoload'));
      24.  
      25.     // Инициализируем соединение
      26.     Doctrine_Manager::connection($db['default']['dsn'], $db['default']['database']);
      27.  
      28.     // Устанавливаем тип загрузки моделей в "conservative/lazy"
      29.     Doctrine_Manager::getInstance()->setAttribute('model_loading', 'conservative');
      30.  
      31.     // Загружаем модели в autoloader
      32.     Doctrine::loadModels(APPPATH . 'models');
      33.  }
      34. }
      * This source code was highlighted with Source Code Highlighter.

      Этот файл будет отвечать за инициализацию Doctrine внутри нашего проекта. Получается своеобразный loader;
    4. В корень нашего application помещаем файлы doctrine и doctrine.php

      doctrine:
      1. #!php
      2. <?php
      3.  
      4.  // Директории system и application относительно текущей
      5.  $sys_folder = '../';
      6.  $app_folder = '.';
      7.  
      8.  include('doctrine.php');
      * This source code was highlighted with Source Code Highlighter.


      doctrine.php:
      1. <?php
      2.  
      3. define('BASEPATH', str_replace('\\', '/', $sys_folder) . '/');
      4. define('APPPATH',  str_replace('\\', '/', $app_folder) . '/');
      5.  
      6. require_once(APPPATH . 'libraries/doctrineORM.php');
      7. new DoctrineORM();
      8.  
      9. // Конфигурируем "Doctrine Cli"
      10. $config = array(
      11.                 'data_fixtures_path' => APPPATH . '/fixtures',
      12.                 'models_path'         => APPPATH . '/models',
      13.                 'migrations_path'     => APPPATH . '/migrations',
      14.                 'sql_path'            => APPPATH . '/sql',
      15.                 'yaml_schema_path'    => APPPATH . '/schema'
      16.               );
      17.  
      18. $cli = new Doctrine_Cli($config);
      19. $cli->run($_SERVER['argv']);
      * This source code was highlighted with Source Code Highlighter.

      Эти файлы будут отвечать за CLI интерфейс библиотеки Doctrine. Общаться с Doctrine мы сможем через консольную инструкцию «php doctrine», выполненную из директории application;
    5. Создать в директории application поддиректории:
      • application/fixtures;
      • application/migrations;
      • application/schema;
      • application/sql.

    6. Ввнести верные данные для соединения с СУБД в application/config/database.php. Это следует делать так же, как если бы вы работали с нативным ORM CodeIgniter;
    7. Занести библиотеку doctrineORM в список автозагружаемых библиотек в конфигурационном файле application/config/autoloader.php

      Пример:
      1. $autoload['libraries'] = array('doctrineORM', 'session');
      * This source code was highlighted with Source Code Highlighter.


    Всё. Теперь мы спокойно можем использовать Doctrine внутри контроллеров и вьюшек, относящихся к данному приложению.
    Опробуем простенький пример приложения с применением нашего вновь прибывшего ORM =)
    1. Создадим в директории application/schema файл user.yml со следующей структурой:

      1. ---
      2. User:
      3.  columns:
      4.     id:
      5.      primary: true
      6.      autoincrement: true
      7.      type: integer(4)
      8.     username: string(255)
      9.     password: string(255)
      10.  relations:
      11.     Groups:                    # Relation alias or class name
      12.      class: Group             # Class name. Optional if alias is the class name
      13.      local: user_id          # Local: User.id = UserGroup.user_id. Optional
      14.      foreign: group_id        # Foreign: Group.id = UserGroup.group_id. Optional
      15.      refClass: UserGroup     # xRefClass for relating Users to Groups
      16.      foreignAlias: Users     # Opposite relationship alias. Group hasMany Users
      17.  
      18. Group:
      19.  tableName: groups
      20.  columns:
      21.     id:
      22.      primary: true
      23.      autoincrement: true
      24.      type: integer(4)
      25.     name: string(255)
      26.  
      27. UserGroup:
      28.  columns:
      29.     user_id:
      30.      type: integer(4)
      31.      primary: true
      32.     group_id:
      33.      type: integer(4)
      34.      primary: true
      35.  relations:
      36.     User:
      37.      local: user_id        # Local key
      38.      foreign: id          # Foreign key
      39.      onDelete: CASCADE     # Database constraint
      40.     Group:
      41.      local: group_id
      42.      foreign: id
      43.      onDelete: CASCADE
      * This source code was highlighted with Source Code Highlighter.

    2. Откроем консоль (или командную строку) и перейдем в директорию нашего приложения (application);
    3. Выполнение следующей команды создаст модели в директории application/models на основе структуры user.yml
      $ php doctrine generate-models-yaml<br>generate-models-yaml - Generated models successfully from YAML schema<br><br>* This source code was highlighted with Source Code Highlighter.

    4. Теперь можно создать немного фиктивных данных для вноса их в СУБД. Для этого создайте файл application/fixtures/users.yml
      1. ---
      2. User:
      3.  jwage:
      4.     username: jwage
      5.     password: test
      * This source code was highlighted with Source Code Highlighter.

    5. Теперь запустите задание build-all-reload, чтобы очистить базу данных, создать модели и пересоздать структуру в базе
      $ php doctrine build-all-reload<br>build-all-reload - Are you sure you wish to drop your databases? (y/n)<br>y<br><br>* This source code was highlighted with Source Code Highlighter.

    6. Теперь можно использовать Doctrine для получения/записи данных напрямую из нашего контроллера. В контроллер welcome, метод index() добавьте следующий код и откройте
      главную страницу проекта в браузере
      1. $user = new User();
      2. $user->username = 'zYne-';
      3. $user->setPassword('password');
      4. $user->save();
      5.  
      6. $userTable = Doctrine::getTable('User');
      7. $user = $userTable->findOneByUsername('zYne-');
      8.  
      9. echo $user->username; // prints 'zYne-'
      * This source code was highlighted with Source Code Highlighter.


    Надеюсь, кому-нибудь помог. Извиняюсь, что в личный блог, а не в «CodeIgniter» — не хватает кармы.
    UPD: перенес в «CodeIgniter»
    UPD2: в config/autoload.php обязательно надо выключить «database» из списка загружаемых библиотек

    Поделиться публикацией

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

      +3
      Рад, что есть ещё люди, способные совершенствовать велосипеды :)
        –5
        Скажите, а зачем один фреймворк подключать к другому? Какая была у вас реальная задача?
          +1
          Doctrine это не фреймворк, а ORM.
            +3
            CodeIgniter — MVC-фреймворк, Doctrine — это ORM, вещи совсем разные.
            Doctrine, грубо говоря, — один из возможных вариантов для «M» в «MVC».
              –1
              А я думал, что MVC и ORM — это такие концепции в программировании и построении приложений. А фреймворк — это такой набор библиотек и инструментов для построения приложений, вне зависимости от концепции, которую этот фреймворк реализует.
                0
                Вы немного (хм… ну ладно, вру, ужасно!) ошибаетесь. Это можно поправить даже прогуглив эти аббревиатуры. И, кстати, в том, что один фреймворк нужно/можно подключать к другому — тоже ошибаетесь. Вот, к примеру, Symfony 1.1+ позволяет использовать классы ZendFramework и ezComponents с их человеческой автозагрузкой (никаких include_once костылей) — очень даже ничего, к примеру, повзаимствовать у ez классы для построения графов… мм…
                  +1
                  Ну хоть один нормальный ответ. Я обращаюсь к википедии:
                  ru.wikipedia.org/wiki/ORM
                  ru.wikipedia.org/wiki/Model-View-Controller
                  Из прочитанного я понимаю, что и ORM и MVC — это концепции работы с данными и построения архитектуры приложений. Так? Или я читаю не там (реально не могу понять за что минусы ставят)?
                  И почем нельзя назвать фреймворком Doctrine — реализацию ORM на php? Не понятно, может хоть вы проясните, ну пожалуйста.
                    0
                    Нэд.
                    MVC ( Модель-вид-представление ) — одна из моделей архитектур всего приложения в целом, где у вас в одном месте контроллеры, в другом классы моделей (бизнесс-логика, да) и в третьем — шаблоны страниц.
                    CodeIgnitor — MVC фреймкорк.

                    ORM — Object Relational Mapping (мэппинг объектов) — это такая модель работы с базой, в которой каждой таблице в базе каким-то образом ставится в соответствие класс модели данных в вашем приложении. То есть разметка (мэппинг) ваших обэектов в таблицы базы.
                    Doctrine, Propel, Sequel, Active record — все это ORM фреймворки / библиотеки.

                    ORM касается только работы приложения с базой. MVC — архитектура всего приложения.
                      0
                      Ну как же так? Вы говорите «нет», а потом сами называете Doctrine — ORM фреймворком. Я не понимаю где я ошибаюсь в таком случае.
                        0
                        ORM, MVC — паттерны.

                        ORM — паттерн, относящийся к работе с данными.
                        MVC — к всему приложению в целом.

                        Реализации и того и другого можно назвать фреймворком, но это совершенно разные фреймворки.
                          0
                          Ну так да, я же тоже самое и написал — полностью с вами согласен. А меня все (digger, kmike, NaTTs) дружно пытаются убедить что я не прав. Бред какой-то. Еще и в карму насрали :\
                          Самое обидное — так и не получил внятного ответа на свой первый вопрос: для какой задачи понадобилось использовать ORM.
                            0
                            Вообще ORM удобен для работы с данными, а такой ORM, как Doctrine вдвойне удобен. Если логика приложения по работе с данными (модели) реализуется Doctrine, это получается на порядок удобнее, чем встроенный AR CodeIgniter.

                            Для простых задач, где запутиться тяжело, или очень требовательных к производительности проектов, конечно же, лучше использовать SQL напрямую, не пользуясь даже AR CodeIgniter.
                              0
                              Спасибо, SamDark! Теперь мне понятно.
              0
              Cовмещение MVC фреймворка cо сторонним ORM — дело обычное. Например Symphony использует ORM Propel

              CI в отличие от CakePHP, Symphony и т. п. не имеет встроенного ORM. За это его похоже и любят. И ровно по этой причине в него постоянно пытаются воткнуть какой-нибудь ORM.

                0
                Мне кажется, CakePHP и Ci на ранних стадиях не любят потому, что они PHP4-based.
                Мне, к примеру, очень непонравилось в описании про «чтобы сделать метод класса приватным, назовите его с символа подчеркивания». Бррр…
                Это было год назад, затем я за судьбой проекта не следил, если я ошибаюсь — можете щелкнуть по лбу)
              +1
              спасибо за статью.
              У меня еще идея доработать CI'шный профайлер, чтоб он doctrine'овские данные выводил в статистику запросов.
              Надо будет собраться написать :)
                +1
                Насколько Doctrine более тормозной по сравнению с орм от CodeIgniter? Просто, есть версия, ч то использование данного орм убивает одно из основных преимуществ CI — скорость
                  0
                  А в CI разве есть ORM? Там вроде бы реализация ActiveRecord
                    0
                    мог попутать с Kohana
                  –3
                  [offtop]
                  меня, как пользователя cakephp, как-то удручает запись вида:
                  $userTable = Doctrine::getTable('User');
                  $user = $userTable->findOneByUsername('zYne-');
                  вместо
                  $user = new User();
                  $foundUser = $user->findOneByUsername('zYne-');


                  [/offtop]
                    0
                    при генерации классво есть параметр отвечающий за генерацию тэйблов
                    'generateTableClasses' => true
                    точно не помню, но тогда будет что-то вроде
                    $user = new UserTable();
                    $foundUser = $user->findOneByUsername('zYne-');
                    хотя мне больше нравится запись $user = Doctrine::getTable('User')->findOneByUsername('zYne-');
                    а кэйк, кажись, до сих пор поддерживает совместимость с 4-м пыхом? если да, то тогда там так низя.
                    моё знакомство с orm началось как раз с cake
                    0
                    Вопрос — как обстоят дела с производительностью? Не упала?
                      0
                      Ну конечно упала =) Причем значительно… Примерно раза в 2 на моем предыдущем ноутбуке (C2Duo 1.8, 2GB RAM) относительно нативного ORM CI. Однако, гибкость получаемая при разработке с Doctrine лично для меня покрывает все издержки производительности. В конечном счете, мы просто выбираем между производительностью и гибкостью разрабатываемой системы…
                        0
                        Так и предполагал, спасибо =)

                        Думаю, приоритетным фактором должна быть производительность, хотя, если взять ту же Symfony (ветка 1.1.x) с Propel на борту и взглянуть на их официальный сайт, который очень шустро работает, то есть к чему стремиться =)
                          0
                          На крупном проекте производительность рано или поздно все-равно упрется в крышку сервера и вот тогда Doctrine или Propel будут более выгодно выглядеть за счет своей гибкости и масштабируемости в сравнении с менее развитыми решениями. А в мелком проекте вообще не стоит заострять внимание на производительности. IMHO
                      0
                      А чем данный метод не подходит?
                        0
                        blog.medryx.org/2008/10/04/codeigniter-and-doctrine/
                        На 1.7.1 именно он завелся.
                        Метод описанный здесь не заработал, библиотечка не подключилась. Позвала в дебри fw…
                          0
                          да и проще там гораздо.
                            0
                            но спасибо за наводку.
                            ОЧЕНЬ удобно стало)
                          0
                          Эммм, ну раз вы размышляете в таком ключе, то засовывать Doctrine в system/database/ мне кажется нелогичным, следовало ложить все в application/libraries.

                          Ну вот к примеру, как к CI один человек подключает Zend'овский функционал: Ы.

                          Я правда не сильно спец в CI, поправьте, если я не прав. Просто на будущее хочу попробовать использовать именно CI в связке с Doctrine.

                          Еще не решил для себя лучше ли чем-то Kohana, ORM там к сожалению слабоват, очень не хватает Database Schema Generation и задания Mapping Information в PHP (самый удобный лично для меня способ)

                          Кстати, может заодно кто посоветует php фреймворк где есть что-то типа автоматически генерируемых по доп. информации в модели Django'вских admin pages с широкими возможностями кастомизации форм (желательно на уровне элементов форм)? И чтоб это ориентировано было именно на использование в продакшн, а не для разработкт/отладки. (Скафолдинг в CI — это не то).
                          Пока видел такое только модулем от сторонних разработчиков под RoR, с пыхом к сожалению ваще глухо, а вещь коллосально экономит время разработки.

                          Ухх, какой комментарий жирненький получился :) Жаль, мало кто прочитает, топик ведь старый…
                            0
                            Есть старое, почти умершее приложение для CI, могу скинуть архивом, в сети его найти сложно, да и не помню где. Стучитесь в личку, если актулаьно и пишите своё мыло)
                            В принципе пользуемся им — удобно, но иногда приходится допиливать.

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

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