О руководстве

  • Форум
  • Wiki

Пекарня. Все о CakePHP

Руководство разработчика на русском языке

Это руководство предназначено для людей решивших начать программировать на PHP при помощи такого замечательного фреймворка, как CakePHP, но не владеющих в совершенстве английским языком. Данная документация является переводом официальной документации на русский язык.

С любыми предложениями, замечаниями или т.п. пишите на support@cake-php.ru.


Предисловие

1. Аудитория

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

Это руководство требует всего навсего самых базовых знаний PHP и HTML. Также очень приветствуется хотя бы знакомство с шаблоном программирования Model-View-Controller (Модель-Отображение-Контроллер), но если вы не знаете что это такое - ничего страшного. Это руководство, конечно же, будет помогать вам в настройке и устранении ошибок на ваших веб-серверах, но тем не менее это руководство не содержит абсолютно всей информации о Cake.

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

2. CakePHP бесплатен!

CakePHP бесплатен. Вы не должны платить за него, вы можете использовать его как пожелаете. CakePHP разработан под MIT License. CakePHP это проект Open Source, что значит, что у вас есть полный доступ к исходному коду. Лучшее место, где можно достать самую новейшую версию - это на сайте CakePHP (http://www.cakephp.org).

3. Сообщество

CakePHP написан работающим в поте лица сообществом людей. Они пришли из разных стран со всего мира, собрались воедино, чтобы создать фреймворк CakePHP для того, чтобы он принес пользу как можно более широкому кругу людей. Чтобы узнать больше об активных разработчиках продукта и его пользователях посетите http://www.cakephp.org.

Наш IRC канал всегда наполнен знаниями и дружелюбными людьми. Если вы застряли на куске кода, нуждаетесь в том, чтобы выговориться или хотите провести дискуссию о написании кода, заходите: мы с радостью вас выслушаем. Посетите нас на #cakephp на irc.freenode.com.


Введение в CakePHP

1. Что такое CakePHP?

CakePHP - это программное обеспечение с открытым кодом, фреймворк для быстрой разработки на PHP. Это состав библиотек, классов и набор run-time средств для программистов, создающих веб-приложения. CakePHP - это результат работы, вдохновленной платформой Ruby on Rails. Наша главная задача - это открыть перед вами возможность работать в структурированной и быстрой манере - без потери в гибкости.

2. Почему CakePHP?

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

  1. Активное, дружелюбное сообщество

  2. Гибкое лицензирование

  3. Совместимость с PHP4 и PHP5

  4. Интегрирован CRUD для взаимодействия с базой данных и упрощения запросов

  5. Скаффолдинг приложений

  6. Архитектура Model-View-Controller (MVC)

  7. Диспетчер запросов, с возможностью создавать и использовать человеко-понятные ссылки (ЧПУ)

  8. Встроенная верификация данных

  9. Быстрое и гибкое шаблонизирование (PHP синтаксис, с хелперами)

  10. Хелперы, выполняющие за вас рутину по работе с AJAX, Javascript, формами HTML и многим другим

  11. Компоненты безопасности, сессий, и обработки запросов

  12. Гибкие списки контроля доступа(Access Control Lists)

  13. Обработка данных

  14. Гибкое кеширование отображений (View Caching)

  15. Работает из любой поддиректории веб-сайта, без вовлечения конфигурации Apache

3. История CakePHP

В 2005 году, Михаэль Татаринович написал первую версию Быстрого Прикладного Фреймворка (Rapid Application Framework) на PHP. Он заметил, что это было очень удачным стартом. Михаэль опубликовал его под MIT License, окрестив свое детище Cake, и открыл его для сообщества разработчиков, которые сейчас поддерживают Cake под именем CakePHP.


Базовые концепции

1. Введение

Эта глава - короткое введение в концепции MVC а именно как они реализованы в Cake. Если Вы новичок в MVC (Model-View-Controller), то эта глава безоговорочно для вас. Мы начнем с подробного обсуждения основных MVC концепций, перейдем к специфике приложений MVC в CakePHP, и покажем некоторые простые примеры CakePHP с использованием MVC.

2. MVC

Model-View-Controller (Модель-Отображение-Контроллер) - это программный шаблон, который помогает вам логически разделить ваш код, сделать его многократно используемым и подлежащим корректировке, в общем улучшить. Впервые MVC был описан группой авторов единомышленников Gang of Four (Банда Четырех). Dean Helman писал (фрагмент из официального описания Objective Toolkit Pro):

"MVC - это метод разделения приложения, или даже куска его интерфейса на три части: модель, отображение, и контроллер. MVC была изначально разработана для того, чтобы "отразить" роль традиционных приложений в современные реалии графического интерфейса пользователя (GUI).

Ввод -> Обработка -> Вывод

Контроллер -> Модель -> Отображение

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

В терминологии Cake, Модель означает обычную таблицу/запись в базе данных, а также ее отношения к другим таблицам и записям. Модель также включает в себя правила верификации данных, которые применяются когда данные модели добавляются или обновляются. Отображение - это результат конечный вывод обработанной информации на экран пользователя, т.е. это обычные HTML-файлы, в которые внедрен PHP-код. Контроллер Cake управляет запросами сервера. Такой расклад позволяет пользователю вводить данные (URL и POST), затем применяется Модель для чтения и записи данных В и ИЗ базы данных и других источников, и, в конце-концов, обработанная информация попадает к файлам отображения.

Чтобы максимально упростить это, организовать ваше приложение, Cake использует этот шаблон не только для управления тем как объекты взаимодействуют с вашим приложением, но также и тем как сохранены файлы, более детально об этом дальше.

3. Обзор схемы размещения файлов Cake

Когда вы распакуете Cake на свой сервер вы увидите там три основных каталога -

      app
      cake
      vendors

В каталоге cake находятся основные библиотеки Cake, вам не понадобится их трогать.

Каталог app будет служить местом хранения ваших приложений и нужных им каталогов и файлов. Разделение между каталогами cake и app позволит вам иметь множество каталогов с приложениями, которые используют те же самый библиотеки Cake. Это также упрощает процесс обновления CakePHP: вам достаточно только закачать последнюю версию Cake и перезаписать главные библиотеки в папке cake. Нет нужды волноваться, что вы перезапишите что-то, что вы написали для своего приложения.

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

Давайте взглянем на полную схему размещения файлов:

/app
    /config          - Содержит файлы config для вашей базы данных, ACL, и т.д.

    /controllers     - Контроллеры хранятся тут
        /components  - Компоненты хранятся тут

    /index.php       - Позволяет вам использовать cake с /app как DocumentRoot

    /models          - Модели хранятся тут

    /plugins         - Плагины хранятся тут

    /tmp             - Используется для кэша и log'ов

    /vendors         - Содержит сторонние библиотеки для приложений

    /views           - Отображения хранятся тут
        /elements    - Элементы, немного отображений хранятся тут
        /errors      - Ваши настроенные error-страницы хранятся тут
        /helpers     - Хелперы хранятся тут
        /layouts     - Схемы размещения файлов приложений хранятся тут
        /pages       - Статические отображения хранятся тут
    /webroot         - DocumentRoot для приложений
        /css
        /files
        /img
        /js

/cake                - Центральные библиотеки Cake. Не изменяйте ни один из файлов!!!.

index.php

/vendors             - Используется для сторонних библиотек.

VERSION.txt          - Позволяет вам узнать какую версию Cake вы используете.


Установка CakePHP

1. Введение

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

Эта глава опишет, что должно быть установлено на сервере, различные пути настройки сайта, закачки и установки CakePHP, а также некоторые советы по устранению ошибок (вдруг что-то пойдет не так, как запланировано).

2. Требования

Вот требования к настройке сервера для того, чтобы запустить CakePHP:

  1. HTTP сервер (любой, но лучше Apache) с поддержкой сессий и mod_rewrite (не обязательно, но предпочтительно)
  2. PHP 4.3.2 или выше. Да, CakePHP отлично работает как с PHP 4 так и с PHP 5.
  3. Движок базы данных (сейчас поддерживаются MySQL, PostgreSQL и обвертка для ADODB).

3. Установка CakePHP

Загрузка

Есть несколько способов получить копию CakePHP: взять релиз с сайта CakeForge, получить ночную сборку, или скачать свежую версию кода из SVN-хранилища.

Чтобы скачать стабильную версию кода, проверьте секцию с фалами CakePHP проекта CakeForge, посетив http://cakeforge.org/projects/cakephp/.

Ночную сборку можно скачать с http://cakephp.org/downloads/index/nightly. Ночные релизы стабильны и очень часто включают в себя исправления ошибок между официальными релизами.

Чтобы получить свежую копию из нашего SVN хранилища, воспользуйтесь своим любимым SVN клиентом и подключитесь к https://svn.cakephp.org/repo/trunk/cake/. Затем выберите версию поновее.

Распаковка

Теперь, когда вы скачали самый последний релиз, разместите запакованный файл на свой веб-сервер в корневой веб-каталог (webroot). Теперь вам нужно распаковать CakePHP. Есть 2 варианта расположения файлов: для разработчика, что позволит вам отображать множество CakePHP-приложений под одним доменом, или же используя "среду продукции", что позволит отображать одно CakePHP-приложение на домене.

4. Настройка CakePHP

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

ЗАМЕЧАНИЕ: /app/tmp должен быть доступным для записи пользователем

Среда разработчика

Чтобы использовать Cake только для разработки мы можем разместить всю установочную директорию в определенный DocumentRoot как то:

/wwwroot
    /cake
        /app
        /cake
        /vendors
        .htaccess
        index.php

В этом случае каталог wwwroot выступает как веб-корень, поэтому ваши ссылки будут похожи на эту (если вы конечно используете mod_rewrite):

www.example.com/cake/controllerName/actionName/param1/param2

Среда продукции (итог разработки)

Для использования среды продукции, вам понадобятся права для изменения DocumentRoot на вашем сервере. Это делает весь домен одним приложением CakePHP.

В этом случае файлы размещаются так:

../path_to_cake_install
    /app
        /config
        /controllers
        /models
        /plugins
        /tmp
        /vendors
        /views
        /webroot <-- Это должно быть вашим новым DocumentRoot
        .htaccess
        index.php
    /cake
    /vendors
    .htaccess
    index.php

Предлагаемая настройка httpd.conf

DocumentRoot /path_to_cake/app/webroot

В этом случае директория webroot выступает как веб-корень, так что ваши ссылки будут выглядеть подобно этой (если вы используете mod_rewrite):

http://www.example.com/controllerName/actionName/param1/param2

Продвинутая настройка: Альтернативная установка опций

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

Вот три главные части для приложений Cake:

  1. Центральные библиотеки CakePHP - Расположены в /cake
  2. Ваш код приложений (например: контроллеры, модели, схемы размещения, отображения) - распложены в /app
  3. Файлы webroot вашего приложения (например: картинки, javascript и css) - расположены в /app/webroot

Каждая из этих директорий может быть расположена где угодно в вашей файловой системе, за исключением webroot, который должен быть доступен вашему веб-серверу. Вы даже можете переместить каталог webroot из каталога app, только не забудь сообщить об этом Cake. :-)

Для того чтобы разнести эти папки, вам понадобится сделать изменения в /app/webroot/index.php. Вот три константы, которые вам понадобится изменить: ROOT, APP_DIR, and CAKE_CORE_INCLUDE_PATH.

  1. ROOT - это путь к директории которая содержит каталог app.
  2. APP_DIR - тут следует указать путь к каталогу app.
  3. CAKE_CORE_INCLUDE_PATH - это путь к каталогу библиотек Cake.

/app/webroot/index.php (частичный, комментарии удалены)

if (!defined('ROOT'))
{
    define('ROOT', dirname(dirname(dirname(__FILE__))));
}

if (!defined('APP_DIR'))
{
    define ('APP_DIR', basename(dirname(dirname(__FILE__))));
}

if (!defined('CAKE_CORE_INCLUDE_PATH'))
{
    define('CAKE_CORE_INCLUDE_PATH', ROOT);
}

На примере проиллюстрировать будет проще. Представьте, что я хочу заставить Cake работать со следующими настройками:

  1. Хочу чтобы мои библиотеки Cake работали с другими приложениями, находясь в /usr/lib/cake.

  2. Моя директория webroot Cake должна быть /var/www/mysite/.

  3. Файлы моих приложений будут храниться в /home/me/mysite.

Т.е. файлы будут располагаться так:

/home
    /me
        /mysite                  <-- была /cake_install/app
            /config
            /controllers
            /models
            /plugins
            /tmp
            /vendors
            /views
            index.php
/var
    /www
        /mysite                  <-- была /cake_install/app/webroot
            /css
            /files
            /img
            /js
            .htaccess
            css.php
            favicon.ico
            index.php
/usr
    /lib
        /cake                    <-- была /cake_install/cake
            /cake
                /config
                /docs
                /libs
                /scripts
                app_controller.php
                app_model.php
                basics.php
                bootstrap.php
                dispatcher.php
            /vendors

Чтобы все это заработало необходимо чуть-чуть поменять index.php (в этом примере он находится в /var/www/mysite/index.php):

Рекомендуется использовать 'DS'-константу вместо слешей чтобы разграничивать пути к файлам. Это предотвратит ошибки типа 'missing file', которые обычно возникают, если используется неверный разделитель, также это делает ваш код более переносимым.
if (!defined('ROOT'))
{
    define('ROOT', DS.'home'.DS.'me');
}

if (!defined('APP_DIR'))
{
    define ('APP_DIR', 'mysite');
}

if (!defined('CAKE_CORE_INCLUDE_PATH'))
{
    define('CAKE_CORE_INCLUDE_PATH', DS.'usr'.DS.'lib'.DS.'cake');
}

5. Настройка Apache и mod_rewrite

Пока мы разрабатывали работу CakePHP с mod_rewrite было замечено, что некоторые пользователи испытывают большие затруднения при работе с этим модулем, в итоге ничего не работает. Вот несколько советов:

  1. Убедитесь, что разрешено изменение файлов .htaccess: в вашем httpd.conf должен быть раздел, который описывает секцию для каждой директории на вашем сервере. Убедитесь, что AllowOverride установлено на All для нужной директории.

  2. Убедитесь что вы изменяете системный httpd.conf, а не пользовательский httpd.conf отдельного сайта.

  3. По тем или иным причинам у вас может быть копия CakePHP, в которой нет файлов .htaccess. Это иногда случается из-за того, что некоторые операционные системы рассматривают файлы, которые начинаются с '.' как скрытые и не копируют их. Убедитесь, что ваша копия CakePHP содержит эти файлы.

  4. Убедитесь, что вы загружаете mod_rewrite правильно! Вам следует взглянуть в httpd.conf. Там должны быть строки вроде LoadModule rewrite_module libexec/httpd/mod_rewrite.so и AddModule mod_rewrite.c.

  5. Если вы устанавливаете Cake в директорию пользователя (http://example.com/~myusername/), вам нужно подкоректировать .htaccess файл в главной директории. Просто добавьте строку "RewriteBase /~myusername/".

  6. Если вдруг в конце ваших ссылок появляется параметр с ID сессии (http://example.com/posts/?CAKEPHP=4kgj577sgabvnmhjgkdiuy1956if6ska), то можете дописать строку "php_flag session.trans_id off" в .htaccess файл в корневом каталоге.

6. Убедимся, что все работает

Хорошо, давайте посмотрим на Cake в действии. В зависимости от настроек, которые вы использовали направляем свой браузер на http://www.example.com или http://www.example.com/cake. На этой точке вам будет представленна страница CakePHP по умолчанию, а также сообщение, которое расскажет вам о статусе текущего подключения в базе данных.

Поздравляем! Теперь вы готовы к созданию вашего первого приложения на Cake.


Настройка

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

Файл настройки базы данных app/config/database.php расположен там же где и все основные настроечные файлы сайта. После новой установки файл database.php отсутствует, поэтому вам потребуется сделать копию файла database.php.default и переименовать ее в database.php. Скопировав и переименовав файл, вы увидите следующее:

app/config/database.php

var $default = array('driver'   => 'mysql',
                     'connect'  => 'mysql_connect',
                     'host'     => 'localhost',
                     'login'    => 'user',
                     'password' => 'password',
                     'database' => 'project_name',
                     'prefix'   => '');

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

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

CakePHP поддерживает следующие драйвера базы данных:

  1. mysql

  2. postgres

  3. sqlite

  4. pear-drivername (вы можете написать pear-mysql, например)

  5. adodb-drivername

Параметр 'connect' в $default позволяет указать устойчивым ли будет соединение с базой данных или нет. Комментарии в файле database.php.default расскажут вам о том, какой тип соединения следует использовать.

Таблицы вашей базы данных должны также соответствовать следующим требованиям:

  1. Имена таблиц, которые будут использованы Cake'ом должны состоять из английских слов во множественном числе, как "users", "authors" или "articles". Заметьте, что соответствующие модели необходимо называть в единственном числе.

  2. Во всех таблицах обязательно должно быть поле 'id', выставленное как primary key.

  3. Если вы планируете связать таблицы, используйте инородные ключи типа: 'article_id', то есть имя таблицы в единственном числе, затем подчеркивание, затем 'id'.

  4. Если вы включаете в вашу таблицу колонки 'created' и/или 'modified', Cake автоматически заполнит их в подходящий момент датами создания и изменения записи соответственно.

Вы можете так же поменять настройки в переменной $test, находящейся в том же database.php. Можно создать неограниченное количество настроек для соединения с БД в этом файле, а затем в контроллерах писать что-то вроде:

var $useDbConfig = 'test';

2. Глобальные настройки

Глобальные настройки CakePHP расположены в app/config/core.php. Как бы вы не хотели избежать настройки - он должен быть отредактирован. В этом файле есть несколько вещей, которые можно изменить, подсказки в комментариях в файле core.php.

Константа DEBUG: Параметр нужен для того чтобы помочь вам с выводом отладочной информации в процессе создания приложения. Если присвоить ему значения отличные от нуля, то Cake будет выводить результаты функций pr() и debug(), и установит автоматические сообщения. Значение 2 или выше будет выдавать результаты запросов SQL внизу страницы.

Также в отладочном режиме (debug mode) (где параметру DEBUG соответствует 1 или выше), Cake выдаст специально сгенерированные страницы ошибок, например "Отсутствует контроллер", "Отсутствует действие" и т.д. В режиме "готового продукта" (когда параметру DEBUG соответствует 0), Cake выдает страницу "Не найдено", которую можно изменить в файле app/views/errors/error404.thtml.

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

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

  1. high: время конца сессии через 10 минут неактивности, а id-номера сессии будут перегенерироваться при каждом запросе.

  2. medium: время конца сессии через 20 минут неактивности

  3. low: время конца сессии через 30 минут неактивности

CAKE_SESSION_SAVE: Установите предпочтительный способ сохранения данных сессий. Возможные значения:

  1. cake: Данные сессии сохраняются в tmp/ в корневом каталоге Cake.

  2. php: Данные сессии сохраняются так, как это указано в php.ini

  3. database: Данные сессии сохраняются в базу данных, соединяясь с ней по настройкам, указанным в переменной 'default'.

3. Настройка роутов

"Routing" это упрощенный чисто-PHP mode_rewrite - который может преобразовывать ссылки в контроллер/действия/параметры и назад. Эта возможность была добавлена в Cake, чтобы сделать красивые ссылки более настраиваемыми и отделить нас от обязательного mod_rewrite. Однако, использование mod_rewite сделает вашу адресную строку более аккуратной и чистой.

Роуты это индивидуальные правила, которые преобразовывают ссылки в специфические контроллеры и действия. Роуты настраиваются в файле app/config/routes.php. Установки выглядят примерно так:

Образец роута

<?php
$Route->connect (
    'URL',
    array('controller'=>'controllername',
    'action'=>'actionname', 'firstparam')
);
?>

Где:

  1. URL это регулярное выражение Cake, ссылка которую вы хотите преобразовать,

  2. controllername это имя контроллера, который вы хотите активизировать,

  3. actionname это имя действия контроллера, которое вы хотите активизировать,

  4. и firstparam это значения первого параметра действия, которое вы вызываете.

Любые параметры, следующие за firstparam также будут вставлены как параметры действия контроллера.

Следующий пример трансформирует все ссылки начинающиеся с /blog в контроллер BlogController. По умолчания действие будет BlogController::index().

Пример роута

<?php
$Route->connect ('/blog/:action/*', array('controller'=>'Blog', 'action'=>'index'));
?>
Ссылкой вида /blog/history/05/june можно управлять так:

Управление роутом в контроллере

<?php
class BlogController extends AppController
{
 function history ($year, $month=null)
 {
   // .. Отобразить подходящий контент
 }
}
?>

'history' из ссылки подбирается посредством :action route Blog'a. Элементы ссылки подбираются * к активному методу управления параметрами контроллера, отсюда $year и $month. Вызванная ссылкой /blog/history/05 функция history() будет использовать лишь один параметр 05.

Следующий пример это роут по умолчанию в CakePHP используется для установки роута для PagesController::display('home'). Домашняя страница (Home) это отображение, которое можно изменить созданием файла /app/views/pages/home.thtml.

Установка роута по умолчаний

<?php
$Route->connect ('/', array('controller'=>'Pages', 'action'=>'display', 'home'));
?>

4. Продвинутая настройка роута: Admin Routing и веб-службы

Есть несколько настроек в /app/config/core.php благодаря которым вы можете сделать ваши ссылки более понятными для вас и ваших посетителей.

Первая из них это admin routing. Если ваши приложения используют ProductsController или NewsController, вы можете решить сделать несколько ссылок для пользователей с привилегиями администратора, которые дадут доступ к некоторым действиям этих контроллеров. Для того, чтобы ссылки имели приятный и простой вид некоторые люди отдают предпочтение /admin/products/add и /admin/news/post, нежели /products/adminAdd или /news/adminPost.

Чтобы это сделать, первым делом, раскомментируйте строку CAKE_ADMIN в файле /app/config/core.php. По умолчанию ее значение 'admin', но вы можете установить любое, которое вам нравится. Запомните строку, она понадобится вам в ваших административных действиях в вашем контроллере. Так что, действия администратора в этом случае будут именоваться admin_actionName(). Вот некоторые примеры настроек ссылок, CAKE_ADMIN и контроллера:

/admin/products/add          CAKE_ADMIN = 'admin'
                             имя действия в ProductsController = 'admin_add()'

/superuser/news/post         CAKE_ADMIN = 'superuser'
                             имя действия в NewsController = 'superuser_post()'

/admin/posts/delete          CAKE_ADMIN = 'admin'
                             имя действия в PostsController = 'admin_delete()'

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

$this->params[CAKE_ADMIN];
или
$this->params['admin'];
(полагая что 'admin' это значение CAKE_ADMIN).

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

Так же, вы можете включить веб-сервисы роутов в Cake. У вас есть действие контроллера, которое вы хотите сделать видимым? Первое, что нужно сделать это установить значение WEBSERVICES 'on' в /app/config/core.php. Это откроет некоторые автоматические роуты, чем-то напоминающие routing админа, за исключением того, что точные настройки приставок роутов являются доступными:

  1. rss

  2. xml

  3. rest

  4. soap

  5. xmlrpc

Что это дает? Это позволяет вам предоставлять альтернативные отображения, которые будут автоматически доступны из /rss/controllerName/actionName или /soap/controllerName/actionName. Это позволяет вам создавать одно действие, которое будет иметь два отображения: одно для обычных просмотрщиков HTML, другое для пользователей веб-сервисов. Делая это, вы можете с легкостью дать большую функциональность ваших приложений пользователям через веб-сервисы.

Например, скажем у меня есть кое-какой код в моем приложении, который говорит пользователям о том кто звонит мне в офис. У меня уже есть HTML-отображение для этих данных, но я хочу предложить эту функцию еще и в XML, чтобы этой услугой можно было воспользоваться из виджетов для рабочего стола. Первое, что мне надо, это включить routing веб-сервисов Cake'а:

/app/config/core.php (частично)

/**
 *  define ниже используется для включения веб-сервисов cake
 *  допустимые значания on или off. По умолчанию off.
 */
    define('WEBSERVICES', 'on');

Далее, я могу писать свой код как обычно:

messages_controller.php

<?php
class PhonesController extends AppController
{
    function doWhosOnline()
    {
        // тут мы производим действия чтобы узнать кто сейчас звонит...

        // Если я хочу сделать доступным это действие через xml webservices route Cake'а,
        // мне необходимо будет включить отображение в /app/views/posts/xml/do_whos_online.thtml.
        // Замечание: отображение по умолчанию, используемое здесь, находится в /app/views/layouts/xml/default.thtml.

        // Если пользователь сделает запрос /phones/doWhosOnline, он получит HTML версию.
        // Если пользователь сделает запрос /xml/phones/doWhosOnline,он получит XML версию.
    }
}
?>

5. (Опционально) Свои настройки инфлекций

Система именования Cake может быть очень удобной - вы можете назвать свою модель Box, свой контроллер Boxes - и все работает. Есть возможность (особенно для наших, не говорящих по-английски, друзей), когда вы можете столкнуться с ситуацией, где инфлектор Cake'а (класс который делает множественное число, единичное число и подчеркивание) может работать не так как вы этого хотели. Если Cake не может понять во множественном или единственном числе вы даете ему слово, все что вам нужно сделать это изменить по своему настройки инфлекторов.

Найдите файл /app/config/inflections.php - это список переменных Cake, которые вы можете использовать для настройки множественного/единственного числа имен классов в Cake, вместе с объявлением терминов, которые не нужно вообще инфлектировать (например Fish или Deer (олень, олени)) вместе с неправильными глаголами.

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


Модели

1. Что такое модель?

Что она делает? Модель разделяет область кода от представления, отделяя код приложения.

Модель это общая точка доступа к базе данных, а именно, к определенной таблице в базе данных. По умолчанию, каждая модель использует таблицу, чье имя - это множественное число от имени модели, например: модель 'User' использует таблицу 'users'. Модель может также включать в себя правила верификации данных, информацию об ассоциациях, и индивидуальные методы к таблице, которую использует. Вот как простая модель 'User' может выглядить в Cake:

Пример модели User, расположение - /app/models/user.php

<?php

//AppModel дает вам всю функциональность моделей Cake

class User extends AppModel
{
    // Всегда полезно включать эту переменную.
    var $name = 'User';

    // Это используется для верификации данных, подробнее в соответсвующей главе.
    var $validate = array();

    // Вы также можете объявить ассоциации.
    // Посмотрите раздел 6.3 для более подробной информации.

    var $hasMany = array('Image' =>
                   array('className' => 'Image')
                   );

    // Вы также можете включить собственные функции:
    function makeInactive($uid)
    {
        //Вставьте свой код сюда...
    }
}

?>

2. Функции моделей

Со стороны PHP, модели это классы расширения AppModel класса. AppModel класс изначально объявлен в директории cake/, но если вы хотите создать собственный, поместите его в app/app_model.php. Он должен включать в себя методы, которые будут делиться двумя или более моделями. Он сам по себе расширит класс Модели которая находится в стандартной библиотеке Cake в cake/libs/model.php.

Этот раздел будет рассказывать о самых частоиспользуемых функциях моделей Cake, не стоит забывать, что полезно использовать http://api.cakephp.org для полной справки.

Функция User-Defined

Пример индивидуального метода таблицы в модели - это пара методов для скрывания/отображения постов в блоге

Пример функции модели

<?php
class Post extends AppModel
{
   var $name = 'Post';

   function hide ($id=null)
   {
      if ($id)
      {
          $this->id = $id;
          $this->saveField('hidden', '1');
      }
   }

   function unhide ($id=null)
   {
      if ($id)
      {
          $this->id = $id;
          $this->saveField('hidden', '0');
      }
   }
}
?>

Извлечение ваших данных

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

  • findAll
  • string $conditions
  • array $fields
  • string $order
  • int $limit
  • int $page
  • int $recursive

Возвращает определенные поля количеством не больше $limit, соответсвующих $conditions, начинается просмотр со страницы $page (по умолчанию 1). $conditions должна выглядить как бы она выглядела в SQL-запросе: $conditions="race='wookie' AND thermal_detonators > 3", например.

Когда опция $recursive установлена на целое значение выше 1, операция findAll() попытается вернуть модели ассоциированные с найденным операцией findAll(). Если у ваших данных несколько владельцев, то рекурсив операции findAll() в вашей модели Property возвратит все ассоциированные модели.

  • find
  • string $conditions
  • array $fields
  • string $order
  • int $recursive

Возвращает определенные (или же все, если нету специфики) поля из первой записи, соответствующие $conditions.

Если опция $recursive установлена на целое значение между 1 и 3, операция find() попытается вернуть модели ассоциированные с найденными операцией find(). Рекурсивный поиск может работать на три уровня вглубь. Если у ваших данных несколько владельцев, рекурсивная операция find() в вашей модели Property вернет ассоциативные модели до трех уровней вглубь.

  • findAllBy<fieldName>
  • string $value

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

$this->Post->findByTitle('My First Blog Post');
$this->Author->findByLastName('Rogers');
$this->Property->findAllByState('AZ');
$this->Specimen->findAllByKingdom('Animalia');

Результатом будет масив, форматированный как в find() или findAll().

  • findNeighbours
  • string $conditions
  • array $field
  • string $value

Вовзращает масив с соседними моделями (только с определенными полями), определенными по $field и $value, отсортированными по SQL-условиям, $conditions.

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

class ImagesController extends AppController
{
    function view($id)
    {
        // Скажем мы хотим показать изображение...

        $this->set('image', $this->Image->find("id = $id");

        // Но также мы хотим чтобы у нас были предыдущие и следующие изображения...

        $this->set('neighbours', $this->Image->findNeighbours(null, 'id', $id);

    }
}

Это дает полный масив $image['Image'], вместе с $neighbours['prev']['Image']['id'] и $neighbours['next']['Image']['id'] в нашем отображении.

  • field
  • string $name
  • string $conditions
  • string $order

Возвращает как строку одно поле из первой записи, соответствующей $conditions отсортированных по $order.

  • findCount
  • string $conditions

Возвращает количество записей, что соответствуют данному условию.

  • generateList
  • string $conditions
  • string $order
  • int $limit
  • string $keyPath
  • string $valuePath

Это функция - ярлык к получению списка пар ключевых значений - в особенности для создания тега select в html из списка ваших моделей. Используйте параметры $conditions, $order и $limit также как в findAll(). $keyPath и $valuePath для указания модели, где именно искать ключи и значения для генерирования списка. Например, если вы хотите сгенерировать список ролей, базируемый на модели Role. Полный запрос может выглядеть похоже на это:

$this->set(
    'Roles',
    $this->Role->generateList(null, 'role_name ASC', null, '{n}.Role.id', '{n}.Role.role_name')
);

//Это может вернуть что-то похожее на это:
array(
    '1' => 'Account Manager',
    '2' => 'Account Viewer',
    '3' => 'System Manager',
    '4' => 'Site Visitor'
);
  • read
  • string $fields
  • string $id

Используйте эту функцию, чтобы получить поля и их значения из ныне загруженной записи, или записи определенной по $id.

Пожалуйста отметьте что операция read() сделает выборку только с первого уровня ассоциативных моделей, не обращая внимания на значение $recursive в модели. Чтобы получить дополнительные уровни, используйте find() и findAll().

  • query
  • string $query
  • execute
  • string $query

Настраеваемые SQL-запросы можно сделать, используя query() и методы execute() модели. Разница между ними в том, что query() используется для настройки SQL-запросов (результаты которой возвращаются), а execute() используется для настройки SQL-команд (которые не требует возврата результатов).

Настройка запросов SQL с query()

<?php
class Post extends AppModel
{
    var $name = 'Post';

    function posterFirstName()
    {
        $ret = $this->query("SELECT first_name FROM posters_table
                                 WHERE poster_id = 1");
        $firstName = $ret[0]['first_name'];
        return $firstName;
    }
}
?>

Сложные условия поиска (используя масивы)

Большинство модельных поисковиков подразумевают вовлечение пересылки параметров условий разными способами. Простейший способ приблизиться к этому - использовать фрагмент SQL-выражения WHERE, но если вам нужно больше контроля, вы можете использовать масивы. Масивы яснее и легче читать, а также легче строить запросы. Этот синтаксис также разбивает элементы запроса (поля, значения, операторы и т.д.) на более понятные, манипулируемые части. Это позволяет Cake генерировать более результативные запросы, гарантируя подходящий синтаксис SQL.

Самое простой масивный запрос выглядит так:

Пример использования самых простых условий поиска используя масив:

$conditions = array("Post.title" => "This is a post");

//Пример использования с моделью:
$this->Post->find($conditions);

Структура даже не требует объяснений: это найдет любой пост, название которого будет соответсвовать "This is a post". Отметьте, что мы могли использовать только "title" как имя поля, но создавая запросы, полезно указывать и имя модели, так как это улучшает четкость кода и помогает предотвратить несостыковки в будущем. Что насчет других типов соответствий? Все так же просто. Скажем мы хотим найти все посты где в названии нету "This is a post":

array("Post.title" => "<> This is a post")

Все что мы добавили это '<>' перед выражением. Cake может проанализировать любой действительный оператор SQL, включая выражения как LIKE, BETWEEN или REGEX, так же как вы оставляете место между оператором и выражением или значением. Одно исключение в стиле (...) соответсвия. Скажем вы хотите найти посты, названия которых соответсвуют одному из списка значений:

array("Post.title" => array("First post", "Second post", "Third post"))

Добавление дополнительных фильтров в условия это так же просто как добавления дополнительных пар ключ/значение в масив:

array
(
    "Post.title"   => array("First post", "Second post", "Third post"),
    "Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
)

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

array
("or" =>
    array
    (
        "Post.title" => array("First post", "Second post", "Third post"),
        "Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
    )
)

Cake примает все действующие логические операторы SQL, включая AND, OR, NOT, XOR и т.д., и они могут быть в верхнем или нижнем регистре, как вам больше нравится. Скажем у вас есть hasMany/belongsTo отношения между Posts и Authors, результат поиска в Posts будет в LEFT JOIN. Скажем, вы хотите найти все посты, что содержат ключевое слово или которые были созданы в прошедшие две недели, но вы хотите ограничиться постами, написанными Бобом:

array
("Author.name" => "Bob", "or" => array
    (
        "Post.title" => "LIKE %magic%",
        "Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks")
    )
)

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

Чтобы сохранить что-либо, вам нужно снабдить сохранение данными, которые нужно записать. Данные передаваемые в метод save() должны быть размещены в следующей форме:

Array
(
    [ModelName] => Array
        (
            [fieldname1] => 'value'
            [fieldname2] => 'value'
        )
)

В случае, если вам нужно чтобы ваши данные были отправлены в контроллер таким способ, будет проще использовать HTML Хелпер, потому что он создает элементы формы, называя их именно так, как ожидает этого Cake. Вы не обязаны его использовать, однако: только убедитесь, что имена элементов формы выглядят как data[Modelname][fieldname]. Однако, использование $html->input('Model/fieldname') - проще.

Форма данных автоматически форматируется и помещается в $this->data в ваш контроллер, так что сохранение ваших данных из веб-формы - это дело одного клика. Функция редактирования для вашего собственного контроллера может выглядить так:

function edit($id)
{

   //Заметьте: Нужная модель автоматически загружена для нас в $this->Property.

   // Проверяем есть ли у нас форма для данных...
   if (empty($this->data))
   {
        $this->Property->id = $id;
        $this->data = $this->Property->read();//размножаем поля формы для данной строки
   }
   else
   {
      // Вот здесь мы пытаемся сохранить наши данные. Автоматическая верификация.
      if ($this->Property->save($this->data['Property']))
      {
         //Выводить сообщение и перенаправление.
         $this->flash('Ваша информация сохранена.',
                     '/properties/view/'.$this->data['Property']['id'], 2);
      }
      //если в каких-то полях данные не правильные или сохранение провалилось, форма сообщит
   }
}

Обратите внимания как операция сохранения помещена внутрь условия: когда вы пытаетесь сохранить данные в модель, Cake автоматически пытается проверить их, используя правила, которые ему предоставили. Узнать больше о проверке данных, вы можете в главе "Верификация данных". Если вы не хотите чтобы save() пытался проверить ваши данные, используйте save($data, false)

Другие полезные функции сохранения:

  • del
  • string $id
  • boolean $cascade

Удаляет модель, выбранную по $id.

Если эта модель ассоциирована с другими моделями, и ключ зависимости был установлен в ассоциативном масиве, этот метод также удалил те ассоциативные модели, если $cascade присвоено значение true.

Возвращает true при успешном выполнении.

  • saveField
  • string $name
  • string $value

Используется для сохранения значения отдельного поля.

  • getLastInsertId

Возвращает ID записи, которая создана последней.

Колбеки модели

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

  • beforeFind
  • string $conditions

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

  • afterFind
  • array $results

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

  • beforeValidate

Используйте этот колбек для модифицирования данных перед их проверкой. Это также используется для добавления дополнительных, более сложных правил проверки, используя Model::invalidate(). В этом контексте, модель данных доступна через $this->data. Эта функция также должна возвращать true, иначе выполнение save() будет отменено.

  • beforeSave

Поместите любой передующий сохранению код в эту функцию. Эта функция выполняется сразу после проверки данных (только в случае что данные были проверены, иначе save() отменяется, а этот колбек не выполняется), но перед тем как данные сохранены. Это функция также должна возвращать true если вы хотите, чтобы операция сохранения продолжалась, и false если хотите чтобы была отменена.

Одно из использований beforeSave может быть формирования времени и даты для сохранения в специфическом месте базы данных:

// Дата/время поля созданы HTML Хелпером:
// Этот код будет виден в отображении

$html->dayOptionTag('Event/start');
$html->monthOptionTag('Event/start');
$html->yearOptionTag('Event/start');
$html->hourOptionTag('Event/start');
$html->minuteOptionTag('Event/start');

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

// Функция колбека модели используемая для того чтобы сшить
// данные о дате вместе для сохранения
// Этот код будет виден в модели Event:

function beforeSave()
{
    $this->data['Event']['start'] = $this->_getDate('Event', 'start');

    return true;
}

function _getDate($model, $field)
{
    return date('Y-m-d H:i:s', mktime(
        intval($this->data[$model][$field . '_hour']),
        intval($this->data[$model][$field . '_min']),
        null,
        intval($this->data[$model][$field . '_month']),
        intval($this->data[$model][$field . '_day']),
        intval($this->data[$model][$field . '_year'])));
}
  • afterSave

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

  • beforeDelete

Поместите любой передующий удалению код в эту функцию.Это функция должна возвращать true если вы хотите чтобы удаление продолжилось или false, чтобы отменилось.

  • afterDelete

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

3. Переменные модели

Есть несколько специальных переменных, которые вы можете установить, если хотите получить доступ к функциональности Cake, создавая свою модель:

$primaryKey

Если эта модель относится к таблице базы данных, и главный ключ (primary key) не называется 'id', используйте эту переменную, чтобы сообщить Cake'у имя главного ключа.

$recursive

Это устанавливает число уровней, которые вы хотите чтобы Cake выбирал в операциях find() и findAll().

Представьте что у вас есть группы (Groups) в которых множество пользователей (Users), у которых будет много статей (Articles).

Model::recursive options

$recursive = 0 | Cake делает выборку данных группы
$recursive = 1 | Cake делает выборку группы и ассоциирует ее с пользователями
$recursive = 2 | Cake делает выборку группы, ассоциирует ее с пользователями, а пользователи ассоциируются со статьями

$transactional

Говорит Cake'у делать ли доступным транзакции для этой модели (например начать/совершить/откатить). Значение логического типа. Доступно только для баз данных с поддержкой.

$useTable

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

$validate

Масив, используемый для проверки входящих в эту модель данных. См. главу "Верификация данных".

$useDbConfig

Помниче что настройки базы данных можно устанавливать в /app/config/database.php? Используйте эту переменную, чтобы переключаться между ними - просто используйте имя переменной с настройками соединения с базой данных, которую вы создали в конфигурационном файле. По умолчанию, вы должны были догадаться, 'default'.

4. Ассоциации

Введение

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

Есть четыре типа ассоциаций в CakePHP:

  1. hasOne

  2. hasMany

  3. belongsTo

  4. hasAndBelongsToMany

Когда ассоциации между моделями объявлены, Cake автоматически сделает выборку моделей пребывающих в отношении с моделью, с которой вы работаете. Например, если модель Post имеет отношения к модели Author используя тип ассоциации hasMany, делая запрос к $this->Post->findAll() в контроллере будет сделана как выборка записей Post, так и всех записей Author, к которым они относятся.

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

Вот обзор того, что Cake ожидает от имен этих разных элементов: (взгляните на "Соглашение Cake" для подробной информации о присвоении имен)

  1. Сторонние ключи(Foreign Keys): [имя модели в единичном числе]_id. Например, сторонний ключ в таблицы "authors", возвращаясь к теvе о постах принадлежащих авторам, будет именоваться "post_id".

  2. Имена таблиц(Table Names): [имя объекта во множественном числе]. С тех пор как нам понадобилось хранить информацию о постах блога и их авторах, имена таблиц "posts" и "authors" соответсвенно.

  3. Имена моделей(Model Names): [CamelCased, имя таблицы в единичном числе]. Имя модели для таблицы "posts" - "Post", а имя модели для таблицы "authors" - "Author".

Скаффолдинг CakePHP ожидает, что ваши ассоциации построены в том же порядке, что и колонки. То есть, если у меня есть Articles с типом ассоциаций belongsTo, то есть принадлежит трем другим моделям (Author, Editor и Publisher), мне понадобятся три ключа: author_id, editor_id и publisher_id. Скаффолдинг ожидает, что ваши ассоциации происходят в том же порядке, что и ключи в таблице (наприер, сначала Author, второй Editor и последний Publisher).

Чтобы показать как некоторые из этих ассоциаций работают, давайте продолжим использовать приложение блога как пример. Представьте, что мы собираемся создать простую систему управления пользователями блога. Я предполагаю это будет не для того, чтобы следить за пользователями, а для того чтобы у каждого пользователя был ассоциированный профиль (Profile, пользователь hasOne профиль). Пользователи также смогут создавать коментарии оставляя ассоциацию с ними (пользователь hasMany коментарии). Сделав пользовательскую систему, мы переходим к тому, чтобы позволить постам относится к тег-объектам, используя тип ассоциаций hasAndBelongsToMany (имеетИПринадлежитМногим, то есть пост имеет и принадлежит многим тегам).

Объявления и запросы с hasOne

Что бы установить эту ассоциацию, мы предполагаем что вы уже создали модели User и Profile. Чтобы объявить hasOne-ассоциацию между ними, нам понадобится добавить масив к модели, чтобы сообщить Cake'у как они будут относится. Вот как это выглядит:

/app/models/user.php hasOne

<?php
class User extends AppModel
{
    var $name = 'User';
    var $hasOne = array('Profile' =>
                        array('className'    => 'Profile',
                              'conditions'   => '',
                              'order'        => '',
                              'dependent'    =>  true,
                              'foreignKey'   => 'user_id'
                        )
                  );
}
?>

Масив $hasOne это то, что Cake использует для создания ассоциации между User и Profile моделями. Каждый ключ в масиве позволяет настроить ассоциацию:

  1. className (обязателен): имя класса модели, которую вы хотите ассоциировать

    Для нашего примера имя класса модели 'Profile'.

  2. conditions: фрагменты условий SQL, которые объявляют отношения

    Мы можем использовать это, чтобы сообщить Cake'у что ассоциировать нужно только Profile с зеленым заголовком, если захотим. Чтобы объявить условие подобное этому, нужно присвоить фрагменту SQL-условия значение для этого ключа: "Profile.header_color = 'green'".

  3. order: упорядочивание ассоциатируемых моделей

    Если мы хотим присвоить специфический порядок ассоциатируемым моделям, устанавливает значение этого ключа, используя выражение упорядочивания SQL: "Profile.name ASC", например.

  4. dependent: если установленно true, то ассоциатируемая модель уничтожается, когда уничтожается первая.

    Например, если профиль "Cool Blue" ассоциатирован с "Bob", и я удаляю пользователя "Bob", профиль "Cool Blue" тоже удаляется.

  5. foreignKey: имя стороннего ключа, что указывает на ассоциатируемую модель.

    Вот оно, если вы используете базу данных, не следуя схеме присвоения имен CakePHP.

Теперь когда мы выполняем запросы find() или findAll(), используя Profile модель, мы также увидим здесь и ассоциативную модель User:

$user = $this->User->read(null, '25');
print_r($user);

//исходящее:

Array
(
    [User] => Array
        (
            [id] => 25
            [first_name] => John
            [last_name] => Anderson
            [username] => psychic
            [password] => c4k3roxx
        )

    [Profile] => Array
        (
            [id] => 4
            [name] => Cool Blue
            [header_color] => aquamarine
            [user_id] = 25
        )
)

Объявления и запросы с belongsTo

Теперь пользователь может увидить свой профиль, нам нужно объявить ассоциацию так, чтобы пользователь мог видеть только свой профиль. Это делается использованием типа ассоциаций belongsTo. В модели Profile, мы делаем следующее:

/app/models/profile.php belongsTo

<?php
class Profile extends AppModel
{
    var $name = 'Profile';
    var $belongsTo = array('User' =>
                           array('className'  => 'User',
                                 'conditions' => '',
                                 'order'      => '',
                                 'foreignKey' => 'user_id'
                           )
                     );
}
?>

Масив $belongsTo это то, что Cake использует для создания ассоциаций между User и Profile моделями. Каждый ключ в масиве позволяет вам настраивать ассоциацию:

  1. className (обязателен): имя класса модели, которую вы хотите ассоциировать

    В нашем примере это 'User'.

  2. conditions: фрагменты условий SQL, которые объявляют отношения

    Мы можем использовать это чтобы сообщить Cake'у что нужно ассоциировать только с активным пользователем. Вы можете сделать это, присвоением ключу значения: "User.active = '1'", или чем-то подобным.

  3. order: упорядочивание ассоциатируемых моделей

    Если вы хотите придать специфическое упорядочивание ассоциируемым моделям, установите значение для этого ключа, используя выражения SQL для упорядочивания: "User.last_name ASC", например.

  4. foreignKey: имя стороннего ключа, которое указывает на ассоциируемую модель.

    Вот оно, если вы не следуете схеме присвоения имен.

Теперь, когда мы выполняем запросы find() или findAll(), используя Profile модель, мы должны увидить нашу ассоциированную модель User:

$profile = $this->Profile->read(null, '4');
print_r($profile);

//исходящее:

Array
(

    [Profile] => Array
        (
            [id] => 4
            [name] => Cool Blue
            [header_color] => aquamarine
            [user_id] = 25
        )

    [User] => Array
        (
            [id] => 25
            [first_name] => John
            [last_name] => Anderson
            [username] => psychic
            [password] => c4k3roxx
        )
)

Объявления и запросы с hasMany

Теперь User и Profile модели ассоциированы и работают как надо, давайте создадим систему, в которой записи пользователей будут ассоциированы с записями коментариев. Это делается в модели User так:

/app/models/user.php hasMany

<?php
class User extends AppModel
{
    var $name = 'User';
    var $hasMany = array('Comment' =>
                         array('className'     => 'Comment',
                               'conditions'    => 'Comment.moderated = 1',
                               'order'         => 'Comment.created DESC',
                               'limit'         => '5',
                               'foreignKey'    => 'user_id',
                               'dependent'     => true,
                               'exclusive'     => false,
                               'finderQuery'   => ''
                         )
                  );

    // Вот отношение hasOne, которое мы объявили раньше...
    var $hasOne = array('Profile' =>
                        array('className'    => 'Profile',
                              'conditions'   => '',
                              'order'        => '',
                              'dependent'    =>  true,
                              'foreignKey'   => 'user_id'
                        )
                  );
}
?>

Масив $hasMany это то, что использует Cake для создания ассоциаций между User и Comment моделями. Каждый ключ масива позволяет вам настроить ассоциацию:

  1. className (Обязателен): имя класса модели, которую вы хотите ассоциировать

    В нашем примере это 'Comment'.

  2. conditions: фрагменты услвоий SQL, которые объявляют отношения

    Мы можем использовать это для того, чтобы сообщить Cake'у, что нужно ассоциировать только коментарии, которые могут быть модерированы. Это можно сделать присвоением ключу значения "Comment.moderated = 1", или что-то схожее.

  3. order: упорядочивание ассоциатируемых моделей

    Если вы хотите придать специфическое упорядочивание ассоциируемым моделям, установите значение ключа, используя SQL-выражения для упорядочивания: "Comment.created DESC", например.

  4. limit: максимальное количество ассоциируемых моделей, которые вы хотите чтобы Cake добавил в выборку.

    Для этого примеры, мы не хотим отбирать все коментарии пользователя, а всего пять.

  5. foreignKey: имя стороннего ключа, которое указывает на ассоциируемую модель.

    Вот оно, если вы не следуете схеме присвоения имен.

  6. dependent: если установленно значение true, то ассоциируемая модель будет удалена в случае, если будет удалена первая.

    К примеру есть профиль "Cool Blue" ассоциированный с "Bob", и я удаляю пользователя "Bob", профиль "Cool Blue" тоже будет удален.

  7. exclusive: Если установленно значение true, все ассоциированные объекты будут удалены во время одного запроса SQL минуя их beforeDelete колбек.

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

  8. finderQuery: установите полное SQL-выражение для добавления в выборку ассоциации.

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

Теперь когда мы выполняем запросы find() и findAll(), используя модель User, мы должны увидить все ассоциированные коментарии:

$user = $this->User->read(null, '25');
print_r($user);

//исходящее:

Array
(
    [User] => Array
        (
            [id] => 25
            [first_name] => John
            [last_name] => Anderson
            [username] => psychic
            [password] => c4k3roxx
        )

    [Profile] => Array
        (
            [id] => 4
            [name] => Cool Blue
            [header_color] => aquamarine
            [user_id] = 25
        )

    [Comment] => Array
        (
            [0] => Array
                (
                    [id] => 247
                    [user_id] => 25
                    [body] => The hasMany assocation is nice to have.
                )

            [1] => Array
                (
                    [id] => 256
                    [user_id] => 25
                    [body] => The hasMany assocation is really nice to have.
                )

            [2] => Array
                (
                    [id] => 269
                    [user_id] => 25
                    [body] => The hasMany assocation is really, really nice to have.
                )

            [3] => Array
                (
                    [id] => 285
                    [user_id] => 25
                    [body] => The hasMany assocation is extremely nice to have.
                )

            [4] => Array
                (
                    [id] => 286
                    [user_id] => 25
                    [body] => The hasMany assocation is super nice to have.
                )

        )
)

Хоть мы и не документируем процес, но было бы очень полезно объявить ассоциацию Comment belongsTo User, чтобы каждая из моделей видела друг друга.

Объявления и запросы с hasAndBelongsToMany

Тперь когда вы освоили простейшие ассоциации, давайте перейдем к последнему типу ассоциаций hasAndBelongsToMany (или HABTM). Эта последняя ассоциация - самая сложная, но также и одна из самых полезных. Ассоциация НАВТМ полезна когда у вас есть две модели, которые связаны между собой связной таблицей. Связная таблица содержит индивидуальные строки, которые относятся друг к другу.

Разница между hasMany и hasAndBelongsToMany в том, что с hasMany ассоциированная модель не общая. Если User hasMany Comments, это *только* пользователь ассоциирован с теми коментариями. С НАВТМ, ассоциированная модель - общая. Это хорошо для того, что мы будем делать дальше: ассоциировать модель Post с моделью Tag. Пока модель Tag принадлежит модели Post, мы не хотим чтобы она была 'истощена', мы хотим продолжить, ассоциировать ее с другими постами.

Для этого там нужно установить коректные таблицы для это ассоциации. Конечно нам понадобится таблица "tags" для модели Tag, и таблица "posts" для постов, но также нам будет нужна связная таблица для этой ассоциации. Схема присвоения имен для связных таблиц НАВТМ это [имя первой модели во множественном числе]_[имя второй модели во множественном числе], где имена моделей должны идти в алфавитном порядке:

Связные таблицы HABTM: Простые модели и имена их связных таблиц

  1. Posts и Tags: posts_tags

  2. Monkeys и IceCubes: ice_cubes_monkeys

  3. Categories и Articles: articles_categories

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

Вот как будут выглядить дампы SQL для нашего Posts HABTM Tags примера:

--
-- Table structure for table `posts`
--

CREATE TABLE `posts` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `user_id` int(10) default NULL,
  `title` varchar(50) default NULL,
  `body` text,
  `created` datetime default NULL,
  `modified` datetime default NULL,
  `status` tinyint(1) NOT NULL default '0',
  PRIMARY KEY  (`id`)
) TYPE=MyISAM;

-- --------------------------------------------------------

--
-- Table structure for table `posts_tags`
--

CREATE TABLE `posts_tags` (
  `post_id` int(10) unsigned NOT NULL default '0',
  `tag_id` int(10) unsigned NOT NULL default '0',
  PRIMARY KEY  (`post_id`,`tag_id`)
) TYPE=MyISAM;

-- --------------------------------------------------------

--
-- Table structure for table `tags`
--

CREATE TABLE `tags` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `tag` varchar(100) default NULL,
  PRIMARY KEY  (`id`)
) TYPE=MyISAM;

Давайте объявим ассоциацию в модели Post с нашими настройками таблиц:

/app/models/post.php hasAndBelongsToMany

<?php
class Post extends AppModel
{
    var $name = 'Post';
    var $hasAndBelongsToMany = array('Tag' =>
                               array('className'    => 'Tag',
                                     'joinTable'    => 'posts_tags',
                                     'foreignKey'   => 'post_id',
                                     'associationForeignKey'=> 'tag_id',
                                     'conditions'   => '',
                                     'order'        => '',
                                     'limit'        => '',
                                     'unique'       => true,
                                     'finderQuery'  => '',
                                     'deleteQuery'  => '',
                               )
                               );
}
?>

Масив $hasAndBelongsToMany это что Cake использует для создания ассоциации между моделями Post и Tag. Каждый ключ таблицы позволяет вам настроить ассоциацию:

  1. className (обязателен): имя класса модели, которую вы хотите ассоциировать

    Для нашего примера это будет 'Tag'.

  2. joinTable: это для случая если база данных не использует схему присвоения имен Cake. Если имя вашей таблицы не похоже на [имя первой модели во множественном числе]_[имя второй модели во множественном числе], вы можете вписать сюда имя вашей связной таблицы.

  3. foreignKey: имя стороннего ключа в связной таблице которое направляет на данную модель.

    Вот оно, если вы не следуете схеме присвоения имен.

  4. associationForeignKey: имя стороннего ключа, который направляет на ассоциируемую модель.

  5. conditions: фрагменты условий SQL, которые объявляют отношения

    Мы можем использовать это, чтобы сообщить Cake'у что нужно ассоциировать с тегами, которые были одобрены. Вы можете это сделать присваивая значение ключу: "Tag.approved = 1", или что-то схожее.

  6. order: упорядочивание ассоциируемых моделей

    Если вы хотите придать определенное упорядочивание ассоциирумым моделям, установите значение ключа, используя выражения SQL для упорядочивания: "Tag.tag DESC", например.

  7. limit: максимальное количество ассоциирумых моделей, которое вы хотите чтобы Cake добавил в выборку.

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

  8. unique: если установлено значение true, дублирование ассоциированых объектов будет игнорироваться средствами доступа и запросами.

    В основном если ассоциации отдельные, устанавливайте true. В этом случае тег "Awesomeness" может быть приписат только посту "Cake Model Assosiations" и только раз, и будет виден только единожды в масиве результатов.

  9. finderQuery: установите полное выражение SQL для добавления в выборку ассоциации.

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

  10. deleteQuery: полный запрос SQL, который будет использован для удаления ассоциаций между НАВТМ моделями.

    Если вам не нравится как Cake представляет удаление, или вы хотите настроить это по-другому, вы можете изменить ход работы удаления, введя свой запрос здесь.

Теперь выполняя find() или findAll() запросы, используя модель Post, мы должны увидеть также ассоциированную модель Tag:

$post = $this->Post->read(null, '2');
print_r($post);

//исходящее:

Array
(
    [Post] => Array
        (
            [id] => 2
            [user_id] => 25
            [title] => Cake Model Associations
            [body] => Time saving, easy, and powerful.
            [created] => 2006-04-15 09:33:24
            [modified] => 2006-04-15 09:33:24
            [status] => 1
        )

    [Tag] => Array
        (
            [0] => Array
                (
                    [id] => 247
                    [tag] => CakePHP
                )

            [1] => Array
                (
                    [id] => 256
                    [tag] => Powerful Software
                )
        )
)

Сохранение данных ассоциированых моделей

Важно запомнить одну вещь при работе с ассоциативными моделями. Это то, что сохранение данных должно происходить через соответствующую модель Cake. Если сохраняете новый пост и ассоциированные с ним коментарии, тогда вам нужно использовать обе модели Post и Comment во время операции сохранения.

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

/app/controllers/posts_controller.php (partial)

function add()
{
    if (!empty($this->data))
    {
        //Мы можем сохранить данные поста:
        //они должны быть в $this->data['Post']

        $this->Post->save($this->data);

        //Теперь нам нужно сохранить данные коментария
        //Но сначала нам нужно узнать ID
        //поста, который мы только что сохранили...

        $post_id = $this->Post->getLastInsertId();

        //Теперь мы добавляем эту информацию в сохраняемые данные
        //и сохраняем коментарий.

        $this->data['Comment']['post_id'] = $post_id;

        //Поскольку у нас Post hasMany Comments, мы можем получить доступ
        //к модели коментария через модель Post:

        $this->Post->Comment->save($this->data);

    }
}

Если, все же, родительская модель уже существует в системе (например, добавление коментария к существующему посту), нам нужно знать ID родительской модели перед сохранением. Мы можем поместить этот ID в ссылку или как скрытый элемент в форме...

/app/controllers/posts_controller.php (partial)

//Вот как будет выглядеть ссылка если мы используем ее для передачи параметра...
function addComment($post_id)
{
    if (!empty($this->data))
    {
        //Вы можете захотеть сделать данные $post_id более защищенными,
        //но этого будет достаточно для работающего примера..

        $this->data['Comment']['post_id'] = $post_id;

        //Поскольку у нашего Post hasMany Comments, мы можем получить доступ
        //доступ к модели Comment через модель Post:

        $this->Post->Comment->save($this->data);
    }
}

Если ID был пропущен как скрытый элемент формы, вы можете захотеть назвать поле (если используете HTML хелпер) это находится в конце данных, где и должно быть:

If the ID for the post is at $post['Post']['id']...

<?php echo $html->hidden('Comment/post_id', array('value' => $post['Post']['id'])); ?>

Таким образом, доступ к ID родительской модели Post можно получить от $this->data['Comment']['post_id'] и все готово к простому запросу $this->Post->Comment->save($this->data).

Этот самый способ будет работать если вы сохраняете несколько дочерних моделей, просто поместите их запрос save() в цикл (и не забудьте очистить информацию модели, используя Model::create()).

Подбивая итоги, если вы сохраняете ассоциированные данные (для belongTo, hasOne и hasMany типов ассоциаций), главное получение ID родительской модели и сохранение его в дочерней модели.

Сохранение hasAndBelongsToMany типа ассоциаций

Сохранение моделей, которые ассоциированы типами hasOne, belongTo и hasMany довольно просто: вы просто заполняете поле стороннего ключа ассоциированной модели. Когда это сделано вы просто делаете запрос save() на модели, и все связано коректно.

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

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

Когда вы сохраняете модель саму по себе в Cake, имя тега (если вы используете HTML Хелпер) выглядит как 'Модель/имя_поля'. Давайте просто начнем с создания формы, которая создаст наш пост:

/app/views/posts/add.thtml Форма для создания постов

<h1>Write a New Post</h1>
<table>
    <tr>
        <td>Title:</td>
        <td><?php echo $html->input('Post/title')?></td>
    </tr>
    <tr>
        <td>Body:<td>
        <td><?php echo $html->textarea('Post/body')?></td>
    </tr>
    <tr>
        <td colspan="2">
            <?php echo $html->hidden('Post/user_id', array('value'=>$this->controller->Session->read('User.id')))?>
            <?php echo $html->hidden('Post/status' , array('value'=>'0'))?>
            <?php echo $html->submit('Save Post')?>
        </td>
    </tr>
</table>

Форма как она есть сейчас будет просто создавать записи в Post. Давайте добавим кое-какой код, который позволит нам привязывать данный пост к одному или нескольким тегам:

/app/views/posts/add.thtml (Дополнительный код для ассоциирования с тегами)

<h1>Write a New Post</h1>
<table>
    <tr>
        <td>Title:</td>
        <td><?php echo $html->input('Post/title')?></td>
    </tr>
    <tr>
        <td>Body:</td>
        <td><?php echo $html->textarea('Post/body')?></td>
    </tr>
    <tr>
        <td>Related Tags:</td>
        <td><?php echo $html->selectTag('Tag/Tag', $tags, null, array('multiple' => 'multiple')) ?>
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <?php echo $html->hidden('Post/user_id', array('value'=>$this->controller->Session->read('User.id')))?>
            <?php echo $html->hidden('Post/status' , array('value'=>'0'))?>
            <?php echo $html->submit('Save Post')?>
        </td>
    </tr>
</table>

Для запроса $this->Post->save() в контроллере, чтобы сохранить связи между этим новым постом и ассоциацией с тегами, имя поля должно быть в форме "Tag/Tag" (отображаемое имя атрибута будет выглядить как 'data[ИмяМодели][ИмяМодели][]'). Подтвержденные данные должы быть одним ID или масивом ID-номеров связанных записей. Поскольку мы используем возможность выбора не одного тега, то подтвержденные данные для Tag/Tag будут масивом ID-номеров.

Переменная $tags здесь - просто масив где ключи это ID-номера возможных тегов, а значения это отображаемые имена тегов в многоэлементном выборе.

Смена ассоцииаций на лету, используя bindModel() и unbindModel()

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

Давайте сделаем несколько моделей так что мы сможем посмотреть как bindModel() и unbindModel() работают. Мы начнем с двух моделей:

leader.php и follower.php

<?php

class Leader extends AppModel
{
    var $name = 'Leader';

    var $hasMany = array(
        'Follower' => array(
            'className' => 'Follower',
            'order'     => 'Follower.rank'
        )
    );
}

?>

<?php

class Follower extends AppModel
{
    var $name = 'Follower';
}

?>

Теперь, в LeaderController, мы можем использовать find() в модели Leader. Как вы можете увидить выше, ассоциативный масив в модели Leader объявляет ассоциацию "Leader hasMany Followers". Для демонстрации замысла давайте используем unbindModel() чтобы убрать эту ассоциацию.

leaders_controller.php (частично)

function someAction()
{
    //Это делает выборку Leaders и ассоциированные с ними Followers
    $this->Leader->findAll();

    //Давайте уберем hasMany...
    $this->Leader->unbindModel(array('hasMany' => array('Follower')));

    //Теперь используя функцию поиска вернем Leaders, но уже без Followers
    $this->Leader->findAll();

    //ЗАМЕЧАНИЕ: unbindModel распространится только на следующую функцию поиска..
    //Дополнительный запрос поиска будет использовать настроенную ассоциированную информацию.

    //Мы уже использовали функцию findAll() после unbindModel(), так что это сделает выборку
    //Leaders с ассоциированными Followers еще раз...
    $this->Leader->findAll();
}

Функция unbindModel() работает также с остальными ассоциациями: просто измените названия типа ассоциаций и имя класса модели. Основное применение unbindModel():

Общий unbindModel() пример

$this->Model->unbindModel(array('associationType' => array('associatedModelClassName')));

Теперь мы успешно убрали ассоциацию на лету, давайте добавим. Наша модель Leader нуждается в какой-то ассоциации с Principles. Файл модели для модели Principles голый, за исключением одной переменной $name выражения. Давайте ассоциируем Principles с нашей Leader на лету (но только для следующего запроса функции поиска):

leaders_controller.php (частично)

funciton anotherAction()
{
    //Нету никакой Leader hasMany Principles в leader.php-файле модели, так что
    //поиск здесь просто сделает выборку Leaders.
    $this->Leader->findAll();

    //Давайте используем bindModel(), чтобы добавить ассоциацию к Principles модели:
    $this->Leader->bindModel(
        array('hasMany' => array(
                'Principle' => array(
                    'className' => 'Principle'
                )
            )
        )
    );

    //Теперь когда мы сделали коректную ассоциацию, мы можем использовать простую функцию поиска
    //чтобы сделать выборку Leaders с ассоциированными Principles:
    $this->Leader->findAll();
}

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

Вот теперь это есть у вас. Основное использование bindModel это герметизация обычного ассоциативного масива внутрь масива, чьи ключи названы после типа ассоциации, которую вы пытаетесь создать:

Общий bindModel() пример

$this->Model->bindModel(
        array('associationName' => array(
                'associatedModelClassName' => array(
                    // обычные ассоциативные ключи здесь...
                )
            )
        )
    );

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


Верификация данных

1. Верификация данных

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

Первый шаг к проверке данных это создания правил верификации в модели. Чтобы сделать это, воспользуйтесь масивом Model::validate в определении модели, например:

/app/models/user.php

<?php
class User extends AppModel
{
   var $name = 'User';

   var $validate = array(
      'login' => '/[a-z0-9\_\-]{3,}$/i',
      'password' => VALID_NOT_EMPTY,
      'email' => VALID_EMAIL,
      'born' => VALID_NUMBER
   );
}
?>

Верификация определяется Perl-совместимыми регулярными выражениями, некоторые из них уже определены в /libs/validators.php. Это:

  • VALID_NOT_EMPTY
  • VALID_NUMBER
  • VALID_EMAIL
  • VALID_YEAR

Если будут присутствовать еще какие-то верификации в определении модели (например, в масиве $validate), они будут проанализированы и проверены во время сохранений (например, в методе Model::save()). Если нужно проверить данные непосредственно, используйте Model::validates() (возвращает false в случае некорректных данных) и Model::invalidFields() (возвращает масив сообщений об ошибках).

Но обычно данные подразумеваются в коде контроллера. Следующий пример демонстрирует как создать действие управления формой:

Действие управления формой в /app/controllers/blog_controller.php

<?php
class BlogController extends AppController {

   var $uses = array('Post');

   function add ()
   {
      if (empty($this->data))
      {
         $this->render();
      }
      else
      {
         if($this->Post->save($this->data))
         {
             //все ок, проверено
         }
         else
         {
            //Опасность, Уилл Робинсон. Ошибки верификации.
            $this->set('errorMessage', 'Please correct errors below.');
            $this->render();
         }
      }
   }
}
?>

Отображение, используемое этим действием может выглядить так:

Отображение формы add в /app/views/blog/add.thtml

<h2>Add post to blog</h2>
<form action="<?php echo $html->url('/blog/add')?>" method="post">
    <div class="blog_add">
        <p>Title:
            <?php echo $html->input('Post/title', array('size'=>'40'))?>
            <?php echo $html->tagErrorMsg('Post/title', 'Title is required.')?>
        </p>
        <p>Body
            <?php echo $html->textarea('Post/body') ?>
            <?php echo $html->tagErrorMsg('Post/body', 'Body is required.')?>
        </p>
        <p><?=$html->submit('Save')?></p>
    </div>
</form>

Controller::validates($model[, $model...]) используется для настраиваемой проверки в модели. Метод Controller::validationErrors() возвращает все ошибки, их можно отобразить используя tagErrorMsg().

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

Контроллер может выглядить так:

<?php

class UsersController extends AppController
{
    function create()
    {
        // Проверяет были ли посланные данные из формы
        if (!empty($this->data['User']))
        {
            //Проверяет существует ли пользователь с таким именем
            $user = $this->User->findByUsername($this->data['User']['username']);

            // Помечаем поле как неверное, чтобы запустить сообщение об ошибке HTML Хелпера
            if (!empty($user['User']['username']))
            {
                $this->User->invalidate('username');//заполняем tagErrorMsg('User/username')
            }

            // Пробуем записать как обычно, не должно работать если поле было помечено как неверное.
            if($this->User->save($this->data))
            {
                $this->redirect('/users/index/saved');
            }
            else
            {
                 $this->render();
            }
        }
    }
}

?>


Контроллеры

1. Что такое контроллер?

Контроллер используется для управления кодом определенной части вашего приложения. Чаще всего, контроллеры используются для управления кодом отдельной модели. Например, если вы создаете сайт, который управляет видео коллекцией, у вас есть VideoController и RentalController для управления видео и прокатом соответственно. В Cake имена контроллеров всегда во множественном числе.

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

Класс AppController может быть объявлен в /app/app_controller.php и должен включать в себя методы, которые делятся между двумя или больше контроллерами. Сам по себе он расширяет класс контроллера, который находится в стандартной библиотеке Cake.

Действие это единица функциональности контроллера. Она автоматически запускается диспетчером (Dispatcher) если его запрашивают на странице. Возвращаясь к нашему примеру о видео-коллекции, наш VideoController может включать в себя действия view(), rent() и search(). Контроллер будет находиться в /app/controllers/videos_controller.php и будет содержать в себе:

class VideosController extends AppController
{
    function view($id)
    {
        //код действия здесь..
    }

    function rent($customer_id, $video_id)
    {
        //код действия здесь..
    }

    function search($query)
    {
        //код действия здесь..
    }
}

Вы сможете получить доступ к этим действиям, переходя по ссылкам:

http://www.example.com/videos/view/253
http://www.example.com/videos/rent/5124/0-235253
http://www.example.com/videos/search/hudsucker+proxy

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

2. Функции контроллера

В этой главе речь пойдет о наиболее часто используемых функция контроллера Cake. Для полной справки пройдите по ссылке http://api.cakephp.org.

Взаимодейсвие с вашими Отображениями

  • set
  • string $var
  • mixed $value

Это основной способ передать данные из контроллера в отображение. Вы можете использовать функцию для передачи всего: отдельных значений, целых масивов и т.п. Пример использования: set('color', 'blue') - создаст переменную $color, доступную в вашем отображении со значением 'blue'.

  • validateErrors

Возвращает количество ошибок сгенерированных неуспешным сохранением.

  • validate

Проверяет данные модели согласно правилам проверки данных модели. Больше о проверке (верификации) смотрите главу "Верификация данных"

  • render
  • string $action
  • string $layout
  • string $file

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

Перенаправление пользователей

  • redirect
  • string $url

Используя эту функцию вы сообщаете пользователю куда перенаправляться. Ссылка может быть как внутренней ссылкой Cake, так полной ссылкой (http://...).

  • flash
  • string $message
  • string $url
  • int $pause

Эта функция показывает $message на $pause внутри вашего флеш-слоя (можно найти в app/views/layouts/flash.thtml) потом перенаправляет пользователя по указанной $url.

Функции Cake redirect() и flash() не включают в себя запрос exit(). Если вы хотите, чтобы ваше приложение остановилось после redirect() или flash(), вам понадобится включить самостоятельно запрос exit(). В зависимости от ситуации вы можете использовать запрос return вместо exit() (например, если вам нужно выполнить какие-нибудь колбеки).

Колбеки контроллера

Контроллеры Cake дают возможность использовать множество колбеков, которые можно вставлять в код до или после важной функции. Чтобы использовать эту функциональность, объявите эти функции в вашем контроллере, используя параметры и возвращенные значения, описанные здесь.

  • beforeFilter

Вызывается перед каждым действием контроллера. Полезно использовать для проверки активных сессий и ролей.

  • afterFilter

Вызывается после каждого действия контроллера.

  • beforeRender

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

Другие полезные функции

Пока эти функции являются частью класса Объект Cake, они также доступны и внутри контроллера:

  • requestAction
  • string $url
  • array $extra

Эта функция вызывает действие контроллера из любого места и возвращает отрендеренное отображение. $url это ссылка Cake (/controllername/actionname/params). Если масив $extra включает в себя ключ 'return', AutoRender автоматически устанавливается на значение true для действия контроллера.

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

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

// Здесь наш простой контроллер:

class UsersController extends AppController
{
    function getUserList()
    {
        return $this->User->findAll();
    }
}

Представьте, что нам нужно создать простую таблицу, показывающую пользователей в системе. Вместо копирования кода в другой контроллер, мы можем получить данные из UsersController::getUserList() используя requestAction().

class ProductsController extends AppController
{
    function showUserProducts()
    {
        $this->set('users', $this->requestAction('/users/getUserList'));

        // Теперь переменная $users в отображении будет иметь данные из
        // UsersController::getUserList().
    }
}

Если у вас есть нечасто-используемый элемент в вашем приложении, можете вы захотите воспользоваться requestAction(), чтобы внедрить его в ваши отображения. Скажем это лучше чем просто вставлять данные из UsersController::getUserList, мы также хотим отрендерить отображение этого действия (которое может состоять из таблицы), в другой контроллер. Это спасет нас от копирование кода отображения.

class ProgramsController extends AppController
{
    function viewAll()
    {
        $this->set('userTable', $this->requestAction('/users/getUserList', array('return')));

        // Теперь мы можем отобразить $userTable в отображении действия
        // чтобы увидить отрендереное отображение, которое также доступно в /users/getUserList.
    }
}

Пожалуйста отметьте, что действие вызванное использованием requestAction() рендерится, используя пустое размещение - поэтому не стоит волноваться про размещение получая рендер в самом размещении.

Функция requestAction() также полезна в ситуации AJAX, где маленькие элементы отображения нужно заполнять до или во время обновления AJAX.

  • log
  • string $message
  • int $type = LOG_ERROR

Вы можете использовать эту функцию, чтобы записывать в лог разные события, которые произошли во время работы вашего веб-приложения. Логи можно найти в директории Cake /tmp.

Если $type равна PHP константе LOG_DEBUG, сообщение будет записано в лог как отладочное сообщение. Если не равна - как ошибка.

// В контроллере, можно использовать log():

$this->log('Mayday! Mayday!');

//Запись в лог:

06-03-28 08:06:22 Error: Mayday! Mayday!

$this->log("Look like {$_SESSION['user']} just logged in.", LOG_DEBUG);

//Запись в лог:

06-03-28 08:06:22 Debug: Looks like Bobby just logged in.
  • postConditions
  • array $data

Метод в который вы можете вписать $this->data, и он будет возвращен в форматированном масиве условий модели.

Например, если у меня есть форма поиска человека:

// app/views/people/search.thtml:

<?php echo $html->input('Person/last_name'); ?>

Подтверждая форму с этим элементом, в результати будет $this->data масив:

Масив
(
    [Person] => Array
        (
            [last_name] => Anderson
        )
)

На этом этапе мы можем использовать postConditions(), чтобы отформатировать эти данные и использовать в модели:

// app/controllers/people_controller.php:

$conditions = $this->postConditions($this->data);

// Выходной масив будет выглядить как:

Array
(
    [Person.last_name] => Anderson
)

// Который может использоваться операцией поиска в модели:

$this->Person->findAll($conditions);

3. Переменные контроллера

Манипулирование несколькими специальными переменными в вашем контроллере позволит вам получить пользу от некоторой дополнительной функциональности Cake:

$name

PHP 4 не любит давать нам имя данного класса в CamelClass (ВерблюжийКласс - то есть слова без пробелов, каждое слово с большой буквы). Используйте эту переменную для установки коректного имени CamelClass вашего класса если у вас возникли проблемы.

$uses

Использует ли ваш контроллер больше одной модели? Ваш FragglesController автоматически загрузит $this->Fraggle, но если вы также хотите получить доступ к $this->Smurf, попробуйте добавить что-то вроде следующего в ваш контроллер:

var $uses = array('Fraggle','Smurf');

Пожалуйста, отметьте что вам следует включить модель Fraggle в масив $uses, даже если она была автоматически доступна ранее.

$helpers

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

var $helpers = array('Html','Ajax','Javascript');

Помните, что вам будет нужно включить HTML Хелпер в масиве $helpers если вы собираетесь использовать его. Он обычно доступен по умолчанию, но если вы объявите $helpers без него, вы получите сообщение об ошибке в вашем отображении.

$layout

Установите этой переменной значение имени размещения, которое вы хотите использовать для контроллера.

$autoRender

Устанавливая false вы остановите автоматический рендеринг для ваших действий.

$beforeFilter

Если вам нужно чтобы кусок кода запускался каждый раз, когда вызвано действие (и перед тем, как запускается код этого действия), используйте $beforeFilter. Эта функциональность довольно полезна для контроля доступа - вы можете проверять привилегии пользователя до того как производить действие. Просто установите эту переменную используя масив состоящий из действий контроллера, которые вы хотите запускать:

class ProductsController extends AppController
{
    var $beforeFilter = array('checkAccess');

    function checkAccess()
    {
        //Код для проверки подлиности пользователя и доступа здесь....
    }

    function index()
    {
        //Когда вызывается это действие перед ним будет вызываться checkAccess().
    }
}

$components

Также как $helpers и $uses, эта переменная используется для загрузки компонентов, которые вам понадобятся:

var $components = array('acl');

4. Параметры контроллера

Параметры контроллера доступны в $this->params в вашем контроллере Cake. Эта переменная используется для отправки данных в контроллер и предоставления доступа к информации о текущем запросе. Чаще всего $this->params используется для получание доступа к информации, которая была передана контроллеру через POST или GET операции.

$this->data

Используется для управления POST данными, передаными формой HTML Хелпера в контроллер.

// HTML Хелпер используемый для создания элементов формы

$html->input('User/first_name');

// Отрендереный как HTML выглядит так:

<input name="data[User][first_name]" value="" type="text" />

// И когда данные отправляются в контроллер через POST,
// показывается в $this->data['User']['first_name']

Array
(
    [data] => Array
        (
            [User] => Array
                (
                    [username] => mrrogers
                    [password] => myn3ighb0r
                    [first_name] => Mister
                    [last_name] => Rogers
                )

        )
)

$this->params['form']

Любые POST данные из формы, хранятся здесь, включая информацию из $_FILES.

$this->params['bare']

Хранит '1' если текущее размещение это bare, '0' если нет.

$this->params['ajax']

Хранит '1' если текущее размещение это ajax, '0' если нет.

$this->params['controller']

Хранит имя текущего контроллера управляющего запросом. Например, если была запрошена ссылка /posts/view/1, $this->params['controller'] будет равен "posts".

$this->params['action']

Хранит имя текущего действия управляющего запросом. Например, если была запрошена ссылка /posts/view/1, $this->params['action'] будет равен "view".

$this->params['pass']

Хранит строку запроса GET работающего с текущим запросом. Например, если была запрошена ссылка /posts/view/?var1=3&var2=4, $this->params['pass'] будет равен "?var1=3&var2=4".

$this->params['url']

Хранит текущую ссылку, которая была запрошена, вместе с парами значений ключей переменных get. Например, если была запрошена ссылка /posts/view/?var1=3&var2=4, $this->params['url'] будет выглядить так:

[url] => Array
        (
            [url] => posts/view
            [var1] => 3
            [var2] => 4
        )


Отображения

Отображение это с страница-шаблон, обычно названная так же как и действие. Например, отображение для PostsController::add() будет /app/views/posts/add.thtml. Отображения Cake это довольно простые PHP-файлы, так что вы можете использовать любой PHP-код внутри них. Хотя большинство ваших файлов отображений и будут включать в себя HTML, но отображение может быть чем угодно: XML, изображения и т.д.

В файле отображения, вы можете использовать данные из соответствующей модели. Эти данные вставляются как вызванный масив $data. Любые данные переданные в отображение использованием set() в контроллере, теперь также доступны и в вашем отображении.

HTML Хелпер доступен в любом отображении по умолчанию, и является самым частоиспользуемым хелпером в отображениях. Он очень полезен при создании форм, включающий скрипты и медиа, связывающий и вспомогательный в проверке данных. Более подробно можете посмотреть в главе "Хелперы".

Большинство функций доступных в отображениях предоставлены Хелперами. Cake снабжен большим набором хелперов (о них в главе "Хелперы") и вы можете также включать собственные. Поскольку отображения не должны содержать много кода, есть немного используемых функций в классе отображений. Одна из полезных это renderElement(), о которой речь пойдет в разделе 1.2.

Верстка

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

Файлы верстки находятся в /app/views/layouts. Верстка по умолчанию Cake может быть заменена на новую в /app/views/layouts/default.thtml. Когда новая верстка по умолчанию создана, код отображения контроллера помещается внутрь верстки, когда страница рендерится.

Когда вы создаете верстку, вам нужно сообщать Cake'у где разместить код отображения вашего контроллера: чтобы сделать это, убедитесь, что ваша верстка влючает место для $content_for_layout (и опционально $title_for_layout). Вот пример того как может выглядить верстка по умолчанию:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><?php echo $title_for_layout?></title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
</head>
<body>

<!-- Если вы хотите чтобы какое-то меню показывалось во всех ваших отображениех, включите его сюда -->
<div id="header">
    <div id="menu">...</div>
</div>

<!-- Я хочу чтобы здесь показывались мои отображения -->
<?php echo $content_for_layout ?>

<!-- Добавляем нижний колонтитул для каждой отображаемой страницы -->
<div id="footer">...</div>

</body>
</html>

Самый простой способ установить заголовок для вашего отображения - это использование переменной $pageTitle из контроллера.

class UsersController extends AppController
{
    function viewActive()
    {
        $this->pageTitle = 'View Active Users';
    }
}

Вы можете создать столько видов верстки сколько только пожелаете для вашего сайта в Cake, просто поместите их в директорию app/views/layouts, и переключайтесь между ними в действиях вашего контроллера, используя переменную контроллера $layout, или функцию setLayout().

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

var $layout = 'default_small_ad';

Элементы

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

Элементы обитают в каталоге /app/views/elements/, имеют расширение .thtml.

Элемент по умолчанию не имеет никакого доступа к данным. Чтобы дать им доступ к данным, вы посылаете их с проименованым параметром в масив.

Вызов элемента без параметров

<?php echo $this->renderElement('helpbox'); ?>

Вызов элемента с вставленным масивом данных

<?php echo
$this->renderElement('helpbox', array("helptext" => "Oh, this text is very helpful."));
 ?>
Внутри файла элемента, все вставленные переменные доступны как имена ключей вставленного масива (больше похоже на set() в работе контроллеров с отображениями). В предыдушем примере, файл /app/views/elements/helpbox.thtml мог использовать переменную $helptext. Конечно, лучше вставлять масив в элемент.

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


Кеширование отображения

1. Что это?

С версии 0.10.9.2378_final, Cake поддерживает кеширование отображения (часто называется кешированием целой страницы). Нет, мы не шутим. Мы теперь можете кешировать свои размещения и отображения. Вы также можете определить части своего отображения, которые будут игнорироваться при кешировании. Возможность, при правильном использовании, позволяет ускорить ваше приложения на порядок.

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

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

2. Как все это работает?

Активирование кеша

По умолчанию кеширование отображений отключено. Включить его можно, изменив значение CACHE_CHECK в /app/config/core.php с false на true:

/app/config/core.php (частично)

define ('CACHE_CHECK', true);

Эта строка говорит Cake'у, что вы хотите включить кеширование.

В контроллере для отображений, которые вы хотите кешировать добавьте хелпер Cache в масив хелперов:

var $helpers = array('Cache');

Затем вам нужно определить что вы хотите кешировать.

Переменная $cacheAction контроллера

В этом разделе мы покажем вам как сообщить Cake'у, что кешировать. Это делается настройкой переменной контроллера, называемой $cacheAction. Переменная $cacheAction должна быть масивом, который содержит действия которые вы хотите кешировать, и время (в секундах) того, сколько хранить кешированные данные. Временное значение также может быть strtotime() (как '1 day' или '60 seconds').

Скажем у нас есть ProductsController с какими-то элементами, которые мы хотим кешировать. Следующие примеры покажут как использовать $cacheAction для того, чтобы дать команду Cake'у кешировать определенные части действий контроллера.

$cacheAction Примеры

//Кеш самых часто-посещаемых страниц за 6 часов:
var $cacheAction = array(
    'view/23/'  => 21600,
    'view/48/'  => 21600
);

//Кешировать целое действие. В это случае список продуктов recalled, на один день:
var $cacheAction = array('recalled/' => 86400);

//Если мы хотим, то можем кешировать каждое действие, установив это в строку:
//в которой strtotime() определяет время кеширования
var $cacheAction = "1 hour";

//вы также можете определять кеширование в действиях, используя $this->cacheAction = array()...

Маркировка содержимого в отображении

Есть исключения, где вы можете захотеть не кешировать какие-то части отображения. Если же у нас есть некие элементы, выделяющие новые продукты, или что-то вроде этого, вы может быть захотите сообщить Cake'у кешировать отображение... за исключением маленькой части.

<cake:nocache> </cake:nocache>.

<cake:nocache> example

<h1> New Products! </h1>
<cake:nocache>
<ul>
<?php foreach($newProducts as $product): ?>
<li>$product['name']</li>
<?endforeach;?>
</ul>
</cake:nocache>

Очистка кеша

Сначала вам полезно будет знать, что Cake автоматически очищает кеш если были сделаны изменения в базе данных. Например, если одно из отображений использует информацию из вашей модели Post, и там были сделаны INSERT, UPDATE или DELETE, Cake очистит кеш для этого отображения.

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

<cake:nocache> example

//Удаляет все кешированные страницы с именем controller.
clearCache('controller');

//Удаляет все кешированные станицы с именем controller_action.
clearCache('controller_action/');

//Удаляет все кешированные страницы с именем controller_action_params.
//Заметка: у вас может быть несколько параметров
clearCache('controller_action_params');

//Вы также можете использовать масив, чтобы удалить несколько кешей за раз.
clearCache(array('controller_action_params','controller2_action_params));

3. Для запоминания

Ниже есть несколько вещей, которые нужно запомнить о кешировании отображения:

  1. Для включения кеша вы устанавливаете CACHE_CHECK на true в /app/config/core.php .

  2. В контроллере для отображений, которые хотите кешировать вы добавляете Cache хелпер в масив хелперов.

  3. Для кеширование определенных ссылок, используйте $cacheAction в контроллере.

  4. Чтобы уберечь некоторые части отображения от кеширования, поместите их в теги <cake:nocache> </cake:nocache>

  5. Cake автоматически очищает определенные копии кеша, когда происходят изменения в базе данных.

  6. Для очистки кеша вручную, используйте clearCache().


Скаффолдинг

1. Что такое Скаффолдинг

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

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

<?php
class CategoriesController extends AppController
{
    var $scaffold;
}
?>

Очень важно знать следующее: скаффолдинг понимает, что любое поле в таблице заканчивающееся на _id - это сторонний ключ таблицы, имя которого написано перед символом подчеркивания. Вот, к примеру, если у вас есть вложенные категории, вы скорее всего создали колонку с именем parent_id. Но лучше назвать эту колонку parentid. Также, когда у вас есть сторонний ключ в вашей таблице (например, в таблице названий есть колонка category_id), и вы связали ее с соответствующей таблицей, в которой указаны эти самые названия, в скаффолдинге для этой таблицы в select box будут автоматически добавлены все категории в отображении show/edit/new. Чтобы установить которое поле из сторонней таблицы показывается, поставьте переменную $displayField в стороннюю модель. Продолжая наш пример о категориях названий:

<?php
class Title extends AppModel
{
    var $name = 'Title';

    var $displayField = 'title';
}
?>

2. Настройка отображений скаффолдинга

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

Если вы хотите изменить отображения скаффолдинга, вам придется разобраться самостоятельно:

Настройка отображений для отдельного контроллера

Настраиваемые отображения скаффолдинга для PostsController должны быть размещены так:
/app/views/posts/scaffold/index.scaffold.thtml
/app/views/posts/scaffold/show.scaffold.thtml
/app/views/posts/scaffold/edit.scaffold.thtml
/app/views/posts/scaffold/new.scaffold.thtml

Настройка отображений скаффолдинга для целого приложения

Должны быть размещены так:

/app/views/scaffold/index.scaffold.thtml
/app/views/scaffold/show.scaffold.thtml
/app/views/scaffold/edit.scaffold.thtml
/app/views/scaffold/new.scaffold.thtml

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

Одна возможность вам можете показаться полезной, это генератор кода Cake: Bake. Bake позволяет вам генерировать код скаффолдинга, который вы потом можете отредактировать под свои нужды и поместить в свое приложение.


Компоненты

1. Представление

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

Парень, именуемый olle в IRC-канале однажды сказал: Компонент это маленький общий "контроллер". Мы решили что это отличное определение. Главная цель - это возможность переиспользования. Компоненты это то же самое для контроллеров, что и хелперы для отображений. Главное отличие лишь в том, что компоненты внедряются в рабочий код, а хелперы в представляемый код. Это отличие очень важно, чаще всего начинающих бейкеров, пытающихся, добиться возможности переиспользования сбивает с толку: Я пытаюсь сделать X, это должно быть компонентом или хелпером?! Ну, ответ очень прост, что делает X? Это рабочий код или код представления, может быть оба? Если это рабочий код, значит это компонент. Если это код представления, значит хелпер. Если оба, тогда.. ну это тогда оба и компонент, и хелпер. Примером последнего случая будет система аутентификации. Вам нужно будет входить, выходить, разделять доступ, и тестировать привилегии пользователей (действия: edit, add, del.. или url), это рабочий код, так что эта система должна быть компонентом. Но также вы хотите добавить какое-то содержимое в главное меню, когда пользователь вошел, а это уже код представления.

2. Создание собственного компонента

Чтобы создать компонент добавьте файл в директорию app/controllers/components/.

Разрешите нам предположить, что вы создали foo.php. Внутри файла вам нужно определить класс, который будет соответствовать имени файла (добавление слова 'Component' к имени файла). Так что в вашем случае вы создадите следующее содержание:

Простой компонент

class FooComponent extends Object
{
    var $someVar = null;
    var $controller = true;

    function startup(&$controller)
    {
        // Этот метод ссылается на контроллер, который его загружает.
        // Выполните инициализацию контроллера здесь.
    }

    function doFoo()
    {
        $this->someVar = 'foo';
    }
}

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

    var $components = array('Foo');

Внутри этого котроллера вы теперь можете использовать:

    $this->Foo->doFoo();

Компонент получает доступ к контроллеру, который его загрузил через startup() метод, показаный выше. Этот метод вызывается сразу после Controller::beforeFilter(). Этот метод позволяет вам установить свойства компонента в beforeFilter, который компонент запустит при своем старте.

Чтобы использовать ваши модели внутри вашего компонента, вы можете создать привязку, вроде этой:

    $foo =& new Foo();

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

    var $components = array('Session');

3. Публикация своего компонента

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

Также загляните в snippet archive чтобы посмотреть на компоненты других пользователей.


Помощники

1. Хелперы

Хелперы предзназначены для предоставления функций, которые часто нужны в отображниях для форматирования и представления данных удобным сопобом.

HTML

Введение

HTML Хелпер, это один из способов Cake сделать разработку менее монотонной и более быстрой. HTML Хелпер имеет две основные цели: помочь в использовании повторяющихся разделов HTML кода, и помочь быстро и легко создавать веб-формы. Следующие разделы проведут вас через самые важные функции хелпера, но помните, что полный справочник всегда доступен по адресу http://api.cakephp.org.

Большинство функций в хелпере используют теги, определенные в файле под названием tags.ini.php. В основной конфигурации Cake содержится tags.ini.php, но если вы хотите внести какие-то изменения, создайте копию /cake/config/tags.ini.php и поместите ее в ваш каталог /app/config/. Хелпер использует теги, определенные в этом файле чтобы сгенерировать теги, которые вы запросили. Использования HTML Хелпера может быть полезным для создания некоторого кода для ваших отображений, также изменения в файле tags.ini.php выплывут в изменениях каскада сайта.

К тому же, если AUTO_OUTPUT имеет значение true в основном конфиг-файле вашего приложения (/app/config/core.php), хелпер будет автоматически выводить теги, а не возвращать значения. Это попытка удовлитворить тех, кто не любит короткие теги (<?= ?>) или множество запросов echo() в своих кодах отображений. Функции включающие параметр $return позволяют вам принудительно заменять настройки в вашем центральном конфиге. Установите $return true если хотите, чтобы хелпер возвратил HTML код не обращая внимания ни на какие настройки AUTO_OUTPUT.

Функции HTML Хелпера также включают в себя параметр $htmlAttributes, который позволяет вам присоединять дополнительные атрибуты к вашим тегам. Например, если у вас есть тег, к которому вы хотите добавить атрибут class, вы вставляете его в переменную $htmlAttribute как значение:

array('class'=>'someClass')

Добавление хорошо согласованных элементов

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

  • charset
  • string $charset
  • boolean $return

Это используется для генерирования кодировки META-тега.

  • css
  • string $path
  • string $rel = 'stylesheet'
  • array $htmlAttributes
  • boolean $return = false

Создает связь с CSS таблицей стилей. Параметр $rel который позволяет вам предоставить rel= значение для вашего тега.

  • image
  • string $path
  • array $htmlAttributes
  • boolean $return = false

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

  • link
  • string $title
  • string $url
  • array $htmlAttributes
  • string $confirmMessage = false
  • boolean $escapeTitle = true
  • boolean $return = false

Используйте эту функцию чтобы создать ссылки в вашем отображении. $confirmMessage используется если вы хотите использовать сообщение подтверждения JavaScript, когда нажимается ссылка. Например, ссылка которая удаляет объект, вероятнее всего нужно будет вывести сообщения для подтверждения действия вроде "Вы уверены?". Установите значение true переменной $escapeTitle если вы хотите, чтобы HTML Хелпер упускал данные, которые вы отправили в переменную $title.

  • tableHeaders
  • array $names
  • array $tr_options
  • array $th_options

Используется для форматирования заголовка таблицы.

  • tableCells
  • array $data
  • array $odd_tr_options
  • array $even_tr_options

Используется для форматирования ячеек таблицы.

  • guiListTree
  • array $data
  • array $htmlAttributes
  • string $bodyKey = 'body'
  • string $childrenKey = 'children'
  • boolean $return = false

Генерирует неупорядочное дерво из масива.

Формы и верификация

HTML Хелпер действительно улыбается вам, когда нужно ускорить написание кода для форм в ваших отображениях. Он генерирует все теги формы, автоматически заполняя значение во время ошибочных ситуаций, убирая сообщения об ошибках. Чтобы помочь проиллюстрировать это, давайте пройдем через быстрый пример. Представьте, что в вашем приложении есть модель Заметка(Note), и вы хотите создать код контроллера и отображения чтобы добавлять и редактировать объекты Заметки. В вашем NotesController, должно быть действие редактирования, которое может выглядить как это:

Действие редактирования в NotesController

   function edit($id)
   {
      // Во-первых, давайте проверим отправлены ли данные из формы
      // в действие.
      if (!empty($this->data['Note']))
      {
         // Вот здесь мы пытаемся проверить данные формы
         // и сохранить их
         if ($this->Note->save($this->data['Note']))
         {
            // Если мы успешно сохранили, отправляем пользователя
            // в подходящее место
            $this->flash('Your information has been saved.', '/notes/edit/' . $id);
            exit();
         }
         else
         {

            // Генерируем сообщения об ошибках для подходищих полей
            // это не обязательно, посколько сохранение это уже сделало, но это пример
            //вызываем $this->Note->validates($this->data['Note']); если вы не делаете сохранения
            //затем используем метод ниже, чтобы заполнить метод хелпера tagErrorMsg()
            $this->validateErrors($this->Note);

            //И рендерим редактированный код отображения
            $this->render();
         }
      }

      //Если мы не получили никаких данных из формы, берем заметку, которую хотим редактировать, и отправляем
      //эту информацию в отображение
      $this->set('note', $this->Note->find("id = $id"));
      $this->render();
   }

Раз уж мы настроили наш контроллер, давайте посмотрим на код отображения (который можно найти в app/views/notes/edit.thtml). Наша модель Заметки достаточно простая на этом этапе, поскольку включает в себя только id. Этот код отображения подразумевает отображение данных заметки и позволяет пользователю вводить новые заничения и сохранять эти данные в модель.

HTML Хелпер доступен во всех отображениях по умолчанию, можно получить доступ используя $html.

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

Образец кода отображения редактирования(edit.thtml)

<!-- Этот тег создает тег нашей формы -->

<?php echo $html->formTag('/notes/edit/' . $html->tagValue('Note/id')?>

<table cellpadding="10" cellspacing="0">
<tr>
   <td align="right">Body: </td>
   <td>

      <!-- Вот где мы используем HTML Хелпер чтобы отрендерить тег
           textarea и его возможные сообщения об ошибках
           переменная $note была создана контроллером, и местит
           данные для заметки, которую мы редактируем. -->
      <?php echo
      $html->textarea('Note/body', array('cols'=>'60', 'rows'=>'10'));
      ?>
      <?php echo $html->tagErrorMsg('Note/body',
      'Please enter in a body for this note.') ?>
   </td>
</tr>
<tr>
   <td></td>
   <td>

      <!-- Мы также можем использовать HTML Хелпер чтобы включить
            спрятанные теги внутрь нашей таблицы -->

      <?php echo $html->hidden('Note/id')?>
      <?php echo $html->hidden('note/submitter_id', $this->controller->Session->read('User.id'))?>
   </td>
</tr>
</table>

<!--И наконец подтверждающая кнопка-->
<?php echo $html->submit()?>

</form>

Большинство функций генерирования тегов (вместе с tagErrorMsg) обязывают вас включать $fieldName. Эта $fieldName позволяет Cake узнавать какие данные вы вставляете, так что он может проверить и сохранить данные коректно. Строка вставленная в параметр $fieldName находится в модели формы "modelname/fieldname". Если вы собираетесь добавить новое поле title в нашу Заметку, вы можете добавить в отображение что-то вроде этого:

<?php echo $html->input('Note/title') ?>
<?php echo $html->tagErrorMsg('Note/title', 'Please supply a title for this note.')?>

Сообщения об ошбиках отображаются функцией tagErrorMsg() согласно &lt;div class="error_message"&gt;&lt;/div&gt; для простоты CSS стайлинга.

Вот теги формы, которые HTML хелпер может сгенерировать (большиство из них прямолинейны):

  • submit
  • string $buttonCaption
  • array $htmlAttributes
  • boolean $return = false
  • password
  • string $fieldName
  • array $htmlAttributes
  • boolean $return = false
  • textarea
  • string $fieldName
  • array $htmlAttributes
  • boolean $return = false
  • checkbox
  • string $fieldName
  • array $htmlAttributes
  • boolean $return = false
  • file
  • string $fieldName
  • array $htmlAttributes
  • boolean $return = false
  • hidden
  • string $fieldName
  • array $htmlAttributes
  • boolean $return = false
  • input
  • string $fieldName
  • array $htmlAttributes
  • boolean $return = false
  • radio
  • string $fieldName
  • array $options
  • array $inbetween
  • array $htmlAttributes
  • boolean $return = false
  • tagErrorMsg
  • string $fieldName
  • string $message

HTML Хелпер также включает набор функций которые помогают в создании тегов, относящихся к датам. С параметром $tagName следует обращаться также как и с параметром $fieldName. Просто предоставьте имя поля, к которому относиться этот тег работы с датой(date option tag). Когда данные обработаются, вы увидите это в вашем контроллере с частью данных, которыми он управляет до конца имени поля. Например, если в моих Заметках есть поле завершения срока, и мой dayOptionTag, параметр $tagName, имеет значение 'note/deadline', данные дня будут показаны в переменной $params, когда данные из формы отправлены в действие контроллера:

$this->data['Note']['deadline_day']

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

Перевод даты перед сохранением модели (выдержка из NotesController)

function edit($id)
   {
      //Во-первых, давайте проверим были ли посланы какие-то данные из формы в действие.
      if (!empty($this->data['Note']))
      {

         //Перевод даты для хранения...
         $this->data['Note']['deadline'] =
            $this->data['Note']['deadline_year'] . "-" .
            $this->data['Note']['deadline_month'] . "-" .
            $this->data['Note']['deadline_day'];

         //Вот где мы пытаемся проверить данные формы (см. Главу 10) и сохранить их
         if ($this->Note->save($this->data['Note']))
         {

         ...

  1. dayOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)

  2. yearOptionTag ($tagName, $value=null, $minYear=null, $maxYear=null, $selected=null, $optionAttr=null)

  3. monthOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)

  4. hourOptionTag ($tagName, $value=null, $format24Hours=false, $selected=null, $optionAttr=null)

  5. minuteOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)

  6. meridianOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)

  7. dateTimeOptionTag ($tagName, $dateFormat= 'DMY', $timeFormat= '12', $selected=null, $optionAttr=null)

AJAX

Ajax Хелпер Cake'а использует постоянно заполненный Prototype и библиотеки script.aculo.us для операций Ajax и эффектов клиентской стороны. Для того, чтобы использовать этот хелпер, у вас должна быть текущая версия библиотек JavaScript из http://script.aculo.us, которую нужно поместить в /app/webroot/js/. Кстати, все отображения, которые собираются использовать Ajax хелпер должны включать эти библиотеки.

Большинство функций этого хелпера подразумевают использование особого масива $options как параметра. Этот масив используется для определения разных вещей об операциях Ajax. Вот разные значения, которые вы можете определить:

Ключи $options Ajax Хелпера

/* General Options */

$options['url']         // URL-адрес действия, которое вы хотите вызвать.

$options['frequency']   // Количество секунд между remoteTimer() или
                        // проверками observeField().

$options['update']      //DOM(объектная модель документов) ID элемента, который вы хотите обновить с
                        // результатами операций Ajax.

$options['with']        // DOM ID элемента формы, который вы хотите перевести в последовательный режим
                        // и послать с формой подтверждения Ajax.

$options['type']        // Любое 'asynchronous' (по умолчанию), или 'synchronous'.
                        //Позволяет вам выбирать между типами операций.

/* Возвращает : код JS для исполнения в определенное
   время, на протяжении процесса XMLHttpRequest*/

$options['loading']     //код JS для исполнения когда удаленный документ
                        //был загружен браузером данными.

$options['loaded']      //код JS для исполнения когда завершил
                        // загрузку удаленного документа.

$options['interactive'] //код JS для исполнения когда пользователь может работать
                        // с удаленным документом, даже если он
                        // еще не до конца загрузился.

$options['complete']    // код JS, который вызывается когда XMLHttpRequest
                        // завершен.

$options['confirm']     //Текст для отображения в диалоге подстверждения перед
                        // тем, как действие XMLHttpRequest начнется.

$options['condition']   //условие JS перед тем как XMLHttpRequest
                        // инициируется.

$options['before']      //код JS, который вызывается перед тем, как инициируется запрос.

$options['after']       //код JS, который вызывается сразу после того, как запрос
                        // инициировался и перед 'loading'.

Вот функции хелпера для того, чтобы сделать Ajax в Cake легким и быстрым:

  • link
  • string $title
  • string $href
  • array $options
  • boolean $confirm
  • boolean $escapeTitle

Отображает связанный текст $title, который находит удаленный документ в $options['url'] и обновляет DOM елемент $options['update']. С этой функцией можно использовать колбеки.

  • remoteFunction
  • array $options

Эта фунция используется для создания нужного JavaScript для удаленных запросов. В основном используется как хелпер для linkToRemote. Используется редко, если вам не нужно сгенерировать какой-то особый скриптинг.

  • remoteTimer
  • array $options

Периодические вызовы определенного действи в $options['url'], каждые $options['frequency'] секунд (по умолчанию 10). Обычно используется для обновления опеределенного раздела (определенного $options['update']) результатами удаленного запроса. С этой функцией можно использовать колбеки.

  • form
  • string $action
  • string $type
  • array $options

Возвращает тег формы, которая отправляет данные в действие $action, используя XMLHttpRequest в фоне, кроме регулярных требующих перезагрузки отправок POST. Данные из этой формы ведут себя как обычные данные (например, они будут доступны в $this->params['form']) . DOM елемент определенный $options['update'] обновится результатами с удаленного документа. С этой функцией можно использовать колбеки.

  • observeField
  • string $field_id
  • array $options

Наблюдает за полем с DOM ID определенным $field_id (каждые $options['frequency'] секунд) и вызывает действие из $options['url'] если содержание поле изменилось. Вы можете обновить DOM элемент с ID $options['update'] или же определить элемент формы используя $options['with']. С этой функцией можно использовать колбеки.

  • observeForm
  • string $form_id
  • array $options

Работает также как и observeField(), только наблюдает за всеми элементами указанной формы.

  • autoComplete
  • string $field
  • string $url
  • array $options

Рендерит текстовое поле с ID $field с autoComplete. Действие из $url должна вернуть условия autoComplete: в основном, нужно чтобы ваши действия снимали из неупорядоченного списка (<ul></ul>) элементы, которые соответсвуют условиям autoComplete. Если вы хотите автозавершить поля, которые возвращают темы постов в вашем блоге, ваше действие контроллера может выглядить как это:

function autocomplete ()
{
    $this->set('posts',
        $this->Post->findAll(
            "subject LIKE '{$this->data['Post']['subject']}'")
        );
    $this->layout = "ajax";
}

А ваше отображения для действия autocomplete() будет выглядить так:

<ul>
<?php foreach($posts as $post): ?>
<li><?php echo $post['Post']['subject']; ?></li>
<?php endforeach; ?>
</ul>

Фактическое поле авто-завершения, как оно будет выглядить в отображении, будет выглядить так:

<form action="/users/index" method="POST">
    <?php echo $ajax->autoComplete('Post/subject', '/posts/autoComplete')?>
    <?php echo $html->submit('View Post')?>
</form>

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

<style type="text/css">

div.auto_complete {
    position         :absolute;
    width            :250px;
    background-color :white;
    border           :1px solid #888;
    margin           :0px;
    padding          :0px;
}

li.selected { background-color: #ffb; }

</style>
  • drag
  • string $id
  • array $options

Делает DOM элемент с ID $id доступным для перетаскивания. Вот некоторые дополнительные вещи, которые вы можете определить используя $options:

// (The version numbers refer to script.aculo.us versions)

$options['handle']     // (v1.0) Устанавливает должен ли элемент быть
                       // доступным для перестасикания только внедреным манипулятором.Значение должно быть
                       //ссылкой на жлемент или id элемента.

$options['handle']     // (V1.5)Как и выше, кроме того, что теперь значение может быть
                       // строкой ссылающейся на значение класса CSS. string referencing a CSS class value. Первый
                       // найденный элемент child/grandchild/etc. у которого есть
                       // это значение класса CSS будет использован
                       // как манипулятор.

$options['revert']     // (V1.0) Если установленно true, то элемент будет возвращаться
                       // на свою исходную позицию по окончанию перетаскивания.

$options['revert']     // (V1.5) Revert также может быть произвольно-вызванной, по окончанию
                       // перетаскивания, функцией.

$options['constraint'] //Если установлено horizontal или vertical,перетаскивание
                       // будет принудительно производится токо по горизонтали
                       //или по вертикали.
  • drop
  • string $id
  • array $options

Делает DOM элемент с ID $id доступным к бросанию (drop). Вот некоторые дополнительные вещи, которые вы можете определить используя $options:

$options['accept']      // Устанавливает доступ строкам или масиву строк JavaScript,
                        // которые описывают классы CSS. Доступность к броску
                        // будет приниматься только от доступных к перетаскиванию элементов
                        //содержащих один или больше из этих классов CSS.

$options['containment'] // Элементы доступные к броску будут принимать только
                        // элементы доступные к перетаскиванию, которые содержат один из данных
                        // id элементов (или элемента). Может быть один элемент
                        // или масив элементов JS.

$options['overlap']     //Если установлено horizontal или vertical, доступные к бросанию
                        // будут только реагировать на элементы доступые к перетаскиваниюif its
                        //если покрытие больше 50% в данном направлении.
  • dropRemote
  • string $id
  • array $options
  • array $ajaxOptions

Используется для создание цели броска, инициируемой XMLHttpRequest когда элемент доступный к перетаскиванию брошен. $options такие же как и в drop(), а $ajaxOptions такие же как в link().

  • sortable
  • string $id
  • array $options

Делает список или группу объектов (определенных ID элементов $id) сортируемым. Масивом $options можно настроить вашу сортировку так:

$options['tag']         // Устанавливает вид тега (дочерних элементов
                        // контейнера) который будет сделан сортируемым. Для UL и
                        // OL контрейнеров, это LI, вам нужно предоставить
                        // вид тега для других видов дочених тегов.
                        // По умолчанию 'li'.

$options['only']        //Дальнейшее ограничение выбора дочерних элементов
                        // элементами с данным классом CSS
                        // (или, если вы предоставите масив строк, любым из
                        // классов).

$options['overlap']     // Либо vertical(по-умолчанию) или horizontal.
                        // Для плавающих сортировок или горизонтальных списков, выбирете
                        // horizontal. Для вертикальных списков используйте vertical.

$options['constraint']  // Ограничивает передвижения элементов доступных к перетаскиванию,
                        // 'vertical' или 'horizontal'.

$options['containment'] // Включает возможность перетаскивания и бросания между сортируемыми.
                        // Беперт масив элементов или id-номеров элементов (из
                        // контейнера).

$options['handle']      // Заствялет созданные доступные к перетаскиванию элементы, использовать манипуляторы,
                        // смотрите опцию handle в drag().

  • editor
  • string $id
  • string $url
  • array $options

Создает внутренний редактор ajax, используя элементы с DOM ID как первый параметр. Когда применен, элементы будут подсвечивать по наведению мышкой, и будут превращатся в поле ввода текста по щелчку. Второй параметр - ссылка, куда должны отправится отредактированные данные. Дополнительные опции внутреннего редактора можно найти по адресу Script.aculo.us wiki.

Javascript

JavaScript Хелпер используется чтобы помочь разработчику в извлечении хорошо отформатированных относяхщихся к JavaScript тегов и данных.

  • codeBlock
  • string $string

Используется для возвращения $script помещенного в рамки JavaScript тегов &lt;script&gt;.

  • link
  • string $url

Возвращает JavaScript с включенным ссылающимся по $url тегом.

  • linkOut
  • string $url

Тоже что и link(), только включая ссылающийся тег, подразумевая, что $url находится не на том же домене.

  • escapeScript
  • string $script

Избегая каретку, возвращает и одинарные и двойные кавычки для сегментов кода JavaScript.

  • event
  • string $object
  • string $event
  • string $observer
  • boolean $useCapture

Прикрепляет событие к элементу. Использует библиотеку Prototype.

  • cacheEvents

Кеширует события JavaScript созданные с event().

  • writeEvents

Записывает кешированные события кешированные с cacheEvent().

  • includeScript
  • string $script

Number

Number хелпер включает в себя несколько милых функций для форматирования нумерованных данных в ваших отображениях.

  • precision
  • mixed $number
  • int $precision = 3

Возвращает $number с точностью определенной $precision.

  • toReadableSize
  • int $sizeInBytes

Возвращает удобчитаемый размер, данный $size в байтах. В основном, вы вводите число байт, и эта функция возвращает вам подходящее удобчитаемое значение в КБ, МБ, ГБ или ТБ.

  • toPercentage
  • mixed $number
  • int $precision = 2

Возвращает данное число $number как проценты, ограниченные точностью установленной в $precision.

Text

Text Хелпер предоставляет методы, которые могут понадобиться разработчику для извлечение хорошо отформатированного текста в браузер.

  • highlight
  • string $text
  • string $highlighter = '<span class="highlight">\1</span>'

Возвращает текст перенесенный тегами определенными в $highlighter.

  • stripLinks
  • string $text

Возвращает текст с удаленными HTML ссылками (&lt;a href= ...).

  • autoLinkUrls
  • string $text
  • array $htmlOptions

Возвращает $text с ссылками помещенными в соответсвующие теги &lt;a&gt;.

  • autoLinkEmails
  • string $text
  • array $htmlOptions

Возвращает $text с адресами e-mail помещенными в соответствующие теги &lt;a&gt;.

  • autoLink
  • string $text
  • array $htmlOptions

Возвращает $text с ссылками и адресами e-mail помещенными в соответсвующие теги &lt;a&gt;.

  • truncate
  • string $text
  • int $length
  • string $ending = '...'

Возвращает $length число символов $text следующего за $ending ('...' по умолчанию).

  • excerpt
  • string $text
  • string $phrase
  • int $radius = 100
  • string $ending = '...'

Извлекает отрывок из $text, набирая $phrase с количеством символов с каждой стороны определенных $radius.

  • autoLinkEmails
  • string $text
  • boolean $allowHtml = false

Text-to-HTML транслятор, такой же как Textile или RedCloth только с другим синтаксисом.

Time

Time Хелпер предоставляет методы, которые могут понадобится разработчику для извлечения временных отметок Unix и/или строки времени-даты в более понятном виде в браузер.

Даты могут быть предоставлены во все функции как действительные строки времени-даты PHP или временные отметки Unix.

  • fromString
  • string $dateString

Возвращает временную отметку UNIX, данную как временную отметку UNIX или существующую строку даты strtotime().

  • nice
  • string $dateString
  • boolean $return = false

Возвращает приятно-отформатированную строку даты. Даты форматируются как "D, M jS Y, H:i", или 'Mon, Jan 1st 2005, 12:00'.

  • niceShort
  • string $dateString
  • boolean $return = false

Форматирует строки дат как определено в nice(), но выводит "Сегодня 12:00" если эта дата сегодня, или "Вчера 12:00", если дата вчерашняя.

  • isToday
  • string $dateString

Возвращает true если данная строка времени-даты - сегодняшняя.

  • daysAsSql
  • string $begin
  • string $end
  • string $fieldName
  • boolean $return = false

Возвращает частичную строку SQL для поиска всех записей между двумя датами.

  • dayAsSql
  • string $dateString
  • string $fieldName
  • boolean $return = false

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

  • isThisYear
  • string $dateString
  • boolean $return = false

Возвращает true если данная строка времени-даты в пределах текущего года.

  • wasYesterday
  • string $dateString
  • boolean $return = false

Возвращает true если данная строка времени-даты была вчера.

  • isTomorrow
  • string $dateString
  • boolean $return = false

Возвращает true если данная строка времени-даты будет завтра.

  • toUnix
  • string $dateString
  • boolean $return = false

Возвращает временную отметку UNIX из текстового описания времени-даты.

  • toAtom
  • string $dateString
  • boolean $return = false

Возвращает дату, форматированную для Atom RSS.

  • toRSS
  • string $dateString
  • boolean $return = false

Форматирует дату для RSS.

  • timeAgoInWords
  • string $dateString
  • boolean $return = false

Возвращает либо относительную дату либо форматированную дату, в зависимости от разницы между текущим временем и данными времени-даты. $datetime должна быть в формате strtotime как время-дата MySQL.

  • relativeTime
  • string $dateString
  • boolean $return = false

Работает как timeAgoInWords(), но включает в себя способность создатвать также временные отметки и в будущем (например "Вчера 10:33", "Сегодня 9:42", и также "Завтра 4:34").

  • relativeTime
  • string $timeInterval
  • string $dateString
  • boolean $return = false

Возвращает true если определенные время-дата были в пределах установленного интервала, иначе false. Временной интервал нужно определять как с цифрами так и единицами: '6 hours', '2 days', etc.

Cache

2. Создание собственного хелпера(ов)

Нужна помощь с кодом отображения? Если вам понадобится определенная часть кода снова и снова, вы можете создать свой собственный View Хелпер.

Расширение класса Хелпера Cake

Скажем нам захотелось создать хелпер, который можно использовать для извлечения стилизованных ссылок CSS, которые нам нужны в приложении. Чтобы добавить свой код в существующую структуру хелпера Cake, нам понадобится создать новый класс в /app/views/helpers. Давайте назовем наш хелпер LinkHelper. Тогда файл класса PHP будет выглядить так:

/app/views/helpers/link.php

class LinkHelper extends Helper
{
    function makeEdit($title, $url)
    {
        //Здесь идет код создания специально-отформатированной ссылки...
    }
}

Вот несколько функций, которые включает в себя класс хелпера Cake, которые вам могут пригодиться:

  • output
  • string $string
  • boolean $return = false

Решает вывести или вернуть строку, базируясь на AUTO_OUTPUT (см. /app/config/core.php) и значении $return. Используйте эту функцию для возврата данных в ваше отображение.

  • loadConfig

Возвращает текущую центральную настройку вашего приложение и определения тегов.

Давайте используем output() чтобы отформатировать наш титул и ссылку и вернуть их в отображение.

/app/views/helpers/link.php (код добавлен)

class LinkHelper extends Helper
{
    function makeEdit($title, $url)
    {
        // Используем функцию извлечения хелпера чтобы вернуть
        // форматированные данные обратно в отображение:

        return $this->output("<div class=\"editOuter\"><a href=\"$url\" class=\"edit\">$title</a></div>");
    }
}

Подключение других хелперов

Может быть вы захотите воспользоваться функциональностью других, уже существующих, хелперов. Воспользоваться этим вы можете, указав хелперы, которые хотите использовать в масиве $helpers.

/app/views/helpers/link.php (использование других хелперов)

class LinkHelper extends Helper
{

    var $helpers = array('Html');

    function makeEdit($title, $url)
    {
        // Используем HTML Хелпер для извлечения
        // форматированных данных:

        $link = $this->Html->link($title, $url, array('class' => 'edit'));

        return $this->output("<div class=\"editOuter\">$link</div>");
    }
}

Использование вашего настроенного хелпера

Когда вы создали свой хелпер и поместили его в /app/views/helpers/, вы можете использовать его в своих контроллерах, при помощи специальной переменной $helpers.

class ThingsController
{
    var $helpers = array('Html', 'Link');
}

Не забудьте включить HTML хелпер в масив если планируете его где-то использовать. Схема присвоения имен та же, что и у моделей.

  1. LinkHelper = имя класса

  2. link = ключ в масиве хелперов

  3. link.php = имя файла php в /app/views/helpers.

Содействие

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


Плагины

CakePHP позволяет вам создавать комбинации контроллеров, моделей и отображений и выпускать их как объеденные в пакет плагины приложения, чтобы другие могли использовать их в своих приложениях. У вас есть приятный модуль управления, простой блог или модуль веб-сервиса в одном вашем приложении? Упакуйте это все как плагин CakePHP и вы сможете использовать его в других приложениях.

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

1. Создание плагина

Как рабочий пример, давайте сделаем плагин, который заказывает для вас пиццу. Что может быть более полезным в любом приложении CakePHP? Для начала нам понадобится поместить файл нашего плагина в каталог /app/plugins. Имя родительского каталога для любого плагина очень важно, и будет использоваться во многих местах, так что выбирайте его с умом. Для этого плагина, давайте будем использовать имя 'pizza'. Вот как будет выглядеть настройка в конце:

Схема размещения файлов Заказчика Пиццы

/app
    /plugins
        /pizza
            /controllers                <- контроллеры плагина
            /models                     <- модели плагина
            /views                      <- отображения плагина
            /pizza_app_controller.php   <- AppController плагина, названный после плагина
            /pizza_app_model.php        <- AppModel плагина, названый после плагина

В то время как определение AppController и AppModel обычно не обязательно, определение их для плагина - обязательно. Вам придется создать их перед тем как плагин заработает. Эти два специальных класса названы после плагина, и расширяют родительские AppController и AppModel. Вот как они должны выглядеть:

AppController плагина: /app/plugins/pizza_app_controller.php

<?php

class PizzaAppController extends AppController
{
    //...
}

?>

AppModel плагина Pizza: /app/plugins/pizza_app_model.php

<?php

class PizzaAppModel extends AppModel
{
    //...
}

?>

Если вы забудете определить специальные классы, CakePHP будет выдавать вам ошибку "Missing Controller" до тех пор, пока проблема не будет решена.

2. Контроллеры плагинов

Контроллеры для нашего плагина будут храниться в /app/plugins/pizza/controllers. С тех пор как нашей главной целью является заказ пиццы, нам нужен OrdersController для нашего плагина.

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

Итак мы помещаем наш новый PizzaOrdersController в /app/plugins/pizza/controllers и выглядит он так:

/app/plugins/pizza/controllers/pizza_orders_controller.php

<?php

class PizzaOrdersController extends PizzaAppController
{
    var $name = 'PizzaOrders';

    function index()
    {
        //...
    }

    function placeOrder()
    {
        //...
    }
}

?>

Заметьте как этот контроллер расширяет Appcontroller плагина (называемый PizzaAppController) в отличии от просто родительского AppController приложения

3. Модели плагинов

Модели для плагинов хранятся в /app/plugins/pizza/models. Мы уже определили PizzaOrdersController для этого плагина, так что давайте создадим модель для этого контроллера, под названием PazzaOrders (имя класса PizzaOrders соответсвует нашей схеме присвоения имен и является достаточно уникальным, поэтому мы оставим его как есть).

/app/plugins/pizza/models/pizza_order.php

<?php

class PizzaOrder extends PizzaAppModel
{
    var $name = 'PizzaOrder';
}

?>

Снова, отметьте что этот класс расширяет PizzaAppModel больше чем AppModel.

4. Отображения плагинов

Отображения ведут себя также как и в обычных приложениях. Просто помещаем их в правильный каталог, а именно /app/plugins/[plugin]/views. Для нашего плагина по заказу пиццы, нам понадобится хотя бы одно отображения для нашего действия PizzaOrdersController::index(), так что давайте включим его:

/app/plugins/pizza/views/pizza_orders/index.thtml

<h1>Order A Pizza</h1>
<p>Nothing goes better with Cake than a good pizza!</p>
<!-- Здесь можно разместить каку-то форму для заказа....-->

5. Работа с плагинами

Что ж вы все создали, и это все должно быть готовым для распространения (хотя мы предлагаем вам также распространять несколько дополнений, таких как readme,sql файлы и т.д.).

Раз плагин уже установлен в /app/plugins, вы можете получить к нему доступ через ссылку /pluginname/controllername/action. В нашем плагине заказа пиццы, мы получаем доступ к нашему PizzaOrdersController в /pizza/pizzaOrders.

Несколько последних советов по работе с плагинами в ваших приложениях CakePHP:

  1. Когда у вас нету [Plugin]AppController и [Plugin]AppModel, то у вас появляются ошибки о недостающем контроллере, когда вы пытаетесь получить доступ к контроллеру плагина.

  2. Если у вас есть контроллер с именем плагина по умолчанию, вы можете получить доступ через /[plugin]/action. Например плагин названный 'users' с контроллером под названием UsersController, к нему можно получить доступ из /users/add если нету плагина названного AddController в каталоге [plugin]/controllers.

  3. Плагины будут использовать верстки из каталога /app/views/layouts по умолчанию.

  4. Вы можете создать меж-плагинную связь используя

    $this->requestAction('/plugin/controller/action');
    в вашем контроллере.

  5. Если вы используете requestAction убедитесь что у контроллера и модели уникальные имена. Иначе вы можете получить ошибку PHP "redefined class ...".

Большая благодарность Felix Geisendorfer (the_undefined) за первоначальную информацию для этой главы.


Компонент управления запросами

1. Введение

Компонент управления запросами (Request Handler component) используется в Cake чтобы определять информацию о поступающих HTTP запросах. Вы можете использовать его для того, чтобы лучше информировать ваш контроллер о запросах AJAX, получать информацию об IP-адресах удаленных клиентов и типах запросов, или избавляться от нежелательных данных при отображении. Чтобы воспользоваться компонентом управления запросами, вам нужно убедиться, что он определен в вашем масиве контроллера $components.

class ThingsController extends AppController
{
    var $components = array('RequestHandler');

    // ...
}

2. Получение информации о клиентах/запросах

Давайте просто углубимся:

  • accepts
  • string $type

Возвращает информацию о типах контента, который принимает клиент, зависит от значение $type. Если ноль или значение не присвоено, то вернет масив типов контента, которые принимает клиент. Если введена строка, то возвращает true если клиент принимает данный тип, проверяется $type по отношению к карте типов контента (см. setContent()). Если $type это масив, каждая строка проверяется отдельно, и accepts() вернет true если хотя бы одна из них соответствует принимаемому типу контента. Например:

class PostsController extends AppController
{
    var $components = array('RequestHandler');

    function beforeFilter ()
    {
        if ($this->RequestHandler->accepts('html'))
        {
            // Выполнить код только если клиент принимает HTML (text/html)
        }
        elseif ($this->RequestHandler->accepts('rss'))
        {
            // Выполнить только код RSS
        }
        elseif ($this->RequestHandler->accepts('atom'))
        {
            // Выполнить только код Atom
        }
        elseif ($this->RequestHandler->accepts('xml'))
        {
            // Выполнить только код XML
        }

        if ($this->RequestHandler->accepts(array('xml', 'rss', 'atom')))
        {
            //Выполняется если клиент принимает что-то из: XML, RSS или Atom
        }
    }
}

  • getAjaxVersion

Если вы используете библиотеки JS Prototype, эта функция возвращает используемую версию Prototype.

  • getClientIP

Возвращает IP-адрес удаленного клиента.

  • getReferrer

Возвращает имя сервера, с которого к вам пришел пользователь.

  • isAjax

Возвращает true если текущий запрос был XMLHttpRequest.

  • isAtom

Возвращает true если клиент принимает тип контента Atom (application/atom+xml).

  • isDelete

Возвращает true елси текущий вопрос проходил через DELETE.

  • isGet

Возвращает true если текущий запрос проходил через GET.

  • isMobile

Возвращает true, елси строка агента пользователя соответсвует мобильному веб-браузеру.

  • isPost

Возвращает true если текущий запрос проходил через POST.

  • isPut

Возвращает true если текущий запрос проходил через PUT.

  • isRss

Возврвщает true если клиент принимает тип контента RSS (application/rss+xml).

  • isXml

Возвращает true если клиент принимает тип контента XML (application/xml или text/xml).

  • setContent
  • string $name
  • string $type

Добавляет карту псевдонимов типов контента, для использования с accepts() и prefers(), где $name - имя контента (строка), а $type - либо строка, либо масив строк, каждая из которых это MIME. Встроенная карта похожа на следующее:

// Имя     => Тип
  'js'      => 'text/javascript',
  'css'     => 'text/css',
  'html'    => 'text/html',
  'form'    => 'application/x-www-form-urlencoded',
  'file'    => 'multipart/form-data',
  'xhtml'   => array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'),
  'xml'     => array('application/xml', 'text/xml'),
  'rss'     => 'application/rss+xml',
  'atom'    => 'application/atom+xml'

3. Избавление от данных

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

  • stripAll
  • string $str

Убирает пробелы, изображения и скрипты из $str (используя stripWhitespace(), stripImages(), и stripScripts()).

  • stripImages
  • string $str

Убирает любой код HTML из $str, определяющий изображения.Strips any HTML embedded images from $str.

  • stripScripts
  • string $str

Убирает все <script> и <style> теги из $str.

  • stripTags
  • string $str
  • string $tag1
  • string $tag2...

Убирает теги определенные по $tag1, $tag2, и т.д. из $str.

$someString = '<font color="#FF0000"><bold>Foo</bold></font> <em>Bar</em>';

echo $this->RequestHandler->stripTags($someString, 'font', 'bold');

// исходящее: Foo <em>Bar</em>
  • stripWhiteSpace
  • string $str

Убирает пробелы из $str.

4. Другие полезные функции

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

// list.thtml
<ul>
<? foreach ($things as $thing):?>
<li><?php echo $thing;?></li>
<?endforeach;?>
</ul>

//-------------------------------------------------------------

//Действие "список" моего ThingsController:
function list()
{
    $this->RequestHandler->setAjax($this);
    $this->set('things', $this->Thing->findAll());
}

Когда обычный браузер делает запрос к /things/list, рендерится неупорядоченный список в верстке приложения по умолчанию. Если ссылка запрошена как часть операции AJAX, список автоматически рендерится в верстке AJAX.


Компонент сессий Cake

1. Опции хранения сессий Cake

Cake может сохранять данные сессии тремя способами: как временные файлы в директории Cake, используя механизм PHP по умолчанию, или сохранять в последовательности в базе данных. По умолчанию Cake использует настройки PHP. Заменить эту опцию, чтобы воспользоваться временными файлами или базой данных, можно откоредактировав конфигурационный файл /app/config/core.php. Измените константу CAKE_SESSION_SAVE на 'cake', 'php' или 'database', в зависимости от того, что вам нужно.

core.php настройка сессии

/**
 * CakePHP включает в себя 3 типа сохранений сессий
 * в базе данных или в файлах. Настройте это так как предпочитаете.
 * Если вы хотите использовать свое место для хранения, укажите это в
 * app/config/name.php НЕ ИСПОЛЬЗУЙТЕ имя файла или базы данных как имя.
 *
 * Устанавливая значение cake, вы получите сохранение файлов сессии в директорию /cakedistro/tmp
 * Устанавливая значение php, вы получите сохранение файлов сессии в директорию php по умолчанию
 * Устанавливая значение database, вы получите сохранение данных сессии в базу данных
 *
 *
 */
define('CAKE_SESSION_SAVE', 'php');

Если вы хотите использовать сохранение данных сессии в базу данных, вам понадобиться создать таблицу в вашей базе. Схема этой таблицы находится в /app/config/sql/sessions.sql.

2. Использование компонента сессий Cake

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

Вот некоторые функции, которые вы будете использовать чаще всего:

  • check
  • string $name

Проверяет установлен ли в сессии текущий ключ определенный $name.

  • del
  • string $name
  • delete
  • string $name

Удаляет переменную сессии определенную $name.

  • error

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

  • flash
  • string $key = 'flash'

Возвращает последнее сообщение в сессии, установленное setFlash(). Если $key был установлен, самое последнее, сохраненное под этим ключом, сообщение.

  • read
  • string $name

Возвращает переменную сессии определенную $name.

  • renew

Обновляет активную сессию, создавая новый ID сессии, старый стирается, данные старой сессии помещаются в новую.

  • setFlash
  • string $flashMessage
  • string $layout = 'default'
  • array $params
  • string $key = 'flash'

Пишет сообщение определенное $flashMessage в сессию (чтобы потом можно было извлечь его с помощью flash()).

Если $layout имеет значение 'default', сообщение храниться как '<div class="message">'.$flashMessage.'</div>'. Если $default имеет значение '' сообщение храниться просто как пропущенное. Если установлено любое другое значение, сообщение хранится внутри отображения Cake определенного $layout.

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

Переменная $key позволяет вам хранить сообщения flash под ключами. См. flash() для получения сообщения flash сохраненного без ключа.

  • valid

Возвращает true если сессия действительна. Лучше всего использвать перед операциями чтения read() чтобы убедиться, что данные сессии, к которым вы пытаетесь получить доступ на самом деле действительны.

  • write
  • string $name
  • mixed $value

Записывает переменную определенную $name и $value в активную сессию.


Списки контроля доступа

1. Понимание как работает ACL

Большинство важных мощных вещей требуют какого-нибудь контроля доступа. Списки контроля доступа это удобный способ управлять разрешениями вашего приложения. Списки контроля доступа или ACL, обрабатывают две основные вещи: вещи которые требуют что-то, и вещи которых требуют. На жаргоне ACL, вещи (чаще всего пользователи), которые требуют использования чего-то называются объектами запроса доступа(access request objects), или AROсами. Вещи, которых требуют в системе (чаще всего действия или данные) называются объектами контроля доступа, или ACOсами. Вообще эти "вещи" называют объектами, поскольку иногда запрашивающие объекты это не люди - иногда вам может понадобится ограничить доступ определенному контроллеру Cake, чтобы он не инициировал код в других частях вашего приложения. АСОсы могут быть чем угодно, что вы будите контролировать, от действий контроллера до веб-сервисов, или же линии с он-лайнового дневника вашей бабушки.

Ну и чтобы использовать все акронимы сразу: ACL это то, что решает когда ARO может получить доступ к ACO.

Теперь, чтобы помочь вам понять это, давайте воспользуемся практическим примером. Представьте на мгновение компьютерную систему, которую будет использовать группа искателей приключений. Лидер группы хочет продвигаться вперед в их задании, соблюдая приличную толику конфиденциальности и безопасности для остальных членов глуппы. AROсы вовлечены так:

Гендальф
Арагорн
Бильбо
Фродо
Голлум
Леголас
Гимли
Пиппин
Мерри

Это объекты в системе, которые будут запрашивать вещи (АСОсы) из системы. Кстати вы должны отметить что ACL это *не* система аутентификации пользователей. У вас уже должен быть способ хранить информацию о пользователях и способ индентифицировать пользователей, когда они входят в систему. Когда вы уже знаете кем является пользователь, тогда уже сияет ACL. Ладно - вернемся к нашему приключению.

Дальше Гендальфу нужно сделать исходный список вещей, или АСОсов, с которыми будет работать система. Его список может выглядить так:

Оружие
Одно Кольцо
Соленная свинина
Дипломатия
Эль

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

         Оружие    Одно Кольцо    Соленная свинина   Дипломатия       Эль
Гендальф    X             X              O               O            O
Арагорн     O             X              O               O            O
Бильбо      X             X              X               X            O
Фродо       X             O              X               X            O
Голлум      X             X              O               X            X
Леголас     O             X              O               O            O
Гимли       O             X              O               X            X
Пиппин      X             X              X               O            O
Мерри       X             X              X               X            O

На первый взгляд, кажется что этот вид системы может работать достаточно хорошо. Распределение может быть сделано, чтобы поддержать безопасность (только Фродо может получить доступ к кольцу) и защититься от несчасных случаев (держит хоббитов подальше от соленной свинины). Она кажется достаточно продуктивной и достаточно легкой для чтения, не так ли?

Для маленькой системы вроде этой, возможно матричные настройки и будут работать. Но для растущей системы, или системы с огромным количеством ресурсов (АСОсов) и пользователей (ARCсов), таблица достаточно быстро может стать неуклюжей. Представьте себе попытки получить доступ к сотням военных расположений и попытки управлять ими по одному бойцу, например. Еще один недостаток матриц - вы действительно не можете логически групировать разделы пользователей, или создавать каскадные изменения привилегий в группе, базируясь на этом логическом группировании. Например, наверняка было бы прекрасно автоматически давать доступ хоббитам к элю и свинине, когда битва закончена: Делать это индивидуально каждому пользователю, нудно и очень легко допустить ошибку, в то время как сделать каскадные изменения в привилегиях всем хоббитам было бы легким и быстрым занятием.

ACL чаще всего используется в структуре дерева. Обычно дерево AROсов и дерево АСОсов. Огранизовуя свои объекты в деревья, привилегии по-прежнему могут быть согласованы в форме гранул, в то же время поддерживается хороший захват на большой картине. Будучи мудрым лидером, Гендальф решил использовать ACL в своей новой системе, и организовать свои объекты согласно следующему:

Fellowship of the Ring:
Воины
    Арагорн
    Леголас
    Гимли
Маги
    Гендальф
Хоббиты
    Фродо
    Бильбо
    Мерри
    Пиппин
Зрители
    Голлум

Структрируя нашу группу таким образом, мы можем определить контроль доступа к целому дереву, и применить эти привилегии к любому дочернему объекту. По умолчанию привилегии - доступ закрыт ко всему. Пока вы работаете с деревом, вы устанавливаете привилегии и применяет их. Так что, используя наше ARO дерево, Гендальф может повесить несколько привилегий:

Братство кольца: [Запретить: ВСЕ]
    Воины                [Разрешить: Оружие, Эль, Рацион эльфов, Соленная свинина]
        Арагорн
        Леголас
        Гимли
    Маги                 [Разрешить: Соленная свинина, Дипломатия, Эль]
        Гендальф
    Хоббиты                 [Разрешить: Эль]
        Фродо
        Бильбо
        Мерри
        Пиппин
    Зрители                 [Разрешить: Соленная свинина]
        Голлум

Если мы хотим воспользоваться ACL чтобы узнать разрешен ли Пиппину доступ к элю, мы сначала берем его путь в дереве, это Fellowship->Hobbits->Pippin. Затем смотрим на разные привилегии и используем наиболее относящееся к Элю и Пиппину разрешение.

  1. Братство = Запрещено Эль, значит запрещен (потому что запрещенны все АСОсы)

  2. Хоббиты = РАЗРЕШЕН Эль, значит разрешен

  3. Пиппин = ?; Здесь действительно нету специальной информации об эле, значит заполняем как РАЗРЕШЕН.

  4. Конечный результат: разрешен эль.

Дерево также позволяет нам создать лучшие регулировки для более гранулированого контроля - в то же время поддерживая возможность делать более размашистые изменения в группах AROсов:

Братство кольца: [Запретить: ВСЕ]
    Воины                [Разрешить: Оружие, Эль, Рацион эльфов, Соленная свинина]
        Арагорн             [Разрешить: Дипломатия]
        Леголас
        Гимли
    Маги                 [Разрешить: Соленная свинина, Дипломатия, Эль]
        Гендальф
    Хоббиты                 [Разрешить: Эль]
        Фродо               [Разрешить: Кольцо]
        Бильбо
        Мерри               [Запретить: Эль]
        Пиппин              [Разрешить: Дипломатия]
    Зрители                 [Разрешить: Соленная свинина]
        Голлум

Вы можете видеть это, поскольку ARO Арагорн поддерживает привилегии как и другие в группе ARO Воины, но вы также можете сделать регулировку и установить дополительные привилегии. Снова, привилеги по умолчанию - ЗАПРЕТИТЬ, и изменяются вниз по дереву привилегиями РАЗРЕШИТЬ. Чтобы проверить может ли Мерри получить доступ к Элю, мы найдем его путь в дереве: Fellowship->Hobbits->Merry и пойдем вниз по дереву, просматривая привилегии связанные с элем:

  1. Братство = ЗАПРЕЩЕНО (потому что стоит запретить все), так что эль запрещен.

  2. Хоббиты = РАЗРЕШЕНО: эль, значит эль разрешен

  3. Мерри = ЗАПРЕЩЕН эль, значит эль запрещен

  4. Окончательный результат: эль запрещен.

2. Определение привилегий: ACL Cake'а, базируемый на INI

Первый ACL Cake'а не базировался на файлах INI, хранящихся в директории Cake. В то время как это полезно и стабильно, мы рекомендуем использовать базу данных с ACL, особенно из-за того, что есть возможность создавать новые АСОсы и AROсы на лету. Мы имеем ввиду использование в простых приложениях - и особенно для тех людей, которые по каким-то причинам не используют базу данных.

ARO/ACO привилегии определены в /app/config/acl.ini.php. Иструкции по определению доступа находятся в начале acl.ini.php:

; acl.ini.php - конфигурация Cake ACL
; ---------------------------------------------------------------------
; Используйте этот файл для определения привилегий пользователей.
; aco = access control object (что-то в ваших приложениях)
; aro = access request object (что-то запрашивающее доступа)
;
; Записи пользователей добавлены как следующее:
;
; [uid]
; groups = group1, group2, group3
; allow = aco1, aco2, aco3
; deny = aco4, aco5, aco6
;
; Записи груп добавлены в той же манере:
;
; [gid]
; allow = aco1, aco2, aco3
; deny = aco4, aco5, aco6
;
; Allow, deny, и groups разделы - опциональные.
; ЗАМЕТКА: имена груп *не могут* совпадать с именами пользователей!

Используя файл INI, вы можете определить пользователей (ARO), группу(ы) к которой они принадлежат, и их личные привилегии. Вы также можете определить группы с их привилегиями. Чтобы узнать как использовать компонент ACL Cake'а, чтобы проверить привилегии используя этот файл INI, взгляните на раздел 11.4.

3. Определение привилегий: База данных ACL Cake'а

Начинаем

По умолчанию использование привилегий ACL подразумевает хранение данных в базе данных. База данных ACL, или dbACL состоит из набора основных моделей, и скрипта командной строки, которая поставляется с Cake. Модели используются для взаимодействия Cake с вашей базой данных для хранения и извлечения узлов деревьев ACL. Скрипт командной строки используется, чтобы помочь вам начать и получить возможность взаимодействовать с вашими деревьями.

Для начала, убедитесь, что ваш /app/config/database.php существует и коректно настроен. Для новой установки Cake, самый легкий способ сообщить что все проверено, это войти в установочную директорию, используя браузер. Вверху страницы вы должны увидить сообщения "Файл настройки вашей базы данных существует." и "Cake может подключиться к базе данных." если вы все сделали правильно. Посмотрите раздел 4.1, там больше информации по настройке базы данных.

Далее, воспользуйтесь скриптом командой строки, чтобы инициализировать вашу базу данных для хранения информации ACL. Закончить это вам поможет скрипт, находящийся в /cake/scripts/acl.php. Инициализируйте базу данных для ACL выполнением следующей команды (из вашей директории /cake/scripts/):

Инициализация вашей базы данных, используя acl.php

$ php acl.php initdb

Initializing Database...
Creating access control objects table (acos)...
Creating access request objects table (acos)...
Creating relationships table (aros_acos)...

Done.

На этом этапе, вы уже можете проверить базу данных своего проекта и увидеть новые таблицы. Если вам любопытно, как Cake хранит информацию дерева в этих таблицах, просмотрите модифицированные деревья в базе данных. В таблицах асоs и aros храняться узлы для соответсвующих деревьев, а таблица aros_acos используется для связи ваших AROсов с ACOсами, к которым у них есть доступ.

Теперь, вы можете создавать свои ARO и ACO деревья.

Создание AROсов и АСОсов

Есть два способа сослаться на AROсы/АСОсы. Первый, присваивая им номерованные id, которые обычно являются главными ключами таблицы, к которой принадлежат. Второй, присваивая им строковые псевдонимы. Второй - не очень эксклюзивный.

Создать новый ARO можно методами определенными моделью Cake Aro. Метод create() класса Aro требует три параметра: $link_id, $parent_id и $alias. Этот метод создает новый объект ACL под родительским с id определенным parent_id - или как корневой объект если $parent_id равна нолю. Переменная $link_id позволяет вам связать текущий объект со структурой ACL Cake'а. Параметр $alias позволяет вам направлять ваш объект, используя не целое ID.

Перед тем как мы сможем создать наши АСОсы и AROсы, нам понадобится загрузить эти классы. Самый простой способ сделать это, включить ACL компонент в свой контроллер, использовав масив $components:

var $components = array('Acl');

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

$aro = new Aro();

// Сначала создадим несколько AROсов.
// Эти объекты не будут дочерними.

$aro->create( 1, null, 'Bob Marley' );
$aro->create( 2, null, 'Jimi Hendrix');
$aro->create( 3, null, 'George Washington');
$aro->create( 4, null, 'Abraham Lincoln');

// Теперь мы можем создать группы, чтобы организовать этих пользователей:
// Заметьте, что номера ID этих объектов ноли, потому что
//      они никогда не будут связывать пользователей в вашей системе

$aro->create(0, null, 'Presidents');
$aro->create(0, null, 'Artists');

//Теперь прикрепим AROсы к соответствующим группам:

$aro->setParent('Presidents', 'George Washington');
$aro->setParent('Presidents', 'Abraham Lincoln');
$aro->setParent('Artists', 'Jimi Hendrix');
$aro->setParent('Artists', 'Bob Marley');

//Вкратце, вот как создать ARO:
$aro = new Aro();
$aro->create($user_id, $parent_id, $alias);

Вы также можете создать AROсы, используя скрипт командной строки, вводя $acl.php create aro <link_id> <parent_id> <alias>.

Создание АСО происходит в той же манере:

$aco = new Aco();

//Создаем какие-то АСОсы:
$aco->create(1, null, 'Electric Guitar');
$aco->create(2, null, 'United States Army');
$aco->create(3, null, 'Fans');

// Я предполагаю мы можем создать группы для этих
// АСОсов используя setParent(), но мы пропустим это
// в этом примере.

//Итак, чтобы создать АСО:
$aco = new Aco();
$aco->create($id, $parent, $alias);

Соответствующая команда для командной строки: $acl.php create aco <link_id> <parent_id> <alias>.

Назначение привилегий

После того, как мы создали АСОсы и AROсы, мы наконец-то можем назначить привилегии между двумя группами. Это делается центральным ACL компонентом Cake'а. Давайте продолжим с нашим примером:

// Сначала, в контроллере, нам понадобиться доступ к
// ACL компоненту Cake'а:

class SomethingsController extends AppController
{
    // Может быть, вместо этого, вы захотите поместить это в AppController,
    // но здесь он также отлично работает.

    var $components = array('Acl');

    // Запомните: ACL всегда будет запрещать то,
    // информации на что у него нет. Если любые
    // проверки были сделаны на всем, это будет
    // запрещено. Давайте дадим доступ ARO к АСО.

    function someAction()
    {
        //РАЗРЕШИТЬ

        // Вот как вы можете дать ARO полный доступ к АСО
        $this->Acl->allow('Jimi Hendrix', 'Electric Guitar');
        $this->Acl->allow('Bob Marley',   'Electric Guitar');

        // Мы также можем назначать привилегии группам, помните?
        $this->Acl->Allow('Presidents', 'United States Army');

        // Метод allow() имеет третий параметр, $action.
        // Вы можете определять частичный доступ, используя этот параметр.
        // $action может быть равен create, read, update или delete.
        // Если не определенно никакого действия, это воспринимается как полный доступ.

        // Смотрите, но не трогайте, джентльмен:
        $this->Acl->allow('George Washington', 'Electric Guitar', 'read');
        $this->Acl->allow('Abraham Lincoln',   'Electric Guitar', 'read');

        //ЗАПРЕТИТЬ

        //Запрет работает в той же манере:

        //Когда его срок истек...
        $this->Acl->deny('Abraham Lincoln', 'United States Army');

    }
}

Частичный контроллер не особо полезен, но он в основном нужен был, чтобы показать вам как происходит процесс. Ипользование Acl компонента в соединении с вашим контроллером управления будет лучшим вариантом. Когда пользователь был создан в системе, его ARO может быть создан и помещен в подходящее место в дереве, и можно назначать привилегии, чтобы определить ACO или группы АСО.

Привилегии также можно назначить из командной строки. Синтаксис такой же как у функции модели, и можно его отобразить использовав $php acl.php help.

4. Проверка привилегий: ACL Component

Проверка привилегий этой самое простое в использовании ACL Cake'а: она состоит из использования отдельного метода из Acl компонента: check(). Хороший способ для использования ACL в вашем приложении - это поместить действие в ваш AppController, который выполняет ACL проверки. Когда действие помещено туда, вы можете получить доступ к Acl компоненту и выполнять проверку привилегий на ширину всего приложения. Вот пример испльзования:

class AppController extends Controller
{
    // Получаем наш компонент
    var $components = array('Acl');

    function checkAccess($aco)
    {
        // Проверка доступа используя компонент:
        $access = $this->Acl->check($this->Session->read('user_alias'), $aco, $action = "*");

        //доступ закрыт
        if ($access === false)
        {
            echo "access denied";
            exit;
        }
        //доступ разрешен
        else
        {
            echo "access allowed";
            exit;
        }
    }
}

Делая Acl компонент доступным в AppController, он будет доступен из любого контроллера в вашем приложении.

//Вот основной формат:
$this->Acl->Check($aro, $aco, $action = '*');


Обработка данных

1. Использование обработки в вашем приложении

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

// Сначала, включите центральную библиотеку:

uses('sanitize');

// Далее, создайте новый объект обработки:

$mrClean = new Sanitize();

// Отсюда вы можете воспользоваться обработкой, чтобы почитистить свои данные
// (Эти методы объясняются в следующем разделе)

2. Модифицирование данных для безопасного использования в SQL и HTML

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

  • paranoid
  • string $string
  • array $allowedChars

Эта функция убирает все из целевой $string, что не является цифрами и буквами. Однако, вы можете разрешить использование определенных символов, просто поместите их в масив $allowed.

$badString = ";:<script><html><   // >@@#";

echo $mrClean->paranoid($badString);

// исходящее: scripthtml

echo $mrClean->paranoid($badString, array(' ', '@'));

// исходящее: scripthtml    @@
  • html
  • string $string
  • boolean $remove = false

Этот метод помогает вам приготовить к отображению в существующей HTML верстке, отправленные пользователем данные. Особенно он полезен, если вы не хотите, чтобы пользователь поломал вашу верстку или вставлял изображения или скрипты в коментарии вашего блога, посты форума и т.п. Если $remove установлен на true, любой код HTML будет удален из $string, а потом отрендерен как объект HTML.

$badString = '<font size="99" color="#FF0000">HEY</font><script>...</script>';

echo $mrClean->html($badString);

// исходящее: &lt;font size=&quot;99&quot; color=&quot;#FF0000&quot;&gt;HEY&lt;/font&gt;&lt;script&gt;...&lt;/script&gt;

echo $mrClean->html($badString, true);

// исхдящее: font size=99 color=#FF0000 HEY fontscript...script
  • sql
  • string $string

Используется для избежания запросов SQL, добавляя слеши. Зависит от текущих настроек системы magic_quotes_gpc.

  • cleanArray
  • array @$dirtyArray

Эта функция прямолинейный, многоцелевой чистильщик. Используется на полных масивах (вроде $this->params['form'], например). Функция берет масив и чистит его: ничего не возвращает, поскольку очистка происходит в самом масиве, над каждым элементом:

  1. Странные пробелы (включая 0xCA) заменяются регулярными пробелами.

  2. HTML заменяется соответсвиями (включая \n в <br>).

  3. Двойная проверка специальных символов и перенос каретки назад для увеличения безопасности SQL..

  4. Добавляет слеши в SQL (просто запрашивает функции sql как упоминалось выше).

  5. Заменяет обратные слеши, которые поставил пользователь на доверенные обратные слеши.


Компонент безопасности

1. Введение

Компонент безопасности используется для защиты действий вашего контроллера от злоумышленных и неподходящих запросов. Это позволяет вам устанавливать условия, только при выполнении которых можно будет запросить действие, и опционально определить что делать с запросами, которые не удовлетворяют условия. Опять-таки, перед использованием компонента безопасности, вы должны убедиться что 'Security' добавлен в масив $components вашего контроллера./p>

2. Защита действий контроллера

Компонент безопасности содержит два основных метода для распределения доступа к действиям контроллера:

  • requirePost
  • string $action1
  • string $action2
  • string $action3...

Для выполнения определенного действия, нужно чтобы оно было запрошено через POST.

  • requireAuth
  • string $action1
  • string $action2
  • string $action3...

Убеждается, что запрос пришел из пределов приложения, проверяя ключ аутентификаци в данных отправленых POST, сравнивая его с ключом аутентификации сохраненной в сессии пользователя. Если они соответсвую, то действию разрешаено выполняться. Имейте ввиду, по причине гибкости, эта проверка запускается только если данные формы были отправлены методом POST. Если же действие вызывается регулярным запросом GET, requireAuth() не будет ничего делать. Для максимальной безопасности вам стоит использовать requirePost() и requireAuth() на действиях, которые вы хотите полностью защетить. Подробнее о том, как генерируется ключ атунтификации в Разделе 4 ниже.

Но сначала давайте взглянем на простой пример:

class ThingsController extends AppController
{
    var $components = array('Security');

    function beforeFilter()
    {
        $this->Security->requirePost('delete');
    }

    function delete($id)
    {
        // Это будет происходить только если действие вызвано через POST
        $this->Thing->del($id);
    }
}

Здесь мы говорим компоненту безопасности, что действие 'delete' требует запроса POST. Метод beforeFilter() обычно там где вы хотите сообщить Security (и большинству других компонентов) что делать с самим собой. Он тогда будет делать что сказано сразу после того, как вызван beforeFilter(), но сразу перед тем как вызвано само действие.

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

3. Управление недейсвительными запросами

Что ж, если запрос не соответсвует требованиям, которые мы определили, что с ним происходит? По умолчанию, запрос пропадает (black-holed), что значит, что клиенту посылается ошибка 404, а приложение сразу же завершает свою работу. Однако, у компонента безопасности есть свойство $blackHoleCallback, в котором вы можете вписать имя своей функции колбека, определенной в вашем контроллере.

Вместо того, чтобы просто выдавать ошибку 404 и потом ничего, это свойство позволяет вам выполнять какие-нибудь дополнительные проверки запроса, переадресовывать запрос в другое мето, или даже записать в лог IP адрес грешащего клиента. Однако, если вы выберете свой колбек, выход из приложения в случае недействительного запроса ложится на вашу совесть. Если ваш колбек возвращает true, тогда компонет безопасности продолжит проверять запрос по другим определенным требованиям. Иначе, проверка останавливается, а ваше приложение продолжает свободно работать.

4. Продвинутый запрос аутентификации

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

Например, если у меня есть действие в PostsController, которое отображает содержимое формы, переданное методом POST, в действии в CommentsController, тогда компонет безопасности должен быть включен и в CommentsController (который принимает запросы, и в общем-то защищает действие), и в PostsController (откуда поступают запросы).

Все время компонент безопасности загружен, даже если он не используется для защиты действий, он делает следующие вещи: сначала, он генерирует ключи аутентификации, используя основной класс Security. Потом, он пишет эти ключи в сессии, вместе с датой окончания действия ключа и некоторой дополнительной информацией (срок действия определяется вашими настройками безопасности сессий в /app/config/core.php). Далее, устанавливает ключ в вашем контроллере, с которым свяжутся позже.

Затем в ваших файлах отображения, любой тег формы, который вы генерируете используя $html->formTag() будет также содержать невидимое поле с ключем аутентификации. Таким образом, когда из формы данные отправляются (метод POST), компонент безопасности может сравнить это значение со значением в сессии. После этого, ключ аутентификации регенерируется, а сессия обновляется для следующего запроса.


Глобальные константы и функции Cake

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

1. Глобальные функции

Вот глобально-доступные функции Cake. Большинство из них удобные модификаторы для функций PHP с длинными названиями, но некоторые из них (как vendor() и uses()) используются для включения кода или выполнения других полезных функций. Если вы хотите себе маленькую милую функцию, делающую надоедающую работу, вам сюда.

  • config

Загружает файл основной настройки Cake. Возвращает true при успехе.

  • uses
  • string $lib1
  • string $lib2...

Используется для загрузки основных библиотек Cake(cake/libs/). Предоставьте имя файла библиотеки без разширения '.php'.

uses('sanitize', 'security');
  • vendor
  • string $lib1
  • string $lib2...

Используется для загрузки библиотек разширения, которые можно найти в директории /vendors. Предоставьте имя файла библиотеки без разширения '.php'.

vendor('myWebService', 'nusoap');
  • debug
  • mixed $var
  • boolean $showHtml = false

Уровень отладки (DEBUG) приложения не ноль - $var будет выводиться. Если $showHTML true, данные рендерятся в удобный для браузера вид.

  • a

Возвращает масив параметров, который используется для вызова функции оборачивания (wrapping).

function someFunction()
{
    echo print_r(a('foo', 'bar'));
}

someFunction();

// output:

array(
    [0] => 'foo',
    [1] => 'bar'
)
  • aa

Используется для создания ассоциативных масивов, сформированных из параметров, которые используются для вызова функции оборачивания.

echo aa('a','b');

// исходящее:

array(
    'a' => 'b'
)
  • e
  • string $text

Удобная обвертка для echo().

  • low

Удобная обвертка для strtolower().

  • up

Удобная обвертка для strtoupper().

  • r
  • string $search
  • string $replace
  • string $subject

Удоная обвертка для str_replace().

  • pr
  • mixed $data

Удобная функция, эквивалентная следующему:

echo "<pre>" . print_r($data) . "</pre>";

Просто выводит информацию, если DEBUG не ноль.

  • am
  • array $array1
  • array $array2...

Соединяет и возвращает масивы, объявленные в параметрах.

  • env
  • string $key

Получает переменную окружения из доступных ресурсов. Используется как замена если $_SERVER или $_ENV - недоступны.

Эта функция также эмулирует PHP_SELF и DOCUMENT_ROOT на серверах, которые этого не поддерживают. На самом деле, лучше всегда использовать env() вместо $_SERVER или getenv() (особенно, если вы собираетесь распространять код).

  • cache
  • string $path
  • string $expires
  • string $target = 'cache'

Записывает данные в $data по адресу /app/tmp определенным $path как кеш. Срок действия определяется $expires (значение должно быть действительной строкой strtotime()). $target кешированных данных может быть либо 'cache' либо 'public'.

  • clearCache
  • string $search
  • string $path = 'views'
  • string $ext

Используется для удаления файлов в директориях кеша, или очистке содержимого этих директорий.

Если $search это строка, то соответствующая директория кеша или файлы будут удалены из кеша. Параметр $search может быть объявлен как масив имен файлов/директорий, которые будут очищены из кеша. Если же этот параметр пуст, то все файлы в /app/tmp/cache/views будут удалены.

Параметр $path можно использовать для определения того, какую директорию в /tmp/cache стоит очищать. По умолчанию 'views'.

Параметр $ext используется для определения расширения файлов, которые вы хотите удалить.

  • stripslashes_deep
  • array $array

Рекурсивно убирает слеши со всех значений в масиве.

  • countdim
  • array $array

Возвращает число размерности указанного масива.

  • fileExistsInPath
  • string $file

Ищет данное имя файла в директории, в которой находится. Возвращает путь к файлу, если найден такой, или false если - нет.

  • convertSlash
  • string $string

Конвертирует прямые слеши в подчеркивание и убирает подчеркивания в начале и конце строки.

2. Основные константы определения CakePHP

ACL_CLASSNAME: имя текущего исполняющего и управляющего ACL класса для CakePHP. Эта константа позволяет пользователям интегрировать третестепенные классы.

ACL_FILENAME: имя файла, внутри которого можно найти класс ACL_CLASSNAME.

AUTO_SESSION: если установлено на false, то session_start() не будет автоматически вызываться, во время обращения к приложению.

CACHE_CHECK: если установлено на false, то кеширование отображений отключается для всего приложения.

CAKE_SECURITY: определяет уровень безопасности сессий для приложения в соответствии с CAKE_SESSION_TIMEOUT. Можно установить 'low','medium', или 'high'. В зависимости от установок, CAKE_SESSION_TIMEOUT умножается соответственно следующему:

  1. low: 300

  2. medium: 100

  3. high: 10

CAKE_SESSION_COOKIE: имя куки сессии для приложения.

CAKE_SESSION_SAVE: устанавливается 'php', 'file', или 'database'.

  1. php: Cake использует управление сессиями PHP по умолчанию (обычно определенное в php.ini)

  2. file: данные сессии хранятся и управляются в /tmp

  3. database: используется управление сессиями базой данных Cake(см. главу "Компонент сессий Cake").

CAKE_SESSION_STRING: случайная строка, используемая в управлении сессией.

CAKE_SESSION_TABLE: имя таблицы для хранения данных сессии (если CAKE_SESSION_SAVE == 'database'). Не включайте тут префикс, если он установлен в настройках соединения с базой данных по умолчанию.

CAKE_SESSION_TIMEOUT: количество секунд до окончания действия сессии. Это значение увеличивается в соответсвии с CAKE_SECURITY.

COMPRESS_CSS: если установлено на true, то CSS сжимается на выводе. Это требует, чтобы директория /var/cache была доступна для записи веб-сервером. Чтобы вопсользоваться этим, свяжите ваши таблицы стилей, используя /ccss (а не /css) или воспользуйтесь Controller::cssTag().

DEBUG: определяет уровень отчетов об ошибках и отладочных сообщений, который приложение CakePHP будет рендерить. Устанавливайте как значение целое число от 0 до 3.

  1. 0: Режим продукции. Не выводятся ошибки и отладочные сообщения.

  2. 1: Режим разработки. Показываются и ошибки и отладочные сообщения.

  3. 2: То же, что и 1, но еще и выводится SQL.

  4. 3: То же что и 2, но с полным дампом текущего объекта (обычно контроллера).

LOG_ERROR: Константа ошибок. Используется для диференциации логирования ошибок и отладочных сообщений. Последняя версия PHP поддерживает LOG_DEBUG.

MAX_MD5SIZE: Максимальный размер (в байтах) хеширования md5().

WEBSERVICES: Если установлена на true, то встроенная функциональность веб-сервисов Cake - включена.

3. Константы пути CakePHP

APP: путь к директории приложения.

APP_DIR: имя директории текущего приложения.

APP_PATH: абсолютный путь к директории приложения.

CACHE: путь к директории кешированных файлов.

CAKE: путь к директории cake приложения.

COMPONENTS: путь к директории компонентов приложения.

CONFIGS: путь к директории файлов конфигурации.

CONTROLLER_TESTS: путь к директориям тестовых контроллеров.

CONTROLLERS: путь к контроллерам приложения.

CSS: путь к директории файлов CSS.

ELEMENTS: путь к директории элементов

HELPER_TESTS: путь к директории тестовых хелперов.

HELPERS: путь к директории хелперов.

INFLECTIONS: путь к директории инфлекций (обычно внутри директории конфигурации).

JS: путь к директории файлов JavaScript.

LAYOUTS: путь к директории размещений.

LIB_TESTS: путь к директории тестовых библиотек Cake.

LIBS: путь к директории библиотек Cake.

LOGS: путь к директории логов.

MODEL_TESTS: путь к директории тестовых моделей.

MODELS: путь к директории моделей.

SCRIPTS: путь к директории скриптов Cake.

TESTS: путь к директории тестов (родительской для директорий тестовых моделей, контроллеров, и т.д.)

TMP: путь к директории tmp.

VENDORS: путь к директории вендоров.

VIEWS: путь к директории отображений.

4. Пути конфигурации webroot CakePHP

CORE_PATH: путь к основным библиотекам Cake.

WWW_ROOT: путь к директории webroot приложения (обычно в /cake/)

CAKE_CORE_INCLUDE_PATH: путь к основным библиотекам Cake.

ROOT: имя родительской директории для index.php CakePHP.

WEBROOT_DIR: имя директории webroot приложения.


Соглашения Cake

1. Соглашения, да?

Да, соглашения. Согласно thefreedictionary:

  1. Основное взаимосогласие или принятие определенных практик или позиций: По соглашению, север находится вверху большинства карт.

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

  3. Широко-используемое и принятое устройство или техника, как драма, литература, или рисование: театральное соглашение об отходах в сторону (чтобы прошептать что-то).

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

2. Имена файлов

Имена файлов подчеркнуты. Как основное правило, если у вас есть класс MyNiftyClass, тогда в Cake, этот файл должен быть назван my_nifty_class.php.

Так что если вы найдете snippet вы автоматически узнаете что:

  1. Елси это имя контроллера KissesAndHugsController, то имя его файла kisses_and_hugs_controller.php

  2. Если это имя модели OptionValue, тогда имя файла будет option_value.php

  3. Если это имя компонента MyHandyComponent тогда имя его файла должно быть my_handy.php

  4. Если это имя хелпера BestHelperEver, тогда имя файла должно быть best_helper_ever.php

3. Модели

  1. Имена классов моделей число единичное.

  2. Имя класса модели пишется с большой буквы для однословной модели, и КаждоеСловоИмениМодели с большой в много-словном названии.

    1. Примеры: Человек, Обезьяна, СтекляннаяДверь, РеальноСмешнаяШтука

  3. соединенные таблицы методом многие-к-многим должны быть названы: по-алфавиту_первая_таблица_множественное_число_по-алфавиту_вторая_таблица_множественное_число например: tags_users

  4. Имена файлов моделей используют нижний регистр и подчеркивание.

    1. Примеры: person.php, monkey.php, glass_door.php, line_item.php, really_nifty_thing.php

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

    1. Примеры: people, monkeys, glass_doors, line_items, really_nifty_things

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

  1. Имя модели: Установите var $name в определение вашей модели.

  2. Таблицы базы данных, относящиеся к моделям: установите var $useTable в определение вашей модели.

4. Контроллеры

  1. Имена классов контроллеров в множественном числе.

  2. Схема названий контроллеров та же что и моделей, только в конце добавляется окончание 'Controller'.

    1. Примеры: PeopleController, MonkeysController, GlassDoorsController, LineItemsController, ReallyNiftyThingsController

  3. Имена файлов также как и у моделей, только с окончанием '_controller'. Так что если у вас есть PostsController, то имя его файла должно быть posts_controller.php

    1. Примеры: people_controller.php, monkeys_controller.php, glass_doors_controller.php, line_items_controller.php, really_nifty_things_controller.php

  4. Для защищенной видимости, именам действий контроллера должны передовать '-'.

  5. Для приватной видимости, именам действий контроллера должны передовать '--'.

5. Отображения

  1. Отображения называются как действие, которое они отображают.

  2. Имя файла отображения такое же как имя действия, в нижнем регистре.

    1. Примеры: PeopleController::worldPeace() ожидает отображения
            /app/views/people/world_peace.thtml;
            MonkeysController::banana() ожидает отображения
            /app/views/monkeys/banana.thtml.

Вы можете заставить действие рендерить определенное отображение вызвав $this->render('name_of_view_file_without_dot_thtml'); в конце действия.

6. Хелперы

  1. Имя класса хелперов - каждое слово в имени с большой буквы, окончание "Helper", имя файла с подчеркиванием.

    1. Примеры: class MyHelperHelper это хелпер /app/views/helpers/my_helper.php.

Включается в контроллере масивом $helpers=array('Html','MyHelper'); в отображении доступен с $myHelper->method().

7. Компоненты

  1. Имя класса компонента такое же как у хелперов, но окончание "Component", имя файла с подчеркиванием.

    1. Пример: класс MyComponentComponent расширяет объект в /app/controllers/components/my_component.php.

Включается в контроллер с $components =array('MyComponent'); в контроллере доступен с $this->MyComponent->method().

8. Вендоры

Вендоры не следуют никакому соглашению по очевидным причинам: они третестепенные части кода, у Cake нет контроля над ними.


Обучение: Блог Cake

1. Введение

Добро пожаловать в Cake! Возможно вы просматриваете это обучение, потому что хотите узнать больше о том как работает Cake. Это наша цель, увеличить продуктивность и сделать написание кода более приятным: мы надеемся вы осознаете это, когда углубитесь в код.

Это обучение проведет вас через создание простого приложения блогов. Мы будем доставать и устанавливать Cake, создавая и настраивая базу данных, создавать достаточно кода приложения, чтобы просматривать, добавлять, редактировать и удалять посты в блоге.

Вот, что вам понадобится:

  1. Работающий веб-сервер. Мы будем предполагать, что вы используете Apache, хотя иструкции также должны подойти и к другим серверам. Возможно нам придется еще немного поковыряться в настройках сервера, но большинство достают Cake и запускают его вообще без каких-либо настроек.

  2. Сервер базы данных. Мы собираемся использовать mySQL в этом обучении. Вам нужно знать достаточно об SQL чтобы создать базу данных.

  3. Базовые знания PHP. Чем больше объекто-ориентированого программирования было в вашей жизни, тем лучше: но бойтесь если вы фанат процедурного программирования.

  4. Наконец, вам нужны базовые знания MVC шаблонного программирования. Быстрый обзор можно найти в главе Основные концепции, Раздел 2: MVC. Не волнуйтесь там половина страницы или около того.

Давайте начнем!

2. Достаем Cake

Сначала давайте достанем копию свеженького кода Cake. Самый последний релиз на момент этой документации это CakePHP 1.0.1.2708.

Для скачивания посетите проект CakePHP на Cakeforge: http://cakeforge.org/projects/cakephp/ и закачайте стабильный релиз.

Также можете посмотреть здесь: https://svn.cakephp.org/repo/trunk/cake/1.x.x.x/

Не важно как вы закачали, поместите код в ваш DocumentRoot. Директория должна выглядить как-то так:

/path_to_document_root
    /app
    /cake
    /vendors
    .htaccess
    index.php
    VERSION.txt

Теперь неплохо будет узнать немного о том, как работает структура директории Cake: взгляните на Главу "Основные концепции" Раздел 3: Обзор размещения файлов Cake.

3. Создание базы данных блога

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

/* Сначала, создайте таблицу постов: */
CREATE TABLE posts (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(50),
    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);

/* Затем вставим пару постов для тестирования: */
INSERT INTO posts (title,body,created)
    VALUES ('The title', 'This is the post body.', NOW());
INSERT INTO posts (title,body,created)
    VALUES ('A title once again', 'And the post body follows.', NOW());
INSERT INTO posts (title,body,created)
    VALUES ('Title strikes back', 'This is really exciting! Not.', NOW());

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

Посмотрите на Соглашение Cake для справки, но достаточно сказать, что название таблицы 'posts' автоматически привязывает ее к модели Post, а поля названные 'modiefied' и 'created' автоматически будут управляться Cake'ом.

4. Настройка базы данных Cake

Давайте сообщим Cake где находится наша база данных и как с ней связаться. Это будет первый и последний этап, где вы что-либо настраиваете.

Копия конфигурации базы данных находится в /app/config/database.php.default. Скопируйте этот файл в ту же директорию, только переименуйте в database.php.

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

var $default = array('driver'   => 'mysql',
                     'connect'  => 'mysql_pconnect',
                     'host'     => 'localhost',
                     'login'    => 'cakeBlog',
                     'password' => 'c4k3-rUl3Z',
                     'database' => 'cake_blog_tutorial' );

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

5. Заметка о mod_rewrite

Если приветсвенная страница Cake выгядит слегка смешно (без изображений или стилей css), это скорее всего значит, что mod_rewrite не работает на вашей системе. Вот несколько советов как заставить его работать:

  1. Убедитесь, что подмена .htaccess разрешена: в вашем httpd.conf, у вас должна быть секция, которая определяет секцию для каждой директории на вашем сервере. Убедитесь, что AllowOverride установлен на All для правильной директории.

  2. Убедитесь, что вы редактируете системный httpd.conf, а не пользовательский или определенного сайта.

  3. По тем или иным причинам, у вас может быть копия CakePHP в которой нету .htaccess. Это иногда случается потому, что некоторые операционные системы воспринимают файлы с точкой перед именем, как скрытые и не копирует их. Убедитесь, что ваша копия CakePHP скачана с надежного источника, то есть у нас.

  4. Убедитесь, что вы правильно загружаете mod_rewrite! У вас должно быть что-то вроде LoadModule rewrite_module libexec/httpd/mod_rewrite.so и AddModule mod_rewrite.c в вашем httpd.conf.

Если вы не хотите или не можете заставить mod_rewrite (или подобный модуль) работать на вашем сервере, вам нужно использовать встроенный красивые ссылки Cake. В /app/config/core.php разкоментируйте линию, которая выглядит так:

define ('BASE_URL', env('SCRIPT_NAME'));

Это сделает ваши ссылки похожимы на эти www.example.com/index.php/controllername/actionname/param rather than www.example.com/controllername/actionname/param.

6. Создание модели Post

Класс модели это хлеб и масло приложений CakePHP. Создавая модель Cake, которая будет взаимодействовать с нашей базой данных, мы получим потребность в создании операций показать, добавить, редактировать, и удалить.

Файлы класса модели Cake здесь /app/models, а файл который мы будем создавать будет сохранен в /app/models/post.php. Завершенный файл должен выглядить так:

/app/models/post.php

<?php

class Post extends AppModel
{
    var $name = 'Post';
}

?>

В связи со способом, по которому классу и файлу даны имена, это говорит Cake'у, что вы хотите чтобы модель Post была доступна в вашем PostController, который связан с таблицей в вашей базе данных 'posts'.

Переменная $name всегда стоит того, чтобы ее добавили, и используется чтобы совладать с некоторыми именами классов в PHP4.

Больше о моделях, таких как префиксы таблиц, колбеки и верификация, посмотрите главу Модели.

7. Создание контроллера постов

Дальше мы создадим контроллер для наших постов. Контроллер это то, где будет весь код для взаимодействия с постами, и он также находится там где и все действия для этой модели. Вам нужно разместить этот новый контроллер в файл под названием posts_controller.php в вашем каталоге /app/controllers. Вот как должен выглядить основной контроллер:

/app/controllers/posts_controller.php

<?php

class PostsController extends AppController
{
    var $name = 'Posts';
}

?>

Теперь давайте добавим действие в наш контроллер. Когда пользователи запришивают www.example.com/posts, это то же, что и запрос www.example.com/posts/index. С тех пор как мы хотим, чтобы пользователи могли просматривать список постов, когда они приходят по этой ссылке, действие index будет выглядить как это:

/app/controllers/posts_controller.php (действие index добавлено)

<?php

class PostsController extends AppController
{
    var $name = 'Posts';

    function index()
    {
        $this->set('posts', $this->Post->findAll());
    }
}

?>

Позвольте мне немного пояснить действие. После определения функции index() в нашем PostsController, пользователи могут получить доступ к коду запрашивая www.example.com/posts/index. Также, если бы мы определили функцию foobar(), пользователи могли бы получить доступ по адресу www.example.com/posts/foobar.

Простое указание в действие использует set() для вставки данных в отображение (которое мы создадим следующим). Устанавливается переменная отображения 'posts' еквивалентная возвращаемому значению метода findAll() модели Post. Наша модель Post автоматически доступна в $this->Post, потому что мы следили соглашению Cake по присвоению имен.

Узнать больше о контроллерах Cake можно в главе Контроллеры.

8. Создание отображений постов

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

Отображения Cake это просто фрагменты HTML и PHP, которые устанавливаются в разметку приложения. Разметки можно определять и переключаться между ними, но пока, давайте просто использовать стандартную.

Помните в последнем разделе как мы назначали переменную 'posts' в отображении используя метод set()? Это будет передавать данные в отображение, которое будет выглядить как это:

// print_r($posts) output:

Array
(
    [0] => Array
        (
            [Post] => Array
                (
                    [id] => 1
                    [title] => The title
                    [body] => This is the post body.
                    [created] => 2006-03-08 14:42:22
                    [modified] =>
                )
         )
    [1] => Array
        (
            [Post] => Array
                (
                    [id] => 2
                    [title] => A title once again
                    [body] => And the post body follows.
                    [created] => 2006-03-08 14:42:23
                    [modified] =>
                )
        )
    [2] => Array
        (
            [Post] => Array
                (
                    [id] => 3
                    [title] => Title strikes back
                    [body] => This is really exciting! Not.
                    [created] => 2006-03-08 14:42:24
                    [modified] =>
                )
         )
)

Файлы отображений Cake хранятся в /app/views внутри каталога названного в честь контроллера к которому они принадлежат (мы должны создать каталог 'posts' в таком случае). Для форматирования данных постов в приятную таблицу, наш код отображения может выглядить так:

/app/views/posts/index.thtml

<h1>Blog posts</h1>
<table>
    <tr>
        <th>Id</th>
        <th>Title</th>
        <th>Created</th>
    </tr>

   <!--Здесь мы проходим цикл через наш масив $posts, выводя информацию о постах -->

    <?php foreach ($posts as $post): ?>
    <tr>
        <td><?php echo $post['Post']['id']; ?></td>
        <td>
            <?php echo $html->link($post['Post']['title'], "/posts/view/".$post['Post']['id']); ?>
        </td>
        <td><?php echo $post['Post']['created']; ?></td>
    </tr>
    <?php endforeach; ?>

</table>

Надеюсь это выглядит в какой-то мере просто.

Возможно вы заметили использование объекта названного $html. Это отдельный пример из класса HtmlHelper. Хелперы помогают делать ссылки, формы, JavaScript и Ajax. Подробнее о хелпрах в... вы угадали главе Хелперы, но что важно заметить здесь это то, что метод link() сгенерирует ссылки HTML с данным заголовком (первый параметр) и данным адресом (второй параметр).

Когда определяете ссылки в Cake, вы просто даете путь относящийся к базе приложения, а Cake заполняет оставшееся. То есть, ваши ссылки будут принимать форму /controller/action/id.

Теперь вы можете направить свой браузер на http://www.example.com/posts/index. Вы должны увидеть отображение, коректно сформатированное с заголовком и таблицей со списком постов.

Если вы нажмете на одну из ссылок, которые мы создали в этом отображении (эта ссылка это заголовок поста с адресом /posts/view/some_id), вы наверняка будете осведомлены Cake'ом, что действие еще не определено. Если вы не будете проинформированы, или что-то пошло не так, или же вы уже определили действие, в последнем случае вы подлец. В любом случае, мы создадим его сейчас:

/app/controllers/posts_controller.php (view action added)

<?php

class PostsController extends AppController
{

    var $name = 'Posts';

    function index()
    {
          $this->set('posts', $this->Post->findAll());
    }

    function view($id = null)
    {
        $this->Post->id = $id;
        $this->set('post', $this->Post->read());
    }
}

?>

Запрос set() должен казаться знакомым. Заметьте мы используем read() а не findAll(), потому что мы хотим получить информацию только об одном посте.

Заметьте что действие нашего отображение берет параметр. Этот параметр передан в приложение запросом URL. Если пользователь запрашивает /posts/view/3, тогда значение 3 будет вставлено как $id.

Теперь давайте создадим отображение для нашего нового действия 'view' и разместим в /app/views/posts/view.thtml.

/app/views/posts/view.thtml

<h1><?php echo $post['Post']['title']?></h1>

<p><small>Created: <?php echo $post['Post']['created']?></small></p>

<p><?php echo $post['Post']['body']?></p>

Проверьте работает ли это, пройдя по ссылкам /posts/index или запросом поста /posts/view/1.

9. Добавление постов

Чтение постов из базы данных и показывание их нам это конечно хорошо, но давайте позволим добавление новых постов.

Во-первых, начнем с действия add() в PostsController:

/app/controllers/posts_controller.php ( действие add добавлено)

<?php

class PostsController extends AppController
{
    var $name = 'Posts';

    function index()
    {
         $this->set('posts', $this->Post->findAll());
    }

    function view($id)
    {
        $this->Post->id = $id;
        $this->set('post', $this->Post->read());

    }

    function add()
    {
        if (!empty($this->data))
        {
            if ($this->Post->save($this->data))
            {
                $this->flash('Your post has been saved.','/posts');
            }
        }
    }
}

?>

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

Когда пользователь использует форму для отправки данных в ваше приложение, эта информация доступна в $this->params. Вы можете вывести их с помощью pr() если хотите увидеть их. $this->data это псеводоним для $this->params['data'].

Функция $this->flash() это функция контроллера, которая отображает сообщения пользователю на секунду (используя разметку для флеш-сообщений) потом перенаправляет пользователя по другому адресу (/posts, в нашем случае). Если DEBUG стоит на 0 $this->flash() переадресует автоматически, однако, если DEBUG на 0 вы сможете увидить флеш верстку и кликнуть на сообщении чтобы управлять переадресацией.

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

10. Верификация данных

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

Чтобы воспользоваться преимуществами возможностей проверки, вам надо использовать HTML хелпер в вашем отображении. HTML хелпер доступен по умолчанию во всех отображениях в $html.

Вот наше отображения для добавления:

/app/views/posts/add.thtml

<h1>Add Post</h1>
<form method="post" action="<?php echo $html->url('/posts/add')?>">
    <p>
        Title:
        <?php echo $html->input('Post/title', array('size' => '40'))?>
        <?php echo $html->tagErrorMsg('Post/title', 'Title is required.') ?>
    </p>
    <p>
        Body:
        <?php echo $html->textarea('Post/body', array('rows'=>'10')) ?>
        <?php echo $html->tagErrorMsg('Post/body', 'Body is required.') ?>
    </p>
    <p>
        <?php echo $html->submit('Save') ?>
    </p>
</form>

Как и с $html->link(), $html->url() будет генерировать подходящую ссылку из контроллера и действия, которое мы предоставили. По умолчанию, это выводит тег формы POST, но это можно модифицировать вторым параметром. Функции $html->input() и $html->textarea() убирает элементы формы одинаковых имен. Первый параметр сообщает Cake, к которой модели/полю они относятся, а второй параметр для дополнительных аттрибутов HTML (как размер поля). Снова-таки посмотрите главу Хелперы для подробной информации о хелперах.

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

Если вы хотите обновить свое отображение /app/views/posts/index.thtml чтобы включить новую ссылку "Add Post" направляющую www.example.com/posts/add.

Выглядит достаточно круто, но как мне сообщить Cake о своих требованиях верификации? Вот где мы возвращаемся к модели.

/app/models/post.php (верификационный массив добавлен)

<?php

class Post extends AppModel
{
    var $name = 'Post';

    var $validate = array(

        'title'  => VALID_NOT_EMPTY,
        'body'   => VALID_NOT_EMPTY

    );
}

?>

Массив $validate сообщает Cake как проверять ваши данные когда вызван метод save(). Значение для тех ключей просто константы установленные Cake'ом, который переводит их в регулярные выражения (см. /cake/libs/validators.php). Сейчас проверки Cake'а базируются на регулярных выражениях, но вы так же можете использовать Model::invalidate() чтобы просмотреть свои собственные верификации.

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

11. Удаление постов

Теперь давайте создадим возможность пользователям удалять посты. Начнем с действия delete() в PostsController:

/app/controllers/posts_controller.php (только действие delete)

function delete($id)
{
    $this->Post->del($id);
    $this->flash('The post with id: '.$id.' has been deleted.', '/posts');
}

Этот код удаляет пост определенный по $id, и использует flash() чтобы показать пользователю сообщение подтверждения перед переадрисаций в /posts.

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

/app/views/posts/index.thtml (добавленны ссылки добавления и удаления)

<h1>Blog posts</h1>
<p><?php echo $html->link('Add Post', '/posts/add'); ?></p>
<table>
    <tr>
        <th>Id</th>
        <th>Title</th>
        <th>Created</th>
    </tr>

   <!-- Цикл в котором показуются посты -->

    <?php foreach ($posts as $post): ?>
    <tr>
        <td><?php echo $post['Post']['id']; ?></td>
        <td>
            <?php echo $html->link($post['Post']['title'], '/posts/view/'.$post['Post']['id']);?>
            <?php echo $html->link(
                'Delete',
                "/posts/delete/{$post['Post']['id']}",
                null,
                'Are you sure?'
            )?>
        </td>
        </td>
        <td><?php echo $post['Post']['created']; ?></td>
    </tr>
    <?php endforeach; ?>

</table>

Этот код отображения также позволяет использовать Html хелпер, чтобы подсказать пользователю диалог подтверждения JavaScript перед попыткой удалить пост.

12. Редактирование постов

Итак... редактирование поста: начнем-с. Теперь вы профессионал в Cake, так что вы должны были подобрать образец. Сделайте действие, потом отображение. Вот как будет выглядить действие редактирования PostsController:

/app/controllers/posts_controller.php (только действие edit)

function edit($id = null)
{
    if (empty($this->data))
    {
        $this->Post->id = $id;
        $this->data = $this->Post->read();
    }
    else
    {
        if ($this->Post->save($this->data['Post']))
        {
            $this->flash('Your post has been updated.','/posts');
        }
    }
}

Это проверяет на наличие отправленных данных формы. Если ничего не отправлено, идем искать пост и отправляем его в отображение. Если какие-то данные были отправлены, пробуем сохранить модель Post (или возвращает назад пользователя и показываем верификационные ошибки).

Отображение редактирования может выглядить так:

/app/views/posts/edit.thtml

<h1>Edit Post</h1>
<form method="post" action="<?php echo $html->url('/posts/edit')?>">
    <?php echo $html->hidden('Post/id'); ?>
    <p>
        Title:
        <?php echo $html->input('Post/title', array('size' => '40'))?>
        <?php echo $html->tagErrorMsg('Post/title', 'Title is required.') ?>
    </p>
    <p>
        Body:
        <?php echo $html->textarea('Post/body', array('rows'=>'10')) ?>
        <?php echo $html->tagErrorMsg('Post/body', 'Body is required.') ?>
    </p>
    <p>
        <?php echo $html->submit('Save') ?>
    </p>
</form>

Это отображение выводит форму редактирования (с заполненными значениями), и нужные сообщения об ошибках (если есть). Одна вещь на заметку: Cake будет подразумевать, что вы редактируете модель если поле 'id' существует в текущей сохраненной модели. Если нет 'id' (оглянитесь на наше отображение добавления), Cake будет подразумевать, что вы вводите новую модель, когда вызываете save().

Теперь вы можете обновить ваше отображение index ссылками на редактирование определенных постов:

/app/views/posts/index.thtml (edit links added)

<h1>Blog posts</h1>
<p><?php echo $html->link("Add Post", "/posts/add"); ?>
<table>
    <tr>
        <th>Id</th>
        <th>Title</th>
        <th>Created</th>
    </tr>

   <!-- Здесь в цикле отображаются посты -->

    <?php foreach ($posts as $post): ?>
    <tr>
        <td><?php echo $post['Post']['id']; ?></td>
        <td>
            <?php echo $html->link($post['Post']['title'], '/posts/view/'.$post['Post']['id']);?>
            <?php echo $html->link(
                'Delete',
                "/posts/delete/{$post['Post']['id']}",
                null,
                'Are you sure?'
            )?>
            <?php echo $html->link('Edit', '/posts/edit/'.$post['Post']['id']);?>
        </td>
        </td>
        <td><?php echo $post['Post']['created']; ?></td>
    </tr>
    <?php endforeach; ?>

</table>

13. Роуты

Эта часть опциональная, но полезная в понимании как формулируются ссылки к определенным функциям в Cake. Мы собираемся сделать только быстрые изменения в роутах в этом обучающем примере. Для более полной справки смотрите главу "Настройка", Раздел 3: Настройка роутов.

Роут по умолчанию Cake отведет человека, посещающего корень сайта (например http://www.example.com) к PagesController'у, и отрендерит отображение под названием home. Вместо этого, мы хотим, чтобы пользователи нашего блога шли к нашему PostsController'у, который скоро будет создан.

Роутинг Cake находится в /app/config/routes.php. Вы хотите разкоментировать или удалить линию похожую на эту:

$Route->connect ('/', array('controller'=>'pages', 'action'=>'display', 'home'));

Эта линия связывает ссылку / с домашней страницей Cake по умолчанию. Мы хотим связать ее с нашим собственным контроллером, так что добавьте линию, которая выгляит так:

$Route->connect ('/', array('controller'=>'posts', 'action'=>'index'));

Это должно связывать запрос пользователей '/' с действием index() нашего PostsController.

14. Итог

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

Теперь, когда вы создали начальное приложение, вы готовы к реальным вещам. Начните свой собственный проект, прочтите остальное руководство и API.

Если вам нужна помощь, приходите в #cakephp. Добро пожаловать в Cake!


Пример: Простая аутентификация пользователей

1. Общая картина

Если вы новичок в CakePHP, вам настоятельно рекомендуется копировать код и вставлять в свое приложение для использования. Если нет: эта глава - обсуждение ядра Cake, а не безопасности приложений. Я сомневаюсь, что мы будем обсуждать очевидные засады безопасности, главная цель этого примера - показать как работает ядро Cake, и позволить вам создавать пуленепробиваемые приложения.

В Cake есть контроль доступа через втроенный движок ACL, но как насчет аутентификации пользователей? Как насчет этого?

Что ж, на данный момент, мы установили, что система аутентификации пльзователей изменяется от приложения к приложению. Некоторые люят хешированные пароли, другие, LDAP (Облегченный протокол доступа к кталогам) - и почти все приложения содержат модели User, которые незначительно отличаются. Теперь мы оставляем это на вас. Изменится ли это? Мы не уверены пока. Сейчас, мы думаем, что большие накладные расходы при встаивании этого в фреймворк того не стоят, потому что создание собственной системы аутентификации пользователей с Cake достаточно просто.

Вам нужно всего три вещи:

  1. Способ аутентифицировать пользователей (обычно делается проверкой логина/пароля)

  2. Способ упорно следить что пользователи путешествуют по вашему приложению (обычно делается сессиями)

  3. Способ проверить аутентифицировался ли пользователь (обычно устанавливается с помощью взаимодействия с сессиями)

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

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

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

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

2. Аутентификация и стойкость

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

Table 'users', Fictional Client Management System Database

CREATE TABLE `users` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(255) NOT NULL,
  `password` varchar(32) NOT NULL,
  `first_name` varchar(255) NOT NULL,
  `last_name` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
)

Достаточно просто, не так ли? Модель Cake для этой таблицы может быть достаточно ясной:

<?php
class User extends AppModel
{
    var $name = 'User';
}
?>

Первое, что нам нужно это отображение и действие логина. Это даст возможность пользователям логиниться и возможность системе обрабатывать информацию, чтобы узнать давать пользователю доступ или нет. Отображение это простая форма HTML, созданная при помощи HTML Хелпера:

/app/views/users/login.thtml

<?if ($error): ?>
<p>The login credentials you supplied could not be recognized. Please try again.</p>
<? endif; ?>

<form action="<?php echo $html->url('/users/login'); ?>" method="post">
<div>
    <label for="username">Username:</label>
    <?php echo $html->input('User/username', array('size' => 20)); ?>
</div>
<div>
    <label for="password">Password:</label>
    <?php echo $html->password('User/password', array('size' => 20)); ?>
</div>
<div>
    <?php echo $html->submit('Login'); ?>
</div>
</form>

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

/app/controllers/users_controller.php (частично)

<?php
class UsersController extends AppController
{
    function login()
    {
        //Не показывать сообщение об ошибке, если данные не были отправлены.
        $this->set('error', false);

        //Если пользователь отправил данные из формы:
        if (!empty($this->data))
        {
            // Сначала проверим, есть ли пользователи в базе данных
            //с именем, которое предоставил пользователь в форме:

            $someone = $this->User->findByUsername($this->data['User']['username']);

            // На этом этапе $someone это все данные пользователя, или же она пуста.
            // Давайте сравним пароль из формы с паролем
            //из базы данных.

            if(!empty($someone['User']['password']) && $someone['User']['password'] == $this->data['User']['password'])
            {
                // Заметка: надеемся что пароли в вашей БД хешированы,
                // так что ваше сравнение может выглядеть так:
                // md5($this->data['User']['password']) == ...

                // Это значит, что они были одинаковыми. Теперь мы можем создавать некую основную
                //информацию сессии, чтобы запомнить этого пользователя как "залогиненого".

                $this->Session->write('User', $someone['User']);

                //Теперь, когда у нас они сохранены в сессии, перенаправим их
                //на нужную страницу в приложении.

                $this->redirect('/clients');
            }
            //В другом случае, они предоставили не верные данные:
            else
            {
                //Помните переменную $error в отображении? Давайте поставим ее на true:
                $this->set('error', true);
            }
        }
    }

    function logout()
    {
        //Перенаправляем пользователя на это действие, если они нажали кнопку Выйти.
        //Все что нам здесь нужно, это удалить информацию сессии:

        $this->Session->delete('User');

        //Ну и наверняка нам нужно перенаправить их куда-нибудь...

        $this->redirect('/');
    }
}
?>

Не плохо: содержание действия login() может быть меньше 20 линий, если вы были лаконичны. Результат этого действия либо 1: информация пользователя записана в сессию и он перенаправлен на нужную страницу приложения, или 2: отброшен обратно на страницу входа (в дополнении с сообщением об ошибке).

3. Проверка доступа в вашем приложении

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

Один способ это создать функцию в AppController, которая будет делать проверку сессии и отбрасывание.

/app/app_controller.php

<?php
class AppController extends Controller
{
    function checkSession()
    {
        //Если иформация сессии не была установлена...
        if (!$this->Session->check('User'))
        {
            //Отправляет пользователя к экрану логина
            $this->redirect('/users/login');
            exit();
        }
    }
}
?>

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

Принудительная аутентификация перед всеми действиями контроллера

<?php
class NotesController extends AppController
{
    // Не хотите, чтобы неаутентифицированный пользователь бродил по каким-либо действия
    // в этом контроллере? Воспользуйтесь beforeFilter, чтобы запускать checkSession
    // перед любым кодом действия.

    function beforeFilter()
    {
        $this->checkSession();
    }
}
?>

Принудительная аутентификация перед одним действием контроллера

<?php
class NotesController extends AppController
{
    function publicNotes($clientID)
    {
        //Публичный доступ к этому действию открыт....
    }

    function edit($noteId)
    {
        // Но вы хотите, что бы этим действием пользовались только аутентифицированные пользователи.
        $this->checkSession();
    }
}
?>

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