Development

Documentation/ru_RU/book/1.0/08-Inside-the-Model-Layer

You must first sign up to be able to contribute.

Version 20 (modified by BaBL, 7 years ago)
--

Глава 8 - Внутри слоя Модели

До настоящего времени большая часть обсуждения была посвящена вёрстке страниц и обработке запросов и ответов. Но бизнес-логика веб-приложений в основном зависит от модели данных. В symfony компонент по умолчанию, который реализует модель, основывается на объектно-реляционной проекции(ORM), известной как Propel project. В приложении symfony, вы обращаетесь к данным, которые хранятся в базе данных, и изменяете их посредством объекта; вы никогда явно не обращаетесь к базе данных. Это поддерживает высокий уровень абстракции и переносимость.

Эта глава объясняет, как создать объектную модель данных, и каким образом обращаться и изменять данные в Propel. Также она демонстрирует интеграцию Propel в Symfony.

Зачем использовать ORM и слой абстракции?

Базы данных являются реляционными. PHP5 и symfony – объектно-ориентированные. Для того, чтобы наиболее эффективно обратиться к базе данных в объектно-ориентированном контексте, необходим интерфейс, который преобразовывает объектную логику в реляционную логику. Согласно приведенным объяснениям в Главе 1, этот интерфейс называется объектно-реляционной проекцией (ORM), и состоит из объектов, которые дают возможность доступа к данным и хранят бизнес-правила в своих пределах.

Основным преимуществом ORM является возможность повторного использования, которое позволяет запрашивать методы объекта данных из различных компонентов приложения, даже из других приложений. Уровень ORM также инкапсулирует логику данных – например, подсчет рейтинга пользователя форума основывается на том, сколько он сделал вкладов в развитие форума и насколько они популярны. Когда странице необходимо отобразить рейтинг такого пользователя, она просто делает запрос на метод модели данных, не заморачиваясь о деталях подсчета. Если впоследствии подсчет изменяется, вам просто нужно будет внести поправки в метод подсчета модели, ничего не меняя в других компонентах приложения.

Использование объектов вместо записей и классов вместо таблиц имеет и другое преимущество: они позволяют вам добавлять новые средства доступа к вашим объектам, которые необязательно совпадают со столбцами в таблице. Например, если у вас есть таблица, называемая клиентом, в которой есть 2 поля под названиями first_name и last_name, возможно, вы предпочтете вариант востребования просто Name. В объектно-ориентированном мире это так же просто, как и добавить новый метод средства доступа к классу Client, как в примере 8-1. С точки зрения приложения, разницы между атрибутами FirstName, LastName и Name класса Client нет. Только сам класс может определять, какие свойства соответствуют столбцу базы данных.

Листинг 8-1 - Accessors Mask the Actual Table Structure in a Model Class (Маска средства доступа к фактической структуре таблицы в модели класса).

[php]
public function getName()
{
  return $this->getFirstName().' '.$this->getLastName();
}

Все повторяющиеся функции доступа к данным и бизнес-логика самих данных могут храниться в таких объектах. Представьте себе, что у вас есть категория ShoppingCart, в которой у вас хранятся Items (они же объекты). Чтобы получить общее количество объектов корзины для подсчёта стоимости сделанных покупок в модели класса, напишите специальный метод для инкапсулирования фактического подсчета, как показано в Листинге 8-2.

Листинг 8-2 - Маска средства доступа к логическим данным

[php]
public function getTotal()
{
  $total = 0;
  foreach ($this->getItems() as $item)
  {
    $total += $item->getPrice() * $item->getQuantity();
  }

  return $total;
}

При составлении процедуры доступа к данным нужно учитывать ещё один важный момент – разработчики базы данных используют разные варианты синтаксиса SQL. Переход на другую систему управления базами данных (DBMS) заставляет вас переписать часть SQL запросов, разработанных для предыдущей. Если вы строите запросы, используя независимый от базы данных синтаксис, и поручаете текущее преобразование SQL стороннему элементу, тогда вы безболезненно можете переключаться между системами базы данных. Это цель уровня абстрактных представлений базы данных. Он заставляет вас использовать определенный синтаксис для запросов и выполняет всю грязную работу по согласованию с тонкостями DBMS и оптимизации кода SQL.

Основное преимущество уровня абстрактных представлений это мобильность, т.к. он дает возможность совершать переход на другую базу данных даже в середине проекта. Представьте, что вам нужно написать быстродействующую модель для приложения, но клиент все ещё не решил, какая система базы данных больше всего соответствует его требованиям. Вы можете начать создание приложения при помощи, например, SQLite, и перейти на MySQL, PostgreSQL, или Oracle, как только клиент будет готов принять решение. Просто измените одну строку в файле конфигурации, и дело в шляпе.

Symfony использует Propel в качестве ORM, а Propel использует Creole для абстракции базы данных. Это два сторонних элемента, оба разработаны командой Propel, так органически интегрированы в symfony, что их можно считать частью фреймворка. Описанные в этой главе синтаксис и условные обозначения этих элементов были адаптированы таким образом, чтобы они как можно меньше отличались от синтаксиса и условных обозначений symfony.

NOTE В проекте symfony все приложения используют одну и ту же модель. В этом и весь смысл уровня проекта: перегруппировка приложений, которая зависит от общепринятого делового регламента. Это причина того, почему модель не зависит от приложений, и файлы модели сохраняются в директории lib/model/ в корневом каталоге проекта.

Схема базы данных Symfony

Для того, чтобы создать объектную модель данных, которую будет использовать symfony, Вам нужно преобразовать реляционную модель в вашей базе данных, какая бы она ни была, в объектную модель данных. Для того, чтобы осуществить преобразование данных, ORM необходимо описание реляционной модели и это называется схемой. В схеме вы определяете таблицы, их соотношения, и свойства столбцов.

Для логического соединения синтаксис Symfony использует формат YAML. Файлы schema.yml должны находиться в директории myproject/config/.

NOTE Symfony также читает собственный формат XML схемы Propel, далее это описано в разделе "Вне schema.yml: schema.xml" в этой главе.

Пример листинга

Как преобразить структуру базы данных в схему? Наилучшим способом понять это будет Листинг. Представьте, что у вас есть база данных блога с двумя таблицами: blog_article and blog_comment, и структурой, показанной на рисунке 8-1.

Рисунок 8-1 – Структура таблицы базы данных блога

Структура таблицы базы данных блога

Соотнесённый файл schema.yml должен выглядеть так, как показано в Листинге 8-3.

Листинг 8-3 – Образец schema.yml

propel:
  blog_article:
    _attributes: { phpName: Article }
    id:
    title:       varchar(255)
    content:     longvarchar
    created_at:
  blog_comment:
    _attributes: { phpName: Comment }
    id:
    article_id:
    author:      varchar(255)
    content:     longvarchar
    created_at:

Обратите внимание, что само имя базы данных (блог) не обнаруживается в файле schema.yml. Вместо того, база данных описывается названием логического соединения (в нашем Листинге это propel). Это потому, что фактические настройки логического соединения могут зависеть от среды, в которой работает ваше приложение. Например, когда вы запускаете ваше приложение в среде разработки, вы обращаетесь к базе данных разработки (может быть blog_dev), но с той же схемой, что и база данных правил вывода. Настройки логического соединения будут заданы в файле databases.yml, они описаны далее в этой главе в разделе "Соединение базы данных". Схема не содержит подробных настроек соединения, только название соединения, для сохранения абстракции базы данных.

Основной синтаксис схемы

В файле schema.yml, первый ключ означает название схемы. Он может содержать в себе несколько таблиц, в каждой из которых может находиться ряд столбцов. Согласно синтаксису YAML, ключи оканчиваются двоеточием, и структура отмечается введением отступов (один и больше пробелов, но без табуляций).

В таблице могут быть особые атрибуты, в том числе, phpName (имя класса, который будет создан). Если вы не укажете phpName для таблицы, symfony создаст его на основе варианта имени таблицы camelCase.

TIP Условное обозначение camelCase убирает нижнее подчеркивание и делает заглавной первую букву второй части слова. Варианты camelCase по умолчанию - blog_article и blog_comment выглядят как BlogArticle и BlogComment. Название этого условного обозначения происходит от внешнего вида заглавных букв внутри длинного слова, вызывающего ассоциацию с горбами верблюда.

Таблица состоит из столбцов. Значение столбца может задаваться тремя разными путями:

  • Если вы ничего не задаете, symfony наугад определит наиболее подходящие атрибуты согласно имени столбца и нескольким условным обозначениям, что будет описано далее в этой главе в разделе "Пустые столбцы". Например, столбец id в Листинге 8-3 задавать не нужно. Symfony сделает его целым числом, значение которого увеличивается автоматически, первичным ключом таблицы article_id в таблице blog_comment будет пониматься как внешний ключ к таблице blog_article (столбцы, оканчивающиеся на with id считаются внешними ключами, и зависимая таблица автоматически определяется согласно первой части имени столбца). Столбцы под названием createdat автоматически подпадают под тип timestamp. Для всех этих столбцов вам не нужно указывать какие-либо типы. Это одна из причин того, почему schema.yml так легко пишется.
  • Если вы определяете только один атрибут, то это тип столбца. Symfony понимает обычные типы столбцов: boolean, integer, float, date, varchar(size), longvarchar (к примеру, конвертированный в текст в MySQL), и т.д. Если содержание текста больше 256 символов, вам нужно использовать тип longvarchar, у которого нет размера (но он не может превышать 65KB в MySQL). Обратите внимание, что у типов data и timestamp есть обычные ограничения дат Unix и для них нельзя установить дату раньше 1970-01-01. Поскольку вам может понадобиться установить более ранние даты (к примеру, дата рождения), формат даты "before Unix" может быть использован с bu_date и bu_timestamp.
  • Если вам нужно определить другие атрибуты столбцов (такие как значение по умолчанию, required, и т.д.), вам необходимо записать атрибуты столбца как последовательность ключей: значение. Этот расширенный синтаксис схемы описывается далее в этой главе.

У столбцов также могут быть атрибуты phpName, по сути, это варианты названия, которое пишется с большой буквы (Id, Title, Content, и т.д.) и в большинстве случаев не нуждается в подмене.

Таблицы также могут включать явно внешние ключи и индексы, равно как и несколько структурных описаний, свойственных базе данных. Чтобы узнать больше, обратитесь к разделу " Расширенный синтаксис схемы " в конце этой главы.

Классы модели

Схема используется для того, чтобы построить классы модели слоя ORM. Для экономии времени выполнения эти классы создаются задачей командной строки под названием propel-build-model.

>> symfony propel-build-model

Набор этой команды запустит анализ схемы и создание основных классов модели данных в директории lib/model/om/ вашего проекта:

  • BaseArticle.php
  • BaseArticlePeer.php
  • BaseComment.php
  • BaseCommentPeer.php

Кроме того, текущие классы модели данных будут созданы в lib/model/:

  • Article.php
  • ArticlePeer.php
  • Comment.php
  • CommentPeer.php

Вы задали только 2 таблицы, и в конце вы получили 8 файлов. В этом нет ничего страшного, но стоит объяснить, в чем же дело.

Основные и специальные классы:

Зачем сохранять две версии объектной модели данных в двух разных директориях?

Возможно, вам нужно будет добавить специальные методы и свойства в объектную модель (вспомните о методе getName() в Листинге 8-1). Но по мере развития вашего проекта, вы будете также добавлять таблицы и столбцы. Всякий раз, когда вы изменяете файл schema.yml, вам нужно восстанавливать классы объектной модели путем нового обращения к propel-build-model. Если ваши специальные методы были написаны в фактически созданных классах, они будут удаляться после каждого создания.

Классы Base, которые хранятся в директории the lib/model/om/ это классы, непосредственно созданные из этой схемы. Вам не следует их изменять, поскольку каждый новый билд модели будет полностью удалять эти файлы.

С другой стороны, специальные объектные классы, которые хранятся в директории lib/model/, фактически наследуют классы Base. Когда задача propel-build-model запрашивается существующей моделью, эти классы не изменяются. Так что сюда вы можете добавлять специальные методы.

Листинг 8-4 показывает пример обычного класса модели, созданного при первом обращении к задаче propel-build-model.

Листинг 8-4 – Пример файла класса модели в lib/model/Article.php

<?php

class Article extends BaseArticle
{
}

Он унаследует все методы класса BaseArticle, и видоизменение схемы не повлияет на него.

Механизм расширения основных обычных классов позволяет вам начать программирование, даже не зная конечную реляционную модель вашей базы данных. Соотнесённая структура файла делает модель более настраиваемой и эволюционной.

Объектный и одноранговый класс

Article и Comment - это объектные классы, которые представляют собой запись в базе данных. Они предоставляют доступ к столбцам записи и к соотнесенным записям. Это значит, что вы сможете узнать название статьи путем обращения к методу объекта Article, как показано в Листинге 8-5.

Листинг 8-5 – Механизмы for Record Columns Are Available in the Object Class

$article = new Article();
...
$title = $article->getTitle();

ArticlePeer и CommentPeer – это одноранговые классы; т.е., классы, которые вкючают статические методы для to operate on the tables. They provide a way to retrieve records from the tables. Their methods usually return an object or a collection of objects of the related object class, as shown in Листинг 8-6.

Листинг 8-6 - Статические методы для извлечения записей доступны в классе Peer

$articles = ArticlePeer::retrieveByPks(array(123, 124, 125));
// $articles is an array of objects of class Article

NOTE С точки зрения модели данных, одноранговый объект не может существовать. Вот потому к методам одноранговых классов обращаются при помощи :: (для вызова статического метода), вместо обычного -> (для вызова копии метода).

Итак, сочетание объекта и одноранговых классов в основной и обычной версиях So combining object and peer classes in a base and a custom version имеет результатом четыре класса, созданных посредством таблицы и описанных в схеме. По сути, существует пятый класс, созданный в директории lib/model/map/, который включает информацию о метаданных о таблице, необходимую для режима рабочего цикла. runtime environment. Но поскольку вы никогда не измените этот класс, можете забыть об этом.

Обращение к данным

В symfony вы обращаетесь к данным через объекты. Если вы привыкли к реляционной модели и использованию SQL для извлечения и изменения ваших данных, методы объектной модели, вероятно, будут выглядеть замысловато. Лишь однажды ощутив мощность объектной ориентации для обращения к данным, вы её, скорее всего, полюбите.

Но сначала давайте удостоверимся, что мы используем одинаковую терминологию. Реляционная и объектная модель данных оперируют похожими понятиями, но у каждой из них своя номенклатура:

Relational    |  Object-Oriented
--------------------------------    
Table         |  Class
Row, record   |  Object
Field, column |  Property

Извлечение значения столбца

Когда symfony строит модель, он создает один основной объектный класс для каждой из таблиц, описанной в schema.yml. Каждый из этих классов comes with конструкторы по умолчанию, аксессоры и мутаторы, основанные на определении столбцов: новые методы getXXX() и setXXX() помогают создать объекты и предоставить доступ к свойствам объекта, как показано в Листинге 8-7.

Листинг 8-7 – Методы образованного объектного класса

$article = new Article();
$article->setTitle('My first article');
$article->setContent('This is my very first article.\n Hope you enjoy it!');

$title   = $article->getTitle();
$content = $article->getContent();

NOTE Образованный объектный класс называется Article, это phpName данное таблице blog_article. Если бы phpName не определилось в этой схеме, тогда класс назывался бы BlogArticle. Аксессоры и мутаторы используют вариант имен столбцов camelCase, таким образом, метод getTitle() извлекает значение столбца заголовка.

Для того, чтобы установить одновременно несколько полей, вы можете использовать метод fromArray(), также создаваемые для каждого объектного класса, как показано в Листинге 8-8.

Листинг 8-8 - Метод fromArray() - многократный механизм установки.

$article->fromArray(array(
  'title'   => 'My first article',
  'content' => 'This is my very first article.\n Hope you enjoy it!',
));

Извлечение зависимых записей

Столбец article_id в таблице blog_comment полностью определяет внешний ключ к таблице blog_article. Каждый комментарий относится к одной статье и у каждой статьи может быть много комментариев. Производные классы включают пять методов, которые преобразовывают данные отношения объектно-ориентированным способом, как показано ниже:

  • $comment->getArticle(): To get the related Article object
  • $comment->getArticleId(): To get the ID of the related Article object
  • $comment->setArticle($article): To define the related Article object
  • $comment->setArticleId($id): To define the related Article object from an ID
  • $article->getComments(): To get the related Comment objects

Методы getArticleId() и setArticleId() показывают, что вы можете считать столбец article_id правильным и устанавливать отношения вручную, но это не очень интересно. Преимущество объектно-ориентированного подхода более заметно в трех других методах. Листинг 8-9 показывает, как использовать производные механизмы установки.

Листинг 8-9 - Внешние ключи преобразовываются в специальные механизмы установки

$comment = new Comment();
$comment->setAuthor('Steve');
$comment->setContent('Gee, dude, you rock: best article ever!');

// Attach this comment to the previous $article object
$comment->setArticle($article);

// Alternative syntax
// Only makes sense if the object is already saved in the database
$comment->setArticleId($article->getId());

Листинг 8-10 показывает, как использовать производные механизмы включения. Так же он демонстрирует, как располагать объекты модели.

Листинг 8-10 – Внешние ключи преобразовываются в специальные механизмы включения

// Many to one relationship
echo $comment->getArticle()->getTitle();
 => My first article
echo $comment->getArticle()->getContent();
 => This is my very first article.
    Hope you enjoy it!

// One to many relationship
$comments = $article->getComments();

Метод getArticle() возвращает объект класса Article, который извлекает пользу из аксессора getTitle(). Это намного лучше, чем выполнение соединения самому, что может занять несколько строк кода (начиная от запроса $comment->getArticleId()).

Переменная $comments в Листинге 8-10 включает множество объектов класса Comment. You can display the first one with $comments[0] or iterate through the collection with foreach ($comments as $comment).

NOTE Объекты из модели по условию задаются именем в единственном числе, и сейчас вы поймете, почему. Внешний ключ, заданный в таблице blog_comment table является причиной создания метода getComments(), названного именем, созданным при помощи прибавления s к имени объекта Comment. Если бы вы дали имя во множественном числе, создание привело бы к методу getCommentss(), что не имеет смысла.

Сохранение и удаление данных

Обращаясь к новому конструктору, вы создали новый объект, но не фактическую запись в таблице blog_article. Внесения изменений в объект тоже не влияет на базу данных. Для того, чтобы сохранить данные в базе данных, вам нужно обратиться к методу объекта save().

$article->save();

ORM довольно разумно, чтобы заметить отношения между объектами, поэтому сохранение объекта $article влечет за собой и сохранение соотнесенного объекта $comment. Оно также знает, или сохраненный объект имеет сохраненную копию в базе данных, поэтому обращение к save() иногда преобразовывается в SQL на INSERT, и иногда - UPDATE. Первичный ключ автоматически устанавливается методом save(), поэтому после сохранения вы можете извлечь новый первичный ключ при помощи $article->getId().

TIP Вы можете проверить, или объект новый, путем запроса к isNew(). А если вам интересно, был ли изменен объект и нужно ли его сохранять, обратитесь к его методу isModified().

Почитав комментарии к вашим статьям, вы можете изменить свое мнение об интересе публикаций в Интернете. И если вы не ценитель иронии критиков статей, комментарии можно легко удалить при помощи метода delete(), как показано в Листинге 8-11.

Листинг 8-11 – Удаление комментариев из базы данных методом delete() по соотнесенному объекту

foreach ($article->getComments() as $comment)
{
  $comment->delete();
}

TIP Даже после обращения к методу delete(), объект остается доступным до конца запроса. Чтобы определить, удален ли объект из базы данных, обратитесь к методу isDeleted().

Извлечение записей при помощи первичного ключа

Если вы знаете первичный ключ отдельной записи, используйте метод класса retrieveByPk() однорангового класса, чтобы получить соотнесенный объект.

$article = ArticlePeer::retrieveByPk(7);

Файл schema.yml определяет поля id field как первичный ключ таблицы blog_article, поэтому эта формулировка фактически приведет к переходу на статью под id 7. Поскольку вы использовали первичный ключ, вы должны знать, что лишь одна запись будет возвращена; переменная $article включает объекта класса Article.

В некоторых случаях, первичный ключ может состоять более чем из одного столбца. В таких случаях, метод retrieveByPK() принимает множественные параметры, один для каждого столбца первичного ключа.

Вы также можете выбирать множественные объекты, основанные на своем первичном ключе, путем обращения к созданному методу retrieveByPKs(), который предполагает множество первичных ключей в качестве параметра.

Извлечение записей при помощи Criteria

Если вы хотите извлечь более, чем одну запись, вам нужно обратиться к методу doSelect() однорангового класса, соответствующему объектам, которые вы хотите извлечь. К примеру, чтобы извлечь объекты класса Article, запросите ArticlePeer::doSelect().

Первый параметр метода doSelect() - это объект класса Criteria, по сути, обычный класс запроса оператора, определенный без SQL для абстракции базы данных.

Пустой Criteria извлекает все объекты класса. К примеру, код, показанный в Листинге 8-12, извлекает все статьи.

Листинг 8-12 – Извлечение записей Criteria с помощью doSelect()--Пустой Criteria

$c = new Criteria();
$articles = ArticlePeer::doSelect($c);

// Will result in the following SQL query
SELECT blog_article.ID, blog_article.TITLE, blog_article.CONTENT,
       blog_article.CREATED_AT
FROM   blog_article;

Для более сложного выбора, вам нужен эквивалент WHERE, ORDER BY, GROUP BY, и других SQL-операторов. У объекта Criteria есть методы и параметры для всех этих условий. Например, чтобы получить все комментарии, написанные Стивом (Steve), по дате, создайте Criteria как показано в Листинге 8-13.

Листинг 8-13 – Извлечение записей при помощи Criteria с doSelect()--Criteria с условиями

$c = new Criteria();
$c->add(CommentPeer::AUTHOR, 'Steve');
$c->addAscendingOrderByColumn(CommentPeer::CREATED_AT);
$comments = CommentPeer::doSelect($c);

// Will result in the following SQL query
SELECT blog_comment.ARTICLE_ID, blog_comment.AUTHOR, blog_comment.CONTENT,
       blog_comment.CREATED_AT
FROM   blog_comment
WHERE  blog_comment.author = 'Steve'
ORDER BY blog_comment.CREATED_AT ASC;

Постоянные класса, переданные методам add() как параметры, относятся к именам собственным. Они получают названия от версий имен столбцов, написанных с большой буквы. К примеру, чтобы обратиться к стыкуемому столбцу таблицы blog_article,используйте постоянную класса ArticlePeer::CONTENT.

NOTE Зачем использовать CommentPeer::AUTHOR вместо blog_comment.AUTHOR, как сделать так, чтобы он в любом случае выводился в SQL-запросе? Предположим, вам нужно изменить имя авторского поля на источник базы данных. Если бы вы использовали _comment.AUTHOR, вам нужно было изменять его каждый раз при обращении к модели. С другой стороны, используя CommentPeer::AUTHOR, вам просто нужно изменить имя столбца в файле schema.yml, сохранить phpName как AUTHOR, и перестроить модель.

Таблица 8-1 сопоставляет синтаксис SQL с объектным синтаксисом Criteria

Таблица 8-1 – Синтаксис SQL и объектный синтаксис Criteria

SQL                                                        |  Criteria
-----------------------------------------------------------------------------------------------------------
WHERE column = value                                       |  ->add(column, value);
WHERE column <> value                                      |  ->add(column, value, Criteria::NOT_EQUAL);

Other Comparison Operators

> , <                                                      |  Criteria::GREATER_THAN, Criteria::LESS_THAN
>=, <=                                                     |  Criteria::GREATER_EQUAL, Criteria::LESS_EQUAL
IS NULL, IS NOT NULL                                       |  Criteria::ISNULL, Criteria::ISNOTNULL
LIKE, ILIKE                                                |  Criteria::LIKE, Criteria::ILIKE
IN, NOT IN                                                 |  Criteria::IN, Criteria::NOT_IN

Other SQL Keywords

ORDER BY column ASC                                        |  ->addAscendingOrderByColumn(column);
ORDER BY column DESC                                       |  ->addDescendingOrderByColumn(column);
LIMIT limit                                                |  ->setLimit(limit)
OFFSET offset                                              |  ->setOffset(offset)
FROM table1, table2 WHERE table1.col1 = table2.col2        |  ->addJoin(col1, col2)
FROM table1 LEFT JOIN table2 ON table1.col1 = table2.col2  |  ->addJoin(col1, col2, Criteria::LEFT_JOIN)
FROM table1 RIGHT JOIN table2 ON table1.col1 = table2.col2 |  ->addJoin(col1, col2, Criteria::RIGHT_JOIN)

TIP Наилучший способ узнать и понять, какие методы в созданных классах доступны - это посмотреть основные файлы в папке lib/model/om/ после создания. Названия методов довольно очевидны, но если вам нужно больше пояснений к ним, установите параметр propel.builder.addComments как true в файле config/propel.ini и перестройте модель.

Листинг 8-14 приводит другой пример Criteria с множественными условиями. Он извлекает все комментарии Стива (Steve) к статьям, содержащие слово "enjoy," по дате.

Листинг 8-14 – Другой пример извлечения записей с помощью Criteria с doSelect()—Criteria с условиями

$c = new Criteria();
$c->add(CommentPeer::AUTHOR, 'Steve');
$c->addJoin(CommentPeer::ARTICLE_ID, ArticlePeer::ID);
$c->add(ArticlePeer::CONTENT, '%enjoy%', Criteria::LIKE);
$c->addAscendingOrderByColumn(CommentPeer::CREATED_AT);
$comments = CommentPeer::doSelect($c);

// Will result in the following SQL query
SELECT blog_comment.ID, blog_comment.ARTICLE_ID, blog_comment.AUTHOR,
       blog_comment.CONTENT, blog_comment.CREATED_AT
FROM   blog_comment, blog_article
WHERE  blog_comment.AUTHOR = 'Steve'
       AND blog_article.CONTENT LIKE '%enjoy%'
       AND blog_comment.ARTICLE_ID = blog_article.ID
ORDER BY blog_comment.CREATED_AT ASC

Поскольку SQL - это простой язык, который позволяет вам строить очень сложные запросы, объект Criteria может оперировать условиями на любом уровне сложности. Но поскольку многие разработчики думают сначала об SQL перед преобразованием условия в объектно-ориентированную логику, объект Criteria поначалу может быть сложен для понимания. Наилучшим способом понять его является изучение при помощи примерах и моделях конкретного применения. На веб-сайте проекта symfony , к примеру, полно примеров построения Criteria, которые обучат вас во многих отношениях.

Вдобавок к методу doSelect(), каждый одноранговый класс имеет метод doCount(), который просто подсчитывает число записей, удовлетворяющих критерии, передаваемых в качестве параметров, и возвращает подсчет в виде целого числа. Поскольку нет объекта для возврата, процесс hydrating в этом случае не происходит, и метод doCount() быстрее, чем doSelect().

Одноранговые классы также предвидят методы doDelete(), doInsert(), и doUpdate() все они подразумевают Criteria как параметр. Эти методы позволяют вам издавать запросы DELETE, INSERT, и UPDATE в вашу базу данных. Проверьте созданные одноранговые классы в вашей модели для получения более детальной информации по данным методам Propel.

В конце концов, если вам просто нужен возврат первого объекта, замените doSelect() обращением doSelectOne()l. Это может быть тот случай, когда вы знаете, что Criteria вернет лишь один результат и преимущество в том, что этот метод возвращает объект лучше, чем множество объектов.

TIP Если запрос doSelect() возвращает большое количество результатов, вам может понадобиться отобразить только их подмножество в вашем результате. Symfony provides a pager class called sfPropelPager, which automates the pagination of results. Зайдите на страницу документации http://www.symfonyproject.org/cookbook/trunk/pager, чтобы получить больше информации и примеров использования.

Использование необработанных(сырых) SQL-запросов

Иногда вам не нужно извлекать объекты, но вы хотите получить комплексные результаты, подсчитанные базой данных. К примеру, чтобы получить все последние по времени создания статьи, не имеет смысла извлекать все статьи и замыкать множество в цикл. Вы предпочтете запросить базу данных на возврат лишь одного результата, так как это пропустит объектный процесс hydrating.

С другой стороны, вам не нужно обращаться к командам PHP для явного управления базой данных, потому как тогда вы потеряете преимущество абстракции базы данных. Это значит, что вам нужно обойти ORM (Propel) но не абстракцию базы данных (Creole).

Запрос к базе данных при помощи Creole требует от вас выполнения следующих действий: 1. Получить соединение базы данных. 2. Построить строку запроса 3. Из нее создать оператор 4. Повторить множество результата, что следует из выполнения оператора.

Если для вас это выглядит полной тарабарщиной, код в Листинге 8-15, возможно, будет более понятным.

Листинг 8-15 – Обычный SQL-запрос при помощи Creole

$connection = Propel::getConnection();
$query = 'SELECT MAX(%s) AS max FROM %s';
$query = sprintf($query, ArticlePeer::CREATED_AT, ArticlePeer::TABLE_NAME);
$statement = $connection->prepareStatement($query);
$resultset = $statement->executeQuery();
$resultset->next();
$max = $resultset->getInt('max');

Подобно выборкам Propel, запросы Creole довольно-таки сложны, когда вы впервые начинаете пользоваться ими. Опять же, примеры из существующих приложений и учебных пособий покажут вам, что к чему.

Если вы склоняетесь к тому, чтобы обойти этот процесс и напрямую обратиться к базе данных, вы рискуете утратить безопасность абстракцию, которые обеспечивает Creole. Конечно, выполнение этого путем Creole длиннее, но это заставляет вас нарабатывать хорошую практику, выполняя действия, которые гарантируют выполнение, мобильность и защиту вашего приложения. Особенно это истинно для запросов, которые включают в себя параметры из ненадежных источников (такие, как пользователь Internet). Creole выполняет все необходимые переходы и обеспечивает защиту вашей базы данных. Обращение к базе данных немедленно ставит вас под угрозу атак типа SQL-injection.

Использование специальных столбцов даты

В большинстве случаев, если в таблице есть столбец под названием created_at, это значит, что он хранился во временной метке даты, когда была сделана запись. Тоже самое относится и к столбцам updated_at, которые должны обновляться всякий раз, когда обновляется сама запись, к значению текущего времени.

Хорошо то, что symfony распознает названия этих колонок и обрабатывает для вас их обновления. Вам не придется вручную настраивать столбцы created_at и updated_at s; они обновятся автоматически, как показано в Листинге 8-16. Тоже самое касается и столбцов named created_on и updated_on.

Листинг 8-16 – Столбцы created_at и updated_at автоматически соединяются

$comment = new Comment();
$comment->setAuthor('Steve');
$comment->save();

// Show the creation date
echo $comment->getCreatedAt();
  => [date of the database INSERT operation]

Кроме того, механизмы установки для столбцов дат принимают формат даты как независимую переменную:

echo $comment->getCreatedAt('Y-m-d');

Соединения базы данных

Модель данных не зависит от используемой базы данных, но вы определенно будете использовать базу данных. Минимум информации, необходимой symfony для посылки запросов к проектной базе данных составляет имя, коды доступа, и тип базы данных. Эти настройки подключения нужно зафиксировать в файле databases.yml, расположенном в директории config/. Листинг 8-17 приводит Листинг такого файла.

Листинг 8-17 – Пример настроек соединения базы данных в myproject/config/databases.yml

prod:
  propel:
    param:
      hostspec:           mydataserver
      username:           myusername
      password:           xxxxxxxxxx

all:
  propel:
    class:                sfPropelDatabase
    param:
      phptype:            mysql     # Database vendor
      hostspec:           localhost
      database:           blog
      username:           login
      password:           passwd
      port:               80
      encoding:           utf8      # Default charset for table creation
      persistent:         true      # Use persistent connections

Настройки соединения зависят от режима. Вы можете определить четкие настройки для режимов prod, dev или любого другого режима в вашем приложении. Эта конфигурация также может быть подменена при применении путем установки разных значений в специфическом для приложения файле, таком как в apps/myapp/config/databases.yml. К примеру, вы можете использовать этот подход для того, чтобы иметь другую политику безопасности для фронтального и внутреннего приложения, и определить нескольких пользователей базы данных с разными правами доступа к вашей базе данных для оперирования с ней.

Для каждого режима вы можете задать разные соединения. Каждое соединение ссылается на схему, помеченную тем же именем. В листинге 8-17, соединение propel ссылается на схему propel в Листинге 8-3.

Допустимые значения параметра phptype это те значения систем баз данных, которые поддерживаются Creole: * mysql sqlserver pgsql sqlite oracle

hostspec, database, username, and password это обычные настройки соединения базы данных. Они также могут писаться более кратко, чем имя источника данных (DSN). Листинг 8-18 эквивалентен всей части Листинга 8-17.

Листинг 8-18 – Условное обозначение настроек соединения базы данных

all:
  propel:
    class:          sfPropelDatabase
    param:
      dsn:          mysql://login:passwd@localhost/blog

Если вы используете базу данных SQLite, параметр hostspec должен быть указан в пути к файлу базы данных. К примеру, если вы храните базу данных своего блога в data/blog.db, файл databases.yml будет выглядеть как Листинг 8-19.

Листинг 8-19 - Настройки соединения базы данных для SQLite

all:
  propel:
    class:          sfPropelDatabase
    param:
      phptype:  sqlite
      database: %SF_DATA_DIR%/blog.db

Расширение Модели

Созданные методы модели замечательны, но часто они бывают недостаточными. Как только вы применяете свою собственную бизнес-логику, Вам нужно ее расширить или посредством добавления новых методов, или подменив уже существующие.

Добавление новых методов

Вы можете добавить новые методы к незаполненным моделям класса, созданным в директории lib/model/. Используйте $this для запроса методов текущего объекта, и используйте self::, чтобы запросить статические методы текущего класса. Помните, что обычные классы наследуют методы классов Base, расположенных в директории lib/model/om/.

Например, для объекта Article, созданного на основе Листинга 8-3, вы можете добавить волшебный метод __ toString ()таким образом, чтобы отклик объекта класса Article отобразил его название, как показано в Листинге 8-20.

Листинг 8-20 - Настройка Модели в lib/model/Article.php:

<?php

class Article extends BaseArticle
{
  public function __toString()
  {
    return $this->getTitle();  // getTitle() is inherited from BaseArticle
  }
}

Также вы можете расширять одноранговые классы - например, добавить метод, чтобы извлечь все статьи в порядке согласно дате их создания, как показано в Листинге 8-21.

Листинг 8-21 - Настройка модели в lib/model/ArticlePeer.php:

<?php

class ArticlePeer extends BaseArticlePeer
{
  public static function getAllOrderedByDate()
  {
    $c = new Criteria();
    $c->addAscendingOrderByColumn(self::CREATED_AT);
    return self::doSelect($c);
  }
}

Новые методы доступны так же, как и созданные, как показано в Листинге 8-22.

Листинг 8-22 - Использование обычных методов модели похоже на использование созданных методов.

foreach (ArticlePeer::getAllOrderedByDate() as $article)
{
  echo $article;      // Will call the magic __toString() method
}

Подмена существующих методов

Если некоторые из созданных методов в классах Base не соответствуют вашим требованиям, вы все еще можете подменить их в обычных классах. Просто нужно удостовериться, что вы используете ту же самую сигнатуру метода (то есть, то же самое число независимых переменных).

Например, метод $article->getComments() возвращает массив объектов Comment вразброс. Если вам нужны результаты, размещенные в порядке согласно дате создания данных, и чтобы последний комментарий шел первым, тогда подмените метод getComments (), как показано в Листинге 8-23. Знайте, что оригинал метода getComments () (найденный в lib/model/om/BaseArticle.php) предполагает значение критериев и значения соединения в качестве параметров, таким образом, ваша функция должна делать то же самое.

Листинг 8-23 - Подмена существующих методов модели в lib/model/Article.php:

public function getComments($criteria = null, $con = null)
{
  if (is_null($criteria))
  {
    $criteria = new Criteria();
  }
  else
  {
    // Objects are passed by reference in PHP5, so to avoid modifying the original, you must clone it
    $criteria = clone $criteria;
  }
  $criteria->addDescendingOrderByColumn(CommentPeer::CREATED_AT);

  return parent::getComments($criteria, $con);
}

В конечном счете, обычный метод запрашивает другой из родительского класса Base, и это - отличный прием. Однако, вы можете полностью обойти его и вернуть именно такой результат, который вы хотите.

Использование поведения модели

Некоторые модификации модели являются универсальными и могут использоваться неоднократно. Например, методы, что позволяют подвергать объекты модели сортировке и устанавливать оптимистическую блокировку, что поможет предотвратить конфликты между параллельными сохранениями объектов; характерные для определенного класса расширения, которые можно добавлять ко многим классам.

Symfony помещает эти расширения в характер изменения. Характеры изменения – это внешние классы, которые обеспечивают классы модели дополнительными методами. Классы модели уже содержат методы, и symfony знает, как расширить их посредством sfMixer (подробнее см. Главу 17).

Чтобы активировать характеры изменения в ваших классах модели, вы должны изменить один параметр настройки в файле config/propel.ini:

propel.builder.AddBehaviors = true     // Default value is false

В symfony по умолчанию нет в наличии характеров изменения, но они могут быть установлены через плагины. Как только плагин характера изменения установлен, вы можете назначить классу характер изменения всего одной строкой. Например, если Ввы устанавливаете плагин sfPropelParanoidBehavior в вашем приложении, вы можете расширить класс Article при помощи этого характера изменения, добавляя нижеследующее в конец Article.class.php:

sfPropelBehavior::add('Article', array(
  'paranoid' => array('column' => 'deleted_at')
));

После восстановления модели удаленные объекты артикля останутся в базе данных, невидимой для запросов, осуществляемых при помощи ORM, если только вы временно не отключите характер изменения с помощью sfPropelParanoidBehavior::disable(). Проверьте список плагинов symfony в wiki, чтобы найти характеры изменения (http://trac.symfony-project.com/wiki/SymfonyPlugins#Propelbehaviorplugins). У каждого из них есть своя документация и руководство по установке.

Расширенный синтаксис схемы

Файл schema.yml может быть простым, как показано в Листинге 8-3. Но реляционные модели часто сложны. Вот почему у схемы такой обширный синтаксис, способный оперировать почти в любой ситуации.

Атрибуты

Соединения и таблицы могут иметь определенные атрибуты, как показано в Листинге 8-24. Они установлены под ключом _attributes.

Листинг 8-24 - Атрибуты соединений и таблиц.

propel:
  _attributes:   { noXsd: false, defaultIdMethod: none, package: lib.model }
  blog_article:
    _attributes: { phpName: Article }

Возможно, вы захотите, чтобы ваша схема была проверена перед генерацией кода. Для этого нужно деактивировать атрибут noXSD для соединения. Соединение также поддерживает атрибут defaultIdMethod. Если ни один из них не предусмотрен, то будет использоваться «родной» метод генерации ID - например, autoincrement для MySQL, или sequences для PostgreSQL. Другие возможные величины равны none.

Атрибут package похож на пространство имён; он определяет путь, где сохранены созданные классы. По умолчанию он ведет к lib/model/, но вы можете изменить его, чтобы организовать вашу модель в подмодули. Например, если вы не хотите путать основные деловые классы, и классы, определяющие сохраненный базой данных механизм статистики в той же директории, то определите две схемы с помощью модулей lib.model.business и lib.model.stats.

Вы уже видели атрибут таблицы phpName, используемый для установки имени созданного класса, который отображает таблицу.

Таблицы, которые содержат локализированный контент (то есть, несколько версий контента в соотносимой таблице, для локализации) также приобретают два дополнительных атрибута (подробнее см. Главу 13), как показано в Листинге 8-25.

Листинг 8-25 – Атрибуты для таблиц i18n.

propel:
  blog_article:
    _attributes: { isI18N: true, i18nTable: db_group_i18n }

Детали Столбца

Базовый синтаксис предоставляет вам два выбора: позволить symfony выводить характеристики столбца из его имени (задавая пустые значения) или определить тип с одним из типичных ключевых слов. Листинг 8-26 демонстрирует эти варианты.

Листинг 8-26 – Основные атрибуты столбца

propel:
  blog_article:
    id:                 # Let symfony do the work
    title: varchar(50)  # Specify the type yourself

Но для столбца вы можете определить намного больше. Если это так, то вам нужно определить параметры настройки столбца как ассоциативное множество, как показано в Листинге 8-27.

Листинг 8-27 - Сложные атрибуты столбца

propel:
  blog_article:
    id:       { type: integer, required: true,primaryKey: true, autoIncrement: true }
    name:     { type: varchar(50), default: foobar, index: true }
    group_id: { type: integer, foreignTable: db_group,foreignReference: id, onDelete: cascade }

Параметры столбца следующие:

  • type: Тип столбца. Варианты: boolean, tinyint, smallint, integer, bigint, double, float, real, decimal, char, varchar(size), longvarchar, date, time, timestamp, bu_date, bu_timestamp, blob, и clob.
  • required: Булево. Установите параметр на true, если вы хотите, чтобы столбец был необходим.
  • default: По умолчанию.
  • primaryKey: Булево. Установите на true для первичных ключей.
  • autoIncrement: Булево. Установите на true для столбцов типа integer, которые должны приобретать значение автоматического приращения.
  • sequence: Последовательность имен для базы данных используя последовательность для столбцов autoIncrement (например PostgreSQL и Oracle).
  • index: Булево. Установите на true, если вы хотите, чтобы в столбце образовался простой индекс или на unique, если вам нужен уникальный индекс.
  • foreignTable: Имя таблицы, используемое для создания внешнего ключа для другой таблицы.
  • foreignReference: Имя смежного столбца, при условии если внешний ключ определяется через foreignTable.
  • onDelete: Устанавливает действие на триггер, когда запись, относящаяся к таблице, удалена. Если установлен на setnull, столбец внешнего ключа установлен на null. Когда установлен на cascade, запись удаляется. Если механизм базы данных не поддерживает установленный характер изменения, ORM эмулирует его. Это уместно только для столбцов, имеющих foreignTable и foreignReference
  • isCulture: Булево. Установлен на true для усовершенствованных столбцов в таблицах с локализованным контентом (см. Главу 13).

Внешние ключи

В качестве альтернативы атрибутам столбца foreignTable и foreignReference, вы можете добавить внешние ключи под foreignKeys: ключ в таблице. Схема в Листинге 8-28 создаст внешний ключ в столбце userid, сопоставляемый id столбцу в таблице blog_user.

Листинг 8-28 - Альтернативный синтаксис внешнего ключа.

propel:
  blog_article:
    id:
    title:   varchar(50)
    user_id: { type: integer }
    _foreignKeys:
      -
        foreignTable: blog_user
        onDelete:     cascade
        references:
          - { local: user_id, foreign: id }

Альтернативный синтаксис пригоден для внешних многократно обращаемых ключей и задает внешним ключам имя, как показано в Листинге 8-29.

Листинг 8-29 - Альтернативный синтаксис внешнего ключа, примененный к внешнему многократно обращаемому ключу.

_foreignKeys:
      my_foreign_key:
        foreignTable:  db_user
        onDelete:      cascade
        references:
          - { local: user_id, foreign: id }
          - { local: post_id, foreign: id }

Индексы

В качестве альтернативы атрибуту столбца Index, можно добавить индексы под indexes: ключ в таблице. Если вы хотите установить уникальные индексы, нужно использовать uniques: заголовок вместо него. Листинг 8-30 показывает альтернативный синтаксис для индексов.

Листинг 8-30 - Индексы и альтернативные индексы уникального синтаксиса

propel:
  blog_article:
    id:
    title:            varchar(50)
    created_at:
    _indexes:
      my_index:       [title, user_id]
    _uniques:
      my_other_index: [created_at]

Пустые Столбцы

Встречая столбец без значения, symfony, взмахнув волшебной палочкой, автоматически сам добавит собственное значение. См. Листинг 8-31 для деталей, добавленных к пустым столбцам.

Листинг 8-31 - Детали столбца, выведенные из имени столбца

// Empty columns named id are considered primary keys
id:         { type: integer, required: true, primaryKey: true, autoIncrement: true }

// Empty columns named XXX_id are considered foreign keys
foobar_id:  { type: integer, foreignTable: db_foobar, foreignReference: id }

// Empty columns named created_at, updated at, created_on and updated_on
// are considered dates and automatically take the timestamp type
created_at: { type: timestamp }
updated_at: { type: timestamp }

Для внешних ключей symfony находит таблицу, имеющую тот же самый phpName,как и начало имени столбца, и если он что-то найдет, то он возьмет это имя таблицы как foreignTable.

Таблицы I18n

Symfony поддерживает локализацию контента в соотносимых таблицах. Это означает, что если у вас есть контент, подлежащий локализации, то он будет сохранен в двух отдельных таблицах: одна с постоянными столбцами и другая - с локализованными столбцами.

В файле schema.yml, все становится явным, если вы называете таблицу foobar_i18n. Например, схема, показанная в Листинге 8-32, будет автоматически завершена столбцами и атрибутами таблицы для того, чтобы привести механизм локализованного контента в работу. Внутри symfony поймет это так, как будто это было написано так же, как и Листинг 8-33. Глава 13 расскажет вам о i18n больше.

Листинг 8-32 – Неявный механизм i18n

propel:
  db_group:
    id:
    created_at:

  db_group_i18n:
    name:        varchar(50)

Листинг 8-33 – Явный механизм i18n.

propel:
  db_group:
    _attributes: { isI18N: true, i18nTable: db_group_i18n }
    id:
    created_at:

  db_group_i18n:
    id:       { type: integer, required: true, primaryKey: true,foreignTable: db_group, foreignReference: id, onDelete: cascade }
    culture:  { isCulture: true, type: varchar(7), required: true,primaryKey: true }
    name:     varchar(50)

Вне schema.yml: schema.xml

Фактически, формат schema.yml является внутренним для symfony. Когда вы вызываете Propel-команду, symfony фактически преобразовывает этот файл в созданный файл schema.xml, который является ожидаемым Propel типом файла, чтобы точно выполнить задачи по модели.

Файл schema.xml содержит ту же самую информацию, что и ее эквивалент YAML. Например, Листинг 8-3 обращен в файл XML, показанный в Листинге 8-34.

Листинг 8-34 - Образец schema.xml, соответствующий Листингу 8-3.

<?xml version="1.0" encoding="UTF-8"?>
 <database name="propel" defaultIdMethod="native" noXsd="true" package="lib.model">
    <table name="blog_article" phpName="Article">
      <column name="id" type="integer" required="true" primaryKey="true"autoIncrement="true" />
      <column name="title" type="varchar" size="255" />
      <column name="content" type="longvarchar" />
      <column name="created_at" type="timestamp" />
    </table>
    <table name="blog_comment" phpName="Comment">
      <column name="id" type="integer" required="true" primaryKey="true"autoIncrement="true" />
      <column name="article_id" type="integer" />
      <foreign-key foreignTable="blog_article">
        <reference local="article_id" foreign="id"/>
      </foreign-key>
      <column name="author" type="varchar" size="255" />
      <column name="content" type="longvarchar" />
      <column name="created_at" type="timestamp" />
    </table>
 </database>

Описание формата schema.xml можно найти в документации и разделах "Начало Работы" на сайте проекта Propel. (http://propel.phpdb.org/docs/user_guide/chapters/appendices/AppendixB-SchemaReference.html).

Формат YAML был разработан для того, чтобы делать схемы простыми для чтения и написания, но минус в том, что самые сложные схемы не могут быть описаны файлом schema.yml. С другой стороны, формат XML дает возможность полного описания схемы независимо от ее сложности, и включает в себя базу данных зависящих от поставщика параметров, наследование таблицы, и так далее.

Symfony понимает даже схемы, написанные в формате XML. Так что если ваша схема слишком сложна для синтаксиса YAML, или если у вас есть существующая схема XML, или если вы уже знакомы с Propel синтаксисом XML, Вам не нужно переключаться на YAML синтаксис symfony. Разместите ваш файл schema.xml в проект директории config/, постройте модель, и – поехали!

Не создавайте модель дважды

Как уступку в использовании ORM вы должны определить структуру данных дважды: один раз для базы данных, и один раз для объектной модели. К счастью, symfony предлагает инструменты командной строки, для создания одной структуры, основанной на другой; таким образом, вы можете избежать двойной работы.

Построение структуры базы данных SQL, основанной на существующей схеме

Если вы запускаете ваше приложения при помощи файла schema.yml, symfony может сформировать SQL- запрос, который создает таблицы непосредственно из модели данных YAML. Чтобы воспользоваться запросом, зайдите в вашу корневую директорию проекта, и наберите следующее:

> symfony propel-build-sql

Файл lib.model.schema.sql будет создан в myproject/data/sql/. Отметьте, что созданный код SQL будет оптимизирован для системы базы данных, определенной в параметре phptype файла propel.ini.

Для создания таблицы вы можете использовать непосредственно файл schema.sql. Например, в MySQL наберите следующее:

> mysqladmin -u root -p create blog
> mysql -u root -p blog < data/sql/lib.model.schema.sql

Созданный SQL также пригоден для восстановления базы данных в другом режиме, или для изменения на другую DBMS. Если параметры настройки соединения определены должным образом в вашем propel.ini, вы даже можете использовать команду symfony propel-insert-sql, чтобы сделать это автоматически.

TIP Командная строка также предлагает задачу наполнения вашей базы данных данными, основанными на текстовом файле. См. Главу 16 для дополнительной информации по задаче "Propel-load-data" и прикрепленные файлы YAML.

Создание модели данных YAML из существующей базы данных

Symfony может использовать уровень доступа базы данных Creole, чтобы создать файл schema.yml из существующей базы данных благодаря самоанализу (способность баз данных определять структуру таблиц, в которых они работают). Это может быть особенно полезным тогда, когда вы осуществляете обратное проектирование, или если вы предпочитаете провести сначала работу над базой данных, а потом - над моделью объекта.

Чтобы это сделать, вам нужно удостовериться, что проект файл propel.ini указывает на правильную базу данных и содержит все настройки соединения, а затем вызвать команду " propel-build-schema ":

> symfony propel-build-schema

Совершенно новый файл schema.yml, построенный из вашей структуры базы данных, будет создан в директории config/. Вы можете строить свою модель, основанную на этой схеме.

Команда создания схемы весьма мощная и может прибавить к вашей схеме много информации, зависящей от базы данных. Поскольку формат YAML не оперирует этим видом информации от поставщика, вам нужно образовать схему XML для использования в своих целях. Вы можете сделать это просто - добавить независимую переменную xml к задаче build-schema:

> symfony propel-build-schema xml

Вместо создания файла schema.yml будет создан файл schema.xml, полностью совместимый с Propel и содержащий всю информацию от поставщика. Но имейте в виду, что созданные схемы XML имеют тенденцию быть весьма многословными и трудными для чтения.

Заключение

Symphony использует Propel в качестве ORM и Creole - как слой абстракции базы данных. Это означает, что вы должны описать реляционную схему вашей базы данных в YAML прежде, чем создавать классы объектной модели. Затем, во времени выполнения, используйте методы объекта и одноранговые классы для извлечения информации о записях или ряде записей. Вы можете подменить их и легко расширить модель путем добавления методов к обычным классам. Настройки соединения определены файлом databases.yml, который может поддерживать более чем одно соединение. А командная строка содержит специальные задачи для того, чтобы избежать дупликации описания структуры.

Уровень модели является самым сложным в структуре symfony. Одной из причин этой сложности является то, что обработка данных сама по себе запутанна. Соотносимые вопросы безопасности являются ключевыми для веб-сайта и их нельзя игнорировать. Другая причина заключается в том, что symfony больше подходит для приложений от среднего до большого масштаба в среде предметной области. В таких приложениях автоматизация, обеспеченная моделью symfony, действительно выигрышна во времени, и стоит инвестиций в изучение его свойств.

Так что не медлите с тратой времени на тестирование объектов модели и методов, чтобы целиком и полностью понять их. Устойчивость и расширяемость ваших программ станут великой наградой.