Zend Framework i Doctrine - łatwa integracja

by Mateusz Tymek — on Zend Framework, PHP

Head's up! This post was written back in 2010 and is very likely to contain outdated information.

Walidacja rozbudowanych formularzy

Ostatnio zainteresowałem się możliwością zastosowania Doctrine'a w jednym z moich projektów. Znalazłem wiele informacji na ten temat w internecie, niestety w większości zdezaktualizowanych. Okazało się że z wersji na wersję twórcy Doctrine coraz bardziej ułatwiają życie koderom piszącym w ZF. Aktualny przepis znalazłem na stronie ZendCasts. Podczas prób okazało się, że można to wszystko zrobić jeszcze prościej niż proponuje autor, zatem w tym wpisie podam własny przepis, przetestowany z Doctrine 1.2.1 i Zend-em 1.10 beta (1.9 też powinien działać).

Do stworzenia nowego projektu wykorzystam Zend_Tool - w obecnej wersji nie ma jeszcze zbyt rozbudowanych możliwości, jednak doskonale nadaje się do zbudowania podstawowej struktury naszej aplikacji.
Wystarczy jedno polecenie:

mateusz:~/public_html> zf create project zfdoctrine
Creating project at /home/mateusz/public_html/zfdoctrine

Jeśli Zend_Tool jest poprawnie skonfigurowany to otrzymamy katalog zfdoctrine z działającym projektem. Następnym krokiem jest uzupełnienie podkatalogu library - kopiujemy lub linukujemy do niego Zenda i Doctrine (wystarczy folder /lib/Doctrine z pobranej paczki, no i oczywiście zawarte w nim pliki - lib/vendor i lib/Doctrine.php można pominąć).

Otwieramy plik zfdoctrine/application/configs/application.ini, dodajemy kilka linijek do sekcji [production] aby uzyskać następujący rezultat:

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
appnamespace = "Application"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.params.displayExceptions = 0

autoloaderNamespaces[] = "Doctrine"

doctrine.dsn = "mysql://UŻYTKOWNIK:HASŁ[email protected]/NAZWA_BAZY"

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 = true
doctrine.generate_models_options.generateBaseClasses = true
doctrine.generate_models_options.baseClassPrefix = "Base_"
doctrine.generate_models_options.baseClassesDirectory =
doctrine.generate_models_options.classPrefixFiles = false
doctrine.generate_models_options.classPrefix = "Application_Model_"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.params.displayExceptions = 1

Tworzymy podkatalogi dla doctrine, zgodnie z powyższymi ustawieniami:

zfdoctrine/doctrine/data
zfdoctrine/doctrine/data/fixtures
zfdoctrine/doctrine/data/sql
zfdoctrine/doctrine/migrations
zfdoctrine/doctrine/schema

Edytujemy plik application/Bootstrap.php, dodając metodę _initDoctrine:

<?php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{

    public function _initDoctrine()
    {
        $doctrineConfig = $this->getOption('doctrine');
        $manager = Doctrine_Manager::getInstance();
        $manager->setAttribute(Doctrine_Core::ATTR_AUTO_ACCESSOR_OVERRIDE, true);
        $conn = Doctrine_Manager::connection($doctrineConfig['dsn'],'doctrine');
        $conn->setAttribute(Doctrine_Core::ATTR_USE_NATIVE_ENUM, true);
        return $conn;
    }

}

Jeśli zrobić porównanie do bootstrapu opisanego w screencascie, można zauważyć że mój jest dużo prostszy. To dlatego że moje modele będą miały nazwy w zalecanej przez twórców ZF konwencji (na przykład Application_Model_User). Przez to nie muszę zawracać sobie głowy autoloaderem Doctrine - Zend_Loader w zupełności wystarczy.

Ostatnia rzecz do zrobienia to konsolowy interfejs Doctrine. Proponuję umieścić skrypt w folderze /zfdoctrine/scripts, w pliku doctrine-cli.php:

<?php

define('APPLICATION_ENV', 'development');

define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../library'),
    get_include_path(),
)));

require_once 'Zend/Application.php';

// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);

$loader = Zend_Loader_Autoloader::getInstance();
$loader->pushAutoloader(array('Doctrine_Core', 'autoload'));

$application->getBootstrap()->bootstrap('doctrine');

$cli = new Doctrine_Cli($application->getOption('doctrine'));
$cli->run($_SERVER['argv']);

Z jakiegoś powodu w tym skrypcie trzeba wykorzystać autoloadera Doctrine. To nic - liczy się fakt że nie będzie zawadzał w samej aplikacji.

I to już wszystko. Nasz projekt jest gotowy do współpracy z Doctrine. Możemy teraz przetestować jego działanie działanie, budując przykładowy model.

W pliku zfdoctrine/doctrine/schema/schema.yml zdefiniujmy klasę User:

connection: doctrine
options:
  collate: utf8_unicode_ci
  charset: utf8

User:
  tableName: users
  columns:
    id:
      type: integer
      fixed: false
      unsigned: true
      primary: true
      autoincrement: true
    name:
      type: string(50)
      notnull: true
    password:
      type: string(64)
      notnull: true
    email:
      type: string(70)
      notnull: true

Spróbujmy go zbudować przy pomocy linii poleceń:

mateusz:~/public_html/zfdoctrine/scripts# php5 ./doctrine-cli build-all-reload

Zostaniemy zapytani czy na pewno chcemy usunąć bazę danych (jeśli już istnieje). Naciskamy "Y". Jeżeli wszystko zrobiliśmy dobrze to skrypt uraczy nas następującymi komunikatami:

build-all-reload - Successfully dropped database for connection named 'doctrine'
build-all-reload - Generated models successfully from YAML schema
build-all-reload - Successfully created database for connection named 'doctrine'
build-all-reload - Created tables successfully
build-all-reload - Data was successfully loaded

Wreszcie, czas skorzystać z naszego modelu. W pliku IndexController.php, w akcji index wpisujemy kod:

$user = new Application_Model_User();
$user->email = '[email protected]';
$user->name = 'test';
$user->password = sha1('pass');
$user->save();

Otwieramy aplikację w przeglądarce a następnie sprawdzamy bazę danych - powinna zawierać zdefiniowanego wyżej użytkownika.


comments powered by Disqus