Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
ZFConf 2010




 Zend Framework и Doctrine
                  Степан Танасийчук
                   ceo@stfalcon.com
Чем я занимаюсь?

                  Web разработкой занялся в
                   2003 году
                  С Zend Fram...
Содержание доклада

   Подключение Doctrine к ZF проекту
   Скрипт для работы с Doctrine_Cli
   Генерация моделей по YA...
Несколько слов о Doctrine

   ORM библиотека для PHP 5.2.3+
   Использует паттерны Active Record, Data Mapper и Metadata...
Подключаем Doctrine к ZF проекту

   Размещаем Doctrine в library/Doctrine:
    $ svn export http://svn.doctrine-
    pro...
Parables_Application_Resource_Doctrine


  Matthew Lurz добавил в Zend Framework proposal
  application-ресурс для подключ...
ZFEngine_Application_Resource_Doctrine


  Мы немного изменили код
  Parables_Application_Resource_Doctrine для работы с
 ...
Подключаем ZFEngine к ZF проекту

   Размещаем ZFEngine в library/ZFEngine:
    $ svn export
    http://svn2.assembla.com...
Настраиваем подключение к БД

 resources.doctrine.connections.primary.dsn.adapter = "mysql"
 resources.doctrine.connection...
Настраиваем Doctrine_Manager

 resources.doctrine.manager.attributes.attr_autoload_table_classes = 1
 resources.doctrine.m...
MODEL_LOADING_PEAR

В Doctrine 1.2 появился новый режим для автозагрузки
моделей — MODEL_LOADING_PEAR, но при
использовани...
Для проектов с НЕмодульной структурой

   Указываем путь к директории с моделями:
    resources.doctrine.manager.models_p...
Настраиваем кеширование

   resources.doctrine.manager.*
    .attributes.attr_result_cache.driver = "memcache"
    .attri...
Настраиваем Doctrine_Cli

 doctrine_cli.data_fixtures_path = APPLICATION_PATH
 "/configs/doctrine/data/fixtures"
 doctrine...
Cкрипт для работы с Doctrine_Cli

 ./application/sripts/common.php
 <?php
 define('APPLICATION_ENV', 'development');
 defi...
Cкрипт для работы с Doctrine_Cli

 ./application/sripts/doctrine
 #!/usr/bin/env php
 <?php
 require_once 'common.php';
 r...
Проверяем как работает

   Запускаем скрипт без параметров:
    $ ./application/sripts/doctrine
    Doctrine Command Line...
Создадим схему модели User

 ./application/configs/doctrine/schema/User.yml
 User:
    tableName: users
    options:
     ...
Генерируем модели по YAML схемам

   Запускаем скрипт с параметром generate-models-yaml:
    $ ./application/sripts/doctr...
Сгенерированный код базовой модели
User
./application/models/Base/BaseUser.php
<?php
abstract class BaseUser extends Doctr...
Сгенерированный код модели User и
маппера UserTable
 ./application/models/User.php
 <?php
 class User extends BaseUser
 {
...
Напишем свой сеттер для поля email

 ./application/models/User.php
 <?php
 /**
   * User model
   */
 class User extends B...
Пишем экшн для проверки работы

 ./application/controllers/IndexController.php
 <?php
 class IndexController extends Zend_...
Запускаем в браузере

array
  'id' => null
  'login' => string 'stfalcon' (length=8)
  'email' => string 'ceo@stfalcon.com...
Миграции

   Сгенерируем первый класс миграций. Его можно
    генерировать из классов моделей или БД (см. мануал к
    Do...
Сгенерированный код первой модели
миграций

 ./application/configs/doctrine/migrations/1268942153_adduser.php
 <?php
 clas...
Создадим БД и накатим на неё наши
изменения

   Создаем БД (например на production сервере):
    mysql> CREATE DATABASE `...
Проверяем работу скрипта

   Структура таблицы в которой хранится номер миграции:
    mysql> SHOW CREATE TABLE `migration...
Наследование в YAML схемах

 ./application/configs/doctrine/schema/Administrator.yml
 ## Administrator schema
 Administrat...
Работаем с Doctrine_Cli

   В первую очередь делаем migration-diff — он генерирует
    классы миграций на основе различий...
Работаем с Doctrine_Cli

   Генерируем код моделей:
    $ ./application/sripts/doctrine generate-models-yaml
    generate...
Работаем с Doctrine_Cli

   Смотрим, что получилось:
    mysql> SHOW CREATE TABLE `administrators`;

    CREATE TABLE `ad...
По-моему пора сделать авторизацию

   Сначала напишем сеттер для password:
    ./application/models/Administrator.php
   ...
Сгенерируем аккаунт для админа и
сохраним его в БД
 ./application/controllers/IndexController.php
 <?php
 class IndexContr...
Проверяем содержимое таблицы
administrators
 mysql> SELECT * FROM `administrators`;
 +----+----------+------------------+-...
ZendX_Doctrine_Auth_Adapter

 ./application/controllers/IndexController.php
       public function indexAction()
       {
...
Открываем страницу в браузере




   Все ОК :)
   И не забудьте сохранить данные авторзации в хранилище:
    $data = $au...
Увековечим учетную запись
администратора
 ./application/configs/doctrine/data/fixtures/users.yml

 Administrator:
   Admin...
Сделаем глобальный reload

 $ ./application/sripts/doctrine build-all-reload
 build-all-reload - Are you sure you wish to ...
Адаптер для Zend_Paginator

 Мы используем ZFEngine_Paginator_Adapter_Doctrine, это
 немного переработанный с учетом наших...
ZFEngine_Paginator_Adapter_Doctrine

   Давайте выведем список администраторов с постраничной
    навигацией. Для этого с...
Расширяем функционал
AdministratorTable

   Создадим метод getQueryToFetchAll(), который будет
    возвращать запрос на в...
Работаем с пагинатором

 ./application/controllers/IndexController.php
 <?php
 class IndexController extends Zend_Controll...
Оформляем вывод списка в view
шаблоне
 ./application/views/scripts/index/index.phtml
 <h1>
       <?php echo $this->transl...
digg.phtml

   digg.phtml я выложил здесь — http://pastie.org/832023 (за
    основу взят шаблон с ZendPaginationHelper)
Открываем страницу в браузере

                     И наслаждаемся
                      результатом :)
ZFEngine и использование Doctrine в
модульном ZF приложении

   Мы написали несколько тасков (собственно таски написал
  ...
Пример структуры модульного ZF
проекта
 ./application/
 |-- Bootstrap.php
 |-- configs
 |   `-- application.ini
 |-- layou...
Настройки для модульной структуры

   Прописываем следующие настройки в application.ini:
    ; Указываем, где находятся н...
Cтруктура модуля users

 ./application/modules/users/
 |-- Bootstrap.php
 |-- configs
 |   `-- doctrine
 |       |-- data
...
Схема User.yml
## User schema
Users_Model_User:
  tableName: users
  options:
    type: INNODB
    collate: utf8_unicode_c...
Cтруктура модуля products

 ./application/modules/products/
 |-- Bootstrap.php
 |-- configs
 |   |-- acl.php
 |   |-- doct...
Схема Product.yml

 ## Product schema
 Products_Model_Product:
   tableName: products
   options:
     ...
   columns:
   ...
Схема Product.yml (продолжение)

 ...
 # Прописываем связь один-ко-многим
 # User и Products – алиасы, через которые мы см...
Новый скрипт для Doctrine_Cli

   Скрипт для работы с Doctrine_Cli в модульном ZF
    приложении лежит в репозитории ZFEn...
Генерируем модели по YAML схемам

   Все также как в предыдущих примерах, только команда с
    префиксом zfengine:
    $ ...
Сгенерированые модели
 Между моделями из разных модулей сгенерировались связи:
 ./application/modules/users/models/Base/Us...
Миграции

   Сгенерируем миграции:
    Первую миграцию (на новом проекте) делаем через:
    $ ./application/sripts/doctri...
Структура таблицы `products`

   Смотрим, что получилось в БД:
    mysql> SHOW CREATE TABLE `products`;

    CREATE TABLE...
На этом все ;)


 Благодарю за внимание! Задавайте
 вопросы.




                            Степан Танасийчук
           ...
Upcoming SlideShare
Loading in …5
×

Zend Framework и Doctrine

7,468 views

Published on

  • Be the first to comment

Zend Framework и Doctrine

  1. 1. ZFConf 2010 Zend Framework и Doctrine Степан Танасийчук ceo@stfalcon.com
  2. 2. Чем я занимаюсь?  Web разработкой занялся в 2003 году  С Zend Framework начал работать в 2008 году  Руковожу собственной веб- студией с 2009 года  Активный участник сообщества zendframework.ru  Люблю прикольные смайлы :]
  3. 3. Содержание доклада  Подключение Doctrine к ZF проекту  Скрипт для работы с Doctrine_Cli  Генерация моделей по YAML схемам  Механизм миграций  Наследование в моделях  Шаблоны расширений  Адаптер для Zend_Auth  Адаптер для Zend_Paginator  ZFEngine и использование Doctrine в модульном ZF приложении
  4. 4. Несколько слов о Doctrine  ORM библиотека для PHP 5.2.3+  Использует паттерны Active Record, Data Mapper и Metadata Mapping  Собственный язык запросов — DQL (по мотивам HQL)  Связи один-к-одному, один-ко-многим и многие-к-многим  Автогенерация моделей по yaml схемам  Экспорт и импорт из/в yaml  Механизм миграций  Шаблоны поведений (l18n, Versionable, NestedSet, etc.)
  5. 5. Подключаем Doctrine к ZF проекту  Размещаем Doctrine в library/Doctrine: $ svn export http://svn.doctrine- project.org/tags/1.2.1/lib/Doctrine/ ./library/Doctrine  Прописываем следующие настройки в application.ini: autoloadernamespaces[] = "Doctrine"
  6. 6. Parables_Application_Resource_Doctrine Matthew Lurz добавил в Zend Framework proposal application-ресурс для подключения Doctrine. Его класс называется Parables_Application_Resource_Doctrine и лежит здесь http://github.com/mlurz71/parables
  7. 7. ZFEngine_Application_Resource_Doctrine Мы немного изменили код Parables_Application_Resource_Doctrine для работы с Doctrine 1.2.x и храним его в репозитории ZFEngine как ZFEngine_Application_Resource_Doctrine ZFEngine это сборная солянка классов, которые мы используем при разработке проектов на ZF. Лежит все здесь: http://zfengine.com В основном код наш. Также есть чужой, но с некоторыми изменениями. Надеюсь, что это все в рамках закона ^_~.
  8. 8. Подключаем ZFEngine к ZF проекту  Размещаем ZFEngine в library/ZFEngine: $ svn export http://svn2.assembla.com/svn/zfengine/trunk/library/ZFEngine/ ./library/ZFEngine  Прописываем следующие настройки в application.ini: autoloadernamespaces[] = "ZFEngine" pluginPaths.ZFEngine_Application_Resource = "ZFEngine/Application/Resource"
  9. 9. Настраиваем подключение к БД resources.doctrine.connections.primary.dsn.adapter = "mysql" resources.doctrine.connections.primary.dsn.username = "root" resources.doctrine.connections.primary.dsn.password = "******" resources.doctrine.connections.primary.dsn.host = "localhost" resources.doctrine.connections.primary.dsn.dbname = "zfconf" resources.doctrine.connections.primary.options.charset = "utf8" resources.doctrine.connections.primary.options.collate = "utf8_unicode_ci"
  10. 10. Настраиваем Doctrine_Manager resources.doctrine.manager.attributes.attr_autoload_table_classes = 1 resources.doctrine.manager.attributes.attr_use_native_enum = 1 resources.doctrine.manager.attributes.attr_quote_identifier = 1 resources.doctrine.manager.attributes.attr_auto_free_query_objects = 1 resources.doctrine.manager.attributes.attr_auto_accessor_override = 1 resources.doctrine.manager.attributes.attr_model_loading = "model_loading_conservative"
  11. 11. MODEL_LOADING_PEAR В Doctrine 1.2 появился новый режим для автозагрузки моделей — MODEL_LOADING_PEAR, но при использовании этого режима не работает generate- migration-diff :(. Я заметил это уже в процессе подготовки доклада и пока просто написал в багрепорт Doctrine.
  12. 12. Для проектов с НЕмодульной структурой  Указываем путь к директории с моделями: resources.doctrine.manager.models_path = APPLICATION_PATH "/models"
  13. 13. Настраиваем кеширование  resources.doctrine.manager.* .attributes.attr_result_cache.driver = "memcache" .attributes.attr_result_cache.lifespan = 3600 .attributes.attr_result_cache.options.servers.host = "localhost" .attributes.attr_result_cache.options.servers.port = 11211 .attributes.attr_result_cache.options.servers.persistent = 1 .attributes.attr_result_cache.options.compression = 0
  14. 14. Настраиваем Doctrine_Cli doctrine_cli.data_fixtures_path = APPLICATION_PATH "/configs/doctrine/data/fixtures" doctrine_cli.models_path = APPLICATION_PATH "/models" doctrine_cli.migrations_path = APPLICATION_PATH "/configs/doctrine/migrations" doctrine_cli.sql_path = APPLICATION_PATH "/configs/doctrine/data/sql" doctrine_cli.yaml_schema_path = APPLICATION_PATH "/configs/doctrine/schema" doctrine_cli.generate_models_options.generateBaseClasses = 1 doctrine_cli.generate_models_options.baseClassesDirectory = "Base" doctrine_cli.generate_models_options.generateTableClasses = 1
  15. 15. Cкрипт для работы с Doctrine_Cli ./application/sripts/common.php <?php define('APPLICATION_ENV', 'development'); define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/..')); set_include_path(implode(PATH_SEPARATOR, array( realpath(APPLICATION_PATH . '/../library'), get_include_path(), )));
  16. 16. Cкрипт для работы с Doctrine_Cli ./application/sripts/doctrine #!/usr/bin/env php <?php require_once 'common.php'; require_once 'Zend/Application.php'; $application = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini' ); $application->getBootstrap() ->bootstrap(); $cli = new Doctrine_Cli($application->getOption('doctrine_cli')); $cli->run($_SERVER['argv']);
  17. 17. Проверяем как работает  Запускаем скрипт без параметров: $ ./application/sripts/doctrine Doctrine Command Line Interface ./application/sripts/doctrine generate-sql ./application/sripts/doctrine create-db ./application/sripts/doctrine generate-yaml-models ./application/sripts/doctrine dql ./application/sripts/doctrine generate-migrations-models ./application/sripts/doctrine generate-yaml-db ./application/sripts/doctrine generate-models-yaml ./application/sripts/doctrine generate-migrations-diff ./application/sripts/doctrine generate-migration ./application/sripts/doctrine create-tables ./application/sripts/doctrine drop-db ./application/sripts/doctrine generate-migrations-db ... и ещё 9ть команд, которые не поместились на этом слайде (:
  18. 18. Создадим схему модели User ./application/configs/doctrine/schema/User.yml User: tableName: users options: type: INNODB collate: utf8_unicode_ci charset: utf8 columns: id: type: integer(4) primary: true autoincrement: true login: string(32) email: string(255)
  19. 19. Генерируем модели по YAML схемам  Запускаем скрипт с параметром generate-models-yaml: $ ./application/sripts/doctrine generate-models-yaml generate-models-yaml - Generated models successfully from YAML schema  Получаем готовые модели: ./application/models |-- Base | `-- BaseUser.php |-- User.php `-- UserTable.php Важная деталь: сами YAML схемы можно сгенерировать непосредственно с структуры БД используя команду generate-yaml-db.
  20. 20. Сгенерированный код базовой модели User ./application/models/Base/BaseUser.php <?php abstract class BaseUser extends Doctrine_Record { public function setTableDefinition() { $this->setTableName('users'); $this->hasColumn('id', 'integer', 4, array('type' => 'integer', 'unsigned' => true, 'primary' => true, 'autoincrement' => true, 'length' => '4')); // Здесь было описание полей login и email ... $this->option('type', 'INNODB'); $this->option('collate', 'utf8_unicode_ci'); $this->option('charset', 'utf8'); } public function setUp() { parent::setUp(); } }
  21. 21. Сгенерированный код модели User и маппера UserTable ./application/models/User.php <?php class User extends BaseUser { } ./application/models/UserTable.php <?php class UserTable extends Doctrine_Table { }
  22. 22. Напишем свой сеттер для поля email ./application/models/User.php <?php /** * User model */ class User extends BaseUser { /** * Set email adress into lowercase * * @param string $email * @return void */ public function setEmail($email) { $this->_set('email', strtolower($email)); } }
  23. 23. Пишем экшн для проверки работы ./application/controllers/IndexController.php <?php class IndexController extends Zend_Controller_Action { /** * Simple action * * @return void */ public function indexAction() { $user = new User(); $user->login = 'stfalcon'; $user->email = 'CEO@STFalcon.COM'; Zend_Debug::dump($user->toArray()); } }
  24. 24. Запускаем в браузере array 'id' => null 'login' => string 'stfalcon' (length=8) 'email' => string 'ceo@stfalcon.com' (length=16)
  25. 25. Миграции  Сгенерируем первый класс миграций. Его можно генерировать из классов моделей или БД (см. мануал к Doctrine). $ ./application/sripts/doctrine generate-migrations-models generate-migrations-models - Generated migration classes successfully from models  Получаем готовую модель миграций: ./application/configs/doctrine/ |-- data | |-- fixtures | `-- sql |-- migrations | `-- 1268942153_adduser.php `-- schema `-- User.yml
  26. 26. Сгенерированный код первой модели миграций ./application/configs/doctrine/migrations/1268942153_adduser.php <?php class Adduser extends Doctrine_Migration_Base { public function up() { $this->createTable('user', array('id' => array('type' => 'integer', 'unsigned' => true, 'primary' => true, 'autoincrement' => true, 'length' => 4), // Здесь были параметры для создания полей login и email ... ), array('type' => 'INNODB', 'indexes' => array(), 'primary' => array(0 => 'id'), 'collate' => 'utf8_unicode_ci', 'charset' => 'utf8')); } public function down() { $this->dropTable('user'); } }
  27. 27. Создадим БД и накатим на неё наши изменения  Создаем БД (например на production сервере): mysql> CREATE DATABASE `zfconf`; Query OK, 1 row affected (0,00 sec)  Накатываем на неё миграцию: $ ./application/sripts/doctrine migrate migrate - migrated successfully to version #1  Выведем список таблиц: mysql> SHOW TABLES; migration_version users
  28. 28. Проверяем работу скрипта  Структура таблицы в которой хранится номер миграции: mysql> SHOW CREATE TABLE `migration_version`; CREATE TABLE `migration_version` ( `version` int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1  Структура таблицы пользователей: mysql> SHOW CREATE TABLE `users`; CREATE TABLE `users` ( `id` int(10) NOT NULL AUTO_INCREMENT, `login` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
  29. 29. Наследование в YAML схемах ./application/configs/doctrine/schema/Administrator.yml ## Administrator schema Administrator: tableName: administrators inheritance: extends: User type: concrete columns: password_hash: string(32) password_salt: string(8) actAs: [Timestampable]
  30. 30. Работаем с Doctrine_Cli  В первую очередь делаем migration-diff — он генерирует классы миграций на основе различий между кодом моделей и YAML схемами: $ ./application/sripts/doctrine generate-migrations-diff generate-migrations-diff - Generated migration classes successfully from difference ./application/configs/doctrine |-- data | |-- fixtures | `-- sql |-- migrations | |-- 1268942153_adduser.php | `-- 1268942505_version2.php `-- schema |-- Administrator.yml `-- User.yml
  31. 31. Работаем с Doctrine_Cli  Генерируем код моделей: $ ./application/sripts/doctrine generate-models-yaml generate-models-yaml - Generated models successfully from YAML schema  Накатываем изменения на БД: $ ./application/sripts/doctrine migrate migrate - migrated successfully to version #2
  32. 32. Работаем с Doctrine_Cli  Смотрим, что получилось: mysql> SHOW CREATE TABLE `administrators`; CREATE TABLE `administrators` ( `id` int(10) NOT NULL AUTO_INCREMENT, `login` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `password_hash` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `password_salt` varchar(8) COLLATE utf8_unicode_ci DEFAULT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
  33. 33. По-моему пора сделать авторизацию  Сначала напишем сеттер для password: ./application/models/Administrator.php <?php class Administrator extends BaseAdministrator { // Здесь был phpDoc блок ... public function setPassword($password) { if (strlen($password)) { $passwordSalt = substr(md5(mktime()), 0, rand(5,8)); $passwordHash = md5($password . $passwordSalt); $this->_set('password_hash', $passwordHash); $this->_set('password_salt', $passwordSalt); } } }
  34. 34. Сгенерируем аккаунт для админа и сохраним его в БД ./application/controllers/IndexController.php <?php class IndexController extends Zend_Controller_Action { // Здесь был phpDoc блок ... public function indexAction() { $administrator = new Administrator(); $administrator->email = 'CEO@STFalcon.COM'; $administrator->login = 'stfalcon'; $administrator->password = 'qwerty'; $administrator->save(); Zend_Debug::dump($administrator->toArray()); } }
  35. 35. Проверяем содержимое таблицы administrators mysql> SELECT * FROM `administrators`; +----+----------+------------------+---------------------------------- +---------------+---------------------+---------------------+ | id | login | email | password_hash | password_salt | created_at | updated_at | +----+----------+------------------+---------------------------------- +---------------+---------------------+---------------------+ | 1 | stfalcon | ceo@stfalcon.com | bcd3987603a947d54480285c16f06fde | fc1ed | 2010-03-18 23:04:11 | 2010-03-18 23:04:11 |
  36. 36. ZendX_Doctrine_Auth_Adapter ./application/controllers/IndexController.php public function indexAction() { $authAdapter = new ZendX_Doctrine_Auth_Adapter( Doctrine_Core::getConnectionByTableName('Administrator')); $authAdapter->setTableName('Administrator a') ->setIdentityColumn('a.login') ->setCredentialColumn('a.password_hash') ->setCredentialTreatment('MD5(CONCAT(?,a.password_salt))') ->setIdentity('stfalcon')->setCredential('qwerty'); $auth = Zend_Auth::getInstance(); $result = $auth->authenticate($authAdapter); if ($result->isValid()) { echo '<h1>OK</h1>'; } else { echo '<h1>FAIL</h1>'; } }
  37. 37. Открываем страницу в браузере  Все ОК :)  И не забудьте сохранить данные авторзации в хранилище: $data = $authAdapter->getResultRowObject(null, array('password_hash', 'password_salt')); $auth->getStorage()->write($data);
  38. 38. Увековечим учетную запись администратора ./application/configs/doctrine/data/fixtures/users.yml Administrator: Admin_1: login: stfalcon email: mymail@gmail.com password_hash: bcd3987603a947d54480285c16f06fde password_salt: fc1ed # Admin_2: # login: stfalcon # ...
  39. 39. Сделаем глобальный reload $ ./application/sripts/doctrine build-all-reload build-all-reload - Are you sure you wish to drop your databases? (y/n) y build-all-reload - Successfully dropped database for connection named 'primary' build-all-reload - Generated models successfully from YAML schema build-all-reload - Successfully created database for connection named 'primary' build-all-reload - Created tables successfully build-all-reload - Data was successfully loaded mysql> SELECT * FROM `administrators`; | id | login | email | password_hash | password_salt | created_at | updated_at | +----+----------+------------------+---------------------------------- +---------------+---------------------+---------------------+ | 1 | stfalcon | ceo@stfalcon.com | bcd3987603a947d54480285c16f06fde | fc1ed | 2010-03-18 23:04:11 | 2010-03-18 23:04:11 |
  40. 40. Адаптер для Zend_Paginator Мы используем ZFEngine_Paginator_Adapter_Doctrine, это немного переработанный с учетом наших потребностей и изменений в Doctrine 1.2 SmartL_Zend_Paginator_Adapter_Doctrine http://code.google.com/p/smart-framework/ Ещё раз пропиарю наш ZFEngine :) http://zfengine.com
  41. 41. ZFEngine_Paginator_Adapter_Doctrine  Давайте выведем список администраторов с постраничной навигацией. Для этого создадим в таблице administrators 10 случайных записей:
  42. 42. Расширяем функционал AdministratorTable  Создадим метод getQueryToFetchAll(), который будет возвращать запрос на выборку всех администраторов: ./application/models/AdministratorTable.php <?php class AdministratorTable extends UserTable { /** * Query to fetch all administrators * @return Doctrine_Query */ public function getQueryToFetchAll() { return $this->createQuery('a') ->orderBy('a.created_at'); } }
  43. 43. Работаем с пагинатором ./application/controllers/IndexController.php <?php class IndexController extends Zend_Controller_Action { public function indexAction() { $query = Doctrine_Core::getTable('Administrator') ->getQueryToFetchAll(); $paginator = new Zend_Paginator( new ZFEngine_Paginator_Adapter_Doctrine($query)); $paginator->setCurrentPageNumber($this->_getParam('page', 1)); $paginator->setItemCountPerPage(4); $this->view->paginator = $paginator; } }
  44. 44. Оформляем вывод списка в view шаблоне ./application/views/scripts/index/index.phtml <h1> <?php echo $this->translate('Администраторы'); ?>: </h1> <?php if (count($this->paginator)): ?> <ul> <?php foreach ($this->paginator as $administrator): ?> <li> <?php echo $administrator->login; ?>&nbsp; &lt;<?php echo $administrator->email; ?>&gt; </li> <?php endforeach; ?> </ul> <?php endif; ?> <?php echo $this->paginationControl($this->paginator, 'Sliding', 'digg.phtml'); ?>
  45. 45. digg.phtml  digg.phtml я выложил здесь — http://pastie.org/832023 (за основу взят шаблон с ZendPaginationHelper)
  46. 46. Открываем страницу в браузере  И наслаждаемся результатом :)
  47. 47. ZFEngine и использование Doctrine в модульном ZF приложении  Мы написали несколько тасков (собственно таски написал Валерий Рабиевский, а я только немного порефакторил) для Doctrine, которые позволяют генерировать модели и использовать механизм миграций в ZF проектах с модульной архитектурой.  При этом между моделями разных модулей работает связывание и наследование.  Также работает механизм миграций для проекта в целом.
  48. 48. Пример структуры модульного ZF проекта ./application/ |-- Bootstrap.php |-- configs | `-- application.ini |-- layouts | `-- scripts | |-- admin.phtml | `-- index.html `-- modules |-- products `-- users
  49. 49. Настройки для модульной структуры  Прописываем следующие настройки в application.ini: ; Указываем, где находятся наши модули для Zend resources.frontController.moduleDirectory = APPLICATION_PATH "/modules" ; и для Doctrine_Cli doctrine_cli.modules_path = APPLICATION_PATH "/modules/" ; а также прописываем путь к папке, где будут хранится yaml-схемы предыдущих версий (old), и новые (temp), собранные с модулей в одну папку. Именно по различиям между ними и будут генерироваться миграции. doctrine_cli.old_schema_path = APPLICATION_PATH "/configs/doctrine/schema/old/" doctrine_cli.temp_schema_path = APPLICATION_PATH "/../tmp/schema/" resources.modules[] = "" ; подгружаем ресурс для подержки модулей ; И убираем строки, где задавали расположение моделей: ; resources.doctrine.manager.models_path = APPLICATION_PATH "/models" ; doctrine_cli.models_path = APPLICATION_PATH "/models" ; так как теперь модели подгружаются самим Zend'ом
  50. 50. Cтруктура модуля users ./application/modules/users/ |-- Bootstrap.php |-- configs | `-- doctrine | |-- data | | |-- fixtures | | `-- sql | `-- schema | `-- User.yml |-- controllers | `-- IndexController.php |-- models `-- views `-- scripts `-- index `-- index.phtml
  51. 51. Схема User.yml ## User schema Users_Model_User: tableName: users options: type: INNODB collate: utf8_unicode_ci charset: utf8 columns: id: type: integer(4) unsigned: true primary: true autoincrement: true login: string(32) email: string(255)
  52. 52. Cтруктура модуля products ./application/modules/products/ |-- Bootstrap.php |-- configs | |-- acl.php | |-- doctrine | | `-- schema | | `-- Product.yml | `-- routes.xml |-- controllers | `-- IndexController.php |-- forms |-- models `-- views |-- helpers `-- scripts `-- index `-- index.phtml
  53. 53. Схема Product.yml ## Product schema Products_Model_Product: tableName: products options: ... columns: id: type: integer(4) unsigned: true primary: true autoincrement: true user_id: type: integer(4) unsigned: true name: string(255) description: string actAs: [Timestampable] ...
  54. 54. Схема Product.yml (продолжение) ... # Прописываем связь один-ко-многим # User и Products – алиасы, через которые мы сможем обращаться # из одной модели к другой relations: User: class: Users_Model_User foreign: id local: user_id foreignAlias: Products onUpdate: CASCADE onDelete: CASCADE
  55. 55. Новый скрипт для Doctrine_Cli  Скрипт для работы с Doctrine_Cli в модульном ZF приложении лежит в репозитории ZFEngine.  Единственное его отличие от обычного скрипта, это наличие кода для подключения тасков с ZFEngine и справка по командам ZFEngine при запуске скрипта с ключем info: $ ./application/scripts/doctrine info zfengine-generate-migrations-models -> для генерации новой миграций zfengine-generate-migrations-diff -> для генерации изменений миграций zfengine-generate-models-yaml -> для генерация моделей из yaml-файлов zfengine-prepare-schema-files-for-migrations -> для копирования shema- файлов для сравнения при генерации миграций Очередность действий: При создании новой миграции: zfengine-generate-models-yaml zfengine-generate-migrations-models migrate При создании изменений миграции: ...
  56. 56. Генерируем модели по YAML схемам  Все также как в предыдущих примерах, только команда с префиксом zfengine: $ ./application/sripts/doctrine zfengine-generate-models-yaml Generated models for module "Products" successfully Generated models for module "Users" successfully Generated models finished  Получаем готовые модели: ./application/modules/users/ |-- models |-- Base | `-- User.php |-- User.php `-- UserTable.php Только теперь модели именуются согласно стандартам ZF и подгружаются родным автозагрузчиком: BaseUser → Users_Model_Base_User User → Users_Model_User
  57. 57. Сгенерированые модели Между моделями из разных модулей сгенерировались связи: ./application/modules/users/models/Base/User.php <?php ... public function setUp() { $this->hasMany('Products_Model_Product as Products', array( 'local' => 'id', 'foreign' => 'user_id')); } ./application/modules/products/models/Base/Product.php <?php ... public function setUp() { $this->hasOne('Users_Model_User as User', array( 'local' => 'user_id','foreign' => 'id', 'onDelete' => 'CASCADE', 'onUpdate' => 'CASCADE')); } При работе с моделью пользователя коллекция моделей продуктов будет подгружена только при необходимости. Например при получении всех продуктов пользователя: $products = $user->Products;
  58. 58. Миграции  Сгенерируем миграции: Первую миграцию (на новом проекте) делаем через: $ ./application/sripts/doctrine zfengine-generate-migrations-models Так миграции генерируются на основании существующих классов моделей, а последующие — уже на основании изменений в yaml-схемах командой: $ ./application/sripts/doctrine zfengine-generate-migrations-diff  И накатываем миграции на базу: $ ./application/sripts/doctrine migrate migrate - migrated successfully to version #3
  59. 59. Структура таблицы `products`  Смотрим, что получилось в БД: mysql> SHOW CREATE TABLE `products`; CREATE TABLE `products` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(10) unsigned DEFAULT NULL, `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `description` text COLLATE utf8_unicode_ci, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), KEY `products_user_id_users_id` (`user_id`), CONSTRAINT `products_user_id_users_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
  60. 60. На этом все ;) Благодарю за внимание! Задавайте вопросы. Степан Танасийчук ceo@stfalcon.com

×