60
ZFConf 2010 Zend Framework и Doctrine Степан Танасийчук [email protected]

Zend Framework и Doctrine

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Zend Framework и Doctrine

ZFConf 2010

Zend Framework и Doctrine

Степан Танасийчук[email protected]

Page 2: Zend Framework и Doctrine

Чем я занимаюсь?

Web разработкой занялся в 2003 году

С Zend Framework начал работать в 2008 году

Руковожу собственной веб-студией с 2009 года

Активный участник сообщества zendframework.ru

Люблю прикольные смайлы :]

Page 3: Zend Framework и Doctrine

Содержание доклада

Подключение Doctrine к ZF проекту Скрипт для работы с Doctrine_Cli Генерация моделей по YAML схемам Механизм миграций Наследование в моделях Шаблоны расширений Адаптер для Zend_Auth Адаптер для Zend_Paginator ZFEngine и использование Doctrine в модульном ZF

приложении

Page 4: Zend Framework и Doctrine

Несколько слов о Doctrine

ORM библиотека для PHP 5.2.3+ Использует паттерны Active Record, Data Mapper и Metadata

Mapping Собственный язык запросов — DQL (по мотивам HQL) Связи один-к-одному, один-ко-многим и многие-к-многим Автогенерация моделей по yaml схемам Экспорт и импорт из/в yaml Механизм миграций Шаблоны поведений (l18n, Versionable, NestedSet, etc.)

Page 5: Zend Framework и Doctrine

Подключаем Doctrine к ZF проекту

Размещаем Doctrine в library/Doctrine:$ svn export http://svn.doctrine-project.org/tags/1.2.1/lib/Doctrine/ ./library/Doctrine

Прописываем следующие настройки в application.ini:autoloadernamespaces[] = "Doctrine"

Page 6: Zend Framework и Doctrine

Parables_Application_Resource_Doctrine

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

Его класс называется Parables_Application_Resource_Doctrine и лежит здесь http://github.com/mlurz71/parables

Page 7: Zend Framework и Doctrine

ZFEngine_Application_Resource_Doctrine

Мы немного изменили код Parables_Application_Resource_Doctrine для работы с Doctrine 1.2.x и храним его в репозитории ZFEngine как ZFEngine_Application_Resource_Doctrine

ZFEngine это сборная солянка классов, которые мы используем при разработке проектов на ZF. Лежит все здесь: http://zfengine.com

В основном код наш. Также есть чужой, но с некоторыми изменениями. Надеюсь, что это все в рамках закона ^_~.

Page 8: Zend Framework и Doctrine

Подключаем 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"

Page 9: Zend Framework и Doctrine

Настраиваем подключение к БД

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"

Page 10: Zend Framework и Doctrine

Настраиваем 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"

Page 11: Zend Framework и Doctrine

MODEL_LOADING_PEAR

В Doctrine 1.2 появился новый режим для автозагрузки моделей — MODEL_LOADING_PEAR, но при использовании этого режима не работает generate-migration-diff :(.Я заметил это уже в процессе подготовки доклада и пока просто написал в багрепорт Doctrine.

Page 12: Zend Framework и Doctrine

Для проектов с НЕмодульной структурой

Указываем путь к директории с моделями:resources.doctrine.manager.models_path = APPLICATION_PATH "/models"

Page 13: Zend Framework и Doctrine

Настраиваем кеширование

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

Page 14: Zend Framework и Doctrine

Настраиваем 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

Page 15: Zend Framework и Doctrine

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(),

)));

Page 16: Zend Framework и Doctrine

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']);

Page 17: Zend Framework и Doctrine

Проверяем как работает

Запускаем скрипт без параметров:$ ./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ть команд, которые не поместились на этом слайде (:

Page 18: Zend Framework и Doctrine

Создадим схему модели 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)

Page 19: Zend Framework и Doctrine

Генерируем модели по 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.

Page 20: Zend Framework и Doctrine

Сгенерированный код базовой модели 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();

}

}

Page 21: Zend Framework и Doctrine

Сгенерированный код модели User и маппера UserTable

./application/models/User.php

<?php

class User extends BaseUser

{

}

./application/models/UserTable.php<?php

class UserTable extends Doctrine_Table

{

}

Page 22: Zend Framework и Doctrine

Напишем свой сеттер для поля 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));

}

}

Page 23: Zend Framework и Doctrine

Пишем экшн для проверки работы

./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 = '[email protected]';

Zend_Debug::dump($user->toArray());

}

}

Page 24: Zend Framework и Doctrine

Запускаем в браузере

array

'id' => null

'login' => string 'stfalcon' (length=8)

'email' => string '[email protected]' (length=16)

Page 25: Zend Framework и Doctrine

Миграции

Сгенерируем первый класс миграций. Его можно генерировать из классов моделей или БД (см. мануал к 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

Page 26: Zend Framework и Doctrine

Сгенерированный код первой модели миграций

./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');

}

}

Page 27: Zend Framework и Doctrine

Создадим БД и накатим на неё наши изменения

Создаем БД (например на 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

Page 28: Zend Framework и Doctrine

Проверяем работу скрипта

Структура таблицы в которой хранится номер миграции: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

Page 29: Zend Framework и Doctrine

Наследование в 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]

Page 30: Zend Framework и Doctrine

Работаем с 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

Page 31: Zend Framework и Doctrine

Работаем с 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

Page 32: Zend Framework и Doctrine

Работаем с 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

Page 33: Zend Framework и Doctrine

По-моему пора сделать авторизацию

Сначала напишем сеттер для 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);

}

}

}

Page 34: Zend Framework и Doctrine

Сгенерируем аккаунт для админа и сохраним его в БД

./application/controllers/IndexController.php

<?php

class IndexController extends Zend_Controller_Action

{

// Здесь был phpDoc блок ...

public function indexAction()

{

$administrator = new Administrator();

$administrator->email = '[email protected]';

$administrator->login = 'stfalcon';

$administrator->password = 'qwerty';

$administrator->save();

Zend_Debug::dump($administrator->toArray());

}

}

Page 35: Zend Framework и Doctrine

Проверяем содержимое таблицы administrators

mysql> SELECT * FROM `administrators`;

+----+----------+------------------+----------------------------------+---------------+---------------------+---------------------+

| id | login | email | password_hash | password_salt | created_at | updated_at |

+----+----------+------------------+----------------------------------+---------------+---------------------+---------------------+

| 1 | stfalcon | [email protected] | bcd3987603a947d54480285c16f06fde | fc1ed | 2010-03-18 23:04:11 | 2010-03-18 23:04:11 |

Page 36: Zend Framework и Doctrine

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>';

}

}

Page 37: Zend Framework и Doctrine

Открываем страницу в браузере

Все ОК :) И не забудьте сохранить данные авторзации в хранилище:

$data = $authAdapter->getResultRowObject(null, array('password_hash', 'password_salt'));

$auth->getStorage()->write($data);

Page 38: Zend Framework и Doctrine

Увековечим учетную запись администратора

./application/configs/doctrine/data/fixtures/users.yml

Administrator:

Admin_1:

login: stfalcon

email: [email protected]

password_hash: bcd3987603a947d54480285c16f06fde

password_salt: fc1ed

# Admin_2:

# login: stfalcon

# ...

Page 39: Zend Framework и Doctrine

Сделаем глобальный 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 | [email protected] | bcd3987603a947d54480285c16f06fde | fc1ed | 2010-03-18 23:04:11 | 2010-03-18 23:04:11 |

Page 40: Zend Framework и Doctrine

Адаптер для 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

Page 41: Zend Framework и Doctrine

ZFEngine_Paginator_Adapter_Doctrine

Давайте выведем список администраторов с постраничной навигацией. Для этого создадим в таблице administrators 10 случайных записей:

Page 42: Zend Framework и Doctrine

Расширяем функционал 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');

}

}

Page 43: Zend Framework и Doctrine

Работаем с пагинатором

./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;

}

}

Page 44: Zend Framework и Doctrine

Оформляем вывод списка в 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'); ?>

Page 45: Zend Framework и Doctrine

digg.phtml

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

Page 46: Zend Framework и Doctrine

Открываем страницу в браузере

И наслаждаемся результатом :)

Page 47: Zend Framework и Doctrine

ZFEngine и использование Doctrine в модульном ZF приложении

Мы написали несколько тасков (собственно таски написал Валерий Рабиевский, а я только немного порефакторил) для Doctrine, которые позволяют генерировать модели и использовать механизм миграций в ZF проектах с модульной архитектурой.

При этом между моделями разных модулей работает связывание и наследование.

Также работает механизм миграций для проекта в целом.

Page 48: Zend Framework и Doctrine

Пример структуры модульного ZF проекта

./application/

|-- Bootstrap.php

|-- configs

| `-- application.ini

|-- layouts

| `-- scripts

| |-- admin.phtml

| `-- index.html

`-- modules

|-- products

`-- users

Page 49: Zend Framework и Doctrine

Настройки для модульной структуры

Прописываем следующие настройки в 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'ом

Page 50: Zend Framework и Doctrine

Cтруктура модуля users

./application/modules/users/

|-- Bootstrap.php

|-- configs

| `-- doctrine

| |-- data

| | |-- fixtures

| | `-- sql

| `-- schema

| `-- User.yml

|-- controllers

| `-- IndexController.php

|-- models

`-- views

`-- scripts

`-- index

`-- index.phtml

Page 51: Zend Framework и Doctrine

Схема 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)

Page 52: Zend Framework и Doctrine

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

Page 53: Zend Framework и Doctrine

Схема 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]

...

Page 54: Zend Framework и Doctrine

Схема Product.yml (продолжение)

...

# Прописываем связь один-ко-многим

# User и Products – алиасы, через которые мы сможем обращаться

# из одной модели к другой

relations:

User:

class: Users_Model_User

foreign: id

local: user_id

foreignAlias: Products

onUpdate: CASCADE

onDelete: CASCADE

Page 55: Zend Framework и Doctrine

Новый скрипт для 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

При создании изменений миграции: ...

Page 56: Zend Framework и Doctrine

Генерируем модели по 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

Page 57: Zend Framework и Doctrine

Сгенерированые модели

Между моделями из разных модулей сгенерировались связи:

./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;

Page 58: Zend Framework и Doctrine

Миграции

Сгенерируем миграции:Первую миграцию (на новом проекте) делаем через:

$ ./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

Page 59: Zend Framework и Doctrine

Структура таблицы `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

Page 60: Zend Framework и Doctrine

На этом все ;)

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

Степан Танасийчук[email protected]