Дневник разработчика

28.08.2010
13:48

Первоначальные сведения об Extbase

О чем это?

В марте 2010 появилась английская версия докуменатции для ознакомления с  Внешняя ссылка открывается в новом окнеExtbase. Наткнулся на нее я лишь недавно, начав плотнее разбираться с программированием расширений для TYPO3. Здесь хочу представить перевод этого документа.

Это ознакомительное руководство написано Franz Ripfel,  franz.ripfel@abezet.de.

Благодарим Jochen Rau, Sebastian Kurfürst и остальных, работающих над доступностью программирования в стиле FLOW3 в TYPO3 v4.

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

Введение

Это – часть большого документа о новом способе создания расширений. Весь документ включает следующие книги:

  • Первоначальные сведения – руководства по написанию расширений на ExtBase и Fluid.
  • Подробное руководство – разъяснения всего необходимого для использования системы.
  • Справочник – все справочные материалы, соглашения и т.п.
  • Справочник разработчика – рассказывает о внутреннем устройстве структуры для людей, программирующих на нем.

Во всех книгах одно и то же введение, поэтому, если вы уже знакомы с ним, – смело пропускайте!

Новый способ создания расширений

Расширения были представлены в TYPO3, начиная с версии 3.5 и стали одной из основных причин гибкости, популярности и широты распространения TYPO3. Но основной класс для дополнений внешнего интерфейса (называемый tslib_pibase, поэтому мы называем расширения старого стиля – расширения в стиле pibase) не менялся с тех пор, как был представлен несколько лет назад.

Но за это время в широко распространенную разработку среди сообщества PHP и TYPO3 вошло множество более продвинутых концепций, в частности принцип дизайна Model-View-Controller ( Внешняя ссылка открывается в новом окнеMVC, Модель-Представление-Поведение). Мы отлично представляем выгоду от использования такой современной парадигмы, дающей лучшую структурированность, направленность, помогающую программистам расширений и помогающую пониманию и восприятию внешних расширений.

Все идеи, представленные в этом руководстве, применяются командами разработчиков TYPO3 v5 / FLOW3, а Extbase обеспечивает поддержку функционала MVC и DDD (Domain Driven Design) из FLOW3. Мы хотим поблагодарить множество людей, вовлеченных в разработку FLOW3, либо поддержку таких функций в TYPO3 v4. Ваше участие делает этот проект по истине общественным проектом.

Важные определения

Здесь хотелось бы дать несколько кратких определений для наиболее употребимых в документации терминов.

Model View Controller (MVC): широко используемая в дизайне схема, определяющая три важные концепции, лежащие в основе всех проектов программного обеспечения – модель данных, отображения данных для пользователя и элементы управления, обеспечивающие взаимодействие с пользователем и потоком данных.

Domain Driven Design (DDD): идея структурированной модели. Идея моделирования реального мира с присутствующими в нем объектами, а также описание их поведения и данных. Акцент на разделении модели и репозиторя для доступа к модели.

FLOW3: следующее поколение технологии PHP, используемой в качестве основы для TYPO3 5.0.

Extbase: возможность использования функциональности MVC и DDD из FLOW3 в TYPO3 v4.

Fluid: движок шаблонирования, разработанный для упрощения использования, гибкости и расширяемости. Он был специально разработан для FLOW3, а затем возможность его использования появилась в TYPO3 v4.

Запуск в работу официального демонстрационного blog_example

Для этого необходимо установить  Внешняя ссылка открывается в новом окнеblog_example и посмотреть на его работу.

Требования

Для Extbase требуется как минимум TYPO3 версии 4.3, а лучше самая новая версия.  Внешняя ссылка открывается в новом окнеЗагрузите его с www.typo3.org и установите как обычно.

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

У нас – одна страница для примера блога  (blogexample), а другая – для тестирования fluid (fluid tester) (EXT: viewhelpertest):

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

Установка необходимых расширений из MVC-Project и добавление дополнений на страницу

Установите расширение blog_example, при этом понадобиться установить следующие расширения: extbase, fluid. Загрузить их можно из Внешняя ссылка открывается в новом окнерепозитория проекта MVC на TYPO3. Сам репозиторий расположен по адресу Внешняя ссылка открывается в новом окнеhttps://svn.typo3.org/TYPO3v4/CoreProjects/MVC/.

Для вывода во внешний интерфейс, вставьте расширение 'A blog example' и установите начальную точку на ту же страницу, куда установили дополнение. При вызове страницы "blogexample", должна появиться страница приветствия blog_example.

Можно добавить дополнение 'Fluid Tester' на другую страницу (смотрите рисунок выше - на fluid tester), чтобы увидеть несколько основных параметров fluid template в действии и получить представление об их работе. Позже мы исследуем также и шаблоны html, созданные посредством  fluid в blog_example.

Поздравляю, теперь мы должны увидеть что-то выводимое из blog_example – первого вашего работающего расширения Extbase!

Проверка функциональности blog_example

Настало время проверить blog_example в действии, попытаться добавить шаблонные данные или создать собственный сетевой журнал, смотрите или добавьте сообщения, комментарии и т.д. Это простейшие действия в сетевом журнале. Проверьте также и создаваемые данные, переключившись в режим списка на странице blogexample (во внутреннем интерфейсе).

Разъяснение важнейших моментов и принципов работы

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

Структура файлов и соглашение об их именовании

Структура директорий Extbase расширений напоминает пакеты FLOW3. Поэтому, соглашение об именовании файлов в основном такое же, как и в FLOW3. Детальное представление о файловой структуре можно получить из раздела "Структура нового расширения", основной документации. А сейчас, просто исследуйте структуру файлов в blog_example, она крайне проста для понимания.

Помните о названии класса, который всегда основан на структуре файлов: название класса Tx_BlogExample_Controller_BlogController происходит из blog_example/Classes/Controller/BlogController.php.

Как TYPO3 узнает, что нужно вставить наше расширение

Волшебство приосходит при вызове Tx_Extbase_Utility_Extension::registerPlugin() в ext_tables.php и Tx_Extbase_Utility_Extension::configurePlugin() в ext_localconf.php. В основном – это дополненная версия старого t3lib_extMgm::addPlugin. Для углубленного понимания, исследуйте метод Tx_Extbase_Utility_Extension::configurePlugin(). Теперь Вы уже должны знать, как найти файл класса, просто прочитав его название, не так ли?

									Tx_Extbase_Utility_Extension::configurePlugin (
									$_EXTKEY,// The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
									'Pi1',// A unique name of the plugin in UpperCamelCase
									array(// An array holding the controller-action-combinations that are accessible
									// The first controller and its first action will be the default
									'Blog' => 'index,show,new,create,delete,deleteAll,edit,update,populate',
									'Post' => 'index,show,new,create,delete,edit,update',
									'Comment' => 'create'
									),
									array(// An array of non-cachable controller-action-combinations (they must already be enabled)
									'Blog' => 'new,edit',
									'Post' => 'new,create,edit'
									)
									);
									
Tx_Extbase_Utility_Extension::configurePlugin()

Обратите внимание на комментарий "// The first controller and its first action will be the default". Мы повторяем: посредством default, TYPO3/Extbase вызовет первый контроллер в этом массиве, а затем – первое заданное действие в списке действий. Запомните это для своих расширений.

Особенно важно решить, какие части должны кешироваться (объект USER), а какие – нет (объект USER_INT). Решить это можно на подобие традиционных дополнений внешнего интерфейса. Новая и удобнейшая вещь в Extbase, то, что Вы можете определить комбинацию контроллера и действия, которые должны кешироваться. На практике, все действия по изменения данных в репозитории (который является вашей точкой доступа к базе данных) не должны кешироваться. Все методы не для кеширования также помещаются во второй массив.

Посредством Extbase, TYPO3 использует автосоздаваемый TypoScript (например, в tt_content.list.20.blogexample_pi1), взгляните на Tx_Extbase_Utility_Extension::configurePlugin(), это идет вразрез традиционному подходу, где по умолчанию вызывается основная функция каждого конкретного дополнения. В tt_content.list.20.blogexample_pi1 можно обнаружить: userFunc = tx_extbase_dispatcher->dispatch. Поэтому диспетчер (dispatcher) через свой метод dispatch (отправка) – главная отправная точка для поиска на предмет того, как и что работает в Extbase. А сейчас мы посмотрим на пример кода, относящегося к созданию собственного расширения в будущем.

Исследование диспетчера журнала (blog controller)

Давайте взглянем на класс Tx_BlogExample_Controller_BlogController. Здесь можно обнаружить целое скопище действий. В настройке (в ext_localconf.php) мы определяем это indexAction(), которое вызывается по умолчанию, если других действий не запрошено. Так как диспетчер определяет, что отобразить и как получить правильные данные для отображения, зачастую здесь можно увидеть код, относящийся к классам для просмотра и репозитория.

									/**
									* Index action for this controller. Displays a list of blogs.
									*
									* @return string The rendered view
									*/
									public function indexAction() {
									$this->view->assign('blogs', $this->blogRepository->findAll());
									}
									
Tx_BlogExample_Controller_BlogController::indexAction()

В одной этой строчке кода содержится крайне многое. Запрашиваются все статьи из blogRepository в виде массива объектов Blog, и все это передается в переменную 'blogs', посредством шаблона fluid. Теперь мы можем последовательно пройти по этим объектам и получить необходимые для отображения во внешнем интерфейсе элементы. Шаблоны html мы рассмотрим повнимательнее позже в следующем разделе.

									/**
									* Updates an existing blog
									*
									* @param Tx_BlogExample_Domain_Model_Blog $blog A not yet persisted clone of the original blog containing the modifications
									* @return void
									*/
									public function updateAction(Tx_BlogExample_Domain_Model_Blog $blog) {
									$this->blogRepository->update($blog);
									$this->flashMessages->add('Your blog has been updated.');
									$this->redirect('index');
									}
									
Tx_BlogExample_Controller_BlogController::updateAction()

Действие updateAction вызывается при сохранении формы редактирования журнала во внешнем интерфейсе. Тремя строчками кода мы обязываем blogRepository обновить текущие данные журнала новыми (отредактированными), передать сообщение в контейнер flashMessages (который будет показан на выводимой), а затем просто перенаправить (реализовано через заголовок http) на indexAction(), что приведет к отображению всего журнала. Что особенно интересно, и, возможно, поразительно – автоматическое создание объекта blog из данных формы. На стороне диспетчера необходимо добавить корректное определение @param в PHPDoc с правильным названием класса журнала.

Здесь мы не будем детально рассматривать это, просто запомните на будущее, что всегда нужно проверять PHPDoc на предмет корректного названия класса, чтобы эта (framework) смогла предоставить правильный объект в качестве параметров при вызове метода. Детально мы обсудим это позже.

Исследование шаблонов html (и структуры папок для них)

Вы уже видели, что папка blog_example/Classes/View пустая? Но позвольте, мы же должны иметь какое-то представление для вывода во внешний интерфейс. Да, мы должны обеспечить внешнее представление и необходимое для этого представление по умолчанию уже имеется в Extbase. Если нам не нужно выводить что-то специальное, мы просто помещаем наши шаблоны html в нужную папку и они автоматически обнаруживаются Extbase. Давайте взглянем внутрь Resources/Private/Templates/Blog. Там находится шаблон html для страницы blogexample, что же возникает в indexAction()?

									<f:layout name="default" />
									<f:section name="content">
									<p>Welcome to the Blog Example! This installation serves as a demonstration for
									basic development techniques used in Extbase/FLOW3 applications.</p>
									<f:if condition="{blogs}">
									<f:then>
									<p>Here is a list of blogs:</p>
									<dl>
									<f:for each="{blogs}" as="blog">
									<dt>
									<f:link.action action="index" controller="Post"	arguments="{blog : blog}">
									{blog.title} (<f:count subject="{blog.posts}" />)
									</f:link.action>
									</dt>
									<dd>
									<f:format.nl2br>{blog.description}</f:format.nl2br>
									<f:link.action action="edit" arguments="{blog : blog}">Edit</f:link.action>
									<f:link.action action="delete" arguments="{blog : blog}">Delete</f:link.action>
									<dd>
									</f:for>
									</dl>
									<p>
									<f:link.action action="new">Create another blog</f:link.action>
									<br /><f:link.action action="populate">Create example data</f:link.action>
									<br /><f:link.action action="deleteAll">Delete all Blogs [!!!]</f:link.action>
									</p>
									</f:then>
									<f:else>
									<p>
									<strong><f:link.action action="new">Create your first blog</f:link.action></strong>
									<br /><f:link.action action="populate">Create example data</f:link.action>
									</p>
									</f:else>
									</f:if>
									</f:section>
									
index.html

Попытайтесь отредактировать текст в нем, очистить кеш внешнего интерфейса и посмотрите, как внесенные изменения отражаются на внешнем интерфейсе. Если изменений не произошло, то либо Вы редактируйте не тот шаблон html, либо это не та страница во внешнем интерфейсе. Начиная с этого момента, необходимо научиться определять какой шаблон html используется во внешнем интерфейсе и в какой части blog_example. Вы поняли взаимосвязь между названиями папок,  файлов диспетчеров и названиями классов?

Blog -> BlogController, Post -> PostController
indexAction -> index.html, newAction -> new.html

Это не просто совпадение, это возможность обеспечения правильной работы без специальной настройки!

Все, помимо части {blogs}, используется для каждой конструкции. Запомнили? В BlogController мы привязали все журналы к точно такой же переменной, и теперь можем получить нужную информацию для нашего шаблона html. Для лучшего понимания использования Fluid, нового движка шаблонирования, смотрите следующий раздел.

Fluid: разъяснение базовых принципов работы шаблонов html

В этой главе вы получите лишь беглое знакомство с Fluid, здесь не дается детальное его описание. Так как наша TYPO3v4 версия Fluid является лишь возможностью применения мощи FLOW3, последний выпуск FLOW3 – то место, где на данный момент можно найти детальную документацию. Смотрите путь для подверсии  https://svn.typo3.org/FLOW3/Packages/Fluid/trunk/Documentation/Manual/DocBook/en/Index.xml (он может поменяться, ввиду развития разработки, но это хорошая подсказка для поиска). Для открытия документации можно воспользоваться XML Editor.

В TYPO3v4, требующем совместимости с PHP 5.2, невозможно использовать область имен, представленную в PHP 5.3. Вот почему декларация области имен не представлена во Fluid. Синтаксис должен сохраняться. Просто сравните шаблоны html из нашего blog_example, с  используемыми во FLOW3.

Вся работа производиться в так называемых помощниках просмотра (view helpers). Для каждого из них существует соответствующий класс PHP. Основные из них можно найти в fluid/Classes/ViewHelpers/. Помните, что соглашение об именовании поможет найти нужный класс, если вы находитесь в шаблоне или знаете, как написать правильный тег xml на основе названия класса. Для проверки кода для тега <f:for>, необходимо взглянуть на fluid/Classes/ViewHelpers/ForViewHelper.php.

Проверьте расширение viewhelpertest, с его дополнением для просмотра некоторых примеров использования доступных помощников просмотра.

 

 

И все же, где запросы к базе данных?

Возможно вы используете для получения или записи данных в базу данных свои собственные отчеты SQL. В Extbase существует новый слой между рабочей логикой и базой данных: репозиторий.

Давайте посмотрим в папку blog_example/Classes/Domain/Model/. Здесь находятся файлы, представляющие объекты нашей модели: Blog, Post, Comment, Tag и т.д. Вместе с папкой Model находится папка Repository, в которой находятся классы с названиями BlogRepository и PostRepository. Оба класса из данных файлов расширяют Tx_Extbase_Persistence_Repository и нашего «контактного лица» для получения и сохранения данных. Это значит, что, по крайней мере в обычной обстановке, нет необходимости писать SQL, нужно просто вызвать соответствующий метод из репозитория. Это поможет сконцентрироваться на логике работы.

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

Так как Extbase все еще находится в разработке, отношения в базе данных, поддерживающиеся на данный момент, ограничены. Следите за группой новостей typo3.projects.typo3v4mvc на news.typo3.org, если вам необходима поддержка пока неподдерживаемых типов отношений.

Получение специфических данных (отображение формы редактирования журнала с текущими данными)

Если Вы уже проглядели BlogController (в том числе упомянутый метод indexAction), как говорилось выше, то возможно заметили, что вместе с вызовом, например $this->blogRepository->findAll(), можно получить готовые объекты. Просмотрите Tx_Extbase_Persistence_Repository, на предмет методов для получения данных из Репозиториев. Сложнее понять, что произойдет, если отредактировать уже созданный журнал:

Контроллер снова потребует небольшого кодирования:

									/**
									* Edits an existing blog
									*
									* @param Tx_BlogExample_Domain_Model_Blog $blog The original blog
									* @return string Form for editing the existing blog
									*/
									public function editAction(Tx_BlogExample_Domain_Model_Blog $blog) {
									$this->view->assign('blog', $blog);
									}
									
Tx_BlogExample_Controller_BlogController::editAction()

Вы видите какой-нибудь код, говорящий о получении данных из базы данных? Здесь какое-то волшебство. editAction() вызывается, так как мы щелкаем по ссылке редактирования (например, index.php?id=3&amp;tx_blogexample_pi1[blog][uid]=17&amp;tx_blogexample_pi1[action]=edit&amp;tx_blogexample_pi1[controller]=Blog&amp;cHash=4b3828862d), при этом пересылается информация о том, что мы хотим редактировать журнал с uid 17. Так как Extbase желает работать с объектами, оно автоматически производит объект Blog и заполняет его определенными данными. С этих пор вам просто нужно захотеть добавить комментарий @param[ClassName_for_object_you_want_to_have_created_from_your_given_id] для информирования Extbase о необходимом объекте. Если хотите покопаться, что же при этом происходит, взгляните на Tx_Extbase_MVC_Controller_ActionController->mapRequestArgumentsToControllerArguments().

Сохранение / удержание данных (сохранение/удержание измененных данных из формы)

Контроллер снова нуждается коде:

									/**
									* Updates an existing blog
									*
									* @param
									Tx_BlogExample_Domain_Model_Blog $blog A not yet persisted clone of the original blog containing the modifications
									* @return void
									*/
									public function updateAction(Tx_BlogExample_Domain_Model_Blog $blog) {
									$this->blogRepository->update($blog);
									$this->flashMessages->add('Your blog has been updated.');
									$this->redirect('index');
									}
									
Tx_BlogExample_Controller_BlogController::updateAction()

Просто нужно сказать своему BlogRepository заменить существующий журнал на свой updatedBlog, остальное делается в пределах схемы. Для правильного получения параметра $updatedBlog, необходимо назвать форму редактирования "updateBlog". Проверьте файл Resources/Private/Templates/Blog/edit.html, где имеется строчка вида <f:form method="post" controller="Blog" action="update" name="updatedBlog" object="{blog}" arguments="{blog: blog}">

Создание новой записи Blog крайне просто. Взгляните в Tx_BlogExample_Controller_BlogController::createAction() и Resources/Private/Templates/Blog/new.html.
Все остальное, вроде добавления сообщений или комментариев, следует той же схеме. Взгляните на контроллеры (Controllers) и шаблоны html, откуда должно быть понятно, что происходит.

Вернемся: создание собственного первого расширения на основе Extbase

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

Давайте начнем – создадим свое первое (и простое) расширение на основе Extbase! Примеры всегда к месту, поэтому, хорошо бы проверить TER или forge на предмет новых расширений на основе  Extbase. Просто введите Extbase и можно найти такие расширения. Один очень простой пример – это efempty от Patrick Lobacher. Спасибо Patrick! Он не привносит функциональности, просто дает базовую структуру для первоначального тестирования.

Обсудите и решите назначение (Specification)

Первый шаг для успешного расширения на основе Extbase (и, мы уверены, для расширений в старом стиле) – составить полную картину того, что расширение должно делать, что по другому называется списком функций.

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

Для первого альфа выпуска нужен список функций, в который входят следующие элементы:

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

Разработка модели с использованием kickstarter

Так как kickstarter находится в разработке, мы использовали стабильный выпуск Revision 28014 (проверьте forge.typo3.org, ключ расширения extbase_kickstarter) для нашего примера. Вы можете проводить тестирование с наиболее свежей версией, но результаты, возможно, будут отличаться от приведенных здесь. Результаты из  kickstarter мы поместили в архив zip (https://svn.typo3.org/TYPO3v4/CoreProjects/MVC/doc_extbase/Documentation/Manual/en/quickstart/resources/). Если появятся сложности с kickstarter, можно воспользоваться этим кодом.

  1. Откройте форму для общих данных расширения.
  2. Обычно здесь вводится entity (объект).  В качестве практического правила, можно сказать: объекты (entity) необходимы для всех объектов, которые нужно хранить в базе данных.
  3. Aggregate root (общий корень) – начальная точка для группы объектов. Все объекты, запрашиваемые из Репозитория, являются общими корнями (aggregate roots).
  4. Создание контроллера (controller) с определенным действием по умолчанию, просто сделайте и увидите, что получиться.
  5. Назначение отношений между объектами, на приведенном рисунке, голубая линия должна соединяться с голубой точкой, для обозначения отношения.
  6. Команды для управления вашими моделями.

Получение первых результатов

Результирующая структура из kickstarter должна быть похожей на приведенный рисунок:

И если это так, установите расширение и создайте тестовую страницу, на которой можно будет поместить наше дополнение во внешний интерфейс.

Просто убедитесь, что TCA было создано правильно, просто добавьте свой первый холодильник (fridge) и немного еды (food) прямо во внутреннем интерфейсе на новой странице (модуль Список -> создать записи). Таким образом, у нас появятся несколько образцовых записей, которые будут позже отображены во внешнем интерфейсе.

Пока не будет финального выпуска Extbase kickstarter, невозможно помочь в проверке правильности структуры файлов и классов. Поэтому мы начнем только лишь с части наших задач, и попытаемся получить нужное во внешнем интерфейсе (в нашем случае, просто список холодильников (fridges). Все остальные функции и объекты мы добавим позже).

Если kickstarter еще не добавил дополнение, мы должны сделать это вручную. Как описано выше для blog_example, мы добиваемся этого через два файла, регистрирующий и настраивающий дополнение. Если не помните, что это значит, посмотрите выше.

									Tx_Extbase_Utility_Extension::registerPlugin(
									$_EXTKEY,
									'Pi1',
									'Fridgemaster'
									);
									
ext_tables.php
									Tx_Extbase_Utility_Extension::configurePlugin(
									$_EXTKEY,
									'Pi1',
									array(
									'Fridge' => 'list,show,new,create,delete,edit,update,addFood,removeFood',
									),
									array(
									'Fridge' => 'new,edit',
									)
									);
									
ext_localconf.php

После очистки кеша, должно быть возможно добавить дополнение на уже сделанную страницу Fridgemaster. Не забудьте установить начальную точку на страницу с дополнение и данными (это, по крайней мере в нашей версии, не устанавливается по умолчанию). Если Вы уже добавили данные через внутренний интерфейс, то во внешнем интерфейсе уже должно что-то выводиться. Шаблон HTML для этого находится в Resources/Private/Template/Fridge/list.html. Попытайтесь его отредактировать для получения изменений во внешнем интерфейсе. Не забудьте очистить кеш.

Как мы теперь знаем, этот контроллер и шаблон в основном правильные, и мы хотим показать наши холодильники в виде списка. Как только мы используем blog_example в качестве  шаблона и проверяем, как там сделан BlogController. Там мы видим, что просто должны получить холодильники (fridges) из репозитория и отдать результирующий объект массив в шаблон и использовать там хороший ViewHelper. Просто добавьте недостающий код к уже существующему FridgeController и шаблону.

									class Tx_Fridgemaster_Controller_FridgeController extends Tx_Extbase_MVC_Controller_ActionController {
									/**
									* @var Tx_Fridgemaster_Domain_Repository_FridgeRepository
									*/
									protected $fridgeRepository;
									/**
									* Initializes the current action
									*
									* @return void
									*/
									public function initializeAction() {
									$this->fridgeRepository = t3lib_div::makeInstance('Tx_Fridgemaster_Domain_Repository_FridgeRepository');
									}
									/**
									* List action
									*
									* @return string The rendered view
									*/
									public function listAction() {
									$this->view->assign('fridges', $this->fridgeRepository->findAll());
									}
									}
									
Tx_Fridgemaster_Controller_FridgeController
									<ul>
									<f:for each="{fridges}" as="fridge">
									<li>{fridge.location}</li>
									</f:for>
									</ul>
									
list.html

Получился список холодильников? Отлично! Если же нет, вернитесь назад и проверьте все по порядку!

Запуск CRUD (create, read, update, delete – создание, чтение, редактирование и удаление)

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

Добавление новых холодильников

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

									<p><f:link.action action="new">Create a new fridge</f:link.action></p>
									
list.html
									/**
									* new action for this controller. Displays a form for a new fridge.
									*
									* @param Tx_Fridgemaster_Domain_Model_Fridge $newFridge A fresh Fridge object not yet in the repository
									* @return string The rendered view
									*/
									public function newAction(Tx_Fridgemaster_Domain_Model_Fridge $newFridge=null) {
									$this->view->assign('newFridge', $newFridge);
									}
									
Tx_Fridgemaster_Controller_FridgeController::newAction()
									<h1>New fridge </h1>
									<p>Edit the information about your fridge below:</p>
									<f:form method="post" controller="Fridge" action="create" name="newFridge" object="{newFridge}">
									<label for="location">Location</label>
									<f:form.textbox property="location" /><br /><br />
									<f:form.submit value="Send" />
									</f:form>
									
new.html

Теперь, после щелчка по ссылке, должна появиться форма для ввода данных по новому холодильнику. Вы же знаете куда нужно поместить new.html, не так ли? Он должен находиться в Resources/Private/Templates/Fridge/new.html.

И опять, просто посмотрите, как написана форма и какой для нее использован ViewHelpers. Важны параметры в f:form. Вы назначаете контроллер и действие (createAction), а также определяете название объекта, заданного в качестве параметра к нашей createAction. Параметр object="" соответствует заполнению формы данными, отсылаемыми из контроллера, что важно при начале проверки данных.
При этом, createAction не остается ничего другого, как информировать fridgeRepository, что он должен добавить новый холодильник и перенаправить в список, в котором он уже будет присутствовать.

									/**
									* Creates a new fridge
									*
									* @param Tx_Fridgemaster_Domain_Model_Fridge $newFridge A fresh Fridge object which has not yet been added to the repository
									* @return void
									*/
									public function createAction(Tx_Fridgemaster_Domain_Model_Fridge $newFridge) {
									$this->fridgeRepository->add($newFridge);
									$this->redirect('list');
									}
									
Tx_Fridgemaster_Controller_FridgeController::createAction()

Правка и редактирование существующих холодильников

Следующим шагом является редактирование существующих холодильников. Для этого нам понадобится соответствующее действие (editAction) и шаблон html (edit.html). Так как удаление является очень простым действием (также нам нужно отправить идентификатор правильного объекта), мы проделаем это за один этап. И снова, blog_example используется в качестве образца того, что мы хотим получить.

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

									<ul>
									<f:for each="{fridges}" as="fridge">
									<li>{fridge.location}
									<f:link.action action="edit" arguments="{fridge : fridge}">Edit</f:link.action>
									<f:link.action action="delete" arguments="{fridge : fridge}">Delete</f:link.action>
									</li>
									</f:for>
									</ul>
									<p><f:link.action action="new">Create a new fridge</f:link.action></p>
									

Давайте сделаем два новых действия. Так как удаление проще, начнем с него.

									/**
									* Deletes an existing Fridge
									*
									* @param Tx_Fridgemaster_Domain_Model_Fridge $fridge The fridge, which is to be deleted
									* @return void
									*/
									public function deleteAction(Tx_Fridgemaster_Domain_Model_Fridge $fridge) {
									$this->fridgeRepository->remove($fridge);
									$this->redirect('list');
									}
									
Tx_Fridgemaster_Controller_FridgeController::deleteAction()

Это действительно все, что нужно сделать! Запросить репозиторий об удалении холодильника и вернуться назад к списку. Метод remove уже применяется в API Extbase.
Действие editAction крайне просто:

									/**
									* edit action for this controller. Displays a edit form for the choosen fridge.
									*
									* @param Tx_Fridgemaster_Domain_Model_Fridge $fridge The original fridge
									* @return string The rendered view
									*/
									public function editAction(Tx_Fridgemaster_Domain_Model_Fridge $fridge) {
									$this->view->assign('fridge', $fridge);
									}
									
Tx_Fridgemaster_Controller_FridgeController::editAction()

editAction() выглядит похожим на listAction(). Вспомните то, что должны были усвоить (а если нет, вернитесь) из первой части первоначальных сведений при анализе editAction в BlogController. Мы посылаем весь объект Fridge в шаблон, остальное выполняется в шаблоне.

									<h1>Edit fridge from {fridge.location}</h1>
									<p>Edit the information about your fridge below:</p>
									<f:form method="post" controller="Fridge" action="update" name="updatedFridge" object="{fridge}" arguments="{fridge: fridge}" >
									<label for="location">Location</label>
									<f:form.textbox property="location" /><br /><br />
									<f:form.submit value="Send" />
									</f:form>
									
edit.html

Так как edit.html из blog_example используется как образец, мы помним, что нужно определить действие в качестве цели для сохранения отредактированных данных.

И снова, помните, что мы говорили об атрибутах элемента f:form?
Когда делается подготовка, как в приведенном выше edit.html, необходимо применить updateAction, чтобы быть в состоянии сохранить наши впервые отредактированные данные.

									/**
									* Updates an existing fridge
									*
									* @param Tx_Fridgemaster_Domain_Model_Fridge $fridge The existing, unmodified fridge
									* @param Tx_Fridgemaster_Domain_Model_Fridge $updatedFridge A clone of the original fridge with the updated values already applied
									* @return void
									*/
									public function updateAction(Tx_Fridgemaster_Domain_Model_Fridge $fridge, Tx_Fridgemaster_Domain_Model_Fridge $updatedFridge) {
									$this->fridgeRepository->replace($fridge, $updatedFridge);
									$this->redirect('list');
									}
									
Tx_Fridgemaster_Controller_FridgeController::updateAction()

И все. Мы говорим репозиторию обновить холодильник (Fridge) с новыми данными и снова вернуться к списку. Состояние (сохранение в базе данных) полностью обрабатывается из Extbase.

Детальное представление нашего холодильника

Теперь у нас имеется хорошо работающий пример CRUD, но для полноты картины, Fridgemaster должен уметь добавлять и удалять еду из холодильников. Сейчас, в качестве образца мы используем editAction. Так, при помощи editAction, мы отобразим данные каждого из холодильников – это должно быть просто. Нам нужна ссылка на наши холодильники для получения детального представления холодильника. И эту ссылку мы должны добавить в наш list.html.

									<ul>
									<f:for each="{fridges}" as="fridge">
									<li><f:link.action action="show" arguments="{fridge : fridge}">{fridge.location}</f:link.action>
									<f:link.action action="edit" arguments="{fridge : fridge}">Edit</f:link.action>
									<f:link.action action="delete" arguments="{fridge : fridge}">Delete</f:link.action>
									</li>
									</f:for>
									</ul>
									<p><f:link.action action="new">Create a new fridge</f:link.action></p>
									
list.html
									/**
									* Show action for this controller. Displays a single fridge.
									*
									* @param Tx_Fridgemaster_Domain_Model_Fridge $fridge The choosen fridge
									* @return string The rendered view
									*/
									public function showAction(Tx_Fridgemaster_Domain_Model_Fridge $fridge) {
									$this->view->assign('fridge', $fridge);
									}
									
Tx_Fridgemaster_Controller_FridgeController::showAction()

И, как обычно, нужен шаблон show.html для нашего нового showAction. Здесь нам нужен список всей пищи, добавленной в холодильник.

									<h1>fridge: {fridge.location}</h1>
									<ul>
									<f:for each="{fridge.foods}" as="food">
									<li>{food.name}, <f:format.date>{food.expiryDate}</f:format.date></li>
									</f:for>
									</ul>
									
show.html

Вся информация содержится в объекте fridge (холодильник). Мы получаем всю пищу нашего холодильника в цикле и отображаем нужную информацию. Так как expirydate (срок годности) не строка, а дата, нам нужно ее отформатировать для вывода во внешний интерфейс.

Добавление и удаление пищи

Добавлять пищу нам нужно не только из внутреннего, но и из внешнего интерфейса. Это схоже с комментариями в blog_example. Давайте последовательно рассмотрим это. Сначала измените наш шаблон html, чтобы получить нужную нам форму:

									<h1>fridge: {fridge.location}</h1>
									<ul>
									<f:for each="{fridge.foods}" as="food">
									<li>
									{food.name}, <f:format.date>{food.expiryDate}</f:format.date>
									[<f:link.action action="removeFood" arguments="{fridge:fridge,food:food}">Remove this item</f:link.action>]
									</li>
									</f:for>
									</ul>
									<p>place more food into the fridge:</p>
									<f:form method="post" controller="Fridge" action="addFood" name="newFood" object="{newFood}" arguments="{fridge : fridge}">
									<label for="name">Name of food</label>
									<f:form.textbox property="name" /><br />
									<label for="expiryDate">Expiry date of food</label>
									<f:form.textbox property="expiryDate" /><br /><br />
									<f:form.submit value="send" />
									</f:form>
									
show.html

Мы адаптируем существующий showController для новой задачи по отображению еще и нового объекта пищи в случае проблем с достоверностью. Мы должны определить его для отображения.

									/**
									* Show action for this controller. Displays a single fridge.
									*
									* @param Tx_Fridgemaster_Domain_Model_Fridge $fridge The choosen fridge
									* @param Tx_Fridgemaster_Domain_Model_newFood $newFood A fresh food object taken as a basis for the rendering
									* @return string The rendered view
									*/
									public function showAction(Tx_Fridgemaster_Domain_Model_Fridge $fridge, Tx_Fridgemaster_Domain_Model_newFood $newFood = NULL) {
									$this->view->assign('fridge', $fridge);
									$this->view->assign('newFood', $newFood);
									}
									
Tx_Fridgemaster_Controller_FoodController::showAction()

Метод addFood() уже был создан в kickstarter в объекте Food, поэтому нам нужно лишь использовать его. После чего мы снова переходим к детальному представлению нашего текущего холодильника. Для этого нам нужно передать текущий объект fridge.

Удаление пищи очень схоже. Нужно соответствующее действие и метод внутри объекта fridge. Метод для удаления объекта пищи еще не был создан в  kickstarter для объекта Fridge, поэтому нам нужно его создать. Но это просто и очень похоже на добавление элемента пищи.

									/**
									* Removes a single food item from the Fridge
									*
									* @param Tx_Fridgemaster_Domain_Model_Fridge $fridge Our current Fridge
									* @param Tx_Fridgemaster_Domain_Model_Food $food The food item to be removed
									* @return void
									*/
									public function removeFoodAction(Tx_Fridgemaster_Domain_Model_Fridge $fridge, Tx_Fridgemaster_Domain_Model_Food $food) {
									$fridge->removeFood($food);
									$this->redirect('show', 'Fridge', NULL, array('fridge' => $fridge));
									}
									
Tx_Fridgemaster_Controller_FoodController::removeFoodAction()
									/**
									* Remove a Food
									*
									* @param Tx_Fridgemaster_Domain_Model_Food The Food to remove
									* @return void
									*/
									public function removeFood(Tx_Fridgemaster_Domain_Model_Food $food) {
									$this->foods->detach($food);
									}
									
Tx_Fridgemaster_Domain_Model_Fridge::removeFood()

Исправление свойства expirydate в объекте food в случае проблем

В случае неверной записи правильной даты годности в базу данных, нужна проверка свойства expiryDate в объекте food. Необходимо, чтобы она была типа DateTime.

									/**
									* expiryDate
									* @var DateTime
									*/
									protected $expiryDate;
Tx_Fridgemaster_Domain_Model_Food::expiryDate

Это все. Вы успешно создали свое первое расширение на основе Extbase с некими личинами для внешнего интерфейса, даже со схожими данными.

Поздравляю! За дополнительными сведениями обратитесь к основному руководству Extbase и листу рассылки по Extbase.

Информация отправляется...
Оценка: 4.3/5. Голосов: 6.
Щёлкните мышкой, чтобы изменить оценку
  • 0 Комментарий(ии)

Ваш комментарий

Известить меня, когда кто-нибудь добавит комментарий в этой теме
 

Последние комментарии

Изменения расширения
18.10.2011 15:15

Категории

 
 

Скопируйте эту ссылку в программу для чтения новостей RSS

RSS 0.91Записи
RSS 2.0Записи
 
 
webeffector. система продвижения сайта.
Яндекс цитирования
RU-CENTER. Регистрация доменов. Хостинг
Rambler's Top100
 
Система Orphus