<ul>Zend Framework и Doctrine Степан Танасийчук [email_address] </ul>
Чем я занимаюсь? <ul><li>Web разработкой занялся в 2003 году
С Zend Framework начал работать в 2008 году
Руковожу собственной веб-студией с 2009 года
Активный участник сообщества zendframework.ru
Люблю прикольные смайлы :] </li></ul>
Содержание доклада <ul><li>Подключение Doctrine к ZF проекту
Скрипт для работы с Doctrine_Cli
Генерация моделей по YAML схемам
Механизм миграций
Наследование в моделях
Шаблоны расширений
Адаптер для Zend_Auth
Адаптер для Zend_Paginator
ZFEngine и использование Doctrine в модульном ZF приложении </li></ul>
Несколько слов о Doctrine <ul><li>ORM библиотека для PHP 5.2.3+
Использует паттерны Active Record, Data Mapper и Metadata Mapping
Собственный язык запросов — DQL (по мотивам HQL)
Связи один-к-одному, один-ко-многим и многие-к-многим
Автогенерация моделей по yaml схемам
Экспорт и импорт из/в yaml
Механизм миграций
Шаблоны поведений (l18n, Versionable, NestedSet, etc.) </li></ul>
Подключаем Doctrine к ZF проекту <ul><li>Размещаем Doctrine в library/Doctrine:
$   svn export http://svn.doctrine-project.org/tags/1.2.1/lib/Doctrine/ ./library/Doctrine
Прописываем следующие настройки в application.ini:
autoloadernamespaces[] =  &quot;Doctrine&quot; </li></ul>
Parables_Application_Resource_Doctrine <ul>Matthew Lurz  добавил в Zend Framework proposal application-ресурс для подключе...
ZFEngine_Application_Resource_Doctrine <ul>Мы немного изменили код  Parables_Application_Resource_Doctrine  для работы с D...
Подключаем ZFEngine к ZF проекту <ul><li>Размещаем ZFEngine в library/ZFEngine:
$  svn export http://svn2.assembla.com/svn/zfengine/trunk/library/ZFEngine/ ./library/ZFEngine
Прописываем следующие настройки в application.ini:
autoloadernamespaces[] =  &quot;ZFEngine&quot;
pluginPaths.ZFEngine_Application_Resource =  &quot;ZFEngine/Application/Resource&quot; </li></ul>
Настраиваем подключение к БД <ul>resources.doctrine.connections.primary.dsn.adapter =  &quot;mysql&quot; resources.doctrin...
Настраиваем  Doctrine_Manager <ul>resources.doctrine.manager.attributes.attr_autoload_table_classes =  1 resources.doctrin...
<ul>MODEL_LOADING_PEAR </ul><ul>В Doctrine 1.2 появился новый режим для автозагрузки моделей  —   MODEL_LOADING_PEAR , но ...
Для проектов с НЕмодульной структурой <ul><li>Указываем путь к директории с моделями:
resources.doctrine.manager.models_path = APPLICATION_PATH &quot;/models&quot; </li></ul>
Настраиваем кеширование <ul><li>resources.doctrine.manager.*
.attributes.attr_result_cache.driver =  &quot;memcache&quot;
.attributes.attr_result_cache.lifespan =  3600
.attributes.attr_result_cache.options.servers.host =  &quot;localhost&quot;
.attributes.attr_result_cache.options.servers.port =  11211
.attributes.attr_result_cache.options.servers.persistent =  1
.attributes.attr_result_cache.options.compression =  0 </li></ul>
Настраиваем Doctrine_Cli <ul>doctrine_cli.data_fixtures_path =  APPLICATION_PATH &quot;/configs/doctrine/data/fixtures&quo...
Cкрипт для работы с Doctrine_Cli <ul>./application/sripts/common.php <?php define( 'APPLICATION_ENV' ,  'development' ); d...
Cкрипт для работы с Doctrine_Cli <ul>./application/sripts/doctrine #!/usr/bin/env php <?php require_once   'common.php' ; ...
Проверяем как работает <ul><li>Запускаем скрипт без параметров:
$   ./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
Upcoming SlideShare
Loading in …5
×

ZFConf 2010: Zend Framework and Doctrine

4,304
-1

Published on

0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
4,304
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
89
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

ZFConf 2010: Zend Framework and Doctrine

  1. 1. <ul>Zend Framework и Doctrine Степан Танасийчук [email_address] </ul>
  2. 2. Чем я занимаюсь? <ul><li>Web разработкой занялся в 2003 году
  3. 3. С Zend Framework начал работать в 2008 году
  4. 4. Руковожу собственной веб-студией с 2009 года
  5. 5. Активный участник сообщества zendframework.ru
  6. 6. Люблю прикольные смайлы :] </li></ul>
  7. 7. Содержание доклада <ul><li>Подключение Doctrine к ZF проекту
  8. 8. Скрипт для работы с Doctrine_Cli
  9. 9. Генерация моделей по YAML схемам
  10. 10. Механизм миграций
  11. 11. Наследование в моделях
  12. 12. Шаблоны расширений
  13. 13. Адаптер для Zend_Auth
  14. 14. Адаптер для Zend_Paginator
  15. 15. ZFEngine и использование Doctrine в модульном ZF приложении </li></ul>
  16. 16. Несколько слов о Doctrine <ul><li>ORM библиотека для PHP 5.2.3+
  17. 17. Использует паттерны Active Record, Data Mapper и Metadata Mapping
  18. 18. Собственный язык запросов — DQL (по мотивам HQL)
  19. 19. Связи один-к-одному, один-ко-многим и многие-к-многим
  20. 20. Автогенерация моделей по yaml схемам
  21. 21. Экспорт и импорт из/в yaml
  22. 22. Механизм миграций
  23. 23. Шаблоны поведений (l18n, Versionable, NestedSet, etc.) </li></ul>
  24. 24. Подключаем Doctrine к ZF проекту <ul><li>Размещаем Doctrine в library/Doctrine:
  25. 25. $ svn export http://svn.doctrine-project.org/tags/1.2.1/lib/Doctrine/ ./library/Doctrine
  26. 26. Прописываем следующие настройки в application.ini:
  27. 27. autoloadernamespaces[] = &quot;Doctrine&quot; </li></ul>
  28. 28. Parables_Application_Resource_Doctrine <ul>Matthew Lurz добавил в Zend Framework proposal application-ресурс для подключения Doctrine. Его класс называется Parables_Application_Resource_Doctrine и лежит здесь http://github.com/mlurz71/parables </ul>
  29. 29. ZFEngine_Application_Resource_Doctrine <ul>Мы немного изменили код Parables_Application_Resource_Doctrine для работы с Doctrine 1.2.x и храним его в репозитории ZFEngine как ZFEngine_Application_Resource_Doctrine ZFEngine это сборная солянка классов, которые мы используем при разработке проектов на ZF. Лежит все здесь: http://zfengine.com В основном код наш. Также есть чужой, но с некоторыми изменениями. Надеюсь, что это все в рамках закона ^_~. </ul>
  30. 30. Подключаем ZFEngine к ZF проекту <ul><li>Размещаем ZFEngine в library/ZFEngine:
  31. 31. $ svn export http://svn2.assembla.com/svn/zfengine/trunk/library/ZFEngine/ ./library/ZFEngine
  32. 32. Прописываем следующие настройки в application.ini:
  33. 33. autoloadernamespaces[] = &quot;ZFEngine&quot;
  34. 34. pluginPaths.ZFEngine_Application_Resource = &quot;ZFEngine/Application/Resource&quot; </li></ul>
  35. 35. Настраиваем подключение к БД <ul>resources.doctrine.connections.primary.dsn.adapter = &quot;mysql&quot; resources.doctrine.connections.primary.dsn.username = &quot;root&quot; resources.doctrine.connections.primary.dsn.password = &quot;******&quot; resources.doctrine.connections.primary.dsn.host = &quot;localhost&quot; resources.doctrine.connections.primary.dsn.dbname = &quot;zfconf&quot; resources.doctrine.connections.primary.options.charset = &quot;utf8&quot; resources.doctrine.connections.primary.options.collate = &quot;utf8_unicode_ci&quot; </ul>
  36. 36. Настраиваем Doctrine_Manager <ul>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 = &quot;model_loading_conservative&quot; </ul>
  37. 37. <ul>MODEL_LOADING_PEAR </ul><ul>В Doctrine 1.2 появился новый режим для автозагрузки моделей — MODEL_LOADING_PEAR , но при использовании этого режима не работает generate-migration-diff :(. Я заметил это уже в процессе подготовки доклада и пока просто написал в багрепорт Doctrine. </ul>
  38. 38. Для проектов с НЕмодульной структурой <ul><li>Указываем путь к директории с моделями:
  39. 39. resources.doctrine.manager.models_path = APPLICATION_PATH &quot;/models&quot; </li></ul>
  40. 40. Настраиваем кеширование <ul><li>resources.doctrine.manager.*
  41. 41. .attributes.attr_result_cache.driver = &quot;memcache&quot;
  42. 42. .attributes.attr_result_cache.lifespan = 3600
  43. 43. .attributes.attr_result_cache.options.servers.host = &quot;localhost&quot;
  44. 44. .attributes.attr_result_cache.options.servers.port = 11211
  45. 45. .attributes.attr_result_cache.options.servers.persistent = 1
  46. 46. .attributes.attr_result_cache.options.compression = 0 </li></ul>
  47. 47. Настраиваем Doctrine_Cli <ul>doctrine_cli.data_fixtures_path = APPLICATION_PATH &quot;/configs/doctrine/data/fixtures&quot; doctrine_cli.models_path = APPLICATION_PATH &quot;/models&quot; doctrine_cli.migrations_path = APPLICATION_PATH &quot;/configs/doctrine/migrations&quot; doctrine_cli.sql_path = APPLICATION_PATH &quot;/configs/doctrine/data/sql&quot; doctrine_cli.yaml_schema_path = APPLICATION_PATH &quot;/configs/doctrine/schema&quot; doctrine_cli.generate_models_options.generateBaseClasses = 1 doctrine_cli.generate_models_options.baseClassesDirectory = &quot;Base&quot; doctrine_cli.generate_models_options.generateTableClasses = 1 </ul>
  48. 48. Cкрипт для работы с Doctrine_Cli <ul>./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(), ))); </ul>
  49. 49. Cкрипт для работы с Doctrine_Cli <ul>./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' ]); </ul>
  50. 50. Проверяем как работает <ul><li>Запускаем скрипт без параметров:
  51. 51. $ ./application/sripts/doctrine
  52. 52. Doctrine Command Line Interface
  53. 53. ./application/sripts/doctrine generate-sql
  54. 54. ./application/sripts/doctrine create-db
  55. 55. ./application/sripts/doctrine generate-yaml-models
  56. 56. ./application/sripts/doctrine dql
  57. 57. ./application/sripts/doctrine generate-migrations-models
  58. 58. ./application/sripts/doctrine generate-yaml-db
  59. 59. ./application/sripts/doctrine generate-models-yaml
  60. 60. ./application/sripts/doctrine generate-migrations-diff
  61. 61. ./application/sripts/doctrine generate-migration
  62. 62. ./application/sripts/doctrine create-tables
  63. 63. ./application/sripts/doctrine drop-db
  64. 64. ./application/sripts/doctrine generate-migrations-db
  65. 65. ... и ещё 9ть команд, которые не поместились на этом слайде (: </li></ul>
  66. 66. Создадим схему модели User <ul>./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) </ul>
  67. 67. Генерируем модели по YAML схемам <ul><li>Запускаем скрипт с параметром generate-models-yaml :
  68. 68. $ ./application/sripts/doctrine generate-models-yaml
  69. 69. generate-models-yaml - Generated models successfully from YAML schema
  70. 70. Получаем готовые модели:
  71. 71. ./application/models
  72. 72. |-- Base
  73. 73. | `-- BaseUser.php
  74. 74. |-- User.php
  75. 75. `-- UserTable.php
  76. 76. Важная деталь: сами YAML схемы можно сгенерировать непосредственно с структуры БД используя команду generate-yaml-db . </li></ul>
  77. 77. Сгенерированный код базовой модели 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(); } }
  78. 78. Сгенерированный код модели User и маппера UserTable <ul>./application/models/User.php <?php class User extends BaseUser { } ./application/models/UserTable.php <?php class UserTable extends Doctrine_Table { } </ul>
  79. 79. Напишем свой сеттер для поля email <ul>./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 )); } } </ul>
  80. 80. Пишем экшн для проверки работы <ul>./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()); } } </ul>
  81. 81. Запускаем в браузере array 'id' => null 'login' => string 'stfalcon' (length=8) 'email' => string 'ceo@stfalcon.com' (length=16)
  82. 82. Миграции <ul><li>Сгенерируем первый класс миграций. Его можно генерировать из классов моделей или БД (см. мануал к Doctrine).
  83. 83. $ ./application/sripts/doctrine generate-migrations-models
  84. 84. generate-migrations-models - Generated migration classes successfully from models
  85. 85. Получаем готовую модель миграций:
  86. 86. ./application/configs/doctrine/
  87. 87. |-- data
  88. 88. | |-- fixtures
  89. 89. | `-- sql
  90. 90. |-- migrations
  91. 91. | `-- 1268942153_adduser.php
  92. 92. `-- schema
  93. 93. `-- User.yml </li></ul>
  94. 94. Сгенерированный код первой модели миграций <ul>./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' ); } } </ul>
  95. 95. Создадим БД и накатим на неё наши изменения <ul><li>Создаем БД (например на production сервере):
  96. 96. mysql> CREATE DATABASE `zfconf`;
  97. 97. Query OK, 1 row affected (0,00 sec)
  98. 98. Накатываем на неё миграцию:
  99. 99. $ ./application/sripts/doctrine migrate
  100. 100. migrate - migrated successfully to version #1
  101. 101. Выведем список таблиц:
  102. 102. mysql> SHOW TABLES ;
  103. 103. migration_version
  104. 104. users </li></ul>
  105. 105. Проверяем работу скрипта <ul><li>Структура таблицы в которой хранится номер миграции:
  106. 106. mysql> SHOW CREATE TABLE `migration_version`;
  107. 107. CREATE TABLE `migration_version` (
  108. 108. `version` int (11) DEFAULT NULL
  109. 109. ) ENGINE =InnoDB DEFAULT CHARSET =latin1
  110. 110. Структура таблицы пользователей:
  111. 111. mysql> SHOW CREATE TABLE `users`;
  112. 112. CREATE TABLE `users` (
  113. 113. `id` int (10) NOT NULL AUTO_INCREMENT ,
  114. 114. `login` varchar (32) COLLATE utf8_unicode_ci DEFAULT NULL ,
  115. 115. `email` varchar (255) COLLATE utf8_unicode_ci DEFAULT NULL ,
  116. 116. PRIMARY KEY (`id`)
  117. 117. ) ENGINE =InnoDB DEFAULT CHARSET =utf8 COLLATE =utf8_unicode_ci </li></ul>
  118. 118. Наследование в YAML схемах <ul>./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] </ul>
  119. 119. Работаем с Doctrine_Cli <ul><li>В первую очередь делаем migration-diff — он генерирует классы миграций на основе различий между кодом моделей и YAML схемами:
  120. 120. $ ./application/sripts/doctrine generate-migrations-diff
  121. 121. generate-migrations-diff - Generated migration classes successfully from difference
  122. 122. ./application/configs/doctrine
  123. 123. |-- data
  124. 124. | |-- fixtures
  125. 125. | `-- sql
  126. 126. |-- migrations
  127. 127. | |-- 1268942153_adduser.php
  128. 128. | `-- 1268942505_version2.php
  129. 129. `-- schema
  130. 130. |-- Administrator.yml
  131. 131. `-- User.yml </li></ul>
  132. 132. Работаем с Doctrine_Cli <ul><li>Генерируем код моделей:
  133. 133. $ ./application/sripts/doctrine generate-models-yaml
  134. 134. generate-models-yaml - Generated models successfully from YAML schema
  135. 135. Накатываем изменения на БД:
  136. 136. $ ./application/sripts/doctrine migrate
  137. 137. migrate - migrated successfully to version #2 </li></ul>
  138. 138. Работаем с Doctrine_Cli <ul><li>Смотрим, что получилось:
  139. 139. mysql> SHOW CREATE TABLE `administrators`;
  140. 140. CREATE TABLE `administrators` (
  141. 141. `id` int (10) NOT NULL AUTO_INCREMENT ,
  142. 142. `login` varchar (32) COLLATE utf8_unicode_ci DEFAULT NULL ,
  143. 143. `email` varchar (255) COLLATE utf8_unicode_ci DEFAULT NULL ,
  144. 144. `password_hash` varchar (32) COLLATE utf8_unicode_ci DEFAULT NULL ,
  145. 145. `password_salt` varchar (8) COLLATE utf8_unicode_ci DEFAULT NULL ,
  146. 146. `created_at` datetime NOT NULL ,
  147. 147. `updated_at` datetime NOT NULL ,
  148. 148. PRIMARY KEY (`id`)
  149. 149. ) ENGINE =InnoDB DEFAULT CHARSET =utf8 COLLATE =utf8_unicode_ci </li></ul>
  150. 150. По-моему пора сделать авторизацию <ul><li>Сначала напишем сеттер для password:
  151. 151. ./application/models/Administrator.php
  152. 152. <?php
  153. 153. class Administrator extends BaseAdministrator
  154. 154. {
  155. 155. // Здесь был phpDoc блок ...
  156. 156. public function setPassword( $password )
  157. 157. {
  158. 158. if (strlen( $password )) {
  159. 159. $passwordSalt = substr(md5(mktime()), 0, rand(5,8));
  160. 160. $passwordHash = md5( $password . $passwordSalt );
  161. 161. $this ->_set( 'password_hash' , $passwordHash );
  162. 162. $this ->_set( 'password_salt' , $passwordSalt );
  163. 163. }
  164. 164. }
  165. 165. } </li></ul>
  166. 166. Сгенерируем аккаунт для админа и сохраним его в БД <ul>./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()); } } </ul>
  167. 167. Проверяем содержимое таблицы administrators <ul>mysql> SELECT * FROM `administrators`; +----+----------+------------------+----------------------------------+---------------+---------------------+---------------------+ | id | login | email | password_hash | password_salt | created_at | updated_at | +----+----------+------------------+----------------------------------+---------------+---------------------+---------------------+ | 1 | stfalcon | [email_address] | bcd3987603a947d54480285c16f06fde | fc1ed | 2010-03-18 23:04:11 | 2010-03-18 23:04:11 | </ul>
  168. 168. ZendX_Doctrine_Auth_Adapter <ul>./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>' ; } } </ul>
  169. 169. Открываем страницу в браузере <ul><li>Все ОК :)
  170. 170. И не забудьте сохранить данные авторзации в хранилище:
  171. 171. $data = $authAdapter ->getResultRowObject( null , array ( 'password_hash' , 'password_salt' ));
  172. 172. $auth ->getStorage()->write( $data ); </li></ul>
  173. 173. Увековечим учетную запись администратора <ul>./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 # ... </ul>
  174. 174. Сделаем глобальный reload <ul>$ ./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 | [email_address] | bcd3987603a947d54480285c16f06fde | fc1ed | 2010-03-18 23:04:11 | 2010-03-18 23:04:11 | </ul>
  175. 175. Адаптер для Zend_Paginator <ul>Мы используем ZFEngine_Paginator_Adapter_Doctrine , это немного переработанный с учетом наших потребностей и изменений в Doctrine 1.2 SmartL_Zend_Paginator_Adapter_Doctrine http://code.google.com/p/smart-framework/ Ещё раз пропиарю наш ZFEngine :) http://zfengine.com </ul>
  176. 176. ZFEngine_Paginator_Adapter_Doctrine <ul><li>Давайте выведем список администраторов с постраничной навигацией. Для этого создадим в таблице administrators 10 случайных записей: </li></ul>
  177. 177. Расширяем функционал AdministratorTable <ul><li>Создадим метод getQueryToFetchAll(), который будет возвращать запрос на выборку всех администраторов:
  178. 178. ./application/models/AdministratorTable.php
  179. 179. <?php
  180. 180. class AdministratorTable extends UserTable
  181. 181. {
  182. 182. /**
  183. 183. * Query to fetch all administrators
  184. 184. * @return Doctrine_Query
  185. 185. */
  186. 186. public function getQueryToFetchAll()
  187. 187. {
  188. 188. return $this ->createQuery( 'a' )
  189. 189. ->orderBy( 'a.created_at' );
  190. 190. }
  191. 191. } </li></ul>
  192. 192. Работаем с пагинатором <ul>./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 ; } } </ul>
  193. 193. Оформляем вывод списка в view шаблоне <ul>./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' ); ?> </ul>
  194. 194. digg.phtml <ul><li>digg.phtml я выложил здесь — http://pastie.org/832023 (за основу взят шаблон с ZendPaginationHelper ) </li></ul>
  195. 195. Открываем страницу в браузере <ul><li>И наслаждаемся результатом :) </li></ul>
  196. 196. ZFEngine и использование Doctrine в модульном ZF приложении <ul><li>Мы написали несколько тасков (собственно таски написал Валерий Рабиевский , а я только немного порефакторил) для Doctrine, которые позволяют генерировать модели и использовать механизм миграций в ZF проектах с модульной архитектурой.
  197. 197. При этом между моделями разных модулей работает связывание и наследование.
  198. 198. Также работает механизм миграций для проекта в целом. </li></ul>
  199. 199. Пример структуры модульного ZF проекта <ul>./application/ |-- Bootstrap.php |-- configs | `-- application.ini |-- layouts | `-- scripts | |-- admin.phtml | `-- index.html `-- modules |-- products `-- users </ul>
  200. 200. Настройки для модульной структуры <ul><li>Прописываем следующие настройки в application.ini:
  201. 201. ; Указываем, где находятся наши модули для Zend
  202. 202. resources.frontController.moduleDirectory =
  203. 203. APPLICATION_PATH &quot;/modules&quot;
  204. 204. ; и для Doctrine_Cli
  205. 205. doctrine_cli.modules_path = APPLICATION_PATH &quot;/modules/&quot;
  206. 206. ; а также прописываем путь к папке, где будут хранится yaml-схемы предыдущих версий (old), и новые (temp), собранные с модулей в одну папку. Именно по различиям между ними и будут генерироваться миграции.
  207. 207. doctrine_cli.old_schema_path = APPLICATION_PATH &quot;/configs/doctrine/schema/old/&quot;
  208. 208. doctrine_cli.temp_schema_path = APPLICATION_PATH &quot;/../tmp/schema/&quot;
  209. 209. resources.modules[] = &quot;&quot; ; подгружаем ресурс для подержки модулей
  210. 210. ; И убираем строки, где задавали расположение моделей:
  211. 211. ; resources.doctrine.manager.models_path = APPLICATION_PATH &quot;/models&quot;
  212. 212. ; doctrine_cli.models_path = APPLICATION_PATH &quot;/models&quot;
  213. 213. ; так как теперь модели подгружаются самим Zend'ом </li></ul>
  214. 214. Cтруктура модуля users <ul>./application/modules/users/ |-- Bootstrap.php |-- configs | `-- doctrine | |-- data | | |-- fixtures | | `-- sql | `-- schema | `-- User.yml |-- controllers | `-- IndexController.php |-- models `-- views `-- scripts `-- index `-- index.phtml </ul>
  215. 215. Схема 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)
  216. 216. Cтруктура модуля products <ul>./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 </ul>
  217. 217. Схема Product.yml <ul>## Product schema Products_Model_Product : tableName : products options : <ul>... </ul>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] ... </ul>
  218. 218. Схема Product.yml (продолжение) <ul>... # Прописываем связь один-ко-многим # User и Products – алиасы, через которые мы сможем обращаться # из одной модели к другой relations : User : class : Users_Model_User foreign : id local : user_id foreignAlias : Products onUpdate : CASCADE onDelete : CASCADE </ul>
  219. 219. Новый скрипт для Doctrine_Cli <ul><li>Скрипт для работы с Doctrine_Cli в модульном ZF приложении лежит в репозитории ZFEngine.
  220. 220. Единственное его отличие от обычного скрипта, это наличие кода для подключения тасков с ZFEngine и справка по командам ZFEngine при запуске скрипта с ключем info :
  221. 221. $ ./application/scripts/doctrine info
  222. 222. zfengine-generate-migrations-models -> для генерации новой миграций
  223. 223. zfengine-generate-migrations-diff -> для генерации изменений миграций
  224. 224. zfengine-generate-models-yaml -> для генерация моделей из yaml-файлов
  225. 225. zfengine-prepare-schema-files-for-migrations -> для копирования shema-файлов для сравнения при генерации миграций
  226. 226. Очередность действий:
  227. 227. При создании новой миграции:
  228. 228. zfengine-generate-models-yaml
  229. 229. zfengine-generate-migrations-models
  230. 230. migrate
  231. 231. При создании изменений миграции: ... </li></ul>
  232. 232. Генерируем модели по YAML схемам <ul><li>Все также как в предыдущих примерах, только команда с префиксом zfengine :
  233. 233. $ ./application/sripts/doctrine zfengine-generate-models-yaml
  234. 234. Generated models for module &quot;Products&quot; successfully
  235. 235. Generated models for module &quot;Users&quot; successfully
  236. 236. Generated models finished
  237. 237. Получаем готовые модели:
  238. 238. ./application/modules/users/
  239. 239. |-- models
  240. 240. |-- Base
  241. 241. | `-- User.php
  242. 242. |-- User.php
  243. 243. `-- UserTable.php
  244. 244. Только теперь модели именуются согласно стандартам ZF и подгружаются родным автозагрузчиком:
  245. 245. BaseUser -> Users_Model_Base_User
  246. 246. User -> Users_Model_User </li></ul>
  247. 247. Сгенерированые модели <ul>Между моделями из разных модулей сгенерировались связи: ./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 ; </ul>
  248. 248. Миграции <ul><li>Сгенерируем миграции:
  249. 249. Первую миграцию (на новом проекте) делаем через:
  250. 250. $ ./application/sripts/doctrine zfengine-generate-migrations-models
  251. 251. Так миграции генерируются на основании существующих классов моделей, а последующие — уже на основании изменений в yaml-схемах командой:
  252. 252. $ ./application/sripts/doctrine zfengine-generate-migrations-diff
  253. 253. И накатываем миграции на базу:
  254. 254. $ ./application/sripts/doctrine migrate
  255. 255. migrate - migrated successfully to version #3 </li></ul>
  256. 256. Структура таблицы `products` <ul><li>Смотрим, что получилось в БД:
  257. 257. mysql> SHOW CREATE TABLE `products`;
  258. 258. CREATE TABLE `products` (
  259. 259. `id` int (10) unsigned NOT NULL AUTO_INCREMENT ,
  260. 260. `user_id` int (10) unsigned DEFAULT NULL ,
  261. 261. `name` varchar (255) COLLATE utf8_unicode_ci DEFAULT NULL ,
  262. 262. `description` text COLLATE utf8_unicode_ci,
  263. 263. `created_at` datetime NOT NULL ,
  264. 264. `updated_at` datetime NOT NULL ,
  265. 265. PRIMARY KEY (`id`),
  266. 266. KEY `products_user_id_users_id` (`user_id`),
  267. 267. CONSTRAINT `products_user_id_users_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
  268. 268. ) ENGINE =InnoDB DEFAULT CHARSET =utf8 COLLATE =utf8_unicode_ci </li></ul>
  269. 269. На этом все ;) <ul>Благодарю за внимание! Задавайте вопросы. Степан Танасийчук [email_address] </ul>
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×