новости
статьи
.технологии

интегрированные среды для работы с PHP

Часть 1. начало работы с тремя популярными интегрированными средами

Еще совсем недавно PHP обычно критиковали за отсутствие поддержки MVC-архитектур. Времена меняются. Сегодня доступен целый ряд интегрированных сред для работы с PHP. В данной серии статей рассматриваются три популярные интегрированные среды для работы с PHP (Zend, symfony и CakePHP), исследуются их сходство и отличия при создании и расширении приложения в каждой из них.

системные требования

Перед началом работы необходимо иметь соответствующую рабочую среду. Требования минимальны:

1. HTTP-сервер, поддерживающий работу с сессиями (предпочтительно mod_rewrite). В данной серии статей используется Apache V1.3 с mod_rewrite.

2. PHP V5.1.4 или старше. Не все рассматриваемые интегрированные среды требуют использования данной версии PHP, но для простоты все они будут использовать одну и ту же установку PHP. В данной серии статей используется PHP V5.2.3.

3. Не очень старая версия MySQL. Поддерживаются и доступны несколько других баз данных, но в данной серии статей и примере приложения используется MySQL V5.0.37.

4. Операционная система, поддерживающая PHP V5.2.4 или старше и MySQL - в данной серии статей используется Linux®. Если вы работаете в Windows®, необходимо выполнить очевидные корректировки, в частности при создании упоминаемых ниже каталогов.

предварительные требования

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

интегрированные среды для "чайников"

Перед рассмотрением выбранных интегрированных сред имеет смысл поговорить о том, что такое интегрированная среда вообще.

Идея интегрированный среды - предложить дизайн, который можно использовать в нескольких приложениях. Все приложения имеют много общего - в частности, какой-либо интерфейс с базой данных, определенный объем логики приложения, что-то, предоставляющее приложение пользователю. Если вы написали много PHP-приложений, вам известно, как это выглядит. Возможно, у вас есть набор функций или класс, осуществляющий чтение и запись данных в базу данных. Возможно, вы использовали какой-либо шаблонный механизм, например Smarty, для управления UI. Определенно вы писали PHP- код, анализирующий предоставляемые данные и принимающий решения на основе этих данных. И если вы написали много приложений, возможно, вы делали одно и то же снова и снова, копируя иногда код из одного приложения в другое.

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

выбор интегрированной среды

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

При выборе интегрированной среды для вашего проекта учитывайте мнение всех принимающих участие в проекте сторон. И при оценке среды держите с ними постоянный контакт.

Если вы думаете о переходе на интегрированную среду, внимательно рассмотрите ваше приложение и задайте себе вопрос, нужна ли ему интегрированная среда. Она не является необходимостью. Корпоративные приложения будут продолжать создаваться без использования интегрированных сред. Поможет ли такая среда проекту? Сэкономит ли время и усилия каждого? Будет ли приложение работать лучше? Обеспечится ли отсутствующая стабильность работы? Если ответом на любой из этих вопросов является "Да", нужно переходить на интегрированную среду. Если ответ на все эти вопросы "Нет", интегрированная среда только усложнит положение дел.

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

- Zend Framework;

- symfony;

- CakePHP.

Они были выбраны по нескольким причинам, но в основном их классификация такова: "Та, о которой знает ваш босс", "Та, которую кто-то уже установил" и "Та, о которой говорят". Я призываю вас исследовать CodeIgniter, Seagull, Web Application Component Toolkit (WACT), PRADO, Zoop, PHP on Trax и многие другие доступные интегрированные среды PHP. Выбор среды - это во многом дело личного вкуса, и во многом он аналогичен выбору языка программирования. Данная серия статей не подскажет вам, какая интегрированная среда лучше или хуже остальных. Там, где интегрированная среда делает что-либо хорошо, она будет названа. Где среда нежелательна, она тоже будет названа. Хотя мы не рассматриваем в нашем обзоре много интегрированных сред, принятый нами подход поможет вам взвесить достоинства и недостатки других интегрированных сред. Вы должны сформировать свое собственное мнение о рассматриваемых интегрированных средах, т.е. о том, какая из них вам нравится, и с какой вы хотите продолжать работать.

Zend Framework

Каждый знает Zend - "The PHP Company". При загрузке и установке PHP этот пакет загружается с Zend, начиная с V3. Кроме распространения PHP Zend Technologies уже много лет предлагает широкую гамму технологий поддержки PHP. Не удивительно, что Zend предлагает интегрированную среду для PHP, ставшую очень популярной (более 2 миллионов загрузок на сегодняшний день). Если ваш босс слышал об интегрированной среде для PHP, наверняка это была Zend Framework.

symfony

Спонсируемая Sensio symfony "предназначена для ускорения процессов создания и поддержки веб-приложений и для замены повторяющихся задач по кодированию мощью, контролем и удовольствием". Интегрированная среда symfony используется по всему миру в многочисленных приложениях корпоративного уровня, возможно, наиболее известных - Askeet and Yahoo! Bookmarks. Есть вероятность, что если кто-то из ваших знакомых установил, использовал или игрался с интегрированной средой для PHP, этой средой была symfony.

CakePHP

Очень много позаимствовавшая у Ruby on Rails, среда CakePHP предназначена для добавления простоты и масштабируемости в интегрированные среды для PHP. CakePHP недавно была выбрана в качестве ядра Mambo Content Management System V5. Популярность CakePHP постоянно растет, подпитываемая энергичным сообществом и быстро растущей пользовательской базой. Если вы подслушали разговор об интегрированных средах для PHP, вероятно это был разговор о CakePHP.

установка

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

Начнем с создания папки для хранения всего исходного кода, интегрированных сред и включаемых файлов, которые понадобятся для работы с данной серией статей (например, каталог под названием /column). В данном каталоге создайте дополнительные подкаталоги: htdocs, protected, include и src. Измените конфигурационный файл Apache так, чтобы корневой каталог документов (document root) указывал на /column/htdocs, и, пока вы здесь (если не сделали этого раньше), разрешите работу модуля mod_rewrite. Измените конфигурационный файл php.ini для включения каталога /column/include/ в директиву include_path. Не беспокойтесь, если это пока не понятно. Все станет ясно при установке интегрированных сред.

установка Zend Framework

Загрузите Zend Framework . В данной статье используется V1.0.1. Загрузите и разархивируйте Zend Framework в каталог /column/src. Дистрибутив будет содержать несколько текстовых файлов и три каталога: demos, tests и library. Скопируйте содержимое каталога library (это должна быть папка под названием Zend) в каталог /column/include/. Теперь должна иметься папка /column/include/Zend, содержащая несколько каталогов и файлов. Это библиотеки, используемые Zend Framework.

Вот и все! Zend Framework установлена.

установка symfony

Загрузите symfony (см. раздел "Ресурсы"). Установить symfony можно любым методом, перечисленным на странице download, хотя для наших целей рекомендуется загрузить tarball. Разархивируйте его в каталог /column/src/symfony/. Должно создаться четыре каталога (data, doc, lib и licenses) и несколько файлов README. Не все это нужно для установки готового приложения, но пока они не помешают. Процесс установки может показаться простым. Помните, что эти интегрированные среды обычно состоят из вспомогательных программ и библиотек, поэтому установить их не трудно. Использование их потребует небольшой настройки. Это станет понятно попозже.

Не каждый имеет возможность использовать PHP Extension и Application Repository (PEAR). Именно поэтому мы советуем метод установки, описанный выше. Однако если вы можете использовать PEAR любым способом, сделайте это. Оба метода описаны на странице symfony installation.

установка CakePHP

Загрузите CakePHP. В данной серии статей используется версия 1.2.0.5427alpha. Ветка 1.2 CakePHP, несмотря на пометку alpha, широко применяется сообществом CakePHP. Загрузите и разархивируйте tarball, помещая содержимое в /column/src/cakephp. Должно создаться четыре каталога (app, cake, doc и vendors) и два файла (.htaccess и index.php). Позже вы переместите некоторые из этих файлов, но пока все нормально.

тестовое приложение

Вы загрузили и установили три интегрированные среды. Но нужно отметить, что вы пока не будете ничего с ними делать, кроме проверки корректности установки при помощи приложения Hello World. Причина этого заключается в том, что мы собираемся сделать нечто более содержательное (но не намного), чем Hello World.

Создание приложения Hello World в интегрированной среде для PHP может быть не простой процедурой. Использование интегрированной среды подразумевает некоторые накладные расходы, и они проявляются, когда вы пытаетесь сделать что-то такое же простое, как Hello World. Но это принесет свои плоды при работе с более сложными приложениями.

Тестовое приложение, которое вы создадите, пока не велико. Оно позволяет передавать текст в приложение, используя текстовую область ввода данных. Этот текст будет сохраняться в базе данных и извлекаться приложением для отображения по запросу. Используя такое приложение, можно легко создавать и обновлять содержимое Web-сайта без необходимости редактирования HTML или загрузки на сервер новых файлов. Это очень новаторское приложение будет называться Blahg.

что представляет из себя приложение Blahg?

На основании описания Blahg вы, наверное, уже сделали вывод о том, как будет разрабатываться приложение. Для Blahg нужны четыре элемента:

1. Страница с формой. Эта форма должна содержать как минимум текстовую область ввода сообщений. Данная страница будет страницей для записи.

2. Страница, читающая сообщение. На эту страницу нужно будет передать какой-либо идентификатор сообщения (Identifier). Эта страница будет страницей для чтения.

3. Страница, на которой перечислены доступные сообщения. Это индексная страница.

4. Таблица базы данных для хранения сообщений. В этой таблице нужно будет хранить как минимум текст сообщения и его идентификатор (возможно, заголовок и дату последнего изменения).

Очевидно, Blahg мог бы быть более сложным, и позднее в данной серии статей вы сделаете несколько усовершенствований. Но пока будем считать, что этих простых требований достаточно. К сожалению, мы не собираемся пока создавать Blahg в какой-либо интегрированной среде. Это произойдет во второй части (EN). А пока самое время настроить используемые базы данных.

настройка баз данных

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

Создайте три базы данных под названием Zend, symfony и Cake и предоставьте все полномочия для каждой базы данных соответствующему пользователю. Приведенные ниже инструкции для каждой интегрированной среды будут содержать SQL-выражения, необходимые для создания таблиц сообщений. Пока базы данных не заполнены, можно создать также таблицы, которые будет первоначально использовать приложение Blahg. Создайте таблицу posts в базах данных Zend и symfony, используя следующий SQL-запрос.

CREATE TABLE 'posts' (
'id' INT( 10 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
'title' VARCHAR( 255 ) NOT NULL ,
'text' TEXT NOT NULL ,
'modified' TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = MYISAM ;


Для Zend и symfony поддержка столбца modified остается за базой данных. Для CakePHP используется слегка другой SQL-запрос. Он позволит Cake выполнить некоторые магические действия (в частности, CakePHP будет автоматически поддерживать столбец modified).

CREATE TABLE 'posts' (
'id' INT( 10 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
'title' VARCHAR( 255 ) NOT NULL ,
'text' TEXT NOT NULL ,
'modified' DATETIME DEFAULT NULL
) ENGINE = MYISAM ;


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

дальнейшие действия

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


Часть 2. создание примера приложения

создание Blahg в Zend Framework


Для начала создайте каталоги /column/htdocs/zend и /column/protected/zend. В них будут храниться доступные для веб файлы и файлы приложения, которые не должны быть доступны в веб, соответственно. В /column/htdocs/zend создайте файл .htaccess со следующими строками:

RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php


В каждой интегрированной среде имеются собственные требования к структуре каталогов. Для Zend Framework необходимо создать каталоги /column/protected/zend/controllers, /column/protected/zend/models и /column/protected/zend/views для хранения контроллеров, моделей и представлений (views). Кроме того, необходимо создать каталог /column/protected/zend/views/scripts, а для каждого контроллера необходимо создать каталог для хранения некоторых представлений. Например, известно, что приложение Blahg должно иметь некоторые представления для контролера post, поэтому нужно создать каталог /column/protected/zend/views/scripts/post для их хранения.

Каталоги созданы. Можно приступить к созданию модели.

создание таблицы posts и модели в Zend

В первой части данной серии статей вы создали таблицу posts. Если вы этот шаг пропустили, вернитесь и выполните его. Модель для этой таблицы будет очень простой. Она будет называться Posts.php, и ее нужно создать в каталоге /column/protected/zend/models/. Posts.php будет расширять класс Zend_Db_Table, и мы будем использовать большую часть унаследованных (inherited) из этого класса методов. Открыв файл Posts.php в архиве исходного кода, вы увидите очень мало реального кода.

создание контроллера Zend Front и PostController

Для контроллера Front мы создадим файл index.php в /column/htdocs/zend. Этот файл является единственной частью приложения, отображаемой внешнему миру. Доступ ко всему остальному осуществляется через этот файл. Контроллер Front должен делать несколько вещей: загружать Zend Loader (чтобы не включать каждую библиотеку вручную), настраивать соединение с базой данных и распределять запросы соответствующим контроллерам приложения; в данном случае это только контроллер PostController.

Как вы, наверное, догадались, мы создадим файл PostController.php в каталоге /column/protected/zend/controllers. Нужный нам PostController является довольно простым.

Для начала, необходимо включить созданную нами ранее модель posts. И, возможно, нужно будет создать метод init, который будет вызываться при создании объекта PostController. В методе init можно настроить объекты представления и модели, которые будут использоваться при последующих действиях. Возможно, вы догадываетесь, что нам необходимы три действия: indexAction, readAction и writeAction (соглашение xxxxxAction по именованию действий является частью работы в среде Zend Framework).

indexAction является простым: принять все сообщения и назначить их представлению. readAction не намного сложнее: принять ID сообщения из запроса, извлечь сообщение и назначить его представлению. writeAction тоже не сложен. При передаче запроса выделяет все теги и сохраняет данные в базе данных. В противном случае визуализирует форму.

Взгляните на синтаксис и структуру PostController в архиве исходного кода. Теперь, после создания контроллеров, можно перейти к представлениям.

создание представлений в Zend

Уже почти все готово. Приложению Blahg нужны несколько представлений, и все они создаются в каталоге /column/protected/zend/views/scripts/posts. Необходимо, как минимум, три представления (index.php, read.php и write.php), хотя в архиве исходного кода используются четыре. Представления - это простые файлы: представления index и read просто отображают данные, назначенные им, применяя минимальное форматирование, а представление write отображает форму, используемую для ввода информации в Blahg. Для получения детальной информации рассмотрите представления в архиве исходного кода.

Если модель, контроллеры и представления созданы правильно (или код установлен правильно), можно попробовать версию Blahg для Zend Framework, перейдя по адресу http://localhost/zend/post (если все установлено локально).

Возможно, вам не терпится поработать с Blahg. Но не слишком увлекайтесь. Есть еще две интегрированные среды.

создание Blahg в symfony

Создание Blahg в symfony немного отличается. Для начала мы должны инициализировать проект, используя программу командной строки symfony.
выполнение команд init в symfony

Перейдите в окно терминала и создайте каталог /column/protected/sf_column, используя командную строку. Это будет защищенный каталог для хранения всего кода symfony для столбца. Вся работа с командной строкой symfony должна осуществляться из этого каталога. Перейдите в каталог sf_column и выполните следующую команду:

php /column/protected/symfony/data/bin/symfony init-project sf_column.

Эта команда создаст массу каталогов и файлов в /column/protected/sf_column, необходимых для проекта symfony. Затем нужно проинициализировать приложение Blahg. В каталоге sf_column выполните следующую команду:

php /column/protected/symfony/data/bin/symfony init-app blahg.

Эта команда создаст дополнительные каталоги и файлы главным образом в каталоге /column/protected/sf_column/apps. Но перед использованием большинства этих файлов, нужно переставить некоторые из них. Помните, что очень мало файлов отображается внешнему миру. Созданная сценариями symfony init структура приложения не знает структуры ваших каталогов. К счастью, это можно исправить, добавив следующие строки в конец файла /column/protected/sf_column/apps/config/config.php.

$sf_root_dir = sfConfig::get('sf_root_dir');
sfConfig::add(array(
'sf_web_dir_name' => $sf_web_dir_name = 'symfony',
'sf_web_dir' => DIRECTORY_SEPARATOR.'column'.DIRECTORY_SEPARATOR.'htdocs'.
DIRECTORY_SEPARATOR.$sf_web_dir_name,
'sf_upload_dir' => DIRECTORY_SEPARATOR.'column'.DIRECTORY_SEPARATOR.
'htdocs'.DIRECTORY_SEPARATOR.$sf_web_dir_name.DIRECTORY_SEPARATOR.
sfConfig::get('sf_upload_dir_name'), ));


По существу эти строки указывают symfony, где найти ваш каталог web.

Теперь, после добавления этих строк, нужно переместить содержимое /column/protected/sf_column/web в /column/htdocs/symfony (создайте этот каталог, если он отсутствует). И последнее: нужно изменить /column/htdocs/symfony/index.php так, чтобы он знал о реальном корневом каталоге (root) вашего проекта. Замените существующую строку для SF_ROOT_DIR следующей строкой: define('SF_ROOT_DIR', '/column/protected/sf_column');. Почти все готово. Теперь просто нужно инициализировать модуль post для Blahg. Это означает выполнение еще одной команды в командной строке: php /column/protected/symfony/data/bin/symfony init-app blahg. Эта команда, аналогично остальным, создает некоторые файлы и каталоги, на этот раз в /column/protected/sf_column/apps/blahg/modules/post.

Теперь, когда symfony настроена и работает, можно приступить к работе с Blahg.

ПРИМЕЧАНИЕ: symfony предоставляет функциональность автоматического генерирования кода для обеспечения функциональности Create, Read, Update и Delete (CRUD), основанной на вашей базе данных. Хотя это помогает быстро собрать основу приложения, использование этой функциональности в данном контексте не даст вам прочувствовать работу приложения в symfony. Для целей данной статьи мы выполним компоновку приложения вручную.

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

Таблица posts, которую мы будем использовать, была создана в первой части (EN).

Поскольку symfony использует Propel для предоставления отображения Object Relational Mapping, нужно сгенерировать модель при помощи программы командной строки. Она требует описания схемы базы данных и предоставления некоторой информации о базе данных.

ПРИМЕЧАНИЕ: Технически первоначальное предоставление информации о базе данных не является необходимостью. symfony создает модель на основе схемы, а программа командной строки может даже использоваться для генерирования SQL-сценариев создания базы данных на основе определения схемы. Мы попробуем это сделать в следующей статье.

Для файлов определения схемы и базы данных symfony использует язык разметки YAML (Yet Another Markup Language). YAML не очень сложен, но если вы никогда раньше его не использовали, он может показаться запутанным. На данном этапе главное, о чем нужно знать, - пробел в YAML очень важен. Для отступа используются два пробела, символы табуляции никогда не применяются.

Теперь мы изменим файлы database.yml и schema.yml. Они расположены в каталоге /column/protected/sf_column/config. Файл schema.yml нужно изменить для описания таблицы posts. Измените файл схемы так, чтобы он содержал следующую информацию.

propel:
posts :
_attributes: { phpName: Post }
id:
title: varchar(255)
text: longvarchar
modified: timestamp


ПРИМЕЧАНИЕ: Поскольку вы, вероятно, скопировали этот код, вернитесь назад и проверьте, что все отступы выполнены двумя пробелами, а символы табуляции не используются. Например, измененная строка должна иметь в начале четыре пробела (два для отступа posts и два для отступа modified). Файл database.yml должен быть изменен так, чтобы он содержал специфичные параметры нашей базы данных. Он может выглядеть примерно так:

all:
propel:
class: sfPropelDatabase
param:
phptype: mysql
hostspec: localhost
database: symfony
username: frameworks
password: fwpw


Это не должно вызвать затруднений. Теперь, после завершения настройки базы данных и схемы, можно сгенерировать модель. Вернитесь в командную строку, которую использовали ранее, и в каталоге /column/protected/sf_column выполните следующие команды:

php /column/protected/symfony/data/bin/symfony propel-build-model
php /column/protected/symfony/data/bin/symfony clear-cache


Каждая команда будет выводить большое количество данных, но важным конечным результатом является создание файлов Post.php и PostPeer.php в каталоге /column/protected/sf_column/lib/model - это наши модели. Если их открыть, мы увидим не много. Они просто расширяют классы базовой модели.

ПРИМЕЧАНИЕ: При любой перекомпоновке моделей необходимо выполнять команду очистки кэша (clear-cache). Также отмечу, что если symfony была установлена при помощи PHP Extension and Application Repository (PEAR), можно просто выполнить symfony propel-build-model без вызова PHP или предоставления полного пути к двоичному файлу. Не все имеют возможность использовать PEAR в своей рабочей среде. Именно поэтому мы описали альтернативную установку в первой части данной серии статей и привели выше соответствующие инструкции по генерированию модели.

Это все! Модель готова. Можно еще много говорить о моделях, но пора перейти к контроллеру.

создание контроллера

Нет необходимости писать Front Controller, как это было в Zend, поскольку symfony уже предоставляет его.

Наш контроллер post в действительности называется actions.class.php и размещается в каталоге
/column/protected/sf_column/apps/blahg/modules/post/actions. Этот контроллер будет обрабатывать все действия для модуля post. Как и раньше нам нужны три действия, но названия у них другие: executeIndex, executeRead и executeWrite (executeXxxxxx - это соглашение по именованию, используемое в symfony для действий, и ему нужно следовать).

Действие executeIndex является простым: извлечь сообщения и сделать их доступными для представления. Действие executeRead тоже простое: взять ID сообщения, извлечь сообщение и сделать его данные доступными для представления. Действие executeWrite принимает заголовок и текст из запроса и сохраняет данные в базе данных, возвращая ID сохраненного сообщения.

Детали синтаксиса и структуры смотрите в файле post/actions/actions.class.php. Теперь, когда контроллер готов, можно перейти к представлениям.

создание представлений

Представления будут размещаться в каталоге /column/protected/sf_column/apps/blahg/modules/post/templates. Нам нужны три представления: indexSuccess.php, readSuccess.php и writeSuccess.php (соглашение по именованию xxxxSuccess.php - это требование symphony, и ему нужно следовать).

Эти представления не будут визуализировать HTML-документ полностью, как это было в Zend. symfony предоставляет шаблон схемы по умолчанию (в apps/blahg/templates/layout.php), обеспечивающий базовые заголовок и сноску HTML-страницы. Все, что нужно предоставить, - это средняя часть представления (содержимое, специфичное для действия). Например, indexSuccess.php просто проходит в цикле по предоставленному массиву сообщений и форматирует их, readSuccess.php просто форматирует содержимое сообщения, а writeSuccess.php отображает форму или выводит сообщение об успешной операции записи сообщения.

Если все создано и установлено правильно, можно попробовать версию Blahg для symfony, перейдя по адресу http://localhost/symfony/post (предполагая, что все установлено локально).

Итак, мы увидели, как Blahg работает в Zend Framework и symfony. Две среды пройдены, осталась еще одна.

создание Blahg в CakePHP

До начала создания Blahg в CakePHP необходимо переместить несколько файлов. Затем мы настроим соединение с базой данных и дадим возможность среде сделать все остальное.

настройка структуры файлов

Взгляните на каталог /column/protected/cakephp/. Вы должны увидеть четыре каталога: application (в котором будет храниться приложение), cake (в котором содержатся файлы ядра Cake), docs (документация readme) и vendors (в которой размещаются библиотеки сторонних разработчиков, которые вы можете использовать). Имеется также два файла: index.php и .htaccess. Эти файлы работают только после установки CakePHP в webroot. Обычно это плохая идея, поскольку все файлы приложения становятся доступными через браузер.

Предпочтительный метод установки - сделать каталог /column/protected/cakephp/app/webroot корневым каталогом веб-сервера. Также очень важно (и в данной серии статей так делается) скопировать содержимое /column/protected/cakephp/app/webroot в доступный для Web каталог (в данном случае /column/htdocs/cakephp). Сделайте это прямо сейчас.

После копирования файлов нужно открыть /column/protected/cakephp/app/webroot/index.php и сделать пару изменений: необходимо обновить существующие определения для ROOT и APP_DIR. Они должны выглядеть примерно так:

if (!defined('ROOT')) {
define('ROOT', DS . 'column' . DS . 'protected' . DS . 'cakephp');
}
if (!defined('APP_DIR')) {
define('APP_DIR', 'app');
}


Этого достаточно для продолжения работы, но можно также ввести информацию о базе данных и обновить core.php, используя уникальное значение для CAKE_SESSION_STRING.

В каталог /column/protected/cakephp/app/config/ скопируйте database.php.default под названием database.php и введите имя хоста, название учетной записи (login), пароль и название базы данных для вашей системы. Также в файле /column/protected/cakephp/app/config/core.php измените строку для CAKE_SESSION_STRING и включите новое значение, например, define('CAKE_SESSION_STRING', 'He had a Subbuteo player in his hair. I got distracted.');.

Все подготовительные работы выполнены. Пора приступить к пирогу.

ПРИМЕЧАНИЕ: Аналогично symfony, CakePHP предоставляет функциональность автоматического генерирования кода функциональности CRUD, основанного на используемой базе данных. CakePHP также предоставляет систему поддержки (scaffolding), обеспечивающую аналогичный сервис вообще без генерирования какого-либо кода. Обе эти функциональные возможности помогают быстро сформировать основу приложения, но, как мы отмечали, использование их в данном контексте не даст прочувствовать процесс создания приложения в CakePHP.

создание таблицы и модели

Если у вас нет таблицы posts, т.е. вы пропустили этот раздел в первой части, вернитесь назад и создайте ее. Создайте файл post.php в каталоге /column/protected/cakephp/app/models. Он будет сдержать определение класса нашей модели. Определение является простым: классом является Post, и он расширяет AppModel, вы должны установить переменную класса $name в post.

Это действительно все. Наша модель будет использовать все унаследованные из AppModel методы. Теперь можно перейти к контроллеру.

ПРИМЕЧАНИЕ: Возможно, вы заметили, что таблица называется posts (множественное число), а модель называется post (единственное число). Это важная часть соглашений Cake. Модели всегда называются в единственном числе, а соответствующие им таблицы во множественном.

создание контроллера Cake posts

Наш контроллер posts будет простым, как и остальные контроллеры, которые вы написали сегодня. Мы привлечем пару вспомогательных механизмов, затем определим три действия: index, read и write.

Действие index получает список сообщений и делает его доступным для представления. Действие read получает ID сообщения, извлекает его и делает доступными данные этого сообщения для представления. Действие write принимает данные (если они есть) и сохраняет их в базу данных.

Детали синтаксиса и структуры смотрите в файле /column/protected/cakephp/app/controllers/posts_controller.php.

Контроллер готов. Теперь можно перейти к представлениям.

создание представлений в Cake

Нам нужны те же три основных представления, которые создавались для Blahg в других интегрированных средах. В данном случае мы создадим файлы index.ctp, read.ctp и write.ctp в каталоге /column/protected/cakephp/app/views/posts. Это просто шаблоны, которые будет использовать CakePHP, и они во многом похожи на шаблоны, созданные для symfony. Взгляните на эти файлы в архиве исходных кодов. Представления интенсивно используют формы и вспомогательные механизмы HTML (HTML helpers) для вывода ссылок и элементов форм, но они должны быть похожи на представления, созданные вами для symfony.

Обратите внимание на то, что представления не визуализируют HTML-файлы полностью. CakePHP предоставляет схему по умолчанию. Эти файлы схем по умолчанию расположены в каталоге /column/protected/cakephp/cake/libs/views/templates/layouts, но их менять нельзя. Если вы хотите изменить схему по умолчанию, скопируйте файл схемы default.ctp в каталог /column/protected/cakephp/app/views/layouts и измените в соответствии с вашими требованиями.

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

Создайте представления в соответствии с требованиями Blahg или установите представления из загруженного исходного кода. Если все установлено и написано правильно, можно попробовать версию Blahg для CakePHP, перейдя по адресу http://localhost/cakephp/posts (предполагая, что все установлено локально).


Часть 3. аутентификация пользователей

создание таблицы comments


В Zend Framework и symfony используйте следующее SQL-выражение для создания таблицы comments:

CREATE TABLE 'comments' (
'id' INT( 10 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
'post_id' INT( 10 ) NOT NULL ,
'text' TEXT NOT NULL ,
'created' TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = MYISAM ;


В CakePHP используйте следующее SQL-выражение:

CREATE TABLE 'comments' (
'id' INT( 10 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
'post_id' INT( 10 ) NOT NULL ,
'text' TEXT NOT NULL ,
'created' DATETIME DEFAULT NULL
) ENGINE = MYISAM ;


После создания таблицы comments можно приступить к добавлению функциональности работы с комментариями.

добавление комментариев в Zend Framework

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

Все это не обязательно должно происходить именно так. Мы могли бы рассматривать комментарии как логическую часть сообщений и включить их все в один и тот же контроллер, модель и т.д. К тому же, мы могли бы рассматривать сообщения как комментарии без родителя и хранить все в одной таблице. Также мы могли бы написать все приложение Blahg полностью в одном PHP-файле, если бы захотели, но это не кажется нам мудрым решением. Идея заключается в разбиении приложения на отдельные функциональные части.

Модель comments будет выглядеть похожей на модель posts. Все необходимое можно сделать в методах, наследуемых из Zend_Db_Table. Как уже упоминалось, какие-либо специализированные представления для комментариев не нужны, но необходимо изменить представление posts read для отображения всех комментариев и включить в него форму, позволяющую пользователям вводить свои комментарии. Эта форма будет указывать на действие write контроллера comments.

Контроллер comments будет выглядеть как облегченная версия контролера posts, поскольку все, что нам нужно - это действие writeAction. Но есть важное отличие: после записи данных нужно перенаправить пользователя на сообщение, которое он читал до добавления комментария. Что касается контроллера posts, необходимо изменить readAction для извлечения комментариев и ассоциации их с представлением.

Примеры того, как это все делается, приведены в архиве исходного кода. Возможно, вы реализуете другое решение. Это хорошо. Теперь, после добавления комментариев в Zend мы можем приступить к работе с symfony.

добавление комментариев в symfony

Когда дело касается добавления комментариев в версии Blahg для symfony, необходимо начать работу с нашего файла schema.yml (он должен находиться в каталоге /column/protected/sf_column/config) и добавить определение для таблицы comments. Нужно добавить следующие строки:

comments :
_attributes: {phpName: Comment }
id:
post_id:
text: longvarchar
created: timestamp


Как и прежде, если вы копируете этот текст, убедитесь в том, что перед словом comments имеется два пробела, а перед элементами ниже comments - четыре пробела. Это важно.

Примечание. Аналогично CakePHP, symfony будет выполнять вместо вас определенные магические действия с созданными и модифицированными столбцами, если вы назовете их created_at и modified_at соответственно. В данном случае эти действия выполняются базой данных MySQL.

После этого необходимо создать модель propel и очистить кэш. В командной строке (в каталоге /column/protected/sf_column) выполните следующие команды:

php /column/src/symfony/data/bin/symfony propel-build-model
php /column/src/symfony/data/bin/symfony clear-cache


После этого в каталоге /column/protected/sf_column/lib/model должны появиться файлы Comment.php и CommentPeer.php. Разобравшись с моделями, мы можем перейти к оставшейся части функциональности работы с комментариями.

Примечание. Во второй части данной серии статей уже упоминалось, что если symfony устанавливалась при помощи репозитория PEAR (PHP Extension and Application Repository), можно просто выполнить symfony propel-build-model без предварительного вызова PHP или без указания полного пути к библиотеке. Не у каждого есть возможность использовать в своей среде установку через PEAR, поэтому мы даем здесь альтернативные инструкции по установке.

После создания модели нужно изменить файл actions.class.file.php в каталоге /column/protected/sf_column/apps/blahg/modules/post/actions/ для добавления некоторого критерия выбора комментариев и ассоциации их с сообщением. Нужно также выполнить некоторые изменения в файле
readSuccess.php в каталоге /column/protected/sf_column/apps/blahg/modules/post/templates/ для вывода комментариев к данному сообщению. Оба эти действия можно увидеть в архиве исходного кода.

Затем необходимо проинициализировать модуль comment для Blahg. Выполните следующую команду из каталога /column/protected/sf_column: php /column/src/symfony/data/bin/symfony init-module blahg comment.

Теперь добавим действие executeWrite к actions.class.php в каталоге /column/protected/sf_column/apps/blahg/modules/comment/actions/. Это похоже на действие executeWrite из модуля post, за исключением того, что после записи мы хотим перенаправить пользователя обратно к читаемому им сообщению.

Наконец, необходимо изменить файл readSuccess.php для модуля posts и добавить форму, позволяющую пользователям вводить свои комментарии. Этот код включен в архив исходного кода.

добавление комментариев в CakePHP

Для добавления комментариев в версию Blahg для CakePHP понадобится контроллер comments (обрабатывающий процедуру записи комментариев), но что более важно, необходимо написать модель comment и определить ассоциацию между комментарием и сообщением. После указания того, что сообщение "имеет много" комментариев, и комментарий "принадлежит" одному сообщению, тот же запрос, который извлекает сообщение для чтения, будет извлекать комментарии к этому сообщению. В CakePHP это называется ассоциациями моделей (model associations).

Если в архиве исходного кода взглянуть на обновленную версию Blahg для CakePHP, то можно увидеть довольно обычный контроллер comments (comments_controller.php), но с существенным отличием от контроллера posts: переменная класса autoRender установлена в значение false. Это позволяет использовать действия из контроллера без представлений. Если autoRender не установить в false, это приведет к появлению сообщений об ошибке "missing view" (представление не найдено) во время подтверждения комментариев. В архиве исходного кода имеется также новая модель comment и измененная модель post для отображения ассоциации моделей. Можно заметить, что после приведения в порядок ассоциаций моделей, CakePHP будет выполнять большую часть рутинной работы вместо вас.

исключения из правил

Итак, мы подогнали приложение Blahg под каждую интегрированную среду, использовав предлагаемые ими преимущества. Это довольно легко. Все отлично до тех пор, пока используемая нами интегрированная среда может нести ответственность за все, что нужно нашему приложению. Но что если приложение не вписывается в интегрированную среду?

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

Уверены ли вы в том, что оно должно работать именно так? Если да, хорошо. Давайте поговорим о расширении интегрированных сред.

задача

Вы можете помещать сообщения в Blahg, а ваши читатели имеют теперь возможность комментировать их. Хорошо было бы сразу видеть, о чем идет речь. Можно было бы отображать броский заголовок, пытаясь охватить в нем тему сообщения (например, "Мир шпателей" или "Полька для остальных"), но у нас нет никакого контроля над тем, о чем говорится в сообщениях читателей. И иногда может показаться, что общение свелось к какой-то другой теме. А возможно вы помешаны на математике и хотите каких-либо чисел.

Возможным решением может быть нечто, принимающее входящее содержимое, подсчитывающее использованные слова и предоставляющее небольшой точный список самых употребительных слов в вашем приложении Blahg. Задача: где это сделать? Вы могли бы аргументировано доказывать, что данная задача должна выполняться на уровне модели, поскольку происходит категоризация данных. Также вы могли бы аргументировано доказывать, что это задача контроллера, поскольку необходимо выполнить определенный анализ, а анализ подразумевает логику. А если вы хотите увидеть числа, нужно ли что-то в представлении? Но пользователь на самом деле даже не будет посещать контроллер words. Как расширить интегрированную среду для выполнения этой задачи?

Рассмотрим базовый подход. Нам необходимы таблица для хранения слов (назовем эту таблицу "words"). Данная таблица будет хранить слово и количество раз, которое оно встречается. Для определения этой таблицы можно использовать следующий синтаксис:

CREATE TABLE 'words' (
'id' INT( 10 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
'word' varchar(255) NOT NULL,
'occurrences' INT( 10 ) NOT NULL
) ENGINE = MYISAM ;


Код для анализа слов должен будет:

1. Выделять HTML (все равно вам это понадобится).

2. Удалить не алфавитно-цифровые символы, не являющиеся пробелами (при этом не учитываются апострофы, но для начала это нормально).

3. Заменить несколько идущих подряд символов-пробелов одним пробелом.

4. Все перевести в нижний регистр.

5. Разделить по пробелу.

6. Сгенерировать счетчики слов из полученного массива.

7. Обновить таблицу words новыми значениями счетчиков, добавляя строки для новых слов и обновляя для существующих.

Не беспокойтесь, если вам кажется, что объем работы велик. По мере написания кода он будет формировать архитектуру MVC. Все сведется, по существу, к модели, обрабатывающей таблицу words, представлению, позволяющему просматривать счетчики слов, и контроллеру, реализующему некоторую логику.

добавление статистики слов в Zend

Если бы было нужно только лишь собрать статистику слов, не отображая ее, наверное, можно было бы решить эту проблему при помощи
Zend_Controller_Plugin_Abstract. Но поскольку нам на самом деле нужно видеть счетчики слов, есть смысл в создании модуля words с моделью и контроллером. Модель будет довольно обычной, как и другие, создаваемые нами в Zend. Контроллер будет иметь метод indexAction, который извлекает и отображает счетчики слов, а еще необходим метод для обработки текста - processText. Данный метод не завершается в Action. Это сделано преднамеренно. Представление индексов тоже очень простое - оно должно выполнять итерацию по списку слов и отображать их на экране.

Код indexAction не сложен. Просто получите слова и отсортируйте их по частоте появления в тексте (в убывающем порядке). Но код для метода processText должен делать немного больше. Необходимо выделить текст, распределить его в массив и подсчитать число появлений каждого слова в тексте. Затем код должен получить слова из таблицы слов и поместить их в список. Если слово имеется в главном (master) списке, необходимо обновить строку новым значением счетчика. Если слово является новым, нужно вставить строку с этим словом. Все это достаточно просто. Весь код можно увидеть в архиве исходного кода.

Для использования метода processText в контроллерах posts и comments, прежде всего, нужен файл WordController.php. В writeAction необходимо создать экземпляр WordController. После этого нужно передать в него объекты Request и Response. Затем мы просто вызываем метод processText, также как метод любого старого объекта. Для контроллера posts не забудьте передать заголовок и текст.

Совет. Если пропустить текст через фильтр StripTags, не нужно будет выделять теги в дальнейшем. Это может выглядеть примерно так:

$wc = new WordController($this->_request, $this->_response);
$wc->processText($data['title'] . ' ' . $data['text']);


Подробности приведены в архиве исходного кода. Как только все будет на месте, а контроллеры posts и comments будут изменены, у нас должна появиться возможность вводить сообщения и комментарии и просматривать обновленную статистику слов по адресу http://localhost/zend/word. Примечание. Возможно, в конкретных условиях вы будете писать код подобного рода несколько иначе. Наша цель не в том, чтобы показать, как выполнить продвинутый анализ статистики слов при синтаксическом разборе текста, а просто использовать его как пример в контексте попытки сделать что-нибудь, что интегрированная среда делает неуклюже.

добавление статистики слов в symfony

Как вы, возможно, догадались, первое, что необходимо сделать - это добавить определение таблицы words в конфигурационный файл schema.yml. Не забывая о корректных отступах, это определение можно записать следующим образом:

words
_attributes: {phpName: Word }
id:
word: varchar(255)
occurrences: integer


Что дальше? Правильно - мы должны создать модель и очистить кэш, аналогично тому, как делали это при добавлении комментариев. Затем нужно проинициализировать модуль word. Все эти команды следует выполнять из каталога /column/protected/sf_column.

Вперед, но сначала выполните нетрудную часть работы. Мы знаем, что модуль word будет делать одну вещь, к которой имеет доступ пользователь - отображать список слов в таблице words. Возможно, после изучения модуля posts вы догадаетесь, как будут выглядеть действие executeIndex и шаблоны indexSuccess.

Поскольку легкая часть работы сделана, необходимо получить текст из проанализированных и подсчитанных сообщений и комментариев и т.д. Одним из способов сделать это является создание действия executeParsetext в actions.class.php для модуля word и передача управления этому действию после сохранения сообщения или комментария. До этого необходимо запомнить ID сообщения, для того чтобы знать, куда возвращать результаты. В файле actions.class.php модуля post это может выглядеть так:

$this->id = $post->getId();
$this->forward('word', 'Parsetext');


После передачи управления в другой модуль необходимо извлечь переданные переменные. В действительности это несколько мудреный способ. Код для получения переменных выглядит так:

$vars =
$this->request->getContext()->getActionStack()->getFirstEntry()->\
getActionInstance()->getVarHolder()->getAll();


ПРИМЕЧАНИЕ. Наверняка должен быть более простой способ. Можно установить значение в flashMessage и перенаправить только его, но это не намного лучше. Не стесняйтесь сообщить о более элегантном способе.

После получения переменных разбейте текст, как и прежде, и используйте модель word для сохранения соответствующих строк. Чтобы использовать это действие для синтаксического анализа текста сообщений и комментариев, не забудьте инициализировать переменную title. После завершения работы можно передать управление обратно в модуль posts, а поскольку мы сохранили ID сообщения, можно сразу перейти к его чтению.

Все это включено в архив исходного кода. Потратьте некоторое время на его изучение, прежде чем перейдете к среде CakePHP.

добавление статистики слов в CakePHP

В CakePHP данную функциональность мы реализуем в двух частях. Мы создадим так называемый компонент, который является своего рода помощником контроллера, и этот компонент будет обрабатывать текст. Затем мы создадим стандартную модель, контроллер и представление для отображения списка слов из базы данных. Контроллеры posts и comments будут использовать модель words для сохранения счетчиков слов после их обработки. Для создания WordcountComponent создайте файл wordcount.php в каталоге /column/protected/cakephp/app/controllers/components. Определите класс WordcountComponent как расширение object и создайте метод processText, используя уже знакомый код для выделения, разбиения и подсчета текста в массиве. Этот метод должен возвращать массив подсчитанных слов.

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

В наши контроллеры posts и comments необходимо добавить пару строк для указания того, что мы используем модель word и наш новый компонент:

var $uses = array ('Post', 'Word');
var $components = array('Wordcount');


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


Часть 4. поддержка Ajax

Ajax в Zend Framework


В Zend Framework в настоящее время нет какой-либо интегрированной поддержки Ajax. Существует несколько библиотек в Zend Framework, которые можно использовать для добавления Ajax в ваши приложения, например, Zend_Json и Zend_XmlRpc. Имеются также библиотеки, включенные в Zend Framework, которые помогают воспользоваться преимуществами существующих Web API для популярных приложений, например, Flickr. Но если нужно добавить функциональность Ajax в ваше приложение в Zend, необходимо сделать это самостоятельно.

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

Ajax в symfony

Интегрированная среда symfony поставляется с библиотеками Prototype и script.aculo.us. Во второй части данной серии статей, инициализируя проект и копируя содержимое Web-каталога в каталог /column/protected/symfony, мы перемещали копию этих библиотек в место, откуда их можно будет использовать в приложении (вам следует это проверить; они должны находиться в каталоге /column/htdocs/symfony/sf/prototype/js/).

Обеспечивая интеграцию с Prototype, среда symfony может предоставить некоторые вспомогательные средства для уменьшения объема ручного кодирования функциональности Ajax в приложении Blahg. Но есть и обратная сторона: если нужно сделать что-то за рамками возможностей Prototype, это нужно сделать самостоятельно.

Ajax в CakePHP

CakePHP предоставляет интеграцию для библиотек Prototype и script.aculo.us. Необходимо загрузить все библиотеки, которые мы хотим использовать (поместить их в каталог /column/htdocs/cakephp/js). На самом деле нам нужна только библиотека Prototype, для того чтобы использовать вспомогательные средства для Ajax. Библиотека script.aculo.us предназначена для более красивого оформления проделанной работы.

Как и в symfony, предоставляя интеграцию с Prototype, CakePHP облегчает жизнь, когда дело касается создания Ajax-функциональности в приложении Blahg. Но обратная сторона остается: если нужно сделать что-то за рамками возможностей Prototype, это нужно сделать самостоятельно. Вспомогательные средства Ajax не помогут.

Примечание. Если вы предпочитаете работать с библиотекой JQuery, используйте вспомогательные средства CakePHP JavaScript. В результате получается довольно изящный код, если все сделано правильно. Но данная тема выходит за рамки нашей статьи.

настройка базы данных

Начнем с создания таблицы для хранения рейтинга сообщений. Используйте одно и то же SQL-выражение для создания этой таблицы в каждой из трех баз данных.

CREATE TABLE 'ranks' (
'id' INT( 10 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
'post_id' INT( 10 ) NOT NULL ,
'rank' INT( 10 ) NOT NULL
) ENGINE = MYISAM ;


ПРИМЕЧАНИЕ. Нам не нужна целая таблица для хранения только рейтингов сообщений. Можно было бы использовать еще один столбец в таблице posts, если изменить способ обновления модифицированного столбца. Нам не нужно передавать модифицированные данные каждый раз, когда кто-то указывает рейтинг сообщения. Но в зависимости от того, как спроектировано приложение Blahg, MySQL или интегрированная среда будет делать именно это. Кроме того, если потребуется делать с рейтингами что-то еще, например, хранить счетчик изменений рейтингов или времени последнего указания рейтинга сообщения, помещение их в отельную таблицу будет иметь смысл.

добавление рейтингов сообщений в приложение Blahg в среде Zend

Мы должны решить, что использовать для обработки Ajax-запросов. Возможно, мы без каких-либо затруднений сможем реализовать наше собственное решение, но для целей данной статьи каждая интегрированная среда будет использовать библиотеку Prototype. Мы должны создать каталог для хранения библиотеки - такой, к которому можно обратиться из браузера, например, /column/htdocs/zend/scripts/ (именно этот каталог используется в архиве исходного кода).

Необходимо создать базовую модель для рейтингов под названием Ranks.php. Она должна быть просто оболочкой модели, как это было сделано для сообщений и комментариев. И, естественно, необходимо создать контроллер ranks. Мы не будем создавать какие-либо представления - контроллер будет просто выводить результаты всех запросов rank. Нам понадобятся два действия: readAction будет искать рейтинг сообщения, а writeAction будет передавать рейтинг для записи и проверять наличие строки для этого сообщения в таблице. Если она существует, рейтинг будет обновляться новым значением; в противном случае будет вставляться новая строка с рейтингом. В любом случае новый рейтинг будет выводиться, для того чтобы можно было обновить рейтинг в представлении. Все это довольно очевидно и может быть найдено в архиве исходного кода. Теперь нам нужно реализовать Ajax в представлениях.

Вспомните, что мы не хотим добавлять сюда какие-либо представления, предназначенные для отображения рейтингов. Но нам понадобится изменить представление для чтения сообщения и включить в него Ajax-код. Откройте файл /column/protected/zend/views/scripts/post/read.php. В нем мы выполним всю нашу работу. Для начала нужно включить библиотеку Prototype в заголовок:

<script type="text/javascript" src="/zend/scripts/prototype.js"></script>


Перед написанием какого-либо JavaScript-кода поместите в нужное место еще кое-что. Нам нужно отобразить текущий рейтинг. В рейтинге при помощи тега span установите ID, для того чтобы можно было обновить значение рейтинга по запросу. Например:

<h4>Rank: <span id="rank"></span></h4>


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

<input type='button' onclick='rankUp();' value='Hot' /> or <input type='button' onclick='rankDown()' value='Not' />


Для того чтобы сделать скрытое поле ввода post_id более доступным для Prototype, укажите ID post_id для этого поля.
И, наконец, добавьте событие onLoad в тело для получения текущего рейтинга: <body onload='fetchRank();'>.
Не удивительно, что нужно написать три JavaScript-функций: rankUp, rankDown и fetchRank. Они будут выполнять Ajax-запросы. Эти функции довольно просты и выглядят во многом также, как и в других интегрированных средах. Поместите эти функции в строфу <script> заголовка.

function fetchRank() {
var ajax = new Ajax.Request(
'/zend/rank/read',
{
method : 'get',
parameters : {'post_id' : $('post_id').value},
onComplete: parseRank
}
);
}
function rankUp() {
var ajax = new Ajax.Request(
'/zend/rank/write',
{
method : 'get',
parameters : {'post_id' : $('post_id').value, 'rank' : '1'},
onComplete: parseRank
}
);
}
function rankDown() {
var ajax = new Ajax.Request(
'/zend/rank/write',
{
method : 'get',
parameters : {'post_id' : $('post_id').value, 'rank' : '-1'},
onComplete: parseRank
}
);
}


Каждая функция вызывает parseRank по завершении работы. Эта функция просто принимает любой ответ от Ajax-запроса и помещает его в созданный ранее тег span рейтинга.

function parseRank(trans) {
$('rank').innerHTML = trans.responseText;
}

После выполнения всех этих изменений или импорта их из архива кода мы должны уметь читать сообщение в Blahg и менять его рейтинг. Можно даже схитрить и повышать рейтинг сообщения много раз подряд. Все это мы сделали в Zend. Что нужно сделать для получения аналогичной функциональности в symfony?

Примечание. Возможно, больший смысл имело бы использование Ajax.Updater вместо Ajax.Request. Попробуйте выполнить рефакторинг кода, если являетесь новичком в Ajax.

добавление рейтингов сообщений в приложение Blahg в среде symfony

Для добавления рейтингов сообщений в symfony начните со схемы schema.yml (она должна находиться в каталоге /column/protected/sf_column/config) и определите таблицу ranks.

ranks :
_attributes: {phpName: Rank }
id:
post_id:
rank: integer


Помните: необходимо отступить два пробела для ranks и четыре пробела для определений поля.

Что дальше? Создайте модель propel и очистите кэш. Не забудьте в symfony выполнить команды из каталога /column/protected/sf_column (корневой каталог вашего приложения).

php /column/src/symfony/data/bin/symfony propel-build-model
php /column/src/symfony/data/bin/symfony clear-cache


После выполнения этих команд мы увидим файлы Rank.php и RankPeer.php в каталоге /column/protected/sf_column/lib/model. Теперь пойдем дальше и проинициализируем модуль ranks: php /column/src/symfony/data/bin/symfony init-module blahg rank.

Класс действий rank (/column/protected/sf_column/apps/blahg/modules/rank/actions/actions.class.php) будет содержать два действия: executeRead и executeWrite. Сразу после чтения или обновления рейтинга мы отображаем его, вызывая выход в конце каждого действия (это предохраняет symfony от поиска ассоциированных шаблонов представлений, которые нам не нужно создавать). Просмотрите реализацию этих действий в архиве кода. Наконец, необходимо обновить шаблон сообщения readSuccess (/column/protected/sf_column/apps/blahg/modules/post/templates/readSuccess.php) для использования вспомогательной JavaScript-функции symfony и включения некоторых Ajax-ссылок. Начнем с добавления следующей строки в начало файла: <?php use_helper('Javascript') ?>. Эта строка включит библиотеку Prototype и предоставит нам доступ к широкому набору вспомогательных классов Ajax. Мы можем использовать один из них для создания вызова загрузки первоначального рейтинга и пары ссылок, которые будут передавать рейтинги. Не забудьте добавить span для отображения текущего рейтинга.

<h4>Rank: <span id="rank"></span></h4>
<?php echo javascript_tag(remote_function(array('update' => 'rank',
'url' => 'rank/read?post_id=' . $id))) ?>
<?php echo link_to_remote('Hot', array('update' => 'rank',
'url' => 'rank/write?post_id=' . $id . '&rank=1')) ?> or
<?php echo link_to_remote('Not', array('update' => 'rank',
'url' => 'rank/write?post_id=' . $id . '&rank=-1')) ?>


Это все, что нам нужно сделать. Говоря иначе, на самом деле нам не нужно писать какой-либо JavaScript-код. Вспомогательные классы
интегрированной среды symfony делают это за нас. Не верите? Настройте все так, как описано выше (или установите код из архива кода) и прочтите сообщение в symfony-версии приложения Blahg. Затем просмотрите исходный код. Разве вы писали весь этот JavaScript?

добавление рейтингов сообщений в среде CakePHP

Перед добавлением функциональности рейтингов сообщений в CakePHP-версии приложения Blahg убедитесь в том, что загрузили Prototype в каталог /column/htdocs/cakephp/js. Необходимо будет изменить шаблон схемы по умолчанию и включить библиотеку в заголовок. Это файл default.ctp в каталоге /column/protected/cakephp/app/views/layouts/. Добавьте следующую строку в заголовок:

<?php echo $javascript->link('prototype') ?>


Затем нужно создать базовый контроллер AppController, включающий вспомогательные классы JavaScript и Ajax, которые понадобятся ниже. Создайте файл app_controller.php в каталоге /column/protected/cakephp/app. Он должен выглядеть так:

<?php

class AppController extends Controller {
var $helpers = array('Html', 'Form', 'Javascript', 'Ajax');
}
?>


ПРИМЕЧАНИЕ. Технически это неверно. Мы могли бы использовать эти вспомогательные классы в контроллерах posts и ranks, но создание базового контроллера app позволяет нам использовать их в любом контроллере, не добавляя в список вспомогательных объектов. Это правильный способ перегрузки базового объекта AppController.

Теперь необходимо создать базовую модель ranks и контролер ranks. Они более или менее похожи на модели и контроллеры, которые мы уже создали. При создании модели rank мы должны настроить ассоциации между моделями rank и post. Не обязательно добавлять ассоциацию модели в ranks до тех пор, пока мы не решим впоследствии добавить более надежную функциональность рейтингов. В архиве кода обе ассоциации настроены для формы. С позиций здравого смысла в контроллере ranks нам нужна пара новых штучек. Необходимо включить использование компонента RequestHandler и добавить функцию beforeFilter. В функции beforeFilter мы проверяем RequestHandler и, если запрос идет через Ajax, отключаем отладочный код. Во всем остальном контроллер выглядит так, как вы, вероятно, ожидаете. Два метода, read и write, ищут post_id и выводят рейтинг.

Примечание. Код контроллера в архиве исходного кода просто возвращает рейтинг, но если делать более сложные Ajax-вызовы, то можно создать шаблон (например, views/ranks/viewrank.ctp) и визуализировать представление, используя $this->render('viewrank', 'ajax');. Но поскольку нам нужен только текущий рейтинг, это излишество.

Теперь осталось только добавить span rank и пару Ajax-ссылок в представление чтения сообщений.

<h4>Rank: <span id="rank"><?php echo (int) $post['Rank']['rank'] ?></span></h4>
<?php echo $ajax->link('Hot', '/ranks/write/' . $post['Post']['id'] . '/1', array
('update' => 'rank')); ?> or
<?php echo $ajax->link('Not', '/ranks/write/' . $post['Post']['id'] . '/-1', array
('update' => 'rank')); ?>


Эти Ajax-ссылки означают "вызвать этот URL через Ajax и обновить DOM-элемент rank полученным результатом, какой бы он ни был" (хорошо, что мы отключили отладочные сообщения). Теперь, когда все находится на своем месте, загрузите сообщение и попробуйте поработать с ним. Взгляните на исходный код и задумайтесь над тем, что нам не пришлось писать весь этот JavaScript-код. Здорово, не правда ли?


Часть 5. интегрирование внешних задач

Рассмотрим процесс написания автоматизированного задания для приложения Blahg, удаляющего все сообщения, старшие 30 суток. Вы сможете вызывать это задание из командной строки, что позволит автоматизировать выполнение задания при помощи планировщика заданий, например, cron. Мы создадим приложение в каждой интегрированной среде, что позволит получить представление о различиях в обработке автоматизированных заданий в средах Zend, symfony и CakePHP.

давайте сделаем это сейчас

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

Но слово "ненадежный" должно вызывать некоторое беспокойство. Написав автоматизированные задания таким способом, вы делаете их доступными для всего мира. Даже если вы не ссылаетесь каким-либо образом на действие, используете контроллер под названием Mxyzptlk, или требуете 40-символьной уникальной строки, которую можно передать в действие перед выполнением, выполнение этого действия все равно остается возможным извне. Защита путем скрытия вовсе не является защитой. Поскольку многие автоматизированные задания имеют тенденцию интенсивно работать с базой данных, не стоит выполнять эти действия случайным образом.

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

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

Достаточно предисловий. Приступим к делу.

внешние задания в среде Zend Framework

Благодаря природе Zend Framework "выбери и укажи", создание автоматизированного задания, использующего интегрированную среду, является гибким в плане принятия решения, что использовать, а что нет. Поскольку мы не собираемся обращаться к веб-приложению через существующий фронтальный контроллер, обслуживающий веб-запросы, нужно создать дополнительный контроллер для управления выполнением сценариев. Этот контроллер будет похож на фронтальный контроллер в том, что мы будем регистрировать автозагрузчик и определять базовый адаптер для модели. Но важное отличие может заключаться в установке или конфигурации PHP.

По сравнению с активизацией PHP с веб-сервера, PHP из командной строки обычно выполняется при помощи другого прикладного интерфейса - SAPI (Server Application Programming Interface). В зависимости от вашей установки и конфигурации это может быть другой параметр include_path. По этой причине необходимо установить ini-параметр include_path и включить в него каталог /column/include, в который вы установили файлы Zend Framework. Примечание. Помните, что способ определения include_path будет зависеть от вашей операционной системы. Приведенный ниже пример предназначен для Linux.

Создайте новый каталог под названием /column/protected/zend/scripts. Этот каталог будет использоваться для хранения вашего контроллера сценариев и всех создаваемых дополнительных файлов, имеющих отношение к сценариям. Поскольку в данное время мы создаем только одно задание, начнем с создания файла prune.php в новом каталоге сценариев. Верхняя половина этого файла будет содержать основной код контроллера сценариев.

<?php

ini_set('include_path', ini_get('include_path') . ':/column/include');

require_once("Zend/Loader.php");
Zend_Loader::registerAutoload();

$params = array(
'host' => 'localhost',
'username' => 'frameworks',
'password' => 'fwpw',
'dbname' => 'zend'
);

$db = Zend_Db::factory('PDO_MYSQL', $params);

Zend_Db_Table::setDefaultAdapter($db);


Остальная часть этого файла будет содержать простое задание, которое мы хотим выполнять. Подключите и создайте экземпляр модели posts, создайте выражение where и выполните стандартный запрос delete.

require_once("/column/protected/zend/models/Posts.php");

$posts = new Posts();

$where = $posts->getAdapter()->quoteInto('modified < ?',
date('Y-m-d H:i:s', strtotime('-30 days')));

$posts->delete($where);

?>


После этого можно выполнить этот сценарий из командной строки: php /column/protected/zend/scripts/prune.php.
Если можно выполнить этот сценарий из командной строки, значит можно сделать то же самое в пакетном задании или в планировщике cron. Запись crontab для выполнения этого сценария в полночь могла бы выглядеть следующим образом: 00 00 * * * php /column/protected/zend/scripts/prune.php.

После создания контроллера сценариев появляется множество вариантов выполнения сценариев с использованием Zend Framework. Можно использовать те же самые модели, которые использует остальная часть вашего приложения, создать при необходимости сложные объекты script и т.д.

Теперь вы имеете четкое представление о том, как выполнить cron-задание в Zend. А теперь перейдем к symfony.

внешние задания в symfony

Когда мы хотели выполнить сценарий командной строки в Zend Framework, то создавали контроллер сценариев, который используется вместо фронтального контролера. В symfony делается почти то же самое.

Создайте каталог для хранения сценариев symfony-версии приложения Blahg (/column/protected/sf_column/apps/blahg/scripts/) и файл под названием prune.php, который будет нашим новым контроллером сценариев. Первая половина этого файла во многом будет похожа на созданный фронтальный контроллер symfony. Мы определим необходимые константы и подключим (оператор require) конфигурационный файл. Отличие состоит в способе получения экземпляра symfony. Во фронтальном контроллере мы указывали symfony искать контроллер и перенаправлять запрос. Для контроллера сценариев это не нужно.

<?php

define('SF_ROOT_DIR', '/column/protected/sf_column');
define('SF_APP', 'blahg');
define('SF_ENVIRONMENT', 'prod');
define('SF_DEBUG', false);
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR
.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
sfContext::getInstance();


Затем мы создадим базовый объект Criteria для передачи в модель post и выполним delete.

$c = new Criteria();
$c->add(PostPeer::MODIFIED, date("Y-m-d H:i:s",
strtotime("-45 days")), Criteria::LESS_THAN);
$posts = PostPeer::doDelete($c);


Сохраните файл. Он готов к работе. Теперь мы можем выполнить сценарий из командной строки, во многом аналогично тому, как делали это в Zend Framework:

php /column/protected/sf_column/apps/blahg/scripts/prune.php


Вызов этого задания из планировщика тоже похож на вызов в среде Zend Framework (естественно, нужно указать сценарий symfony вместо сценария Zend). Для cron это могло бы выглядеть так:

00 00 * * * php /column/protected/sf_column/apps/blahg/scripts/prune.php.

Наконец, давайте рассмотрим, как сделать то же самое в CakePHP.

внешние задания в CakePHP

Консоль Cake является новой функциональной возможностью в CakePHP V1.2, которая предоставляет интерфейс командной строки для среды Cake. Для создания ваших собственных заданий командной строки создается так называемая программа-оболочка (shell). Она во многом похожа на уже созданные вами контроллеры.

Начните с создания файла prune.php в каталоге /column/protected/cakephp/app/vendors/shells. Это наша новая программа-оболочка под названием prune, которая будет использоваться для удаления всех сообщений, созданных более 30 суток тому назад. Определите новый класс PruneShells, который расширяет класс shell. Поскольку мы собираемся удалять сообщения, программа-оболочка должна использовать модель post, которую можно указать при помощи переменной $uses. По умолчанию, когда вы указываете Cake выполнить программу-оболочку и не передаете специфичное действие, Cake ищет метод main и, если он существует, выполняет его. На данном этапе пустая программа-оболочка выглядит следующим образом:

<?php

class PruneShell extends Shell {
var $uses = array('Post');

function main() {
}
}
?>


Теперь все, что нужно сделать - это добавить код в метод main для удаления всех сообщений, созданных более 30 суток тому назад.

$conditions = array (
"Post.modified" => "< " . date("Y-m-d H:i:s", strtotime("-30 days"))
);
$this->Post->deleteAll($conditions);


Для выполнения этого сценария из командной строки мы указываем Cake, что хотим выполнить программу-оболочку prune. Поскольку весь код находится в методе main, он будет выполняться по умолчанию. Мы должны также указать Cake, где находится каталог app. Это делать не обязательно, если команды выполняются из каталога app, но cron-задание не будет работать, если этот каталог не указать явно:

/column/protected/cakephp/cake/console/cake prune -app /column/protected/cakephp/app/.


Планирование cron-задания для выполнения этой программы-оболочки в полночь может выглядеть примерно так:

00 00 * * *
/column/protected/cakephp/cake/console/cake prune -app /column/protected/cakephp/app/.


ПРИМЕЧАНИЕ. Если добавить каталог /column/protected/cakephp/cake/console в PATH, не нужно будет указывать полный путь, что значительно облегчает работу с консолью. Указание полного пути в пакетном задании или cron-задании будет гарантировать корректное выполнение независимо от пользователя, выполняющего задание.

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

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



Дуэйн Обрайен, PHP-разработчик, независимый писатель. Впервые опубликовано на IBM developerWorks
обсудить статью
© сетевые решения
.
.