2010
24
jan

Doctrine és Zend Framework integráció


Jó ideje Zend Frameworkkel gyártom a “kontentot” és az elmúlt két év tapasztalatából azt a fájdalmas konzekvenciát kellett levonnom, hogy a Zend MVC-jének modell rétegével kínkeserves meló egy korrekt Domain Modell leprogramozása, a modell változásainak követése és karbantartása. Amikor a feladat szükségessé teszi egy komolyabb adatbázis-struktúra kezelését akkor a Zend_Db időről-időre csődöt mond. Hiába próbáltam hosszú időn keresztül elkészíteni a tökéletes adatbázis absztrakciós réteget, sajnos sosem jártam igazi sikerrel. Kipróbáltam mások elképzeléseit is (Model Infrastructure, Domain Model Programming With the Zend Framework) de hosszabb távon, az igények növekedésével ezek a megvalósítások mindig zsákutcának bizonyultak. Nem arról van szó, hogy a Zend modell rétege nem működne, sőt egyszerű modellek esetén nagyon kényelmes és gyors is. Arról van szó, hogy egy megfelelően bonyolult domain modell karbantartása indokolatlanul sok feladatot hárít a fejlesztőre, így sokszor a napi maintenance munka nagy része azzal megy el, hogy az alkalmazás működésében vagy az adatmodellben bekövetkezett változások miatt az domain modellt kell püfölni. Egy agilis fejlesztési folyamat során ez sajnos nem megengedhető, túlzottan sok időt és energiát emészt fel.

A legutóbbi nagyobb projekt során az egyik kolléga hívta fel a figyelmet arra, hogy a jelenlegi – Zend_Db-vel készült – modell struktúra enyhén szólva is kurva bonyolult és átláthatatlan. Mivel napi szinten benne voltam a modell karbantartásában és fejlesztésében ez szinte fel sem tűnt, de amikor bármi miatt módosítani kellett az egy rémálom volt. A javításról nem is beszélve. Eldöntöttük, hogy ideje nyugdíjazni a Zend_Db-t a projekt keretein belül és alternatíva után kezdtem kutakodni. Így került képbe a Doctrine, amit már jó ideje figyelemmel kísértem és konkrét tapasztalataim is voltak vele, de Zend Framework integrációval még sosem próbálkoztam. Találtam egy remek bejegyzést Eric Leclerc blogján Doctrine 1.2 is Zend Framework friendly címmel, valamint a zendcasts.com-on (Podcast) található egy screencast sorozat is a témában:

A Doctrine egy object relational mapper (meg sem próbálkozok a fordításával) amely tulajdonképp az adatbázis absztrakciós réteg tetején ül és integrálódik az OOP-s környezetbe. A parancssori interfész segítségével képes adatbázis sémákból php modelleket generálni (és vica versa), tesztadatok betöltésére vagy akár unit tesztelésre is. Szóval tényleg nagyon erős kis eszköz, igazán szerethető. A Symfony projekt is ezt használja például alapértelmezett ORM-ként a Propel mellett. Ami pedig a legszebb, hogy nagyon gyorsan megtanulható és üzembe állítható. Ezt mi sem bizonyítja jobban, mint az imént említett projekt, ahol a Zend_Db alapú modellek elkészítése több hétig tartott, a Doctrine migráció pedig mindösszesen egyetlen napig (bár az is igaz, hogy egy meglehetősen hosszú nap volt).

Lássuk, hogyan is történik ez integrálás a gyakorlatban. A standard Zend projekt fában a képen látható mappaszerkezetet kell létrehozni. A Doctrine projekt honlapjáról letöltött csomag lib könyvtárából másoljuk be a komplett Doctrine és vendor foldereket a projektünk library könyvtárába a Zend mellé valamint a doctrine.php-t szintén a library folderbe. Ezt követően az application.ini-ben kell némi konfigurációt elvégezni:

autoloadernamespaces[]      = "Doctrine_"
doctrine.connection_string  = "mysql://user:password@mysqlhost/dbname"
doctrine.data_fixtures_path = APPLICATION_PATH "/../doctrine/data/fixtures"
doctrine.models_path        = APPLICATION_PATH "/models"
doctrine.migrations_path    = APPLICATION_PATH "/../doctrine/migrations"
doctrine.sql_path           = APPLICATION_PATH "/../doctrine/data/sql"
doctrine.yaml_schema_path   = APPLICATION_PATH "/../doctrine/schema"
doctrine.generate_models_options.pearStyle              = true
doctrine.generate_models_options.generateTableClasses   = false
doctrine.generate_models_options.generateBaseClasses    = true
doctrine.generate_models_options.baseClassesDirectory   = null
doctrine.generate_models_options.classPrefixFiles       = false
doctrine.generate_models_options.classPrefix            = 'Model_'
doctrine.generate_models_options.baseClassPrefix        = 'Base_'

Majd a Bootstrap.php-ban kell a Doctrine-hez kapcsolódó initet elkészíteni, hogy az alkalmazás szintjén müködjön:

public function _initDoctrine()
{
    require_once 'Doctrine.php';
    $autoloader = Zend_Loader_Autoloader::getInstance();
    $autoloader->registerNamespace('sfYaml')->pushAutoloader(array('Doctrine', 'autoload'), 'sfYaml');
    $doctrineConfig = $this->getOption('doctrine');
 
    $manager = Doctrine_Manager::getInstance();
    $conn = $manager->openConnection($doctrineConfig['connection_string']);
    $conn->setCharset('utf8');
    return $manager;
}

Az application/cli_bootstrap.php-ba a következő kerül:

<?php
// Define path to application directory
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', dirname(__FILE__));
 
// Define application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));
 
set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../library'),
    get_include_path(),
)));
 
/** Zend_Application */
require_once 'Zend/Application.php';
 
// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);

Készíteni kell a projekt mappában egy scripts/doctrine-cli állományt a következő tartalommal:

#!/usr/bin/env php
<?php
define('APPLICATION_ENV', 'development');
require realpath(dirname(__FILE__) . '/../application/cli_bootstrap.php');
 
$application->getBootstrap()->bootstrap('doctrine');
$options = $application->getOption('doctrine'); 
 
$cli = new Doctrine_Cli($options);
$cli->run($_SERVER['argv']);

Majd futtathatóvá kell tenni. Ez lesz a parancssori interfész amelyen keresztül utasításokat lehet adni az ORM-nek. Ezzel a Doctrine teljesen beágyazódik a Zend Frameworkbe és a projekt honlapján lévő dokumentációk alapján már lehet is gyártani a szebbnél szebb modelleket és tolni a kódból a DQL-t :)

Címkék: , , , ,

9 hozzászólás

  • Hivatkozás erre a hozzászólásra solesz
    2010. március 10.

    szia,
    örülök, hogy találtam valamit zend és doctrine ügyben. (és nem valami fórumon odavetett

    adatbázisban, asp-ben (mégcsak nem is .net -ben) OOP-ben jártas vagyok, a PHP ugyan nem idegen számomra, de kezdő szinten vagyok. zend studio-t próbálgatom.
    Mindez együtt nem hangzik valami fényesen, de nem vesztem még el :)

    Mindenesetre, amire még kiváncsi volnék, hogy a a zend_db-s átállás után éreztél-e valamilyen sebesség/memóriaigény változást, és mekkora adatbázissal doglozol? illetve inno_db+foreign_key vagy inkább myisam tábláid vannak?

    kösz, remélem még olvasod a kommentet, és tudsz válaszolsz is

    solesz

  • Hivatkozás erre a hozzászólásra vbali
    2010. március 10.

    A sebesség és memóriaigény észrevehetően nem változott – bár ezzel kapcsolatban nem is végeztem konkrét méréseket. A fejlesztési időben és a kód áttekinthetőségében azonban szembeötlő a változás. Sokkal könnyebb és gyorsabb is a munkavégzés a Doctrine-re épülő modellekkel. Jellemzően myisam táblákkal dolgozok, hisz a Doctrine nagyon szépen kezeli a kapcsolatokat a táblák között.

  • Hivatkozás erre a hozzászólásra sovanytej
    2010. április 21.

    Hello, szép írás, körülbelül két hete foglalkozom doctrine-al -és valamiért nem akarja a parancssori interfésszel kigenerálni az adatbázisból a kapcsolatokat se a yaml fájlba se pedig a modelekbe, nincs ötleted hogy miért lehet? MYSql-t használok, InnoDb engine-nel.

  • Hivatkozás erre a hozzászólásra vbali
    2010. április 23.

    sovanytej: dob valami hibát is esetleg? Sok oka lehet annak, hogy miért nem megy. Jogosultságok, elérési utak stb.

  • Hivatkozás erre a hozzászólásra sovanytej
    2010. április 23.

    köszi, már megvolt a hiba, DBDesingerrel írtam az adatbázist és az SQL generálásakor nem csekkeltem be hogy igen kérem a külső kulcsokat. Én buta meg erre nem is számítottam, mindig csak berántottam a legenerált SQL-t bele se nézve… De ez jó lecke volt

  • Hivatkozás erre a hozzászólásra sovanytej
    2010. április 23.

    hmm egy újabb felmerülő problémám, hogy semmilyen hibát nem dob a doctrine, Zend- dob egy ilyet: Fatal error: Uncaught exception ‘Zend_Controller_Dispatcher_Exception’ with message ‘Invalid controller specified (error)’ kezdetű hibát, de semmi hogy arról, hogy az adatbázisműveletnél lenne hiba. Ha van erre valami jó megoldásod megköszönönném.

  • Hivatkozás erre a hozzászólásra vbali
    2010. április 23.

    Az lehet a probléma, hogy nincs error controllered ami a hibát kezelné és megjelenítené. Itt a leírás a ‘Using the ErrorHandler as a 404 Handler’ szekcióban: http://framework.zend.com/manual/en/zend.controller.plugins.html

    Remélem ez segít!

  • Hivatkozás erre a hozzászólásra sovanytej
    2010. április 23.

    váá örök hála:D valahova eltüntettem az errorcontrollerem, szegénynek megvolt a phtm-je, de hát így szörnyü volt debuggolni, köszi még1x

  • Hivatkozás erre a hozzászólásra Kristóf
    2010. június 22.

    Köszönöm, kitünő írás.

    Nekem ezzel az application.ini-vel nem működött amúgy, az utolsó két sort le kellett cserélni aposztrofok nélkülire, azaz:

    doctrine.generate_models_options.classPrefix = Model_
    doctrine.generate_models_options.baseClassPrefix = Base_

    (Doctrine-1.2.2)

Szólj hozzá te is!