CodeIgniter

индекс
90,72

Правильная интеграция 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
    generate-models-yaml - Generated models successfully from YAML schema


    * 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
    build-all-reload - Are you sure you wish to drop your databases? (y/n)
    y


    * 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
5 октября 2008, 18:18
48

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

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

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

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

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

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

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

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

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


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

Думаю, приоритетным фактором должна быть производительность, хотя, если взять ту же Symfony (ветка 1.1.x) с Propel на борту и взглянуть на их официальный сайт, который очень шустро работает, то есть к чему стремиться =)
0
everzet #
На крупном проекте производительность рано или поздно все-равно упрется в крышку сервера и вот тогда Doctrine или Propel будут более выгодно выглядеть за счет своей гибкости и масштабируемости в сравнении с менее развитыми решениями. А в мелком проекте вообще не стоит заострять внимание на производительности. IMHO
0
merqlove #
А чем данный метод не подходит?
0
merqlove #
blog.medryx.org/2008/10/04/codeigniter-and-doctrine/
На 1.7.1 именно он завелся.
Метод описанный здесь не заработал, библиотечка не подключилась. Позвала в дебри fw…
0
merqlove #
да и проще там гораздо.
0
merqlove #
но спасибо за наводку.
ОЧЕНЬ удобно стало)
0
nayjest #
Эммм, ну раз вы размышляете в таком ключе, то засовывать 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
SolarSoul #
Есть старое, почти умершее приложение для CI, могу скинуть архивом, в сети его найти сложно, да и не помню где. Стучитесь в личку, если актулаьно и пишите своё мыло)
В принципе пользуемся им — удобно, но иногда приходится допиливать.

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