пятница, 27 ноября 2009 г.

Doctrine custom relation

Иногда приходится сталкиваться с тем, что реализация связей в модели по умолчанию не достаточна удобна для использования в коде.
Например, есть модель Page - страница сайта, PageMeta - возможные теги meta для страниц и PageMetaContent - конкретные значения тегов meta для конкретной страницы.

config/doctrine/schema.yml


# страница сайта
Page:
actAs:
NestedSet: { hasManyRoots: true, rootColumnName: root_id }
columns:
id: { type: integer(20), autoincrement: true, primary: true }
name: { type: string(255), notnull: true, default: '' }

# meta страницы сайта
PageMeta:
columns:
id: { type: integer(20), autoincrement: true, primary: true }
name: { type: string(64), notnull: true, unique: true }
title: { type: string(255), notnull: true, unique: true }
ord: { type: integer(4), notnull: true, default: 10 }

# значение meta страницы сайта
PageMetaContent:
columns:
id: { type: integer(20), autoincrement: true, primary: true }
page_id: { type: integer(20), notnull: true }
meta_id: { type: integer(20), notnull: true }
value: { type: string }
relations:
Page: { class: Page, local: page_id, foreign: id, foreignAlias: MetaContent, type: one, foreignType: many, onDelete: CASCADE }
Meta: { class: PageMeta, local: meta_id, foreign: id, foreignAlias: Content, type: one, foreignType: many, onDelete: CASCADE }


data/fixtures/fixtures.yml

Page:
DefaultPage:
name: 'Главная страница'
children: ~

PageMeta:
TitlePageMeta:
name: title
ord: 10
KeywordsPageMeta:
name: keywords
ord: 20
DescriptionsPageMeta:
name: descriptions
ord: 30

PageMetaContent:
Title_DefaultPage_MetaContent:
Page: DefaultPage
Meta: TitlePageMeta
value: Главная страница
Keywords_DefaultPage_MetaContent:
Page: DefaultPage
Meta: KeywordsPageMeta
value: ключевое-слово-1, ключевое-слово-2
Descriptions_DefaultPage_MetaContent:
Page: DefaultPage
Meta: DescriptionsPageMeta
value: Описание главной страницы


Теперь требуется для загруженной из бд страницы в виде объекта Page поменять теги meta.
Вот пример решения такой задачи. Достаточно добавить пару методов класс модели Page


lib/model/doctrine/Page.class.php

[?php
class Page extends BasePage
{
protected $Meta = null;

public function construct()
{
$this->Meta = new Doctrine_Collection(Doctrine::getTable('PageMeta'));
}

public function getMeta()
{
if (0 == $this->Meta->count())
{
$this->Meta = Doctrine::getTable('PageMeta')
->getQuery('pageMeta')
->select('id, name')
->from('PageMeta pageMeta INDEXBY pageMeta.name')
);

$metaContent_list = array();
foreach ($this->MetaContent as $metaContent)
{
$metaContent_list[$metaContent->meta_id] = &$metaContent;
}

foreach ($this->Meta as $meta)
{
$meta->mapValue('PageContent', isset($metaContent_list[$meta->id]) ? $metaContent_list[$meta->id] : new PageMetaContent());
}
}

return $this->Meta;
}
}


Пример использования:

[?php
$page = Doctrine::getTable('Page')->findOneById(1);
$page->Meta['description']->PageContent->value = 'Описание для страницы сайта';
$page->save();