CakePHP

Release 3.9

Cake Software Foundation

Первое знакомство с CakePHP

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

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

Соглашения превыше конфигурации

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

Модель

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

В случае с соц. сетями, Модель заботится о таких задачах, как сохранение пользовательских данных, ассоциации пользователя с его друзьями, хранение и получение пользовательских фото, нахождение возможных друзей, и т.д. Объектами модели в данном случае могут быть «Друг», «Пользователь», «Комментарий», или «Фото». Если нам нужно загрузить некоторые данные из нашей таблицы users мы должны сделать следующее:

use Cake\ORM\TableRegistry;

// Prior to 3.6 use TableRegistry::get('Users')
$users = TableRegistry::getTableLocator()->get('Users');
$query = $users->find();
foreach ($query as $row) {
    echo $row->username;
}

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

Если бы мы хотели создать нового пользователя (с использованием валидации) мы могли бы это сделать например так:

use Cake\ORM\TableRegistry;

// Prior to 3.6 use TableRegistry::get('Users')
$users = TableRegistry::getTableLocator()->get('Users');
$user = $users->newEntity(['email' => 'mark@example.com']);
$users->save($user);

Вид

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

К примеру Вид может использовать данные Модели для вывода HTML-шаблона, который отображает данные в удобочитаемом для пользователя виде, либо оформить данные в формате XML для удобной передачи другим пользователям для дальнейшей обработки:

// В файле шаблона, мы выводим 'element' для каждого пользователя.
<?php foreach ($users as $user): ?>
    <li class="user">
        <?= $this->element('user', ['user' => $user]) ?>
    </li>
<?php endforeach; ?>

Вид предоставляет ряд расширений, таких как Шаблоны Представления, Элементы и Ячейки Представления для вывода повторяющихся элементов(шапка сайта, футер, меню и т.д.).

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

Контроллер

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

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

public function add()
{
    $user = $this->Users->newEntity();
    if ($this->request->is('post')) {
        $user = $this->Users->patchEntity($user, $this->request->data);
        if ($this->Users->save($user, ['validate' => 'registration'])) {
            $this->Flash->success(__('You are now registered.'));
        } else {
            $this->Flash->error(__('There were some problems.'));
        }
    }
    $this->set('user', $user);
}

Как Вы могли заметить мы никогда не указываем явно какой Вид использовать. Благодаря соглашениям CakePHP метод set() передаст данные от Контроллера в нужный Вид.

Цикл обработки запросов CakePHP

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

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

Веб-сервер перенаправляет запрос к webroot/index.php.

Ваше приложение загружено и связано с HttpServer.

Промежуточное ПО Вашего приложения инициализировано.

Запрос и ответ обработан через ромежуточное ПО PSR-7 используемое Вашим приложением. Обычно это включает в себя перехват ошибок и маршрутизацию.

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

Экшен контроллера вызывается и контроллер взаимодействует с нужными Моделями и Компонентами.

Контроллер делегирует создание ответа Виду для вывода данных, полученных от Модели.

Вид используя Хелперы и Ячейки генерирует тело запроса и заголовки.

Ответ посылается обратно через Промежуточное ПО.

HttpServer выбрасывает ответ веб-серверу.

Только начало

Надеемся этот краткий обзор пробудил в Вас интерес. Некоторые другие потрясающие возможности CakePHP:

Фреймворк кэширования взаимодействующий с Memcached, Redis и другими платформами.

Мощные инструменты генерирования кода с которыми Вы можете быстро начать разработку.

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

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

Рекомендуется к прочтению

Где получить помощь

Соглашения CakePHP

Структура папок CakePHP

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Где получить помощь

Официальный сайт CakePHP

https://cakephp.org

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

Кулинарная книга (The Cookbook)

https://book.cakephp.org

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

Пекарня (The Bakery)

https://bakery.cakephp.org

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

Интерфейс программирования приложений (The API)

https://api.cakephp.org/

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

Тестовые случаи (The Test Cases)

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

tests/TestCase/

Онлайн чат

IRC чат irc.freenode.net:

#cakephp – Главный чат

#cakephp-docs – Документация

#cakephp-bakery – Пекарня

#cakephp-fr – Французский канал

Если у вас возникли проблемы, вы можете обратиться в главный чат. Как правило кто-нибудь из команды разработчиков [https://github.com/cakephp?tab=members] находится в чате, особенно в дневное время суток для западного полушария. Мы будем рады вас слышать независимо от того, нужна ли вам помощь, хотите ли вы найти собеседников или пожертвовать нам свой новый спортивный автомобиль.

Официальный форум CakePHP

CakePHP официальный форум [http://discourse.cakephp.org]

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

Stackoverflow

https://stackoverflow.com/ [https://stackoverflow.com/questions/tagged/cakephp/]

Пометьте ваш вопрос тегом cakephp и номером используемой версии, чтобы дать возможность пользователям stackoverflow помочь вам.

Где найти помощь на других языках

Бразилия, Португалия

Brazilian CakePHP Community [http://cakephp-br.org]

Датский

Danish CakePHP Slack Channel [https://cakesf.slack.com/messages/denmark/]

Французский

French CakePHP Community [http://cakephp-fr.org]

Немецкий

German CakePHP Slack Channel [https://cakesf.slack.com/messages/german/]

German CakePHP Facebook Group [https://www.facebook.com/groups/146324018754907/]

Иранский

Iranian CakePHP Community [http://cakephp.ir]

Голландский

Dutch CakePHP Slack Channel [https://cakesf.slack.com/messages/netherlands/]

Японский

Japanese CakePHP Slack Channel [https://cakesf.slack.com/messages/japanese/]

Japanese CakePHP Facebook Group [https://www.facebook.com/groups/304490963004377/]

Португальский

Portuguese CakePHP Google Group [http://groups.google.com/group/cakephp-pt]

Испанский

Spanish CakePHP Slack Channel [https://cakesf.slack.com/messages/spanish/]

Spanish CakePHP IRC Channel

Spanish CakePHP Google Group [http://groups.google.com/group/cakephp-esp]

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Соглашения CakePHP

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

Соглашения Контроллера

Названия классов контроллера обычно пишутся во множественном числе, c использованием ВерблюжьегоРегистра и оканчиваются на слово Controller. К примеру такие имена, как UsersController и ArticleCategoriesController хорошо соответствуют соглашениям.

Публичные методы контроллеров часто становятся так называемыми „экшенами“, доступными через веб-браузер. К примеру такая часть URL-адреса /users/view по умолчанию соответствует методу view() контроллера UsersController. Защищенные (protected) или закрытые (private) методы не доступны системе маршрутизации (роутинга).

URL-адреса и имена котроллеров

Как Вы только что видели, контроллеры с названием из одного слова связываются с соответствующим адресом URL в нижнем регистре. К примеру UsersController (объявленный в файле UsersController.php) доступен по адресу http://example.com/users.

Вы также можете вызывать контроллеры, имена которых состоят из нескольких слов, для этого, в соответствии с соглашениями, Ваши URL-адреса должны быть в нижнем регистре разделенные дефисами с использованием класса DashedRoute, cледовательно /article-categories/view-all будет корректной формой обращения к экшену ArticleCategoriesController::viewAll().

Когда Вы создаете ссылки с использованием метода this->Html->link(), Вы можете воспользоваться следующими соглашениями для массива url:

$this->Html->link('текст-ссылки', [
    'prefix' => 'MyPrefix' // ВерблюжийРегистр
    'plugin' => 'MyPlugin', // ВерблюжийРегистр
    'controller' => 'ControllerName', // ВерблюжийРегистр
    'action' => 'actionName' // верблюжийРегистр
]

Для более подробной информации о URL-адресах CakePHP и обработке параметров, смотрите Подключение маршрутов.

Соглашения о Наименовании классов и Файлов

В общих чертах, имена файлов должны совпадать с именами классов, и следовать стандартам автозагрузки PSR-0 или PSR-4. Вот некоторые примеры имен классов и их файлов:

Класс контроллера LatestArticlesController может быть найден в файле с именем LatestArticlesController.php

Класс компонента MyHandyComponent может быть найден в файле с именем MyHandyComponent.php

Класс таблицы OptionValuesTable может быть найден в файле с именем OptionValuesTable.php.

Класс объекта данных OptionValue может быть найден в файле с именем OptionValue.php.

Класс поведения EspeciallyFunkableBehavior может быть найден в файле с именем EspeciallyFunkableBehavior.php

Класс вида SuperSimpleView может быть найден в файле с именем SuperSimpleView.php

Класс хелпера BestEverHelper может быть найден в файле с именем BestEverHelper.php

Каждый файл может быть найден в соответствующей папке/пространстве имен в папке вашего приложения.

Соглашения Базы данных

Имена классов таблиц обычно пишутся во множественном числе, c использованием ВерблюжьегоРегистра. Users, ArticleCategories, и UserFavoritePages - примеры хорошего соответствия соглашениям.

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

Внешние ключи, со связями между таблицами hasMany (один ко многим), belongsTo/hasOne (один к одному), распознаются по умолчанию как имя связанной таблицы (в форме единственного числа) с последующим постфиксом _id. Например, если таблица Users имеет связь hasMany с таблицей Articles, то таблица articles будет ссылаться на таблицу users по внешнему ключу user_id. Для таких таблиц, как article_categories, у которых названия состоят из нескольких слов, внешним ключом будет поле article_category_id.

Связывающие таблицы, используемые для связи BelongsToMany (многие ко многим) между моделями, следует называть таким образом, чтобы имена связанных таблиц перечислялись в алфавитном порядке (articles_tags, а не tags_articles), иначе команда bake (выпечки) не будет работать. Если вам необходимо добавить дополнительные столбцы в таблицу соединений, вы должны создать отдельный класс entity/table для этой таблицы.

При использовании первичного ключа, вместо обычного автоинкрементного поля, Вы можете использовать UUID поле. CakePHP будет создавать уникальный 36-значный UUID (Cake\Utility\Text::uuid()), каждый раз когда Вы сохраните новую запись, используя метод Table::save().

Соглашения Модели

Имена классов таблиц должны быть во множественном числе, иметь ПаскальСинтаксис и заканчиваться как Table. Если название состоит из нескольких слов, то они разделяются подчеркиваниями. Такие имена, как users, article_categories и user_favorite_pages соответствуют шаблону.

Имена классов сущностей являются исключительными ПаскальСинтаксисными и не имеют суффикса. User, ArticleCategory и UserFavoritePage являются полными примерами имён сущностей, соответствующих users, article_categories и user_favorite_pages таблиц соответственно.

Соглашения Представления (Вида)

Имена файлам представления присваиваются в соответствии с названием метода контроллера использующего его, а если название метода состоит из нескольких слов, то они разделяются символом подчеркивания. Методу viewAll() класса ArticlesController будет соответствовать шаблон src/Template/Articles/view_all.ctp.

Общий принцип именования шаблонов: src/Template/Контроллер/имя_метода.ctp.

Примечание

По умолчанию CakePHP использует английские слова. Если у вас есть таблицы/столбцы базы данных, которые используют другой язык, вам нужно будет добавить правила конвертации (от единственного числа до множественного числа и наоборот). Если же Вы хотите добавить правила обработки для некоторых слов Вашего языка, Вы можете воспользоваться служебным классом Cake\Utility\Inflector. Помимо определения этих пользовательских служебных правил, этот класс также позволяет Вам проверять, что CakePHP понимает Ваш пользовательский синтаксис для форм единственного и множественного числа. Смотрите документацию Inflector для получения более подробной информации.

Обобщение

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

Таблица в базе данных: «articles»

Класс таблицы: ArticlesTable, находится в файле src/Model/Table/ArticlesTable.php

Класс объекта данных: Article, находится в файле src/Model/Entity/Article.php

Класс контроллера: ArticlesController, находится в файле src/Controller/ArticlesController.php

Шаблон Представления находится в файле src/Template/Articles/index.ctp

Используя данные соглашения, Вы будете точно знать, что запрос http://example.com/articles/ вызывает метод index() контроллера ArticlesController, где автоматически появилась модель Articles (которая уже связана с таблицей ‘articles‘ в базе данных) и подключит соответствующее представление. Ни одно из этих отношений не требует никаких настроек, а только создания классов и файлов, которые все равно придется создать.

После того, как Вы познакомились с основами фреймворка CakePHP, Вы можете ознакомиться с примером создания простого приложения - Пример Менеджер Закладок и увидеть все выше описанное на практике.

Соглашения Плагинов

Полезно использовать в имени пакета плагина CakePHP префикс «cakephp-«. Это делает семантически связанное имя с фреймворком, от которого оно зависит.

Не используйте пространство имён CakePHP (cakephp) плагинам в качестве имени поставщика, так как это зарезервировано для CakePHP. Соглашение заключается в использовании строчных букв и тире в качестве разделителя.

// Плохое именование cakephp/foo-bar

// Хорошее именование your-name/cakephp-foo-bar

Для больших подробностей обртитесь к awesome list recommendations [https://github.com/FriendsOfCake/awesome-cakephp/blob/master/CONTRIBUTING.md#tips-for-creating-cakephp-plugins].

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Структура папок CakePHP

После того, как Вы скачали и распаковали приложение CakePHP, Вы должны увидеть следующие папки и файлы:

bin

config

logs

plugins

src

tests

tmp

vendor

webroot

.htaccess

composer.json

index.php

README.md

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

Папка bin содержит приложения консоли Cake.

Папка config содержит (некоторые) файлы Конфигурация используемые CakePHP. Настройки подключения к БД, автоконфигурацию, файлы настроек ядра и многое другое.

Папка plugins содержит Плагины Вашего приложения.

Папка logs содержит Ваши файлы журналов, в зависимости от настроек журналирования.

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

Папка tests будет содержать систему тестирования Вашего приложения.

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

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

Папка webroot это публичная папка Вашего приложения. Она содержит в себе все стили, картинки, скрипты и тому подобное.

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

Папка src

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

Console

Содержит команды и задания консоли для Вашего приложения. Подробнее смотрите Shells, Tasks & Console Tools.

Controller

Содержит контроллеры Вашего приложения и их компоненты.

Locale

Хранит файлы интернационализации.

Model

Содержит части приложения, работающие с БД (таблицы, сущности, поведения).

View

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

Template

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

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Инструкция по началу работы

Лучший способ изучить CakePHP - это сесть и создать что-нибудь с его помощью. Для начала мы создадим простое приложение Менеджер Закладок

Пример Менеджер Закладок

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

Вот что вам понадобится:

Сервер БД. Мы собираемся использовать в этом руководстве сервер MySQL. Вы должны знать достаточно об SQL, чтобы суметь создать базу данных: в остальном можно положиться на CakePHP. Поскольку мы используем MySQL, убедитесь также, что у Вас включено расширение pdo_mysql в PHP.

Базовые знания PHP.

Перед началом вы должны убедиться, что у вас установлена актуальная версия PHP:

php -v

Убедитесь, что Вы используете версию PHP не ниже 5.5.9 (CLI). Версия PHP вашего веб-сервера также должна быть не ниже 5.5.9 и лучше всего должна совпадать с версией CLI. Если вы хотите увидеть полностью рабочее приложение, проверьте cakephp/bookmarker [https://github.com/cakephp/bookmarker-tutorial]. Давайте приступим!

Получение CakePHP

Простейший путь установить CakePHP - это использование Composer. С помощью Composer Вы с легкостью установите фреймворк через командную строку или терминал. Сначала скачайте и установите Composer если у Вас его еще нет. Если у Вас установлен cURL, можете использовать следующую команду:

curl -s https://getcomposer.org/installer | php

Или Вы можете скачать composer.phar с веб-сайта Composer [https://getcomposer.org/download/].

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

php composer.phar create-project --prefer-dist cakephp/app:^3.8 bookmarker

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

Также существуют и другие способы установки, если Вас не устраивает Composer. Проверьте раздел документации Установка, чтобы узнать больше.

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

/cake_install
    /bin
    /config
    /logs
    /plugins
    /src
    /tests
    /tmp
    /vendor
    /webroot
    .editorconfig
    .gitignore
    .htaccess
    .travis.yml
    composer.json
    index.php
    phpunit.xml.dist
    README.md

Теперь настало самое подходящее время для ознакомления с файловой структурой приложения: проверьте раздел документации Структура папок CakePHP.

Проверка нашей установки

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

bin/cake server

Примечание

Для Windows, команда должна быть bin\cake server (с обратным слешем).

Это запустит встроенный веб-сервер PHP на порте 8765. Откройте http://localhost:8765 в вашем веб-браузере, чтобы увидеть страницу приветствия. Все проверки на странице должны быть пройдены, за исключением проверки подключения к базе данных. Если же это не так, возможно вам следует установить еще какие-то расширения PHP или установить права доступа к папкам.

Создание Базы данных

Следующим шагом давайте настроим необходимую базу данных для нашего Менеджера Закладок. Если вы еще этого не сделали - создайте пустую базу данных для работы вашего приложения, с любым удобным именем, например cake_bookmarks. Вы можете выполнить следующий SQL-запрос, для создания необходимых таблиц:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created DATETIME,
    modified DATETIME
);

CREATE TABLE bookmarks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(50),
    description TEXT,
    url TEXT,
    created DATETIME,
    modified DATETIME,
    FOREIGN KEY user_key (user_id) REFERENCES users(id)
);

CREATE TABLE tags (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255),
    created DATETIME,
    modified DATETIME,
    UNIQUE KEY (title)
);

CREATE TABLE bookmarks_tags (
    bookmark_id INT NOT NULL,
    tag_id INT NOT NULL,
    PRIMARY KEY (bookmark_id, tag_id),
    FOREIGN KEY tag_key(tag_id) REFERENCES tags(id),
    FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id)
);

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

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

Конфигурация Базы данных

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

Настройка должна показаться довольно легкой: просто замените значения в массиве Datasources.default в файле config/app.php на нужные вам. В результате у вас должно получиться что-то вроде этого:

return [
    // More configuration above.
    'Datasources' => [
        'default' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Mysql',
            'persistent' => false,
            'host' => 'localhost',
            'username' => 'cake_blog',
            'password' => 'AngelF00dC4k3~',
            'database' => 'cake_bookmarks',
            'encoding' => 'utf8',
            'timezone' => 'UTC',
            'cacheMetadata' => true,
        ],
    ],
    // More configuration below.
];

Как только вы сохраните ваш файл config/app.php, на приветственной странице CakePHP вы увидите сообщение, что База данных обнаружена и подключение к ней прошло успешно.

Примечание

Копия файла с настройками по умолчанию может быть найдена в config/app.default.php.

Генерирование шаблонного кода

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

// В Windows пишите bin\cake.
bin/cake bake all users
bin/cake bake all bookmarks
bin/cake bake all tags

Это сгенерирует код Контроллеров, Моделей, Видов и т.д. для наших ресурсов users, bookmarks, tags. Если вы остановили работу вашего веб-сервера, перезапустите его и перейдите по адресу http://localhost:8765/bookmarks.

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

Примечание

Если у вас отображается 404 ошибка, убедитесь, что модуль Apache mod_rewrite загружен.

Хеширование паролей

Когда вы создали ваших пользователей (посетив адрес http://localhost:8765/users), вы вероятно были оповещены, что пароли сохранены в виде простого текста. Это очень плохо с точки зрения безопасности, давайте исправим это.

Это также довольно подходящий момент, чтобы упомянуть о Моделях в CakePHP. В CakePHP мы разделяем методы, оперирующие с коллекциями объектов и с отдельными объектами, размещая их в отдельных классах. Методы, работающие с коллекцией сущностей, мы размещаем в классе Table, в то время как функции, относящиеся к отдельным записям - в классе Entity.

К примеру, хеширование паролей выполняется индивидуально для каждой записи, таким образом внедрим это поведение в объект сущности (entity). Так как мы хотим хешировать пароль каждый раз, когда он устанавливается, мы будем использовать метод мутатор/сеттер. CakePHP будет вызывать основанные на соглашениях методы-сеттеры каждый раз, когда свойство будет установлено на одной из сущностей. Давайте добавим сеттер для пароля. В файле src/Model/Entity/User.php добавьте следующий код:

namespace App\Model\Entity;

use Cake\Auth\DefaultPasswordHasher; //добавьте эту строку
use Cake\ORM\Entity;

class User extends Entity
{

    // Код от bake.

    protected function _setPassword($value)
    {
        $hasher = new DefaultPasswordHasher();
        return $hasher->hash($value);
    }
}

Теперь обновите одного из пользователей, созданных ранее, если вы измените его пароль, вы должны увидеть хэшированный пароль вместо исходного значения в списке или на страницах Вида. CakePHP хеширует пароли с помощью bcrypt [http://codahale.com/how-to-safely-store-a-password/] по умолчанию. Вы также можете использовать алгоритмы sha1 или md5, если вы работаете с уже существующей базой данных.

Примечание

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

Получение закладок с определенным тегом

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

В идеале у нас должны быть адреса наподобие такого http://localhost:8765/bookmarks/tagged/funny/cat/gifs. Подобный адрес даст нам возможность найти все закладки с тегами „funny“, „cat“ или „gifs“. Прежде чем начать, нам нужно добавить новые маршруты. Ваш файл config/routes.php должен выглядеть примерно так:

<?php
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\Router;

Router::defaultRouteClass(DashedRoute::class);

// Новый маршрут добавляемый нами для нашего экшена для тегов.
// Символ `*` в конце говорит CakePHP что этот экшен
// принимает параметры.
Router::scope(
    '/bookmarks',
    ['controller' => 'Bookmarks'],
    function ($routes) {
        $routes->connect('/tagged/*', ['action' => 'tags']);
    }
);

Router::scope('/', function ($routes) {
    // Connect the default home and /pages/* routes.
    $routes->connect('/', [
        'controller' => 'Pages',
        'action' => 'display', 'home'
    ]);
    $routes->connect('/pages/*', [
        'controller' => 'Pages',
        'action' => 'display'
    ]);

    // Connect the conventions based default routes.
    $routes->fallbacks();
});

Приведенный выше код определяет новый „маршрут“, соединяющий путь /bookmarks/tagged/ с экшеном BookmarksController::tags(). Объявляя маршруты, мы можем отделить то, как URL-адреса выглядят, от того, как они формируются. Если бы мы попытались перейти по адресу http://localhost:8765/bookmarks/tagged, мы бы увидели сообщение об ошибке от CakePHP, говорящее о том, что нужный экшен отсутствует в Контроллере. Давайте теперь добавим нужный метод. В файле src/Controller/BookmarksController.php добавьте следующее:

public function tags()
{
    // Ключ 'pass' предоставляется CakePHP и содержит все
    // передаваемые в URL сегменты пути в запросе.
    $tags = $this->request->getParam('pass');

    // Используем класс BookmarksTable для поиска закладок с тегами.
    $bookmarks = $this->Bookmarks->find('tagged', [
        'tags' => $tags
    ]);

    // Передаем переменные в Вид.
    $this->set([
        'bookmarks' => $bookmarks,
        'tags' => $tags
    ]);
}

Для получения более подробной информации о запросах посмотрите раздел Запрос.

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

В CakePHP мы любим сохранять экшены наших Контроллеров компактными и помещать большую часть логики нашего приложения в Моделях. Если вы пытались посетить URL /bookmarks/tagged, то наверняка увидели ошибку, что метод findTagged() отсутствует, давайте это исправим. В файле src/Model/Table/BookmarksTable.php добавьте следующее:

// Аргумент $query это экземпляр класса конструктора запросов.
// Массив $options будет содержать опцию 'tags' переданную нами
// в find('tagged') в экшене нашего Контроллера.
public function findTagged(Query $query, array $options)
{
    $bookmarks = $this->find()
        ->select(['id', 'url', 'title', 'description']);

    if (empty($options['tags'])) {
        $bookmarks
            ->leftJoinWith('Tags')
            ->where(['Tags.title IS' => null]);
    } else {
        $bookmarks
            ->innerJoinWith('Tags')
            ->where(['Tags.title IN ' => $options['tags']]);
    }

    return $bookmarks->group(['Bookmarks.id']);
}

Мы только что реализовали пользовательский метод поиска. Это очень мощная концепция в CakePHP, которая позволяет вам легко использовать повторяющиеся запросы. Поисковые методы всегда получают объект Query Builder и массив, в котором передаются параметры. Поисквые методы могут манипулировать запросом и добавлять любые необходимые условия и критерии поиска. По завершении работы поисковые методы должны возвращать измененный объект запроса. В нашем поисковом методе мы пользуемся возможностями методов distinct() и matching(), которые позволяют нам находить строго те закладки, которые имеют совпадающий тег. Метод matching() принимает в качестве параметра анонимную функцию [http://php.net/manual/en/functions.anonymous.php], которая получает в качестве аргумента конструктор запросов. Внутри коллбека мы используем конструктор запросов, чтобы определить условия фильтрации закладок, имеющих определенный тег.

Создание Вида

Теперь, если вы посетите URL /bookmarks/tagged, CakePHP выведет ошибку, дающую вам знать, что вы ещё не создали файл Вида. Давайте создадим его для нашего экшена tags(). В файле src/Template/Bookmarks/tags.ctp поместите следующий код:

<h1>
    Закладки с тегами
    <?= $this->Text->toList(h($tags)) ?>
</h1>

<section>
<?php foreach ($bookmarks as $bookmark): ?>
    <article>
        <!-- Используем HtmlHelper для создания ссылок -->
        <h4><?= $this->Html->link($bookmark->title, $bookmark->url) ?></h4>
        <small><?= h($bookmark->url) ?></small>

        <!-- Используем TextHelper для форматирования текста -->
        <?= $this->Text->autoParagraph(h($bookmark->description)) ?>
    </article>
<?php endforeach; ?>
</section>

В приведённом выше коде мы использовали хелперы Html и Text для автоматического генерирования нужной разметки. Также мы использовали функцию h для кодирования в HTML выводимых данных. Вы всегда должны использовать функцию h() для обработки полученных от пользователя данных, чтобы предотвратить угрозу SQL-инъекций.

Файл tags.ctp, который мы только что создали, следует соглашениям CakePHP для файлов шаблонов Вида. Имя шаблона совпадает с именем экшена Контроллера, написано в нижнем регистре с использованием знаков подчеркивания в качестве разделителей слов.

Вы можете заметить, что мы могли использовать переменные $tags и $bookmarks в нашем Виде. Используя метод set() в нашем Контроллере, мы определяем переменные, которые должны быть доступны в Виде. Вид сделает все переданные переменные доступными в качестве локальных.

Теперь вы можете например перейти по URL-адресу /bookmarks/tagged/funny, и увидеть все закладки с тегом „funny“.

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

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

Пример Менеджер Закладок Часть 2

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

Вход пользователя

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

// В src/Controller/AppController.php
namespace App\Controller;

use Cake\Controller\Controller;

class AppController extends Controller
{
    public function initialize()
    {
        $this->loadComponent('Flash');
        $this->loadComponent('Auth', [
            'authenticate' => [
                'Form' => [
                    'fields' => [
                        'username' => 'email',
                        'password' => 'password'
                    ]
                ]
            ],
            'loginAction' => [
                'controller' => 'Users',
                'action' => 'login'
            ],
            'unauthorizedRedirect' => $this->referer() // Если не авторизованы, то возвращаем на страницу, где только что были
        ]);

        // Разрешение экшена display, чтобы наш контроллер pages
        // продолжал работать.
        $this->Auth->allow(['display']);
    }
}

Мы только что сообщили CakePHP, что мы хотим загрузить компоненты Flash и Auth. В дополнение мы кастомизировали конфигурацию компонента Auth, таким образом, что наша таблица users использует email в качестве имени пользователя. Теперь, если вы попробуете перейти по любому URL, то будете переброшены наURL /users/login, который покажет ошибку, так как мы еще не написали необходимый код. Давайте создадим экшен login:

// В src/Controller/UsersController.php
public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $this->Auth->setUser($user);
            return $this->redirect($this->Auth->redirectUrl());
        }
        $this->Flash->error('Ваше имя пользователя или пароль не верны.');
    }
}

И в src/Template/Users/login.ctp добавьте следующее:

<h1>Вход</h1>
<?= $this->Form->create() ?>
<?= $this->Form->input('email') ?>
<?= $this->Form->input('password') ?>
<?= $this->Form->button('Войти') ?>
<?= $this->Form->end() ?>

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

Примечание

Если ни у одного из ваших пользователей нет хешированного пароля, закомментируйте строку loadComponent('Auth'). Затем отредактируйте пользователя, сохранив новый пароль для них.

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

Выход пользователя

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

public function initialize()
{
    parent::initialize();
    $this->Auth->allow(['logout']);
}

public function logout()
{
    $this->Flash->success('Вы вышли из своей учетной записи.');
    return $this->redirect($this->Auth->logout());
}

Этот код делает доступным экшен logout в качестве публичного и реализует метод logout. Теперь вы можете перейти по адресу /users/logout, чтобы разавторизоваться. После этого вы должны быть перенаправлены на страницу входа.

Добавление регистрации пользователей

Если вы не авторизованы, и попытаетесь посетить /users/add, то будете перенаправлены на страницу входа. Мы должны исправить это, так как мы хотим, чтобы у наших пользователей была возможность регистрации в нашем приложении. В UsersController добавьте следующее:

public function initialize()
{
    parent::initialize();
    // Добавили logout в список разрешенных экшенов.
    $this->Auth->allow(['logout', 'add']);
}

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

Ограничение доступа к закладкам

Теперь, когда пользователи могут авторизоваться, мы хотим разрешить им доступ только к их собственным закладкам. Мы сделаем это, используя адаптер „authorization“. Так как наши требования предельно просты, мы можем написать какой-нибудь простой код в нашем контроллере BookmarksController. Но перед этим мы бы хотели сказать компоненту Auth как наше приложение собирается авторизовывать экшены. В AppController добавьте следующий код:

public function isAuthorized($user)
{
    return false;
}

Также добавьте следующее в настройки Auth в вашем AppController:

'authorize' => 'Controller',

Ваш метод initialize() должен выглядеть следующим образом:

public function initialize()
{
    $this->loadComponent('Flash');
    $this->loadComponent('Auth', [
        'authorize'=> 'Controller',//добавили эту строку
        'authenticate' => [
            'Form' => [
                'fields' => [
                    'username' => 'email',
                    'password' => 'password'
                ]
            ]
        ],
        'loginAction' => [
            'controller' => 'Users',
            'action' => 'login'
        ],
        'unauthorizedRedirect' => $this->referer()
    ]);

    // Разрешаем экшен display чтобы наш контроллер pages
    // продолжал работать.
    $this->Auth->allow(['display']);
}

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

public function isAuthorized($user)
{
    $action = $this->request->getParam('action');

    // Экшены add и index всегда разрешены.
    if (in_array($action, ['index', 'add', 'tags'])) {
        return true;
    }
    // Для всех остальных экшенов требуется id.
    if (!$this->request->getParam('pass.0')) {
        return false;
    }

    // Проверяем, что закладка принадлежит текущему пользователю.
    $id = $this->request->getParam('pass.0');
    $bookmark = $this->Bookmarks->get($id);
    if ($bookmark->user_id == $user['id']) {
        return true;
    }
    return parent::isAuthorized($user);
}

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

// В src/Template/Layout/default.ctp
<?= $this->Flash->render() ?>

Теперь вы должны видеть сообщения об ошибках авторизации.

Доработка форм и Вида списка закладок

В то время, как экшены view и delete работают, у экшенов edit, view и index имеются некоторыен проблемы:

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

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

В списке выводятся закладки всех пользователей.

Давайте для начала разберемся с формой для добавления закладок. Удалите input('user_id') из шаблона src/Template/Bookmarks/add.ctp. Также нам нужно обновить экшен add() в src/Controller/BookmarksController.php, чтобы он принял следующий вид:

public function add()
{
    $bookmark = $this->Bookmarks->newEntity();
    if ($this->request->is('post')) {
        $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->getData());
        $bookmark->user_id = $this->Auth->user('id');
        if ($this->Bookmarks->save($bookmark)) {
            $this->Flash->success('Закладка была сохранена.');
            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error('Ошибка сохранения. Пожалуйста попробуйте еще раз.');
    }
    $tags = $this->Bookmarks->Tags->find('list');
    $this->set(compact('bookmark', 'tags'));
    $this->set('_serialize', ['bookmark']);
}

Устанавливая значение сущности (entity) из данных сессии, мы исключаем любую возможность изменения пользователем информации о том кому принадлежит закладка. Мы сделаем то же самое для формы и экшена edit. Ваш экшен edit() из src/Controller/BookmarksController.php должен выглядеть так:

public function edit($id = null)
{
    $bookmark = $this->Bookmarks->get($id, [
        'contain' => ['Tags']
    ]);
    if ($this->request->is(['patch', 'post', 'put'])) {
        $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->getData());
        $bookmark->user_id = $this->Auth->user('id');
        if ($this->Bookmarks->save($bookmark)) {
            $this->Flash->success('Закладка сохранена.');
            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error('Закладка не может быть сохранена. Пожалуйста, попробуйте еще раз.');
    }
    $tags = $this->Bookmarks->Tags->find('list');
    $this->set(compact('bookmark', 'tags'));
    $this->set('_serialize', ['bookmark']);
}

Вид списка

Теперь нам только осталось вывести список закладок текущего пользователя. Мы можем сделать это обновив вызов метода paginate(). Измените ваш экшен index() из src/Controller/BookmarksController.php:

public function index()
{
    $this->paginate = [
        'conditions' => [
            'Bookmarks.user_id' => $this->Auth->user('id'),
        ]
    ];
    $this->set('bookmarks', $this->paginate($this->Bookmarks));
    $this->set('_serialize', ['bookmarks']);
}

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

Улучшение пользовательского опыта в тегах

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

Добавление вычисляемого поля

Так как нам хочется простого способа получения доступа к отформатированным тегам объекта(entity), мы можем добавить виртуальное/вычисляемое поле к нему. В src/Model/Entity/Bookmark.php добавьте следующее:

use Cake\Collection\Collection;

protected function _getTagString()
{
    if (isset($this->_properties['tag_string'])) {
        return $this->_properties['tag_string'];
    }
    if (empty($this->tags)) {
        return '';
    }
    $tags = new Collection($this->tags);
    $str = $tags->reduce(function ($string, $tag) {
        return $string . $tag->title . ', ';
    }, '');
    return trim($str, ', ');
}

Это позволит нам иметь доступ к вычисляемому свойству $bookmark->tag_string. Мы воспользуемся этим свойством позже в полях ввода. Не забудьте добавить свойство tag_string в список _accessible в вашем объекте, так как нам понадобится „сохранять“ его в дальнейшем. This will let us access the $bookmark->tag_string computed property. We’ll use this property in inputs later on. Remember to add the tag_string property to the _accessible list in your entity, as we’ll want to „save“ it later on.

В src/Model/Entity/Bookmark.php добавьте tag_string в $_accessible таким образом:

protected $_accessible = [
    'user_id' => true,
    'title' => true,
    'description' => true,
    'url' => true,
    'user' => true,
    'tags' => true,
    'tag_string' => true,
];

Обновление Видов

Теперь после обновления объекта мы можем добавить новое поле ввода для наших тегов. В src/Template/Bookmarks/add.ctp и src/Template/Bookmarks/edit.ctp замените существующее поле ввода tags._ids следующим:

echo $this->Form->input('tag_string', ['type' => 'text']);

Сохранение строки тегов

Теперь, когда мы можем видеть существующие теги в виде строки, нам бы хотелось также и сохранять эти данные. Так как мы обозначили в качестве доступного поля tag_string, ORM будет копировать эти данные из запроса в наш объект. Мы можем использовать хук beforeSave() для парсинга строки тегов и находить/создавать соответствующие объекты. Добавьте следующее в src/Model/Table/BookmarksTable.php:

public function beforeSave($event, $entity, $options)
{
    if ($entity->tag_string) {
        $entity->tags = $this->_buildTags($entity->tag_string);
    }
}

protected function _buildTags($tagString)
{
    // Выделить из строки отдельные теги
    $newTags = array_map('trim', explode(',', $tagString));
    // Удалить все пустые теги
    $newTags = array_filter($newTags);
    // Устранить дбликаты уже существующих тегов
    $newTags = array_unique($newTags);

    $out = [];
    $query = $this->Tags->find()
        ->where(['Tags.title IN' => $newTags]);

    // Удаление существующих тегов из списка новых тегов.
    foreach ($query->extract('title') as $existing) {
        $index = array_search($existing, $newTags);
        if ($index !== false) {
            unset($newTags[$index]);
        }
    }
    // Добавить существующие теги.
    foreach ($query as $tag) {
        $out[] = $tag;
    }
    // Добавить новые теги.
    foreach ($newTags as $tag) {
        $out[] = $this->Tags->newEntity(['title' => $tag]);
    }
    return $out;
}

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

Заключение

Мы расширили наше приложение для закладок, реализовав сценарии аутентификации и базовой авторизации/контроля доступа. Мы также добавили некоторые милые улучения UX засчет использования хелпера FormHelper и возможностей ORM.

Спасибо за то, что уделили время изучению CakePHP. В довершение вы можете изучить Пример создания блога, узнать больше об Доступ к Базе Данных и ORM, или же можете пролистать Using CakePHP.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Руководство по миграции 3.0

Эта страница обобщает изменения относительно CakePHP 2.x, что поможет вам безболезненно перевести ваши проекты на версию 3.0, а также руководство по изменениям, внесенным в ядро в сравнении с веткой CakePHP 2.x. Убедительно просим прочитать остальные сраницы в этом руководстве для ознакомления со всеми нововведениями и изменениями в API.

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

CakePHP 3.x поддерживает PHP версии 5.4.16 и выше.

CakePHP 3.x требует наличия расширения mbstring.

CakePHP 3.x требует наличия расширения intl.

Предупреждение

CakePHP 3.0 не будет работать если вышеприведенные условия не будут соблюдены.

Инструментарий обновления

В то время, как этот документ охватывает все радикальные изменения и улучшения сделанные в CakePHP 3.0, мы также создали консольное приложение для того, чтобы помочь вам совершить некоторые рутинные операции при обновлении. Вы можете получить инструментарий обновления с github [https://github.com/cakephp/upgrade].

Структура папок приложения

Структура папок приложения притерпела изменения и теперь соответствует стандарту PSR-4 [http://www.php-fig.org/psr/psr-4/]. Вы должны использовать в качестве примера проект каркаса приложения [https://github.com/cakephp/app] при обновлении вашего приложения.

CakePHP должен устанавливаться с помощью Composer

С того момента, как CakePHP больше не может устанавливаться через PEAR или в общую папку, данные опции больше не поддерживаются. Вместо этого вы должны использовать Composer [http://getcomposer.org] для установки CakePHP.

Пространства имен

Все классы ядра CakePHP сейчас используют пространства имен и следуют спецификациям автозагрузки PSR-4. К примеру src/Cache/Cache.php доступен как Cake\Cache\Cache. Глобальные константы и методы-хелперы, такие как __() и debug() не используют пространства имен, чтобы ими было удобнее пользоваться.

Удаленные константы

Приведенные ниже устаревшие константы были удалены:

IMAGES

CSS

JS

IMAGES_URL

JS_URL

CSS_URL

DEFAULT_LANGUAGE

Конфигурация

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

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

Три новые конфигурационные переменные предоставляют настройки путей для файлов плагинов, видов и локалей. Вы можете добавить множество путей в переменные App.paths.templates, App.paths.plugins, App.paths.locales для настройки множества путей для шаблонов, плагинов и локалей соответственно.

Ключ конфигурации www_root был заменен на wwwRoot для лучшей согласованности. Пожалуйста внесите все необходимые правки в ваш файл app.php, а также везде, где используется вызов данного параметра Configure::read('App.wwwRoot').

Новая ORM

CakePHP 3.0 представляет вам новую ORM, которая была перестроена с нуля. Новая ORM значительно отличается от предшествующей и несовместима с ней. Обновление до новой ORM потребует обширных правок в любом из обновляемых приложений. Смотрите новую документацию по Доступу к Базе Данных и ORM для получения информации по использованию новой ORM.

Основы

LogError() был удален, так как не предоставлял никакой выгоды и либо использовался редко, либо не использовался вообще.

Следующие глобальные функции были удалены: config(), cache(), clearCache(), convertSlashes(), am(), fileExistsInPath(), sortByKey().

Отладка

Configure::write('debug', $bool) больше не поддерживает значения 0/1/2. Вместо этого теперь используется простое булево значение, чтобы просто включать/выключать режим отладки.

Настройки/конфигурация объекта

Объекты, используемые в CakePHP теперь поддерживают цепочки вызова методов экземпляра класса. К примеру код ранее использовавший свойство $object->settings теперь должен быть обновлен для использования метода $object->config().

Кеш

Движок``Memcache`` был удален, используйте вместо него Cake\Cache\Cache\Engine\Memcached.

Движки кеширования теперь загружаются при первом использовании с помощью ленивой загрузки.

Добавлен метод Cake\Cache\Cache::engine().

Добавлен метод Cake\Cache\Cache::enabled(). Он заменил опцию Cache.disable.

Добавлен метод Cake\Cache\Cache::enable().

Добавлен метод Cake\Cache\Cache::disable().

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

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

Все подклассы CacheEngine сейчас содержат метод config().

Были добавлены методы Cake\Cache\Cache::readMany(), Cake\Cache\Cache::deleteMany(), и Cake\Cache\Cache::writeMany().

Все методы Cake\Cache\Cache\CacheEngine теперь отвечают за обработку настроенного префикса ключа. Метод Cake\Cache\CacheEngine::write() больше не позволяет устанавливать продолжительность жизни кеша при записи - продолжительность берется из параметров движков кеширования, применяемых во время исполнения. Вызов метода кеширования с пустым ключом теперь выбросит исключение InvalidArgumentException, вместо того, чтобы вернуть false.

Ядро

App

Метод App::pluginPath() удален. Используйте CakePlugin::path().

Метод App::build() удален.

Метод App::location() удален.

Метод App::paths() удален.

Метод App::load() удален.

Метод App::objects() удален.

Константа App::RESET удалена.

Константа App::APPEND удалена.

Константа App::PREPEND удалена.

Константа App::REGISTER удалена.

Plugin

Cake\Core\Plugin::load() не использует автозагрузку классов пока вы не установите опцию autoload в true.

При загрузке плагинов вы больше не можете предоставить вызываемый метод.

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

Configure

Cake\Configure\PhpReader переименован в Cake\Core\Configure\EnginePhpConfig

Cake\Configure\IniReader переименован в Cake\Core\Configure\EngineIniConfig

Cake\Configure\ConfigReaderInterface переименован в Cake\Core\Configure\ConfigEngineInterface

Добавлен метод Cake\Core\Configure::consume().

Метод Cake\Core\Configure::load() теперь ожидает имя файла без расширения, так как оно может быть получено из движка. К примеру используя PhpConfig указывайте app для загрузки app.php.

Назначение переменной $config в файле конфигурации PHP устарело. Cake\Core\Configure\EnginePhpConfig теперь ожидает, что файл конфигурации будет возвращать массив.

Добавлен новый движок Cake\Core\Configure\EngineJsonConfig.

Object

Класс Object был удален. Ранее он содержал кучу различных методов, использовавшихся в разных местах фреймворка. Наиболее востребованные из этих методов были выделены в трейты. Вы можете использовать трейт Cake\Log\LogTrait для получения доступа к методу log(). Трейт Cake\Routing\RequestActionTrait предоставляет requestAction().

Консоль

Исполняемый файл cake был перемещен из папки app/Console в папку bin вместе со всей структурой приложения. Теперь вы можете вызывать консоль CakePHP используя bin/cake.

Заменен класс TaskCollection

Этот класс был переименован в Cake\Console\TaskRegistry. Смотрите раздел Registry Objects для получения более полной информации о возможностях данного класса. Вы можете использовать cake upgrade rename_collections для помощи в обновлении вашего кода. This class has been renamed to Cake\Console\TaskRegistry. Задачи больше не имеют доступа к функциям обратного вызова, поскольку никогда не было таких функций, которые могли бы вызываться подобным образом.

Оболочка (Shell)

Shell::__construct() изменен. Теперь он берет экземпляр класса Cake\Console\ConsoleIo.

Добавлен метод Shell::param() для более удобного доступа к параметрам.

В дополнение к этому все методы оболочки преобразованы для удобства к верблюжьему регистру при их вызове. Например, если у вас внутри оболочки был метод hello_world() и он вызывался как bin/cake my_shell hello_world, вам будет нужно переименовать метод в helloWorld. При вызове команд никаких изменений не потребуется.

ConsoleOptionParser

Добавлен метод ConsoleOptionParser::merge() для объединения парсеров.

ConsoleInputArgument

Добавлен метод ConsoleInputArgument::isEqualTo() для сравнения двух аргументов.

Shell / Task

Оболочки и задачи перемещены из Console/Command и Console/Command/Task в Shell и Shell/Task.

Удалена ApiShell

ApiShell была удалена, так как не представляла никакой выгоды над непосредственно самими исходными текстами и онлайн документацией онлайн документацией/API [https://api.cakephp.org/].

Удалена SchemaShell

SchemaShell была удалена, так как ниогда полностью не реализовывала в полной мере миграцию баз данных, к тому же появились более мощные инструменты, такие как Phinx [https://phinx.org/]. Она была заменена на Плагин Миграции CakePHP [https://github.com/cakephp/migrations], который действует в качестве оболочки между CakePHP и Phinx [https://phinx.org/].

ExtractTask

bin/cake i18n extract больше не включает в себя непереведенные сообщения валидации. Если вы хотите, чтобы ваши сообщения валидации переводились на другие языки, вы должны обертывать их в фунцию __(), как и другой контент.

BakeShell / TemplateTask

Bake больше не является частью ядра и заменен на плагин CakePHP Bake [https://github.com/cakephp/bake]

Шаблоны Bake перемещены в src/Template/Bake.

Синтаксис шаблонов Bake теперь использует erb-стилизованные теги (<% %>) для обозначения логики шаблонов, позволяя php-коду обрабвтываться как простой текст.

Команда bake view переименована в bake template.

Событие

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

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

Опция passParams теперь включена постоянно. Вы не можете отключить ее.

Опция break удалена. Теперь вы должны останавливать события.

Опция breakOn удалена. Теперь вы должны останавливать события.

Логи (журналирование)

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

Движки журналирования теперь используют лениво загружаются при первой записи в логи.

Добавлен метод Cake\Log\Log::engine().

Следующие методы удалены из Cake\Log\Log :: defaultLevels(), enabled(), enable(), disable().

Вы больше не можете создавать пользовательские уровни, используя Log::levels().

При конфигурировании логгеров вы должны использовать 'levels' вместо 'types'.

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

Добавлен Cake\Log\LogTrait. Вы можете использовать этот трейт в своих классах для добавления метода log().

Области видимости, передаваемые Cake\Log\Log::write() теперь пробрасываются методу write() движков журналирования с целью предоставления лучшего контекста движкам.

Движки журналирования теперь обязаны наследовать интерфейс Psr\Log\LogInterface вместо собственного интерфейса Cake LogInterface. В общем, если вы расширяли Cake\Log\Engine\BaseEngine вам просто нужно переименовать метод write() в log().

Cake\Log\Engine\FileLog теперь пишет файлы в ROOT/logs вместо ROOT/tmp/logs.

Роутинг (маршрутизация)

Именованные параметры

Именованные параметы были удпразднены в версии 3.0. Они были добавлены в версии 1.2.0 как „милая“ версия параметров строки запроса. В то время как визуальные преимущества были весомыми, проблемы, возникающие из-за них того не стоили.

Именованные параметры требовали особой обработки в CakePHP так же как и в любой PHP- или JavaScript-библиотеке, взаимодействующей с ними, так как они не распознавались никакой библиотекой, за исключением CakePHP. Дополнительное усложнение структуры кода, необходимое для поддержки именованных параметров не оправдывало их существование, и они были упразднены. Вместо них вы должны использовать стандартные параметры строки запроса или же передаваемые аргументы. По умолчанию Router будет рассматривать любые дополнительные параметры метода Router::url() как параметры строки запроса.

Поскольку многие приложения все еще будут вынуждены обрабатывать входящие URL-адреса, содержащие именованные параметры, был добавлен метод Cake\Routing\Router::parseNamedParams() для обеспечения обратной совместимости с существующими URL.

RequestActionTrait

В методе Cake\Routing\RequestActionTrait::requestAction() изменены некоторые дополнительные параметры:

options[url] заменен на options[query].

options[data] заменен на options[post].

Именованные параметры больше не поддерживаются.

Router

Именованные параметры удалены, более полную информацию смотрите выше.

Опция full_base заменена опцией _full.

Опция ext заменена опцией _ext.

Добавлены опции _scheme, _port, _host, _base, _full, _ext.

Строки URL больше не изменяются добавлением имен плагина/контроллера/префикса.

Стандартная обработка отсутствующих маршрутов была упразднена. Если нет маршрутов подходящих под параметры - будет возвращен путь /.

Классы маршрутов ответственны за всё генерирование URL включая параметры строки запроса. Это делает маршруты намного более мощными и гибкими.

Устойчивые параметры были удалены. Они были заменены методом Cake\Routing\Router::urlFilter(), который предоставляет более гибкий способ видоизменять URL при обратной маршрутизации.

Удален метод Router::parseExtensions(). Используйте вместо этого Cake\Routing\Router::extensions(). Этот метод должен быть вызван перед подключением маршрутов. Он не затронет уже существующие маршруты.

Удален метод Router::setExtensions(). Используйте вместо этого Cake\Routing\Router::extensions().

Удален метод Router::resourceMap().

Опция [метод] переименована в _метод.

Возможность совпадения произвольных заголовков с параметрамив стиле [] была упразднена. If you need to parse/match on arbitrary conditions consider using custom route classes.

Удален метод Router::promote().

Router::parse() теперь выбрасывает исключение, когда URL не может быть обработан каким-либо из маршрутов.

Router::url() теперь выбрасывает исключение, когда ни один маршрут не совпадает с набором параметров.

Добавлены области видимости маршрутов (routing scopes). Они позволяют вам соблюдать принцип DRY (не повторяйся) в ваших файлах маршрутов и дают Роутеру подсказки, как оптимизировать парсинг и обратную маршрутизацию URL.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Руководства и примеры

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

В качестве альтернативы вы можете обратиться к неофициальному репозиторию плагинов CakePHP CakePackages [https://plugins.cakephp.org/] и к Пекарне [https://bakery.cakephp.org/] для ознакомления с готовыми приложениями и компонентами.

Пример Менеджер Закладок

Пример Менеджер Закладок Часть 2

Пример создания блога

Пример создания блога - Часть 2

Пример создания блога - Часть 3

Пример создания блога - Часть 4 - Аутентификация и авторизация

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Пример Менеджер Закладок

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

Вот что вам понадобится:

Сервер БД. Мы собираемся использовать в этом руководстве сервер MySQL. Вы должны знать достаточно об SQL, чтобы суметь создать базу данных: в остальном можно положиться на CakePHP. Поскольку мы используем MySQL, убедитесь также, что у Вас включено расширение pdo_mysql в PHP.

Базовые знания PHP.

Перед началом вы должны убедиться, что у вас установлена актуальная версия PHP:

php -v

Убедитесь, что Вы используете версию PHP не ниже 5.5.9 (CLI). Версия PHP вашего веб-сервера также должна быть не ниже 5.5.9 и лучше всего должна совпадать с версией CLI. Если вы хотите увидеть полностью рабочее приложение, проверьте cakephp/bookmarker [https://github.com/cakephp/bookmarker-tutorial]. Давайте приступим!

Получение CakePHP

Простейший путь установить CakePHP - это использование Composer. С помощью Composer Вы с легкостью установите фреймворк через командную строку или терминал. Сначала скачайте и установите Composer если у Вас его еще нет. Если у Вас установлен cURL, можете использовать следующую команду:

curl -s https://getcomposer.org/installer | php

Или Вы можете скачать composer.phar с веб-сайта Composer [https://getcomposer.org/download/].

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

php composer.phar create-project --prefer-dist cakephp/app:^3.8 bookmarker

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

Также существуют и другие способы установки, если Вас не устраивает Composer. Проверьте раздел документации Установка, чтобы узнать больше.

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

/cake_install
    /bin
    /config
    /logs
    /plugins
    /src
    /tests
    /tmp
    /vendor
    /webroot
    .editorconfig
    .gitignore
    .htaccess
    .travis.yml
    composer.json
    index.php
    phpunit.xml.dist
    README.md

Теперь настало самое подходящее время для ознакомления с файловой структурой приложения: проверьте раздел документации Структура папок CakePHP.

Проверка нашей установки

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

bin/cake server

Примечание

Для Windows, команда должна быть bin\cake server (с обратным слешем).

Это запустит встроенный веб-сервер PHP на порте 8765. Откройте http://localhost:8765 в вашем веб-браузере, чтобы увидеть страницу приветствия. Все проверки на странице должны быть пройдены, за исключением проверки подключения к базе данных. Если же это не так, возможно вам следует установить еще какие-то расширения PHP или установить права доступа к папкам.

Создание Базы данных

Следующим шагом давайте настроим необходимую базу данных для нашего Менеджера Закладок. Если вы еще этого не сделали - создайте пустую базу данных для работы вашего приложения, с любым удобным именем, например cake_bookmarks. Вы можете выполнить следующий SQL-запрос, для создания необходимых таблиц:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created DATETIME,
    modified DATETIME
);

CREATE TABLE bookmarks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(50),
    description TEXT,
    url TEXT,
    created DATETIME,
    modified DATETIME,
    FOREIGN KEY user_key (user_id) REFERENCES users(id)
);

CREATE TABLE tags (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255),
    created DATETIME,
    modified DATETIME,
    UNIQUE KEY (title)
);

CREATE TABLE bookmarks_tags (
    bookmark_id INT NOT NULL,
    tag_id INT NOT NULL,
    PRIMARY KEY (bookmark_id, tag_id),
    FOREIGN KEY tag_key(tag_id) REFERENCES tags(id),
    FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id)
);

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

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

Конфигурация Базы данных

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

Настройка должна показаться довольно легкой: просто замените значения в массиве Datasources.default в файле config/app.php на нужные вам. В результате у вас должно получиться что-то вроде этого:

return [
    // More configuration above.
    'Datasources' => [
        'default' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Mysql',
            'persistent' => false,
            'host' => 'localhost',
            'username' => 'cake_blog',
            'password' => 'AngelF00dC4k3~',
            'database' => 'cake_bookmarks',
            'encoding' => 'utf8',
            'timezone' => 'UTC',
            'cacheMetadata' => true,
        ],
    ],
    // More configuration below.
];

Как только вы сохраните ваш файл config/app.php, на приветственной странице CakePHP вы увидите сообщение, что База данных обнаружена и подключение к ней прошло успешно.

Примечание

Копия файла с настройками по умолчанию может быть найдена в config/app.default.php.

Генерирование шаблонного кода

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

// В Windows пишите bin\cake.
bin/cake bake all users
bin/cake bake all bookmarks
bin/cake bake all tags

Это сгенерирует код Контроллеров, Моделей, Видов и т.д. для наших ресурсов users, bookmarks, tags. Если вы остановили работу вашего веб-сервера, перезапустите его и перейдите по адресу http://localhost:8765/bookmarks.

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

Примечание

Если у вас отображается 404 ошибка, убедитесь, что модуль Apache mod_rewrite загружен.

Хеширование паролей

Когда вы создали ваших пользователей (посетив адрес http://localhost:8765/users), вы вероятно были оповещены, что пароли сохранены в виде простого текста. Это очень плохо с точки зрения безопасности, давайте исправим это.

Это также довольно подходящий момент, чтобы упомянуть о Моделях в CakePHP. В CakePHP мы разделяем методы, оперирующие с коллекциями объектов и с отдельными объектами, размещая их в отдельных классах. Методы, работающие с коллекцией сущностей, мы размещаем в классе Table, в то время как функции, относящиеся к отдельным записям - в классе Entity.

К примеру, хеширование паролей выполняется индивидуально для каждой записи, таким образом внедрим это поведение в объект сущности (entity). Так как мы хотим хешировать пароль каждый раз, когда он устанавливается, мы будем использовать метод мутатор/сеттер. CakePHP будет вызывать основанные на соглашениях методы-сеттеры каждый раз, когда свойство будет установлено на одной из сущностей. Давайте добавим сеттер для пароля. В файле src/Model/Entity/User.php добавьте следующий код:

namespace App\Model\Entity;

use Cake\Auth\DefaultPasswordHasher; //добавьте эту строку
use Cake\ORM\Entity;

class User extends Entity
{

    // Код от bake.

    protected function _setPassword($value)
    {
        $hasher = new DefaultPasswordHasher();
        return $hasher->hash($value);
    }
}

Теперь обновите одного из пользователей, созданных ранее, если вы измените его пароль, вы должны увидеть хэшированный пароль вместо исходного значения в списке или на страницах Вида. CakePHP хеширует пароли с помощью bcrypt [http://codahale.com/how-to-safely-store-a-password/] по умолчанию. Вы также можете использовать алгоритмы sha1 или md5, если вы работаете с уже существующей базой данных.

Примечание

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

Получение закладок с определенным тегом

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

В идеале у нас должны быть адреса наподобие такого http://localhost:8765/bookmarks/tagged/funny/cat/gifs. Подобный адрес даст нам возможность найти все закладки с тегами „funny“, „cat“ или „gifs“. Прежде чем начать, нам нужно добавить новые маршруты. Ваш файл config/routes.php должен выглядеть примерно так:

<?php
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\Router;

Router::defaultRouteClass(DashedRoute::class);

// Новый маршрут добавляемый нами для нашего экшена для тегов.
// Символ `*` в конце говорит CakePHP что этот экшен
// принимает параметры.
Router::scope(
    '/bookmarks',
    ['controller' => 'Bookmarks'],
    function ($routes) {
        $routes->connect('/tagged/*', ['action' => 'tags']);
    }
);

Router::scope('/', function ($routes) {
    // Connect the default home and /pages/* routes.
    $routes->connect('/', [
        'controller' => 'Pages',
        'action' => 'display', 'home'
    ]);
    $routes->connect('/pages/*', [
        'controller' => 'Pages',
        'action' => 'display'
    ]);

    // Connect the conventions based default routes.
    $routes->fallbacks();
});

Приведенный выше код определяет новый „маршрут“, соединяющий путь /bookmarks/tagged/ с экшеном BookmarksController::tags(). Объявляя маршруты, мы можем отделить то, как URL-адреса выглядят, от того, как они формируются. Если бы мы попытались перейти по адресу http://localhost:8765/bookmarks/tagged, мы бы увидели сообщение об ошибке от CakePHP, говорящее о том, что нужный экшен отсутствует в Контроллере. Давайте теперь добавим нужный метод. В файле src/Controller/BookmarksController.php добавьте следующее:

public function tags()
{
    // Ключ 'pass' предоставляется CakePHP и содержит все
    // передаваемые в URL сегменты пути в запросе.
    $tags = $this->request->getParam('pass');

    // Используем класс BookmarksTable для поиска закладок с тегами.
    $bookmarks = $this->Bookmarks->find('tagged', [
        'tags' => $tags
    ]);

    // Передаем переменные в Вид.
    $this->set([
        'bookmarks' => $bookmarks,
        'tags' => $tags
    ]);
}

Для получения более подробной информации о запросах посмотрите раздел Запрос.

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

В CakePHP мы любим сохранять экшены наших Контроллеров компактными и помещать большую часть логики нашего приложения в Моделях. Если вы пытались посетить URL /bookmarks/tagged, то наверняка увидели ошибку, что метод findTagged() отсутствует, давайте это исправим. В файле src/Model/Table/BookmarksTable.php добавьте следующее:

// Аргумент $query это экземпляр класса конструктора запросов.
// Массив $options будет содержать опцию 'tags' переданную нами
// в find('tagged') в экшене нашего Контроллера.
public function findTagged(Query $query, array $options)
{
    $bookmarks = $this->find()
        ->select(['id', 'url', 'title', 'description']);

    if (empty($options['tags'])) {
        $bookmarks
            ->leftJoinWith('Tags')
            ->where(['Tags.title IS' => null]);
    } else {
        $bookmarks
            ->innerJoinWith('Tags')
            ->where(['Tags.title IN ' => $options['tags']]);
    }

    return $bookmarks->group(['Bookmarks.id']);
}

Мы только что реализовали пользовательский метод поиска. Это очень мощная концепция в CakePHP, которая позволяет вам легко использовать повторяющиеся запросы. Поисковые методы всегда получают объект Query Builder и массив, в котором передаются параметры. Поисквые методы могут манипулировать запросом и добавлять любые необходимые условия и критерии поиска. По завершении работы поисковые методы должны возвращать измененный объект запроса. В нашем поисковом методе мы пользуемся возможностями методов distinct() и matching(), которые позволяют нам находить строго те закладки, которые имеют совпадающий тег. Метод matching() принимает в качестве параметра анонимную функцию [http://php.net/manual/en/functions.anonymous.php], которая получает в качестве аргумента конструктор запросов. Внутри коллбека мы используем конструктор запросов, чтобы определить условия фильтрации закладок, имеющих определенный тег.

Создание Вида

Теперь, если вы посетите URL /bookmarks/tagged, CakePHP выведет ошибку, дающую вам знать, что вы ещё не создали файл Вида. Давайте создадим его для нашего экшена tags(). В файле src/Template/Bookmarks/tags.ctp поместите следующий код:

<h1>
    Закладки с тегами
    <?= $this->Text->toList(h($tags)) ?>
</h1>

<section>
<?php foreach ($bookmarks as $bookmark): ?>
    <article>
        <!-- Используем HtmlHelper для создания ссылок -->
        <h4><?= $this->Html->link($bookmark->title, $bookmark->url) ?></h4>
        <small><?= h($bookmark->url) ?></small>

        <!-- Используем TextHelper для форматирования текста -->
        <?= $this->Text->autoParagraph(h($bookmark->description)) ?>
    </article>
<?php endforeach; ?>
</section>

В приведённом выше коде мы использовали хелперы Html и Text для автоматического генерирования нужной разметки. Также мы использовали функцию h для кодирования в HTML выводимых данных. Вы всегда должны использовать функцию h() для обработки полученных от пользователя данных, чтобы предотвратить угрозу SQL-инъекций.

Файл tags.ctp, который мы только что создали, следует соглашениям CakePHP для файлов шаблонов Вида. Имя шаблона совпадает с именем экшена Контроллера, написано в нижнем регистре с использованием знаков подчеркивания в качестве разделителей слов.

Вы можете заметить, что мы могли использовать переменные $tags и $bookmarks в нашем Виде. Используя метод set() в нашем Контроллере, мы определяем переменные, которые должны быть доступны в Виде. Вид сделает все переданные переменные доступными в качестве локальных.

Теперь вы можете например перейти по URL-адресу /bookmarks/tagged/funny, и увидеть все закладки с тегом „funny“.

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

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

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Пример Менеджер Закладок Часть 2

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

Вход пользователя

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

// В src/Controller/AppController.php
namespace App\Controller;

use Cake\Controller\Controller;

class AppController extends Controller
{
    public function initialize()
    {
        $this->loadComponent('Flash');
        $this->loadComponent('Auth', [
            'authenticate' => [
                'Form' => [
                    'fields' => [
                        'username' => 'email',
                        'password' => 'password'
                    ]
                ]
            ],
            'loginAction' => [
                'controller' => 'Users',
                'action' => 'login'
            ],
            'unauthorizedRedirect' => $this->referer() // Если не авторизованы, то возвращаем на страницу, где только что были
        ]);

        // Разрешение экшена display, чтобы наш контроллер pages
        // продолжал работать.
        $this->Auth->allow(['display']);
    }
}

Мы только что сообщили CakePHP, что мы хотим загрузить компоненты Flash и Auth. В дополнение мы кастомизировали конфигурацию компонента Auth, таким образом, что наша таблица users использует email в качестве имени пользователя. Теперь, если вы попробуете перейти по любому URL, то будете переброшены наURL /users/login, который покажет ошибку, так как мы еще не написали необходимый код. Давайте создадим экшен login:

// В src/Controller/UsersController.php
public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $this->Auth->setUser($user);
            return $this->redirect($this->Auth->redirectUrl());
        }
        $this->Flash->error('Ваше имя пользователя или пароль не верны.');
    }
}

И в src/Template/Users/login.ctp добавьте следующее:

<h1>Вход</h1>
<?= $this->Form->create() ?>
<?= $this->Form->input('email') ?>
<?= $this->Form->input('password') ?>
<?= $this->Form->button('Войти') ?>
<?= $this->Form->end() ?>

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

Примечание

Если ни у одного из ваших пользователей нет хешированного пароля, закомментируйте строку loadComponent('Auth'). Затем отредактируйте пользователя, сохранив новый пароль для них.

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

Выход пользователя

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

public function initialize()
{
    parent::initialize();
    $this->Auth->allow(['logout']);
}

public function logout()
{
    $this->Flash->success('Вы вышли из своей учетной записи.');
    return $this->redirect($this->Auth->logout());
}

Этот код делает доступным экшен logout в качестве публичного и реализует метод logout. Теперь вы можете перейти по адресу /users/logout, чтобы разавторизоваться. После этого вы должны быть перенаправлены на страницу входа.

Добавление регистрации пользователей

Если вы не авторизованы, и попытаетесь посетить /users/add, то будете перенаправлены на страницу входа. Мы должны исправить это, так как мы хотим, чтобы у наших пользователей была возможность регистрации в нашем приложении. В UsersController добавьте следующее:

public function initialize()
{
    parent::initialize();
    // Добавили logout в список разрешенных экшенов.
    $this->Auth->allow(['logout', 'add']);
}

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

Ограничение доступа к закладкам

Теперь, когда пользователи могут авторизоваться, мы хотим разрешить им доступ только к их собственным закладкам. Мы сделаем это, используя адаптер „authorization“. Так как наши требования предельно просты, мы можем написать какой-нибудь простой код в нашем контроллере BookmarksController. Но перед этим мы бы хотели сказать компоненту Auth как наше приложение собирается авторизовывать экшены. В AppController добавьте следующий код:

public function isAuthorized($user)
{
    return false;
}

Также добавьте следующее в настройки Auth в вашем AppController:

'authorize' => 'Controller',

Ваш метод initialize() должен выглядеть следующим образом:

public function initialize()
{
    $this->loadComponent('Flash');
    $this->loadComponent('Auth', [
        'authorize'=> 'Controller',//добавили эту строку
        'authenticate' => [
            'Form' => [
                'fields' => [
                    'username' => 'email',
                    'password' => 'password'
                ]
            ]
        ],
        'loginAction' => [
            'controller' => 'Users',
            'action' => 'login'
        ],
        'unauthorizedRedirect' => $this->referer()
    ]);

    // Разрешаем экшен display чтобы наш контроллер pages
    // продолжал работать.
    $this->Auth->allow(['display']);
}

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

public function isAuthorized($user)
{
    $action = $this->request->getParam('action');

    // Экшены add и index всегда разрешены.
    if (in_array($action, ['index', 'add', 'tags'])) {
        return true;
    }
    // Для всех остальных экшенов требуется id.
    if (!$this->request->getParam('pass.0')) {
        return false;
    }

    // Проверяем, что закладка принадлежит текущему пользователю.
    $id = $this->request->getParam('pass.0');
    $bookmark = $this->Bookmarks->get($id);
    if ($bookmark->user_id == $user['id']) {
        return true;
    }
    return parent::isAuthorized($user);
}

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

// В src/Template/Layout/default.ctp
<?= $this->Flash->render() ?>

Теперь вы должны видеть сообщения об ошибках авторизации.

Доработка форм и Вида списка закладок

В то время, как экшены view и delete работают, у экшенов edit, view и index имеются некоторыен проблемы:

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

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

В списке выводятся закладки всех пользователей.

Давайте для начала разберемся с формой для добавления закладок. Удалите input('user_id') из шаблона src/Template/Bookmarks/add.ctp. Также нам нужно обновить экшен add() в src/Controller/BookmarksController.php, чтобы он принял следующий вид:

public function add()
{
    $bookmark = $this->Bookmarks->newEntity();
    if ($this->request->is('post')) {
        $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->getData());
        $bookmark->user_id = $this->Auth->user('id');
        if ($this->Bookmarks->save($bookmark)) {
            $this->Flash->success('Закладка была сохранена.');
            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error('Ошибка сохранения. Пожалуйста попробуйте еще раз.');
    }
    $tags = $this->Bookmarks->Tags->find('list');
    $this->set(compact('bookmark', 'tags'));
    $this->set('_serialize', ['bookmark']);
}

Устанавливая значение сущности (entity) из данных сессии, мы исключаем любую возможность изменения пользователем информации о том кому принадлежит закладка. Мы сделаем то же самое для формы и экшена edit. Ваш экшен edit() из src/Controller/BookmarksController.php должен выглядеть так:

public function edit($id = null)
{
    $bookmark = $this->Bookmarks->get($id, [
        'contain' => ['Tags']
    ]);
    if ($this->request->is(['patch', 'post', 'put'])) {
        $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->getData());
        $bookmark->user_id = $this->Auth->user('id');
        if ($this->Bookmarks->save($bookmark)) {
            $this->Flash->success('Закладка сохранена.');
            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error('Закладка не может быть сохранена. Пожалуйста, попробуйте еще раз.');
    }
    $tags = $this->Bookmarks->Tags->find('list');
    $this->set(compact('bookmark', 'tags'));
    $this->set('_serialize', ['bookmark']);
}

Вид списка

Теперь нам только осталось вывести список закладок текущего пользователя. Мы можем сделать это обновив вызов метода paginate(). Измените ваш экшен index() из src/Controller/BookmarksController.php:

public function index()
{
    $this->paginate = [
        'conditions' => [
            'Bookmarks.user_id' => $this->Auth->user('id'),
        ]
    ];
    $this->set('bookmarks', $this->paginate($this->Bookmarks));
    $this->set('_serialize', ['bookmarks']);
}

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

Улучшение пользовательского опыта в тегах

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

Добавление вычисляемого поля

Так как нам хочется простого способа получения доступа к отформатированным тегам объекта(entity), мы можем добавить виртуальное/вычисляемое поле к нему. В src/Model/Entity/Bookmark.php добавьте следующее:

use Cake\Collection\Collection;

protected function _getTagString()
{
    if (isset($this->_properties['tag_string'])) {
        return $this->_properties['tag_string'];
    }
    if (empty($this->tags)) {
        return '';
    }
    $tags = new Collection($this->tags);
    $str = $tags->reduce(function ($string, $tag) {
        return $string . $tag->title . ', ';
    }, '');
    return trim($str, ', ');
}

Это позволит нам иметь доступ к вычисляемому свойству $bookmark->tag_string. Мы воспользуемся этим свойством позже в полях ввода. Не забудьте добавить свойство tag_string в список _accessible в вашем объекте, так как нам понадобится „сохранять“ его в дальнейшем. This will let us access the $bookmark->tag_string computed property. We’ll use this property in inputs later on. Remember to add the tag_string property to the _accessible list in your entity, as we’ll want to „save“ it later on.

В src/Model/Entity/Bookmark.php добавьте tag_string в $_accessible таким образом:

protected $_accessible = [
    'user_id' => true,
    'title' => true,
    'description' => true,
    'url' => true,
    'user' => true,
    'tags' => true,
    'tag_string' => true,
];

Обновление Видов

Теперь после обновления объекта мы можем добавить новое поле ввода для наших тегов. В src/Template/Bookmarks/add.ctp и src/Template/Bookmarks/edit.ctp замените существующее поле ввода tags._ids следующим:

echo $this->Form->input('tag_string', ['type' => 'text']);

Сохранение строки тегов

Теперь, когда мы можем видеть существующие теги в виде строки, нам бы хотелось также и сохранять эти данные. Так как мы обозначили в качестве доступного поля tag_string, ORM будет копировать эти данные из запроса в наш объект. Мы можем использовать хук beforeSave() для парсинга строки тегов и находить/создавать соответствующие объекты. Добавьте следующее в src/Model/Table/BookmarksTable.php:

public function beforeSave($event, $entity, $options)
{
    if ($entity->tag_string) {
        $entity->tags = $this->_buildTags($entity->tag_string);
    }
}

protected function _buildTags($tagString)
{
    // Выделить из строки отдельные теги
    $newTags = array_map('trim', explode(',', $tagString));
    // Удалить все пустые теги
    $newTags = array_filter($newTags);
    // Устранить дбликаты уже существующих тегов
    $newTags = array_unique($newTags);

    $out = [];
    $query = $this->Tags->find()
        ->where(['Tags.title IN' => $newTags]);

    // Удаление существующих тегов из списка новых тегов.
    foreach ($query->extract('title') as $existing) {
        $index = array_search($existing, $newTags);
        if ($index !== false) {
            unset($newTags[$index]);
        }
    }
    // Добавить существующие теги.
    foreach ($query as $tag) {
        $out[] = $tag;
    }
    // Добавить новые теги.
    foreach ($newTags as $tag) {
        $out[] = $this->Tags->newEntity(['title' => $tag]);
    }
    return $out;
}

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

Заключение

Мы расширили наше приложение для закладок, реализовав сценарии аутентификации и базовой авторизации/контроля доступа. Мы также добавили некоторые милые улучения UX засчет использования хелпера FormHelper и возможностей ORM.

Спасибо за то, что уделили время изучению CakePHP. В довершение вы можете изучить Пример создания блога, узнать больше об Доступ к Базе Данных и ORM, или же можете пролистать Using CakePHP.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Пример создания блога

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

Вот что Вам понадобится для этого:

Веб-сервер. Мы предполагаем, что Вы используете Apache. По мере ознакомления с инструкциями, использование других веб-серверов будет весьма схоже. Возможно, нам придется поработать немного с настройками сервера, но большинство пользователей смогут начать работать с CakePHP без необходимости изменять какие-либо параметры. Убедитесь, что Вы используете версию PHP не ниже 5.5.9, и что расширения mbstring и intl включены в PHP.

Сервер БД. Мы собираемся использовать в этом руководстве сервер MySQL. Вы должны знать достаточно об SQL, чтобы суметь создать базу данных, в остальном можно положиться на CakePHP. Поскольку мы используем MySQL, убедитесь также, что у Вас включено расширение pdo_mysql в PHP.

Базовые знания PHP.

Давайте приступим!

Получение CakePHP

Простейший путь установить CakePHP- это использование Composer. С помощью Composer Вы с легкостью установите фреймворк через командную строку или терминал. Сначала скачайте и установите Composer, если у Вас его еще нет. Если у Вас установлен cURL, можете использовать следующую команду:

curl -s https://getcomposer.org/installer | php

Или Вы можете скачать composer.phar с веб-сайта Composer [https://getcomposer.org/download/].

Затем перейдите в установочную папку и просто наберите следующую строку в Вашем терминале, чтобы создать скелет приложения в ней. Для этого примера мы будем использовать папку «blog», но Вы можете назвать ее так, как Вам будет удобно.:

php composer.phar create-project --prefer-dist cakephp/app:^3.8 blog

В том случае, если Composer установлен глобально, можно ввести следующее:

composer self-update && composer create-project --prefer-dist cakephp/app:^3.8 blog

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

Также существуют и другие способы установки, если Вас не устраивает Composer. Посмотрите раздел документации Установка, чтобы узнать больше.

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

/cake_install
    /bin
    /config
    /logs
    /plugins
    /src
    /tests
    /tmp
    /vendor
    /webroot
    .editorconfig
    .gitignore
    .htaccess
    .travis.yml
    composer.json
    index.php
    phpunit.xml.dist
    README.md

Теперь настало самое подходящее время для ознакомления с файловой структурой приложения: посмотрите раздел документации Структура папок CakePHP.

Разрешения для папок tmp и logs

Папки tmp и logs должны иметь разрешения для записи данных веб-сервером. Если вы использовали Composer для установки, то все должно быть в порядке, если вы подтверждаете заппрос «Permissions set on <folder>». Если же вы получили сообщение об ошибке или же хотите сделать это вручную, лучший способ будет выяснить, под каким пользователем работает ваш веб-сервер (<?= `whoami`; ?>) и изменить права доступа для этих двух папок для этого пользователя. В итоге запускаемая команда (в *nix-системах) должна выглядеть так:

chown -R www-data tmp
chown -R www-data logs

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

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

chmod -R 777 tmp
chmod -R 777 logs

Создание Базы данных блога

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

# Во-первых, создадим таблицу с нашими статьями
CREATE TABLE articles (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(50),
    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);

# Затем добавим несколько статей для тестирования
INSERT INTO articles (title,body,created)
    VALUES ('The title', 'This is the article body.', NOW());
INSERT INTO articles (title,body,created)
    VALUES ('A title once again', 'And the article body follows.', NOW());
INSERT INTO articles (title,body,created)
    VALUES ('Title strikes back', 'This is really exciting! Not.', NOW());

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

Просмотрите Соглашения CakePHP для более полной информации, но следует заметить, что называя нашу таблицу „articles“, мы автоматически делаем ее доступной нашей Модели Articles, а наличие полей с именами „modified“ и „created“ автоматически сделает их доступными для CakePHP.

Конфигурация Базы данных

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

Настройка должна показаться довольно легкой: просто замените значения в массиве Datasources.default в файле config/app.php на нужные вам. В результате у вас должно получиться что-то вроде этого:

return [
    // More configuration above.
    'Datasources' => [
        'default' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Mysql',
            'persistent' => false,
            'host' => 'localhost',
            'username' => 'cake_blog',
            'password' => 'AngelF00dC4k3~',
            'database' => 'cake_blog',
            'encoding' => 'utf8',
            'timezone' => 'UTC'
        ],
    ],
    // More configuration below.
];

Как только вы сохраните ваш файл config/app.php, вам должна стать доступна приветственная страница CakePHP. Она также должна будет вам сказать, что База данных обнаружена и подключение к ней прошло успешно.

Примечание

Копия файла с настройками по умолчанию может быть найдена в config/app.default.php.

Дополнительные настройки

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

Если вы использовали Composer при установке, то он позаботился и об этой проблеме за вас. В противном случае вам нужно будет поменять параметр salt в файле config/app.php. Не важно каким будет новое значение, но оно должно быть достаточно длинным, чтобы его нельзя было угадать:

'Security' => [
    'salt' => 'something long and containing lots of different values.',
],

Замечание о mod_rewrite

Иногда пользователи могут столкнуться с проблемами mod_rewrite. К примеру, если страница приветствия CakePHP выглядит странно (нет картинок или не подключились стили css). Вероятно это из-за того, что в вашей системе не работает mod_rewrite. Пожалуйста обратитесь к разделу Переопределение URL для решения этой проблемы.

Теперь вы можете продолжить, перейдя во вторую часть руководства Пример создания блога - Часть 2, чтобы начать создавать ваше первое приложение CakePHP.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Пример создания блога - Часть 2

Создание Модели статей

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

Файлы классов Модели CakePHP подразделяются на объекты Таблица и Сущность (Entity). Объекты Таблица предоставляют доступ к коллекции сущностей размещенных в определенной таблице. Они находятся в папке src/Model/Table. Класс, который мы создадим, будет храниться в файле src/Model/Table/ArticlesTable.php. Окончательный его вариант будет иметь следующий вид:

// src/Model/Table/ArticlesTable.php

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Timestamp');
    }
}

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

Примечание

CakePHP динамически создаст объект модели для Вас если не сможет найти нужный файл в src/Model/Table. Это также означает, что если Вы случайно неправильно назовете файл (например articlestable.php или ArticleTable.php), CakePHP не распознает их и будет использовать вместо этого сгенерированную модель.

Для более подробной информации о моделях (например валидация данных) ознакомьтесь с главой руководства Доступ к Базе Данных и ORM.

Примечание

Если Вы завершили работу с Частью 1 данного руководства и создали таблицу articles в базе данных нашего блога для большего удобства Вы можете воспользоваться консолью bake CakePHP и ее возможностями генерации кода для создания модели ArticlesTable:

bin/cake bake model Articles

Чтобы получить больше информации о консоли bake, пожалуйста ознакомьтесь с Генерация кода с помощью Bake.

Создание Контроллера статей

Теперь мы создадим Контроллер для наших статей. Контроллер - это то место, где происходит все взаимодействие со статьями. В двух словах - это место, где Вы взаимодействуете с бизнес-логикой, содержащейся в моделях и получаете готовый результат после обработки статей. Мы разместим этот новый контроллер в файле ArticlesController.php внутри папки src/Controller. Вот какой должна быть начальная структура контроллера:

// src/Controller/ArticlesController.php

namespace App\Controller;

class ArticlesController extends AppController
{
}

Давайте добавим экшен в наш контроллер. Экшены часто представляют из себя отдельные функции или интерфейсы в приложении. Например, когда пользователь запрашивает адрес www.example.com/articles/index (то же самое, что и www.example.com/articles/), он ожидает увидеть список статей. Код этого экшена должен выглядеть так:

// src/Controller/ArticlesController.php

namespace App\Controller;

class ArticlesController extends AppController
{

    public function index()
    {
        $articles = $this->Articles->find('all');
        $this->set(compact('articles'));
    }
}

Объявляя метод index() в нашем контроллере ArticlesController, мы даем возможность пользователям получить доступ к логике для обработки запроса www.example.com/articles/index из адресной строки. Схожим образом, если бы мы объявили метод foobar(), пользователям стал бы доступен адрес www.example.com/articles/foobar.

Предупреждение

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

Единственная инструкция в экшене - set() передает данные от Контроллера в Вид (который мы создадим позже). Эта строка назначает переменную Вида, называемую „articles“ совпадающую по названию с результатом, возвращаемым методом find('all') объекта ArticlesTable.

Примечание

Если Вы завершили работу с Частью 1 данного руководства и создали таблицу articles в базе данных нашего блога для большего удобства Вы можете воспользоваться консолью bake CakePHP и ее возможностями генерации кода для создания класса контроллера ArticlesController:

bin/cake bake controller Articles

Чтобы получить больше информации о консоли bake, пожалуйста ознакомьтесь с Генерация кода с помощью Bake.

Чтобы узнать больше о контроллерах CakePHP ознакомьтесь с главой документации Контроллеры.

Создание Вида статей

Теперь, когда унас есть наши данные, получаемые из модели и наша логика, определенная в контроллере, давайте создадим вид для нашего экшена index, созданного ранее.

Виды CakePHP это просто фрагменты, которые размещаются внутри лейаута нашего приложения. Для большинства приложений, они представляют собой HTML код с включениями PHP, но они могут быть оформлены и как XML, CSV, и даже двоичные данные.

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

Помните как в прошлом разделе мы передали в вид переменную „articles“ используя метод set()? Таким образом в вид передается коллекция объекта запроса, которую можно обработать циклом foreach.

Файлы шаблонов CakePHP хранятся в папке src/Template в подпапке названной в соответствии с именем Контроллера (в нашем случае это „Articles“). Чтобы оформить эти данные о статьях в милую таблицу, наш код Вида может выглядеть как-то так:

<!-- Файл: src/Template/Articles/index.ctp -->

<h1>Статьи блога</h1>
<table>
    <tr>
        <th>Id</th>
        <th>Заголовок</th>
        <th>Дата создания</th>
    </tr>

    <!-- Здесь мы проходимся в цикле по объекту запроса $articles, выводя данные статьи -->

    <?php foreach ($articles as $article): ?>
    <tr>
        <td><?= $article->id ?></td>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->id]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
    </tr>
    <?php endforeach; ?>
</table>

Надеемся это является не слишком сложным для Вас.

Вы могли заметить использование объекта $this->Html. Это экземпляр класса CakePHP Cake\View\Helper\HtmlHelper. CakePHP содержит в себе набор хелперов Вида, который значительно упрощает вставку в код страницы таких элементов, как ссылки, формы и др. Вы можете узнать больше об их использовании в Хелперы, но важно отметить, что здесь метод link() сгенерирует HTML-ссылку с нужным заголовком (первый параметр) и URL (второй параметр).

Когда Вы прописываете URL-адреса в CakePHP, желательно использовать формат массива. Это более детально объясняется в разделе о маршрутизации. Использование массива для передачи URL позволяет использовать преимущества обратной маршрутизации CakePHP. Вы можете также определять URL относительно корня вашего приложения в форме /controller/action/param1/param2 либо использовать именованные маршруты.

На данный момент вы должны быть способны направить ваш браузер к адресу http://www.example.com/articles/index. Вы должны видеть ваш вид, корректно отформатированный с заголовком и таблицей, отображающей список статей.

Если Вам случалось переходить по любой из ссылок, созданных в этом Виде (которые связывают заголовок статьи с URL /articles/view/какой-либо\_id) Вы возможно были уведомлены, что экшен еще не был определен. Если же такого сообщения не было, то это значит, что либо что-то пошло не так, либо Вы схитрили и уже указали данный экшен. Как бы там ни было, мы создадим данный экшен в нашем Контроллере ArticlesController сейчас:

// src/Controller/ArticlesController.php

namespace App\Controller;

class ArticlesController extends AppController
{

    public function index()
    {
         $this->set('articles', $this->Articles->find('all'));
    }

    public function view($id = null)
    {
        $article = $this->Articles->get($id);
        $this->set(compact('article'));
    }
}

Вызов метода set() должен быть уже хорошо знаком Вам. Заметьте, мы используем get() вместо find('all'), потому что нам на самом деле нужна информация только об одной конкретной статье.

Обратите внимание, наш экшен „view“ принимает один параметр - ID статьи, которую мы хотим просмотреть. Этот параметр передается в экшен из запрашиваемого URL. Если пользователь запрашивает адрес /articles/view/3, то значение „3“ передается как $id.

Мы также осуществляем некоторую обработку ошибок, чтобы удостовериться, что пользователь на самом деле обращается к существующей записи. Используя метод get() в таблице Articles, мы можем быть уверены, что пользователь получит существующую запись. В случае если запрошенной статьи не окажется в таблице, или id окажется не верным - метод get() выбросит исключение NotFoundException.

Теперь давайте создадим Вид для нашего нового экшена „view“ и поместим его в файл src/Template/Articles/view.ctp

<!-- File: src/Template/Articles/view.ctp -->

<h1><?= h($article->title) ?></h1>
<p><?= h($article->body) ?></p>
<p><small>Created: <?= $article->created->format(DATE_RFC850) ?></small></p>

Проверьте теперь, что все работает попробовав перейти по ссылкам в /articles/index или вручную запросив статью введя /articles/view/{id} заменяя {id} на „id“ статьи.

Добавление статьи

Чтение из Базы данных и отображение статей это неплохое начало, но давайте добавим возможность создания новых статей.

Во-первых создадим экшен add() в Контроллере ArticlesController:

// src/Controller/ArticlesController.php

namespace App\Controller;

use App\Controller\AppController;

class ArticlesController extends AppController
{

    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('Flash'); // Include the FlashComponent
    }

    public function index()
    {
        $this->set('articles', $this->Articles->find('all'));
    }

    public function view($id)
    {
        $article = $this->Articles->get($id);
        $this->set(compact('article'));
    }

    public function add()
    {
        $article = $this->Articles->newEntity();
        if ($this->request->is('post')) {
            $article = $this->Articles->patchEntity($article, $this->request->getData());
            if ($this->Articles->save($article)) {
                $this->Flash->success(__('Your article has been saved.'));
                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('Unable to add your article.'));
        }
        $this->set('article', $article);
    }
}

Примечание

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

Вот что делает экшен add(): если методом HTTP-запроса является POST, попытаться сохранить данные использовав Модель Articles. Если по какой-то причине данные не могут быть сохранены, просто передать Вид. Это дает нам возможность показать пользователю ошибки валидации и другие предупреждения.

Каждый запрос CakePHP включает в себя объект ServerRequest доступный с использованием $this->request. Объект запроса содержит полезную информацию, касающуюся того запроса, который был только что получен, и мoжет быть использован для контроля потока приложения. В данном случае, мы используем метод Cake\Network\ServerRequest::is() для проверки того, что запрос является HTTP POST-запросом.

Когда пользователь отправляет данные через форму методом POST, эта информация становится доступна в свойстве $this->request->data. Вы можете использовать функцию pr() или debug() для распечатки содержимого этого свойства.

Мы используем методы success() и error() Компонента Flash для записи сообщения в сессионную переменную. Эти методы предоставлены с использованием возможностей магических методов PHP [http://php.net/manual/en/language.oop5.overloading.php#object.call]. Флеш-сообщения будут отображены на странице после редиректа. В лейауте у нас вызывается метод <?= $this->Flash->render() ?>, который отображает сообщение и очищает соответствующую сессионную переменную. Метод Контроллера Cake\Controller\Controller::redirect выполняет перенаправление на другой URL. Параметр ['action' => 'index'] преобразуется в URL /articles т.е. к экшену „index“ Контроллера ArticlesController. Вы можете обратиться к описанию метода Cake\Routing\Router::url() в API [https://api.cakephp.org], чтобы просмотреть форматы, в которых Вы можете определять URL для различных функций CakePHP.

Вызов метода save() произведет валидацию данных и отменит сохранение, если выявит при ее выполнении ошибки. Мы обсудим обработку этих ошибок в следующих разделах.

Валидация данных

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

Чтобы воспользоваться возможностями валидации CakePHP, Вы должны использовать хелпер по созданию форм в своих Видах Form. Cake\View\Helper\FormHelper доступен по умолчанию во всех Видах как $this->Form.

Вот наш Вид add:

<!-- Файл: src/Template/Articles/add.ctp -->

<h1>Add Article</h1>
<?php
    echo $this->Form->create($article);
    echo $this->Form->input('title');
    echo $this->Form->input('body', ['rows' => '3']);
    echo $this->Form->button(__('Сохранить статью'));
    echo $this->Form->end();
?>

Мы используем хелпер форм для создания открывающего тега формы. Вот что генерирует метод хелпера $this->Form->create():

<form method="post" action="/articles/add">

Если create() вызывается без дополнительных параметров, то предполагается, что Вы создаете форму, отправляющую данные методом POST к экшену add() текущего Контроллера (или же к экшену edit(), если с данными передается параметр id).

Метод $this->Form->input() используется для создания полей ввода формы с соответствующими именами. Первый параметр говорит CakePHP какому полю он соответствует, второй параметр позволяет вам определять дополнительные настройки - в данном случае количесво строк для элемента textarea. Здесь также присутствует немного магии - метод input() автоматически определяет нужный тип поля ввода в зависимости от типа данных в поле таблицы.

$this->Form->end() закрывает форму. Создает скрытые поля ввода, если включена защита от поддельных запросов (CSRF).

Теперь давайте вернемся назад и обновим наш шаблон src/Template/Articles/index.ctp, добавив ссылку «Добавить статью». Перед тегом <table> добавим следующую строку:

<?= $this->Html->link('Добавить статью', ['action' => 'add']) ?>

Вы можете быть удивлены: как CakePHP узнает о нужных нам правилах валидации? Правила валидации объявляются в Модели. Давайте вернемся к нашей модели Articles, и внесем некоторые изменения:

// src/Model/Table/ArticlesTable.php

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Timestamp');
    }

    public function validationDefault(Validator $validator)
    {
        $validator
            ->notEmpty('title')
            ->requirePresence('title')
            ->notEmpty('body')
            ->requirePresence('body');

        return $validator;
    }
}

Метод validationDefault() сообщает CakePHP как проводить валидацию ваших данных, когда вызывается метод save(). В данном случае мы указываем, что оба поля (body и title) не должны быть пустыми для всех операций (создания и обновления). Движок валидации CakePHP является очень мощным, с множеством встроенных правил(номера кредитных карт, адреса e-mail и т.д.) и гибкостью при добавлении новых правил. Для более подробной информации смотрите раздел Validation.

Теперь так как вы указали необходимые правила, попробуйте добавить статью, оставив пустым поле title, либо поле body, и посмотрите на них в действии. Поскольку мы использовали метод Cake\View\Helper\FormHelper::input(), хелпера форм для создания наших элементов формы, сообщения об ошибках валидации будут отображаться автоматически.

Редактирование статей

Редактирование статей - наконец-то мы дошли до этого момента. Теперь вы уже являетесь профессионалом в CakePHP и можете без проблем догадаться об общем алгоритме действий. Создайте экшен, затем Вид. Вот на что должен быть похож экшен edit() Контроллера ArticlesController:

// src/Controller/ArticlesController.php

public function edit($id = null)
{
    $article = $this->Articles->get($id);
    if ($this->request->is(['post', 'put'])) {
        $this->Articles->patchEntity($article, $this->request->getData());
        if ($this->Articles->save($article)) {
            $this->Flash->success(__('Ваша статья была обновлена.'));
            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error(__('Ошибка обновления вашей статьи.'));
    }

    $this->set('article', $article);
}

Данный экшен в первую очередь проверяет, что пользователь пытается получить доступ к существующей записи. Если параметр $id не будет передан или статья окажется несуществующей, мы выбросим исключение NotFoundException обработчику ошибок CakePHP (ErrorHandler), чтобы он занялся этой проблемой.

Далее экшен проверяет был ли отправленный запрос типа POST или PUT. Если это так, то мы используем данные POST-запроса для обновления записи с помощью метода patchEntity(). И наконец мы используем объект таблицы для сохранения записи, либо же для вывода сообщения об ошибках валидации, если такие имеются.

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

<!-- File: src/Template/Articles/edit.ctp -->

<h1>Редактирование статьи</h1>
<?php
    echo $this->Form->create($article);
    echo $this->Form->input('title');
    echo $this->Form->input('body', ['rows' => '3']);
    echo $this->Form->button(__('Сохранить статью'));
    echo $this->Form->end();
?>

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

CakePHP определит когда метод save() генерирует запрос на обновление, а когда на добавление записи.

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

<!-- Файл: src/Template/Articles/index.ctp  (добавлены ссылки редактирования) -->

<h1>Статьи блога</h1>
<p><?= $this->Html->link("Add Article", ['action' => 'add']) ?></p>
<table>
    <tr>
        <th>Id</th>
        <th>Title</th>
        <th>Created</th>
        <th>Action</th>
    </tr>

<!-- Здесь мы проходимся в цикле по объекту запроса $articles, выводя данные статьи -->

<?php foreach ($articles as $article): ?>
    <tr>
        <td><?= $article->id ?></td>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->id]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
        <td>
            <?= $this->Html->link('Изменить', ['action' => 'edit', $article->id]) ?>
        </td>
    </tr>
<?php endforeach; ?>

</table>

Удаление статей

Теперь давайте дадим пользователям возможность удалять статьи. Начнем с экшена delete() в Контроллере ArticlesController:

// src/Controller/ArticlesController.php

public function delete($id)
{
    $this->request->allowMethod(['post', 'delete']);

    $article = $this->Articles->get($id);
    if ($this->Articles->delete($article)) {
        $this->Flash->success(__('Статья с id: {0} была удалена.', h($id)));
        return $this->redirect(['action' => 'index']);
    }
}

Здесь мы удаляем статью, определенную параметром $id, и используем метод $this->Flash->success(), чтобы показать пользователю подтверждение удаления статьи, после чего перенаправляем его на /articles. Если пользователь попытается произвести удаление через GET-запрос, метод allowMethod() выбросит исключение. Неперехваченные исключения захватываются обработчиком исключений CakePHP, и милое сообщение об ошибке выводится пользователю. Существует множество встроенных исключений, которые могут использоваться для обработки различных ошибок HTTP.

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

<!-- Файл: src/Template/Articles/index.ctp (Добавлены ссылки для удаления) -->

<h1>Статьи блога</h1>
<p><?= $this->Html->link('Add Article', ['action' => 'add']) ?></p>
<table>
    <tr>
        <th>Id</th>
        <th>Заголовок</th>
        <th>Дата создания</th>
        <th>Действия</th>
    </tr>

<!-- Здесь мы проходимся в цикле по объекту запроса $articles, выводя данные статьи -->

    <?php foreach ($articles as $article): ?>
    <tr>
        <td><?= $article->id ?></td>
        <td>
            <?= $this->Html->link($article->title, ['action' => 'view', $article->id]) ?>
        </td>
        <td>
            <?= $article->created->format(DATE_RFC850) ?>
        </td>
        <td>
            <?= $this->Form->postLink(
                'Удалить',
                ['action' => 'delete', $article->id],
                ['confirm' => 'Вы уверены?'])
            ?>
            <?= $this->Html->link('Изменить', ['action' => 'edit', $article->id]) ?>
        </td>
    </tr>
    <?php endforeach; ?>

</table>

Использование метода View\Helper\FormHelper::postLink() создаст ссылку, которая использует JavaScript для отправки POST-запроса для удаления нашей статьи.

Предупреждение

Разрешая удаление контента через GET-запросы, вы подвергаете свое приложение риску удаления всех данных злоумышленниками.

Примечание

Этот код Вида также использует хелпер FormHelper для запроса пользователю с использованием окна подтверждения JavaScript при попытке удаления статьи.

Маршруты

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

Для более полной информации о расширенной маршрутизации смотрите Подключение маршрутов.

По умолчанию CakePHP отвечает на запрос к корню вашего сайта, используя Контроллер PagesController и Вид под названием «home». Вместо этого мы заменим Контроллер по умолчанию на наш ArticlesController, создав соответствующее правило маршрутизации.

Маршруты CakePHP хранятся в файле config/routes.php. Вы можете закомментировать или удалить строку, которая определяет корневой маршрут по умолчанию. Она выглядит так:

$routes->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);

Эта строка соединяет URL „/“ с предустановленной домашней страницей CakePHP. Мы же хотим, чтобы она соединяла пользователя с нашим контроллером, так что заменим эту строку на следующую:

$routes->connect('/', ['controller' => 'Articles', 'action' => 'index']);

Это должно перенаправить пользователей, запрашивающих „/“ на экшен index() Контроллера ArticlesController.

Примечание

CakePHP также поощряет использование „обратной маршрутизации“. Если с объявленным выше маршрутом вы передадите в функцию, ожидающую массив, значение ['controller' => 'Articles', 'action' => 'index'], то на выходе вы получите URL „/“. Следовательно хорошей идеей будет всегда указывать адреса в виде массивов, а маршруты будут определять конечный URL, и вы всегда будете уверены в правильности работы ваших ссылок.

Заключение

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

Теперь когда вы создали простое приложение CakePHP, вы можете либо продолжить знакомство с этим руководством Пример создания блога - Часть 3, либо же попробовать создать свой собственный проект. Вы также можете изучить Using CakePHP или API <https://api.cakephp.org/3.0>, чтобы узнать больше о CakePHP.

Если вам нужна помощь, есть множество способов ее получить - пожалуйста ознакомьтесь со страницей Где получить помощь. Добро пожаловать в CakePHP!

Рекомендуется к прочтению

These are common tasks people learning CakePHP usually want to study next:

Макеты: Настройка лейаута вашего веб-сайта

Элементы: Подключение и использование сниппетов Вида

Генерация кода с помощью Bake: Генерирование CRUD-кода

Пример создания блога - Часть 4 - Аутентификация и авторизация: Справочник по аутентификации и авторизации пользователей

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Пример создания блога - Часть 3

Создание дерева категорий

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

Но сначала нам нужно изменить наши таблицы.

Плагин Миграций

Мы используем плагин миграций [https://github.com/cakephp/migrations] для создания таблицы в нашей базе данных. Если у вас уже создана таблица „articles“ в вашей базе данных, удалите ее.

Откройте файл composer.json вашего приложения. Вероятно вы увидите, что плагин миграций уже находится внутри require. В противном случае добавьте нужную строку выполнив команду:

composer require cakephp/migrations:~1.0

Плагин миграций теперь будет находиться в папке plugins вашего приложения. Также добавьте строку Plugin::load('Migrations'); в файл config/bootstrap.php.

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

bin/cake bake migration CreateArticles title:string body:text category_id:integer created modified

Файл миграции будет создан в папке /config/Migrations со следующим содержимым:

<?php

use Migrations\AbstractMigration;

class CreateArticles extends AbstractMigration
{
    public function change()
    {
        $table = $this->table('articles');
        $table->addColumn('title', 'string', [
            'default' => null,
            'limit' => 255,
            'null' => false,
        ]);
        $table->addColumn('body', 'text', [
            'default' => null,
            'null' => false,
        ]);
        $table->addColumn('category_id', 'integer', [
            'default' => null,
            'limit' => 11,
            'null' => false,
        ]);
        $table->addColumn('created', 'datetime', [
            'default' => null,
            'null' => false,
        ]);
        $table->addColumn('modified', 'datetime', [
            'default' => null,
            'null' => false,
        ]);
        $table->create();
    }
}

Для создания таблицы categories выполните еще одну команду. Если вам нужно определить длину поля, вы можете сделать это используя квадратные скобки после указания типа поля:

bin/cake bake migration CreateCategories parent_id:integer lft:integer[10] rght:integer[10] name:string[100] description:string created modified

Это создаст следующий файл в папке config/Migrations:

<?php

use Migrations\AbstractMigration;

class CreateCategories extends AbstractMigration
{
    public function change()
    {
        $table = $this->table('categories');
        $table->addColumn('parent_id', 'integer', [
            'default' => null,
            'limit' => 11,
            'null' => false,
        ]);
        $table->addColumn('lft', 'integer', [
            'default' => null,
            'limit' => 10,
            'null' => false,
        ]);
        $table->addColumn('rght', 'integer', [
            'default' => null,
            'limit' => 10,
            'null' => false,
        ]);
        $table->addColumn('name', 'string', [
            'default' => null,
            'limit' => 100,
            'null' => false,
        ]);
        $table->addColumn('description', 'string', [
            'default' => null,
            'limit' => 255,
            'null' => false,
        ]);
        $table->addColumn('created', 'datetime', [
            'default' => null,
            'null' => false,
        ]);
        $table->addColumn('modified', 'datetime', [
            'default' => null,
            'null' => false,
        ]);
        $table->create();
    }
}

Теперь, когда файлы миграции созданы, вы можете отредактировать их перед созданием ваших таблиц. Нам нужно изменить значение 'null' => false для поля parent_id на 'null' => true, потому что категории верхнего уровня имеют значение parent_id равное null.

Для создания ваших таблиц выполните следующую команду:

bin/cake migrations migrate

Доработка таблиц

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

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

Нам нужно связать таблицы Articles и Categories. Откройте файл src/Model/Table/ArticlesTable.php и добавьте туда следующее:

    // src/Model/Table/ArticlesTable.php

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Timestamp');
        // Просто добавьте связь belongsTo с таблицей CategoriesTable
        $this->belongsTo('Categories', [
            'foreignKey' => 'category_id',
        ]);
    }
}

Генерирование шаблонного кода для категорий

Создайте все необходимые файлы с помощью команд bake:

bin/cake bake model Categories
bin/cake bake controller Categories
bin/cake bake template Categories

Вы также можете сделать все это одной командой:

bin/cake bake all Categories

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

Примечание

Если вы пользуетесь Windows не забудьте использовать \ вместо /.

Вам придется внести следующие правки в src/Template/Categories/add.ctp и в src/Template/Categories/edit.ctp:

echo $this->Form->input('parent_id', [
    'options' => $parentCategories,
    'empty' => 'Нет родительской категории'
]);

Привязка TreeBehavior к CategoriesTable

TreeBehavior помогает вам управлять структурой иерархического Дерева в таблице базы данных. Оно использует MPTT логику [http://www.sitepoint.com/hierarchical-data-database-2/] для управления данными. Структура дерева MPTT оптимизирована для операций чтения, что зачастую делает ее хорошим выбором для чтения таких ресурсоемких приложений, как блоги.

Если вы откроете файл src/Model/Table/CategoriesTable.php, вы увидите, что поведение TreeBehavior было привязано к таблице CategoriesTable внутри метода initialize(). Bake добавляет это поведение к любым таблицам, содержащим поля lft и rght:

$this->addBehavior('Tree');

С привязанным TreeBehavior вам будут доступны такие возможности, как изменение порядка категорий. Мы скоро это увидим.

А пока что вы должны удалить следующие инпуты в файлах шаблонов „add“ и „edit“ в папке Categories:

echo $this->Form->input('lft');
echo $this->Form->input('rght');

В добавок к этому вы должны удалить или закомментировать опцию requirePresence в валидаторе для полей lft и rght в вашей модели CategoriesTable:

public function validationDefault(Validator $validator)
{
    $validator
        ->add('id', 'valid', ['rule' => 'numeric'])
        ->allowEmpty('id', 'create');

    $validator
        ->add('lft', 'valid', ['rule' => 'numeric'])
    //    ->requirePresence('lft', 'create')
        ->notEmpty('lft');

    $validator
        ->add('rght', 'valid', ['rule' => 'numeric'])
    //    ->requirePresence('rght', 'create')
        ->notEmpty('rght');
}

Эти поля автоматически управляются поведением TreeBehavior при сохранении категории.

Используя ваш веб-браузер добавьте какие-нибудь новые категории с помощью экшена /yoursite/categories/add.

Изменение порядка категорий с TreeBehavior

В вашем файле шаблона „index“, вы можете выводить список категорий и менять их иерархию.

Давайте изменим метод index в вашем CategoriesController.php и добавим методы moveUp() и moveDown() для возможности изменять расположение категорий в дереве:

class CategoriesController extends AppController
{
    public function index()
    {
        $categories = $this->Categories->find()
            ->order(['lft' => 'ASC']);
        $this->set(compact('categories'));
        $this->set('_serialize', ['categories']);
    }

    public function moveUp($id = null)
    {
        $this->request->allowMethod(['post', 'put']);
        $category = $this->Categories->get($id);
        if ($this->Categories->moveUp($category)) {
            $this->Flash->success('Категория была перемещена вверх.');
        } else {
            $this->Flash->error('Категория не может быть перемещена вверх.
                                                    Пожалуйста, попробуйте еще раз.');
        }
        return $this->redirect($this->referer(['action' => 'index']));
    }

    public function moveDown($id = null)
    {
        $this->request->allowMethod(['post', 'put']);
        $category = $this->Categories->get($id);
        if ($this->Categories->moveDown($category)) {
            $this->Flash->success('Категория была перемещена вниз.');
        } else {
            $this->Flash->error('Категория не может быть перемещена вниз.
                                                    Пожалуйста, попробуйте еще раз.');
        }
        return $this->redirect($this->referer(['action' => 'index']));
    }
}

В src/Template/Categories/index.ctp замените содержимое на:

<div class="actions large-2 medium-3 columns">
    <h3><?= __('Действия') ?></h3>
    <ul class="side-nav">
        <li><?= $this->Html->link(__('Новая Категория'), ['action' => 'add']) ?></li>
    </ul>
</div>
<div class="categories index large-10 medium-9 columns">
    <table cellpadding="0" cellspacing="0">
    <thead>
        <tr>
            <th>Id</th>
            <th>Id Родителя</th>
            <th>Lft</th>
            <th>Rght</th>
            <th>Имя</th>
            <th>Описание</th>
            <th>Создано</th>
            <th class="actions"><?= __('Действия') ?></th>
        </tr>
    </thead>
    <tbody>
    <?php foreach ($categories as $category): ?>
        <tr>
            <td><?= $category->id ?></td>
            <td><?= $category->parent_id ?></td>
            <td><?= $category->lft ?></td>
            <td><?= $category->rght ?></td>
            <td><?= h($category->name) ?></td>
            <td><?= h($category->description) ?></td>
            <td><?= h($category->created) ?></td>
            <td class="actions">
                <?= $this->Html->link(__('Просмотреть'), ['action' => 'view', $category->id]) ?>
                <?= $this->Html->link(__('Изменить'), ['action' => 'edit', $category->id]) ?>
                <?= $this->Form->postLink(__('Удалить'), ['action' => 'delete', $category->id], ['confirm' => __('Вы уверены, что хотите удалить # {0}?', $category->id)]) ?>
                <?= $this->Form->postLink(__('Сместить вниз'), ['action' => 'moveDown', $category->id], ['confirm' => __('Вы уверены, что хотите сдвинуть категорию вниз # {0}?', $category->id)]) ?>
                <?= $this->Form->postLink(__('Сместить вверх'), ['action' => 'moveUp', $category->id], ['confirm' => __('Вы уверены, что хотите сдвинуть категорию вверх # {0}?', $category->id)]) ?>
            </td>
        </tr>
    <?php endforeach; ?>
    </tbody>
    </table>
</div>

Изменение контроллера ArticlesController

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

// src/Controller/ArticlesController.php

namespace App\Controller;

// Prior to 3.6 use Cake\Network\Exception\NotFoundException
use Cake\Http\Exception\NotFoundException;

class ArticlesController extends AppController
{

    // ...

    public function add()
    {
        $article = $this->Articles->newEntity();
        if ($this->request->is('post')) {
            $article = $this->Articles->patchEntity($article, $this->request->getData());
            if ($this->Articles->save($article)) {
                $this->Flash->success(__('Ваша статья была сохранена.'));
                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('Невозможно добавить вашу статью.'));
        }
        $this->set('article', $article);

        // Просто добавляем список категорий для возможности выбора
        // одной категории для статьи
        $categories = $this->Articles->Categories->find('treeList');
        $this->set(compact('categories'));
    }
}

Доработка шаблонов статей

Файл add должен выглядеть примерно так:

<!-- Файл: src/Template/Articles/add.ctp -->

<h1>Добавить статью</h1>
<?php
echo $this->Form->create($article);
// просто добавили инпут для выбора категорий
echo $this->Form->input('category_id');
echo $this->Form->input('title');
echo $this->Form->input('body', ['rows' => '3']);
echo $this->Form->button(__('Сохранить статью'));
echo $this->Form->end();

Когда вы перейдёте по адресу yoursite/categories/ вы должны увидеть список категорий.

Когда вы перейдёте по адресу yoursite/categories/add вы должны увидеть форму добавления новой категории.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Пример создания блога - Часть 4 - Аутентификация и авторизация

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

Создание всего кода касающегося пользователей

Во-первых, давайте создадим новую таблицу в нашей базе данных блогов, чтобы хранить данные наших пользователей:

CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50),
    password VARCHAR(255),
    role VARCHAR(20),
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);

Мы придерживались конвенций cakephp при именовании таблиц, но мы также воспользуемся другой конвенцией: использование полей username и password для имени пользователя и пароля соответственно в таблице пользователей, cakephp будет иметь возможность автоматически настраивать большинство вещей для нас при осуществлении входа пользователя в систему.

Следующим шагом является создание класса таблицы users, отвечающего за поиск, сохранение и проверку данных пользователя:

// src/Model/Table/UsersTable.php
namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class UsersTable extends Table
{

    public function validationDefault(Validator $validator)
    {
        return $validator
            ->notEmpty('username', 'A username is required')
            ->notEmpty('password', 'A password is required')
            ->notEmpty('role', 'A role is required')
            ->add('role', 'inList', [
                'rule' => ['inList', ['admin', 'author']],
                'message' => 'Please enter a valid role'
            ]);
    }

}

Давайте также создадим наш UsersController. Следующее содержание соответствует основной части класса UsersController, сгенерированной с помощью утилиты генерации кода - bake, поставляемой в комплекте с cakephp:

// src/Controller/UsersController.php

namespace App\Controller;

use App\Controller\AppController;
use Cake\Event\Event;

class UsersController extends AppController
{

    public function beforeFilter(Event $event)
    {
        parent::beforeFilter($event);
        $this->Auth->allow('add');
    }

     public function index()
     {
        $this->set('users', $this->Users->find('all'));
    }

    public function view($id)
    {
        $user = $this->Users->get($id);
        $this->set(compact('user'));
    }

    public function add()
    {
        $user = $this->Users->newEntity();
        if ($this->request->is('post')) {
            // Prior to 3.4.0 $this->request->data() was used.
            $user = $this->Users->patchEntity($user, $this->request->getData());
            if ($this->Users->save($user)) {
                $this->Flash->success(__('The user has been saved.'));
                return $this->redirect(['action' => 'add']);
            }
            $this->Flash->error(__('Unable to add the user.'));
        }
        $this->set('user', $user);
    }

}

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

<!-- src/Template/Users/add.ctp -->

<div class="users form">
<?= $this->Form->create($user) ?>
    <fieldset>
        <legend><?= __('Add User') ?></legend>
        <?= $this->Form->control('username') ?>
        <?= $this->Form->control('password') ?>
        <?= $this->Form->control('role', [
            'options' => ['admin' => 'Admin', 'author' => 'Author']
        ]) ?>
   </fieldset>
<?= $this->Form->button(__('Submit')); ?>
<?= $this->Form->end() ?>
</div>

Аутентификация (Вход и выход из системы)

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

Чтобы добавить эту возможность в ваше приложение, откройте файл src/Controller/AppController.php и добавьте следующие строки:

// src/Controller/AppController.php

namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Event\Event;

class AppController extends Controller
{
    //...

    public function initialize()
    {
        $this->loadComponent('Flash');
        $this->loadComponent('Auth', [
            'loginRedirect' => [
                'controller' => 'Articles',
                'action' => 'index'
            ],
            'logoutRedirect' => [
                'controller' => 'Pages',
                'action' => 'display',
                'home'
            ]
        ]);
    }

    public function beforeFilter(Event $event)
    {
        $this->Auth->allow(['index', 'view', 'display']);
    }
    //...
}

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

Так как мы хотим, чтобы наши посетители читали список записей без необходимости предварительной регистрации в системе, то мы должны отфильтровать с помощью метода beforeFilter() компонента AuthComponent, все контроллеры не предназначенные для входа, т.е. все index() и view().

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

// src/Controller/UsersController.php
namespace App\Controller;

use App\Controller\AppController;
use Cake\Event\Event;

class UsersController extends AppController
{
    // Другие методы..

    public function beforeFilter(Event $event)
    {
        parent::beforeFilter($event);
        // Разрешить пользователям регистрироваться и выходить из системы.
        // Вы не должны добавлять действие «login», чтобы разрешить список.
        // Это может привести к проблемам с нормальным функционированием
        $this->Auth->allow(['add', 'logout']);
    }

    public function login()
    {
        if ($this->request->is('post')) {
            $user = $this->Auth->identify();
            if ($user) {
                $this->Auth->setUser($user);
                return $this->redirect($this->Auth->redirectUrl());
            }
            $this->Flash->error(__('Invalid username or password, try again'));
        }
    }

    public function logout()
    {
        return $this->redirect($this->Auth->logout());
    }
}

Мы ещё не создали хеширование для паролей, поэтому нам нужен класс Entity, у которого своя специфическая логика. Создадим файл src/Model/Entity/User.php и добавим в него следующее:

// src/Model/Entity/User.php
namespace App\Model\Entity;

use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Entity;

class User extends Entity
{

    // Make all fields mass assignable except for primary key field "id".
    protected $_accessible = [
        '*' => true,
        'id' => false
    ];

    // ...

    protected function _setPassword($password)
    {
        if (strlen($password) > 0) {
            return (new DefaultPasswordHasher)->hash($password);
        }
    }

    // ...
}

Теперь каждый раз пароль, назначенный пользователю, будет хешироваться с помощью класса DefaultPasswordHasher. Теперь создайте простой шаблон файла Вида для функции входа: src/Template/Users/login.ctp и добавьте следующие строки:

<!-- File: src/Template/Users/login.ctp -->

<div class="users form">
<?= $this->Flash->render() ?>
<?= $this->Form->create() ?>
    <fieldset>
        <legend><?= __('Please enter your username and password') ?></legend>
        <?= $this->Form->control('username') ?>
        <?= $this->Form->control('password') ?>
    </fieldset>
<?= $this->Form->button(__('Login')); ?>
<?= $this->Form->end() ?>
</div>

Теперь вы можете зарегистрировать нового пользователя, перейдя по URL-адресу /users/add и войти под учётной записью созданного пользователя. Для этого нужно зайти по URL-адресу /users/login. Кроме того, при попытке получить доступ к любому другому URL-адресу, который не был явно разрешен, например /articles/add, приложение автоматически перенаправит вас на страницу входа.

Функция beforeFilter() говорит AuthComponent что для действия add() требуется логин, а для index() и view(), которые уже были добавлены - он не нужен.

Метод login() вызывает $this->Auth->identify() из AuthComponent, и он работает без каких-либо дальнейших конфигураций, потому что мы следуем конвенции, как упоминалось ранее. То есть, имея таблицу пользователей с именем пользователя и со столбцом пароля и при использовании формы, размещенной на контроллере с данными пользователя. Эта функция возвращает результат, был ли вход успешным или нет, а в случае если вход был успешен, то мы перенаправляем пользователя на заданный URL перенаправления, который мы указали при добавлении AuthComponent в наше приложение.

Выход работает, просто: перейдя по URL /users/logout, пользователь перенаправляется на страницы входа, настроенную в logoutUrl. Этот URL является результатом успешной работы метода AuthComponent::logout().

Авторизация (кому и к чему разрешен доступ)

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

ALTER TABLE articles ADD COLUMN user_id INT(11);

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

// src/Controller/ArticlesController.php

public function add()
{
    $article = $this->Articles->newEntity();
    if ($this->request->is('post')) {
        // Для версий ниже 3.4.0 использовался $this->request->data().
        $article = $this->Articles->patchEntity($article, $this->request->getData());
        // Добавили эту строку
        $article->user_id = $this->Auth->user('id');
        // Также вы могли сделать следующее
        //$newData = ['user_id' => $this->Auth->user('id')];
        //$article = $this->Articles->patchEntity($article, $newData);
        if ($this->Articles->save($article)) {
            $this->Flash->success(__('Your article has been saved.'));
            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error(__('Unable to add your article.'));
    }
    $this->set('article', $article);

    // Просто добавили список категорий, чтобы можно было выбрать
    // категорию для статьи
    $categories = $this->Articles->Categories->find('treeList');
    $this->set(compact('categories'));
}

Функция user() предоставляемая компонентом, возвращает из БД один столбец, того пользователя который вошёл в систему. Мы использовали этот метод для добавления и сохранения данных запроса.

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

Снова откройте AppController класс и добавте еще несколько вариантов конфигурации авторизации:

// src/Controller/AppController.php

public function initialize()
{
    $this->loadComponent('Flash');
    $this->loadComponent('Auth', [
        'authorize' => ['Controller'], // Добавили эту строку
        'loginRedirect' => [
            'controller' => 'Articles',
            'action' => 'index'
        ],
        'logoutRedirect' => [
            'controller' => 'Pages',
            'action' => 'display',
            'home'
        ]
    ]);
}

public function isAuthorized($user)
{
    // Администратор может получить доступ к каждому действию
    if (isset($user['role']) && $user['role'] === 'admin') {
        return true;
    }

    // Иначе, запрещаем по умолчанию
    return false;
}

Мы создали простой механизм авторизации. Пользователь с правами admin сможет получить доступ к любому URL при входе в систему. Все остальные пользователи - те, что с ролью автора - будут иметь такой же доступ как пользователи, которые не вошли в систему.

Это не совсем то, что мы хотели. Нам нужно предоставить больше правил для метода isAuthorized(). Однако вместо того, чтобы сделать это в AppController, мы будем делегировать эти дополнительные правила для каждого отдельного контроллера. Правила, которые мы собираемся добавить в ArticlesController должны позволять авторам созданние статей, но запрещать авторам редактирование не своих статей. Добавьте следующее содержимое в ваш ArticlesController.php:

// src/Controller/ArticlesController.php

public function isAuthorized($user)
{
    // Все зарегистрированные пользователи могут добавлять статьи
    // До 3.4.0 $this->request->param('action') делали так.
    if ($this->request->getParam('action') === 'add') {
        return true;
    }

    // Владелец статьи может редактировать и удалять ее
    // До 3.4.0 $this->request->param('action') делали так.
    if (in_array($this->request->getParam('action'), ['edit', 'delete'])) {
        // До 3.4.0 $this->request->params('pass.0') делали так.
        $articleId = (int)$this->request->getParam('pass.0');
        if ($this->Articles->isOwnedBy($articleId, $user['id'])) {
            return true;
        }
    }

    return parent::isAuthorized($user);
}

Мы сейчас реализовали вызов и готовность родительского класса проверки аторизации пользователя isAuthorized() в контроллере AppController. Если же его нет, то просто позволим пользователю получить доступ к действию добавления, редактироавния и удаления. Сказать имеет ли право пользователь редактировать статьи, мы можем вызвав isOwnedBy(). Давайте реализуем эту функцию:

// src/Model/Table/ArticlesTable.php

public function isOwnedBy($articleId, $userId)
{
    return $this->exists(['id' => $articleId, 'user_id' => $userId]);
}

На этом наш простой учебник по проверке подлинности и авторизации окончен. Для обеспечения безопасности UserController вы можете следовать той же методике, которой мы следовали для ArticlesController. Вы также можете быть более креативными и кодировать что-то более общее в AppController, основываясь на ваших собственных правилах.

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

Рекомендуем к прочению

Генерация кода с помощью Bake Создание базового кода CRUD

Аутентификация: Регистрация пользователя и выход из системы

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Сотрудничество

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

Документация

Билеты

Код

Стандарты кодирования

Руководство по совместимости

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Документация

Внести вклад в документацию просто. Файлы размещаются на https://github.com/cakephp/docs. Не стесняйтесь произвести форк репозитория, добавить своё изменение/улучшение/перевод и возвратить, выдав запрос на pull request (перенос). Вы даже можете редактировать документы в Интернете с помощью GitHub, не загружая файлы – кнопка «Improve this Doc» на любой странице приведёт вас к редактору GitHub для этой страницы.

Документация CakePHP непрерывно интегрируемая [http://en.wikipedia.org/wiki/Continuous_integration], и развёртывается после каждого запроса pull request, на слияние.

Переводы

Отправьте по электронной почте команду docs (docs на cakephp dot org) или перейдите на IRC (#cakephp on freenode), чтобы обсудить любые усилия по переводу если хотите участвовать.

Новый язык перевода

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

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

index.rst

intro.rst

quickstart.rst

installation.rst

/intro folder

/tutorials-and-examples folder

Напоминание для администраторов документов

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

Например, если в en/file.rst создан новый английский файл, вы должны:

Добавить файл и для других языков. : fr/file.rst, zh/file.rst, …

Удалить содержимое, но сохраняя информацию title, meta и возможные toc-tree элементы. Следующее примечание будет добавлено, пока никто не перевёл файл:

File Title
##########

.. note::
    The documentation is not currently supported in XX language for this
    page.

    Please feel free to send us a pull request on
    `Github <https://github.com/cakephp/docs>`_ or use the **Improve This Doc**
    button to directly propose your changes.

    You can refer to the English version in the select top menu to have
    information about this page's topic.

// Если элементы toc-tree находятся в английской версии
.. toctree::
    :maxdepth: 1

    one-toc-file
    other-toc-file

.. meta::
    :title lang=xx: File Title
    :keywords lang=xx: title, description,...

Советы переводчикам

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

Не стесняйтесь использовать ваш язык, если он уже существует в книге.

Используйте Неофициальные формы [http://en.wikipedia.org/wiki/Register_(linguistics)].

Переведите одновременно и контент и заголовок.

По сравнению с английским содержанием, перед отправкой исправления, (если вы что-то исправите, но не интегрируете изменение «вверх по течению» („upstream“) ваше изменение не будет принято).

Если вам нужно написать английский термин, оберните его в теги <em>. Например. «asdf asdf Controller asdf» или «asdf asdf Kontroller (Controller) asfd».

Не отправляйте частичные(незаконченные) переводы.

Не редактируйте раздел ожидающий изменений.

Не используйте HTML сущности [http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references] для акцентирования. В книге используется кодировка UTF-8.

Не меняйте разметку (HTML) и не добавляйте новый контент.

Если в исходном контенте отсутствует какая-либо информация, отправьте её в первую очередь.

Руководство по форматированию документации

Новая документация CakePHP написана с помощью Текста в формате ReST [http://en.wikipedia.org/wiki/ReStructuredText]. ReST (Re Structured Text) - синтаксис простой текстовой разметки, аналогичный markdown (методу уценки), или textile. Для поддержания согласованности рекомендуется, чтобы при добавлении к CakePHP вы следовали этим рекомендациям, о том как форматировать и структурировать свой текст.

Длина строки

Строки текста должны быть обернуты в 80 столбцов (80 символов на строке). Единственным исключение могут быть длинные URL-адреса и фрагменты кода.

Заголовки и разделы

Заголовки разделов создаются путем подчёркивания заголовка символами пунктуации до края текста.

# Используется для обозначения названий страниц.

= Используется для разделов на странице.

- Используется для подразделов.

~ Используется для подсекций.

^ Используется для под-подразделов.

Заголовки не должны вставляться в глубину более 5 уровней. Перед заголовками и после должна идти пустая строка.

Параграфы

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

Встроенная разметка

Одна звездочка: текст для выделения (курсивом) Мы используем его для общего выделения/акцента.

*текст*.

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

**/config/Migrations**, **articles**, например.

Две косых черты(засечки): text для образцов кода. Мы будем использовать его для имён параметров метода, имён столбцов таблицы, имён объектов, за исключением следующего слова «object» и для имён методов/функций – include «()».

``cascadeCallbacks``, ``true``, ``id``, ``PagesController``, ``config()``, etc.

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

Встроенная разметка имеет несколько ограничений:

Это не может быть вложением.

Содержимое не может начинаться или заканчиваться пробелами: * text* это ошибка.

Содержимое должно быть отделено от окружающего текста non-word символами. Используйте обратную косую черту, чтобы избежать удаления пробелов: onelong\ *bolded*\ word.

Списки

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

* This is a bullet
* So is this. But this line
  has two lines.

1. First line
2. Second line

#. Automatic numbering
#. Will save you some time.

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

* First line
* Second line

    * Going deeper
    * Whoah

* Back to the first level.

Списки определений можно сделать так:

term
    definition
CakePHP
    An MVC framework for PHP

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

Ссылки

Существует несколько видов ссылок, и каждый вид имеет своё собственное применение.

Внешние ссылки

Ссылки на внешние документы (ресурсы) могут быть выполнены с помощью следующего:

`External Link to php.net <http://php.net>`_

Полученная ссылка будет выглядеть так: External Link to php.net [http://php.net]

Ссылки на другие страницы

:doc:

Разные страницы документации могут быть связаны между собой с использованием роли :doc:. Вы можете ссылаться на указанный документ, используя абсолютный или относительный путь. Вы должны опустить расширение .rst. Например, если в документе core-helpers/html используется ссылка :doc:`form`, то это значит, что она ссылается на core-helpers/form. Если ссылка была такого вида :doc:`/core-helpers`, то она всегда будет ссылаться на /core-helpers, независимо от места её использования.

Перекрестные ссылки

:ref:

Вы можете перекрёстно ссылаться на любой произвольный заголовок в любом документе, используя :ref: роль. Цели ссылок на метки должны быть уникальными на всём протяжении документации. При создании меток на методы класса, лучше всего использовать class-method в качестве формата вашей метки ссылок.

Наиболее распространенно использование меток над заголовком. Пример:

.. _label-name:

Section heading
---------------

More content here.

В другом месте вы можете ссылаться на вышеуказанный раздел, используя :ref:`label-name`. Текст ссылки будет названием, которому предшествовала ссылка. Вы также можете ввести текст пользовательской ссылки, используя :ref:`Link text <label-name>`.

Предотвращение предупреждений от Sphinx

Sphinx выводит предупреждения, если файл не ссылается в toc-tree. Это отличный способ гарантировать, что все файлы имеют ссылку, направленную на них, но иногда вам не нужно вставлять ссылку на файл, например для ваших epub-contents и pdf-contents файлов. В этих случаях вы можете добавить :orphan: в верхней части файла, чтобы подавить предупреждения о том, что файл не в toc-tree.

Описание классов и их содержания

В документации CakePHP используется phpdomain [http://pypi.python.org/pypi/sphinxcontrib-phpdomain] для предоставления пользовательских директив для описания объектов и конструкций PHP. Использование этих директив и ролей необходимо для правильной индексации и перекрёстных ссылок.

Описание классов и конструкторов

Каждая директива описывает индекс и/или пространство имен.

.. php:global:: name

Эта директива объявляет новую глобальную переменную PHP.

.. php:function:: name(signature)

Определяет новую глобальную функцию вне класса.

.. php:const:: name

Эта директива объявляет новую константу PHP. Вы также можете использовать вложенние внутрь директивы класса, для создания констант класса.

.. php:exception:: name

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

.. php:class:: name

Описывает класс. Методы, атрибуты и константы, принадлежащие классу, должны находиться внутри тела этой директивы:

.. php:class:: MyClass

    Описание класса

   .. php:method:: method($argument)

    Описание метода

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

.. php:class:: MyClass

    Текст о классе

.. php:method:: methodName()

    Текст о методе

См.также

php:method, php:attr, php:const

.. php:method:: name(signature)

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

.. php:method:: instanceMethod($one, $two)

    :param string $one: The first parameter.
    :param string $two: The second parameter.
    :returns: An array of stuff.
    :throws: InvalidArgumentException

   This is an instance method.

.. php:staticmethod:: ClassName::methodName(signature)

Опишите статический метод, его аргументы, возвращаемое значение и исключения, см. php:method для использования параметров.

.. php:attr:: name

Описать свойство/атрибут для класса.

Предотвращение предупреждений от Sphinx

Sphinx выводит предупреждения, если функция ссылается на несколько файлов. Это отличный способ гарантировать, что вы не добавляли функцию два раза, но иногда вы действительно хотите написать функцию в двух или более файлах, например. /development/debugging ссылается на /development/debugging и на /core-libraries/global-constants-and-functions. В этом случае вы можете добавить :noindex: под функцией debug для подавления предупреждений. Сохраните хотя бы одну ссылку без :no-index: чтобы сохранить ссылку на функцию:

.. php:function:: debug(mixed $var, boolean $showHtml = null, $showFrom = true)
    :noindex:

Перекрестная ссылка

Следующие роли относятся к объектам PHP, а ссылки генерируются, если найдена соответствующая директива:

:php:func:

Ссылка на функцию PHP.

:php:global:

Ссылка на глобальную переменную, имя которой имеет префикс $.

:php:const:

Укажите глобальную константу или константу класса. Константы класса должны предшествовать имени класса:

DateTime has an :php:const:`DateTime::ATOM` constant.

:php:class:

Ссылка на класс по имени:

:php:class:`ClassName`

:php:meth:

Ссылка на метод класса. Эта роль поддерживает оба вида методов:

:php:meth:`DateTime::setDate`
:php:meth:`Classname::staticMethod`

:php:attr:

Ссылка на свойство объекта:

:php:attr:`ClassName::$propertyName`

:php:exc:

Ссылка на исключение.

Исходный код

Буквенные кодовые блоки создаются путем окончания абзаца на ::. Литерал блока должен быть отступом, и, как и все абзацы, должен разделяться одиночными линиями:

Это абзац::

    while ($i--) {
        doStuff()
    }

Это снова очередной текст.

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

Примечания и предупреждения

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

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

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

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

.. versionadded:: X.Y.Z «Version added» заметки используются для отображения предупреждений специально для новых функций, добавленных в определенной версии, X.Y.Z является версией с который была добавлена эта функция.

.. deprecated:: X.Y.Z В отличие от предупреждений «version added», «deprecated» предупреждения используются для уведомления об устаревшей функции, X.Y.Z является версией на которой упомянутый признак устарел.

Все примечания сделаны одинаковыми:

.. note::

    Отступ и предшествует и следует пустая строка. Так же, как для параграфа.

Этот текст не является частью примечания.

Образец

Совет

Это полезный tid-bit, который вы вероятно забыли.

Примечание

Вы должны обратить внимание на это.

Предупреждение

Это может быть опасно.

Добавлено в версии 2.6.3: Эта замечательная функция была добавлена в версию 2.6.3

Не рекомендуется, начиная с версии 2.6.3: Эта старая функция стала устаревшей в версии 2.6.3

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Билеты

Получение обратной связи и помощи со стороны сообщества в виде билетов - это является чрезвычайно важной частью процесса разработки CakePHP. Все CakePHP билеты размещены на GitHub [https://github.com/cakephp/cakephp/issues].

Отчеты об ошибках

Хорошо написанные отчеты об ошибках очень полезны. Есть несколько шагов, которые могут помочь в создании хорошего отчета об ошибке:

Сделайте:

Пожалуйста, посетите [https://github.com/cakephp/cakephp/search?q=it+is+broken&ref=cmdform&type=Issues] для поиска аналогичного существующего билета, и убедитесь, что кто-то еще уже не сообщил нам о проблеме или что она уже не была исправлена в репозитории.

Сделайте:

Пожалуйста, укажите подробные инструкции по тому как воспроизвести ошибку. Она может быть в виде тестового примера или фрагмента кода, который демонстрирует проблему разработчикам CackePHP. Не имение представления как воспроизвести проблему, шанс исправления её - уменьшается.

Сделайте:

Пожалуйста, предоставьте как можно больше информации о вашей среде: (OS, версия PHP, версия CakePHP).

Ненужно:

Пожалуйста, не используйте систему билетов, чтобы просто задать вопрос по поддержку. #cakephp IRC-канал на Freenode [https://webchat.freenode.net] имеет много разработчиков, которые могут помочь ответить вам на ваши вопросы. Также взгляните на Stack Overflow [https://stackoverflow.com/questions/tagged/cakephp]

Отчеты по безопасности

Если вы обнаружили проблему безопасности в CakePHP, используйте следующую процедуру вместо обычной системы отчетов об ошибках. Вместо использования трекера ошибок, список рассылки или IRC, пожалуйста, отправьте электронное письмо на security [at] cakephp.org. Письма, отправленные по этому адресу, отправляются в основную группу CakePHP по списку приватной почты.

Для каждого отчета мы пытаемся сначала подтвердить уязвимость. После подтверждения Команда CakePHP предпримет следующие действия:

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

Получаем исправление/патч.

Подготовим сообщение с описанием уязвимости и возможных эксплойтов.

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

Объявляем о выявленной и исправленной проблеме в новом выпуске.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Код

Патчи и запросы (patches и pull requests) на контрибуцию - отличный способ внести код обратно в CakePHP. Pull запросы могут быть созданы в GitHub и предпочтительны для файлов патчей и комментариев к билетам.

Начальная настройка

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

Git

PHP 5.6 или выше

PHPUnit 5.7.0 или выше

Настройте свою пользовательскую информацию с помощью вашего имени/дескриптора и рабочего адреса электронной почты:

git config --global user.name 'Bob Barker'
git config --global user.email 'bob.barker@example.com'

Примечание

Если вы новичок в Git, мы настоятельно рекомендуем вам ознакомиться с превосходной и бесплатной книгой ProGit [http://git-scm.com/book/].

Получите клон исходного кода CakePHP с GitHub:

Если вы не имеете аккаунта на GitHub [http://github.com], создайте его.

Сделайте fork CakePHP repository [http://github.com/cakephp/cakephp], нажав кнопку Fork

После того, как ваш fork сделан, клонируйте его на свою локальную машину:

git clone git@github.com:YOURNAME/cakephp.git

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

cd cakephp
git remote add upstream git://github.com/cakephp/cakephp.git

Теперь, когда у вас установлен CakePHP, вы должны определить переменную $test database connection, и run all the tests.

Работа над патчем

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

Ветка, которую вы создаёте, должна основываться на версии, которую вы исправили/улучшили. Например, если вы исправляете ошибку в 3.x, вы должны использовать master в качестве базы для вашей ветки. Если ваше изменение является исправлением ошибки для 2.x, вы должны использовать ветвь 2.x. Это позже делает слияние ваших изменений намного проще, так как Github не позволяет вам редактировать цель ветви:

# fixing a bug on 3.x
git fetch upstream
git checkout -b ticket-1234 upstream/master

# fixing a bug on 2.x
git fetch upstream
git checkout -b ticket-1234 upstream/2.x

Совет

Использовать описательное имя для своей ветки, ссылающееся на билет или имя функции - хорошее правило. Например имя: билет-1234, особенно-хорошее

Вышеупомянутое создаст локальную ветвь, основанную на верхнюю ветку CakePHP 2.x. Поработайте над своим исправлением и сделайте так много коммитов, сколько вам нужно; но имейте в виду следующий:

Следуйте Стандарты кодирования.

Добавьте тестовый пример, чтобы показать, что ошибка исправлена, или что новая функция работает.

Держите свои коммиты логичными и пишите хорошие четкие и сжатые сообщения о фиксации.

Отправка Pull Request

Как только ваши изменения будут выполнены, и вы будете готовы к их объединению с CakePHP, вы должны обновить свою ветку:

# Rebase fix on top of master
git checkout master
git fetch upstream
git merge upstream/master
git checkout <branch_name>
git rebase master

Это приведет к слиянию + объединению при любых изменениях, произошедших в CakePHP, с того момента, как вы начали. Затем он будет перезагружен - или повторите ваши изменения поверх текущего кода. Вы можете столкнуться с конфликтом во время rebase. Если rebase завершит работу раньше, то вы сможете увидеть какие файлы конфликтуются/не объединяются использовав команду git status. Разрешите каждый конфликт, а затем продолжите rebase:

git add <filename> # do this for each conflicted file.
git rebase --continue

Убедитесь, что все ваши тесты продолжают успешно проходить. Затем нажмите(push) для вашего форка:

git push origin <branch-name>

Если вы переустановили, нажав на свою ветку, вам понадобится принудительное нажатие:

git push --force origin <branch-name>

Как только ваша ветвь попадёт в GitHub, вы сможете отправить запрос на выгрузку на GitHub.

Выберите где будут объединены ваши изменения

Перед выполнением pull запросов, вы должны убедиться, что вы выбрали правильную базу, иначе вы не сможете изменить это после создания запроса pull request.

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

Если ваше изменение является новой функцией или дополнением к структуре, то вам следует выбрать ветку со следующим номером версии. Например, если текущая стабильная версия - 3.2.10, то ветвь принимающая новые функции, должна быть 3.next.

Если ваше изменение нарушает существующие функциональные возможности или API, тогда вы можете выбрать следующий крупный выпуск. Например, если текущий выпуск 3.2.2, то в следующий раз, когда текущее поведение может не быть нарушено, будет 4.x, поэтому вы должны настроить ссылку на эту ветку.

Примечание

Помните, что весь код, который вы вносите в CakePHP, будет лицензирован в соответствии с лицензией MIT и Cake Software Foundation [http://cakefoundation.org/pages/about] станет владельцем любого внесенного кода. Авторы должны следовать CakePHP Community Guidelines [http://community.cakephp.org/guidelines].

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

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Стандарты кодирования

Разработчики CakePHP будут использовать руководство по стилю кодирования PSR-2 [http://www.php-fig.org/psr/psr-2/] в дополнение к следующим правилам стандартов кодирования.

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

Вы можете использовать CakePHP Code Sniffer [https://github.com/cakephp/cakephp-codesniffer], чтобы проверить, что ваш код соответствует требованиям стандартов.

Добавление новых функций

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

Установка IDE

Убедитесь, что ваша среда IDE настроена на «trim right» - обрезку пробелов с права. В строке не должно быть лишних пробелов.

Большинство современных IDE также поддерживают файл .editorconfig. CakePHP содержит скелет приложения, который отправляется с ним по умолчанию. Он уже содержит настройки по умолчанию.

Мы рекомендуем использовать плагин IdeHelper [https://github.com/dereuromark/cakephp-ide-helper], если вы хотите максимизировать совместимость с IDE. Это поможет сохранить обновленные аннотации, которые сделают IDE полностью понимающим, как работают все классы вместе и обеспечит лучшую подсказку и автозаполнение.

Отступ

Для отступов будут использоваться четыре пробела.

Итак, отступы должны выглядеть так:

// base level
    // level 1
        // level 2
    // level 1
// base level

Или:

$booleanVariable = true;
$stringVariable = 'moose';
if ($booleanVariable) {
    echo 'Boolean value is true';
    if ($stringVariable === 'moose') {
        echo 'We have encountered a moose';
    }
}

В случаях, когда вы используете вызов многострочной(multi-line) функции, используйте следующие методические рекомендации:

Открытая скобка многострочной функции должна быть последним элементом на строке.

На одной строке разрешён только один аргумент, при вызове многострочной функции.

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

В качестве примера, вместо использования следующего форматирования:

$matches = array_intersect_key($this->_listeners,
                array_flip(preg_grep($matchPattern,
                    array_keys($this->_listeners), 0)));

Используйте такое форматирование:

$matches = array_intersect_key(
    $this->_listeners,
    array_flip(
        preg_grep($matchPattern, array_keys($this->_listeners), 0)
    )
);

Длина строки

Рекомендуется придерживаться длины линии около 100 символов, для лучшей читаемости кода. Ограничение в 80 или 120 символов заставляет разносить сложную логику или выражения по функциям, а также давать функциям и объектам более короткие, ёмкие и выразительные имена. Длина строки не должна превышать 120 символов.

Вкратце:

100 символов - это мягкий предел.

120 символов - это жёсткий предел.

Управляющие структуры

Управляющие структуры например «if», «for», «foreach», «while», «switch» и т.п.. Ниже приведён пример с «if»:

if ((expr_1) || (expr_2)) {
    // action_1;
} elseif (!(expr_3) && (expr_4)) {
    // action_2;
} else {
    // default_action;
}

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

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

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

Внутренние назначения не должны использоваться внутри структур управления

// неправильно = нет фигурных скобок, плохо размещенное заявление
if (expr) statement;

// неправильно = нет фигурных скобок
if (expr)
    statement;

// хорошо
if (expr) {
    statement;
}

// неправильно = встроенное задание
if ($variable = Class::function()) {
    statement;
}

// хорошо
$variable = Class::function();
if ($variable) {
    statement;
}

Тернарный оператор

Тернарные операторы допустимы, если вся тернарная операция проходит на одной строке. Более длинные тернарные операторы должны быть разделены с использованием if else. Тернарные операторы не должны быть вложенными. По желанию можно использовать круглые скобки, при проверке состояния тернартного оператора, для большей ясности:

// Хорошо, просто и читабельно
$variable = isset($options['variable']) ? $options['variable'] : true;

// Плохо - вложенные тернарные операции
$variable = isset($options['variable']) ? isset($options['othervar']) ? true : false : false;

Файлы шаблонов

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

<?php
if ($isAdmin):
    echo '<p>You are the admin user.</p>';
endif;
?>
<p>The following is also acceptable:</p>
<?php if ($isAdmin): ?>
    <p>You are the admin user.</p>
<?php endif; ?>

Сравнение

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

Для тестирования, если переменная равна нулю, рекомендуется использовать строгую проверку:

if ($value === null) {
    // ...
}

Значение для проверки должно быть размещено с правой стороны:

// не рекомендуется
if (null === $this->foo()) {
    // ...
}

// рекомендуется
if ($this->foo() === null) {
    // ...
}

Вызовы функций

Функции следует вызывать без пробела между именем функции и началом скобки. Между каждым параметром функции должен быть один пробел:

$var = foo($bar, $bar2, $bar3);

Как вы могли увидеть выше, должен быть один пробел по обе стороны от знака равенства (=).

Определение метода

Пример определения метода:

public function someFunction($arg1, $arg2 = '')
{
    if (expr) {
        statement;
    }

    return $var;
}

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

public function connection($dns, $persistent = false)
{
    if (is_array($dns)) {
        $dnsInfo = $dns;
    } else {
        $dnsInfo = BD::parseDNS($dns);
    }

    if (!($dnsInfo) || !($dnsInfo['phpType'])) {
        return $this->addError();
    }

    return true;
}

На обеих сторонах знака равенства есть пробелы.

Тип подсказки

Аргументы, которые ожидают объекты, массивы или обратные вызовы (callable), могут быть определены в соответсвии с их типом. Однако мы вводим их только для общедоступных методов, поскольку typehinting не является „cost-free“:

/**
 * Some method description.
 *
 * @param \Cake\ORM\Table $table The table class to use.
 * @param array $array Some array value.
 * @param callable $callback Some callback.
 * @param bool $boolean Some boolean value.
 */
public function foo(Table $table, array $array, callable $callback, $boolean)
{
}

Здесь $table должен быть экземпляром от \Cake\ORM\Table, $array должен быть от array и callback должны иметь тип callable (валидный обратный вызов).

Обратите внимание: если вы хотите разрешить $array также быть экземпляром \ArrayObject вы не должны описывать typehint, поскольку array принимает только примитивный тип:

/**
 * Some method description.
 *
 * @param array|\ArrayObject $array Some array value.
 */
public function foo($array)
{
}

Анонимные функции (Замыкания)

Определение анонимных функций следует из PSR-2 [http://www.php-fig.org/psr/psr-2/] руководства по стилю кодирования, где они объявленны пробелом после ключевого слова function, и пробелом до и после ключевого слова use:

$closure = function ($arg1, $arg2) use ($var1, $var2) {
    // code
};

Цепочки методов

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

$email->from('foo@example.com')
    ->to('bar@example.com')
    ->subject('A great message')
    ->send();

Комментарии в коде

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

Комментарии могут включать следующие теги phpDocumentor [http://phpdoc.org]:

@author [http://phpdoc.org/docs/latest/references/phpdoc/tags/author.html]

@copyright [http://phpdoc.org/docs/latest/references/phpdoc/tags/copyright.html]

@deprecated [http://phpdoc.org/docs/latest/references/phpdoc/tags/deprecated.html] Использование формата @version <vector> <description>, где version и description являются обязательными. Версия относится к той, которая уже устарела.

@example [http://phpdoc.org/docs/latest/references/phpdoc/tags/example.html]

@ignore [http://phpdoc.org/docs/latest/references/phpdoc/tags/ignore.html]

@internal [http://phpdoc.org/docs/latest/references/phpdoc/tags/internal.html]

@link [http://phpdoc.org/docs/latest/references/phpdoc/tags/link.html]

@see [http://phpdoc.org/docs/latest/references/phpdoc/tags/see.html]

@since [http://phpdoc.org/docs/latest/references/phpdoc/tags/since.html]

@version [http://phpdoc.org/docs/latest/references/phpdoc/tags/version.html]

Теги PhpDoc очень похожи на теги JavaDoc в Java. Теги обрабатываются только в том случае, если они являются первыми в строке DocBlock, например:

/**
 * Tag example.
 *
 * @author this tag is parsed, but this @version is ignored
 * @version 1.0 this tag is also parsed
 */

/**
 * Example of inline phpDoc tags.
 *
 * This function works hard with foo() to rule the world.
 *
 * @return void
 */
function bar()
{
}

/**
 * Foo function.
 *
 * @return void
 */
function foo()
{
}

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

Типы переменных

Типы переменных использующихся в DocBlocks:

Type

Описнаие

mixed

Переменная с неопределенным (или множественным) типом.

int

Целочисленная переменная типа (целое число).

float

Плавающий тип (point number).

bool

Логический тип (true или false).

string

Строковый тип (любые значения в » » или „ „).

null

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

array

Массив.

object

Object (объект). При необходимости следует использовать определенное имя класса.

resource

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

callable

Callable функция.

Вы также можете комбинировать типы, используя символ „|“:

int|bool

Для более чем двух типов обычно лучше всего использовать mixed.

При возврате самого объекта, например. для цепочки, следует использовать вместо этого $this:

/**
 * Foo function.
 *
 * @return $this
 */
public function foo()
{
    return $this;
}

Подключаемые файлы

include, require, include_once и require_once не должны иметь круглых скобок:

// ошибка = круглые скобки
require_once('ClassFileName.php');
require_once ($class);

// хорошо = нет круглых скобок
require_once 'ClassFileName.php';
require_once $class;

При включении файлов с классами или библиотеками используйте вегда только функцию require_once [http://php.net/require_once].

PHP теги

Всегда используйте длинные теги (<?php ?>) Вместо коротких тегов (<? ?>). Короткие echo должны использоваться в файлах шаблонов (.ctp), где это необходимо.

Короткое echo

Короткое эхо должно использоваться в файлах шаблонов вместо <?php echo. После <?= должен следовать один пробел, потом имя переменной или функции, снова один пробел и закрывающий тег ?> php:

// неправильно = есть точка с запятой, нет пробелов
<td><?=$name;?></td>

// хорошо = есть пробелы, нет точки с запятой
<td><?= $name ?></td>

Начиная с PHP 5.4 короткий echo тег (<?=) больше не следует рассматривать как „короткий тег“. Теперь он всегда доступен, независимо от значения short_open_tag директивы ini.

Соглашение об именовании

Функции

Писать все имена функций нужно используя синтаксис camelBack:

function longFunctionName()
{
}

Классы

Писать все классы нужно используя синтаксис CamelCase, для примера:

class ExampleClass
{
}

Переменные

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

Пример:

$user = 'John';
$users = ['John', 'Hans', 'Arne'];

$dispatcher = new Dispatcher();

Видимость элементов

Используйте PHP public, protected и private ключевые слова для методов и переменных.

Примеры адресов

Для всех примеров URL и почтовых адресов используйте «example.com», «example.org» и «example.net», например:

Email: someone@example.com

WWW: http://www.example.com

FTP: ftp://ftp.example.com

Для этого зарезервировано доменное имя «example.com» (см. RFC 2606 [https://tools.ietf.org/html/rfc2606.html]) и рекомендуется для использования в документации или в качестве примеров.

Файлы

Имена файлов, которые не содержат классов, должны быть уменьшены и подчеркнуты, например:

long_file_name.php

Смена ролей

Для смены ролей используйте:

Type

Description

(bool)

Сменить на boolean.

(int)

Сменить на integer.

(float)

Сменить на float.

(string)

Сменить на string.

(array)

Сменить на array.

(object)

Сменить на object.

Используйте (int)$var вместо intval($var) и (float)$var вместо floatval($var), если есть такая возможность.

Константы

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

define('CONSTANT', 1);

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

define('LONG_NAMED_CONSTANT', 2);

Осторожность при использовании empty()/isset()

Хотя empty() - простая в использовании функция, она может маскировать ошибки и вызывать непреднамеренные эффекты, когда даны '0' и 0. Когда переменные или свойства уже определены, использование empty() не рекомендуется. При работе с переменными лучше полагаться на приведение типа к логическому, вместо empty():

function manipulate($var)
{
    // Не рекомендуется, $var уже определен в области
    if (empty($var)) {
        // ...
    }

    // Корректно использовать тип boolean
    if (!$var) {
        // ...
    }
    if ($var) {
        // ...
    }
}

Когда вы имеете дело с определенными свойствами, вы должны использовать null для проверки empty()/isset():

class Thing
{
    private $property; // Определено

    public function readProperty()
    {
        // Не рекомендуется, поскольку свойство определено в классе
        if (!isset($this->property)) {
            // ...
        }
        // Рекомендуется
        if ($this->property === null) {

        }
    }
}

При работе с массивами лучше объединять значения по умолчанию с использованием проверок empty(). Объединив значения по умолчанию, вы можете гарантировать, что требуемые ключи определены:

function doWork(array $array)
{
    // Объединить значения по умолчанию, чтобы удалить необходимость в пустых проверках.
    $array += [
        'key' => null,
    ];

    // Не рекомендуется, как уже было сказанно
    if (isset($array['key'])) {
        // ...
    }

    // Рекомендуется
    if ($array['key'] !== null) {
        // ...
    }
}

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Руководство по совместимости

Мы обеспечиваем то, чтобы вы могли легко и плавно обновлять свои приложения. Вот почему мы нарушаем совместимость на основных этапах релиза. Вы можете быть знакомы с семантическим версированием [https://semver.org/lang/ru/], это общее руководство, которое мы используем для всех проектов CakePHP. Короче говоря, семантическое управление версиями означает, что только основные выпуски (например, 2.0, 3.0, 4.0) могут иметь обратную совместимость. Незначительные релизы („Minor releases“)(например, 2.1, 3.1, 3.2), добавляющие функциональность, могут нарушать обратную совместимость. Исправления ошибок („Bug fix releases“)(например, 2.1.2, 3.0.1) не добавляют новых функций, но исправляют ошибки или повышают производительность.

Примечание

Устаревшее („Deprecations“) удаляется со следующей основной версией фреймворка. Вам рекомендуется на раннем этапе адаптировать ваш код уже к каждому незначительному релизу, как указано в комментарии к устареванию и руководстве по миграции.

Чтобы уточнить, какие изменения вы можете ожидать в каждом следующем выпуске, мы публикуем множнество подробной информации для разработчиков, использующих CakePHP, а также для разработчиков, работающих над CakePHP. Она помогает оправдать ожидания того, что можно сделать в небольших выпусках. Главные релизы („Major releases“) будут иметь наибольшее число изменений.

Руководства по миграции

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

Использование CakePHP

Если вы создаёте своё приложение с помощью CakePHP, следующие рекомендации объяснят стабильность, которую вы можете ожидать.

Интерфейсы

Вне основных выпусков („мajor releases“), интерфейсы предоставляемые CakePHP, не будут содержать существенно изменившиеся методы. Могут быть добавлены новые методы, но никакие существующие методы не будут изменен.

Классы

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

Примечание

Некоторые классы в CakePHP помечены тегом API doc @internal. Эти классы не являются стабильными и не имеют обещаний обратной совместимости.

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

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

If you…

Backwards compatibility?

Typehint against the class

Yes

Create a new instance

Yes

Extend the class

Yes

Access a public property

Yes

Call a public method

Yes

Extend a class and…

Override a public property

Yes

Access a protected property

No 1

Override a protected property

No 1

Override a protected method

No 1

Call a protected method

No 1

Add a public property

No

Add a public method

No

Add an argument to an overridden method

No 1

Add a default argument value to an existing method argument

Yes

Работающим над CakePHP

Если вы помогаете сделать CakePHP еще лучше, соблюдайте следующие рекомендации при добавлении/изменении функциональности:

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

In a minor release can you…

Classes

Remove a class

No

Remove an interface

No

Remove a trait

No

Make final

No

Make abstract

No

Change name

Yes 2

Properties

Add a public property

Yes

Remove a public property

No

Add a protected property

Yes

Remove a protected property

Yes 3

Methods

Add a public method

Yes

Remove a public method

No

Add a protected method

Yes

Move to parent class

Yes

Remove a protected method

Yes 3

Reduce visibility

No

Change method name

Yes 2

Add a new argument with default value

Yes

Add a new required argument to an existing method.

No

Remove a default value from an existing argument

No

Change method type void

Yes

1(1,2,3,4,5)

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

2(1,2)

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

3(1,2)

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

Устаревшее

В каждом незначительном выпуске функции могут быть устаревшими. Если функции устарели, будет добавлена документация API и предупреждения времени выполнения. Ошибки выполнения помогают вам найти код, который необходимо обновить до его „смерти“. Если вы захотите отключить ошибки времени выполнения, вы можете сделать это с помощью конфигурации Error.errorLevel:

// in config/app.php
// ...
'Error' => [
    'errorLevel' => E_ALL ^ E_USER_DEPRECATED,
]
// ...

Отключит предупреждения об устаревании во время выполнения.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Установка

CakePHP довольно прост и легок в установке. Минимальные требования - веб-сервер и копия CakePHP, это все что Вам нужно! Этот раздел сосредоточен на установке CakePHP на Apache (поскольку он прост в установке и настройке), но CakePHP может также работать и с другими веб-серверами, как Nginx, LightHTTP или Microsoft IIS.

Требования к установке

HTTP сервер. Например: Apache. Предпочтительно с mod_rewrite, но необязательно.

PHP 5.6 или выше (включая PHP 7.1).

Расширение mbstring для PHP

Расширение intl для PHP

Примечание

В XAMPP и WAMP, расширение mbstring работает по умолчанию.

В XAMPP, расширение intl установлено, но не включено, чтобы включить intl Вам необходимо раскомментировать extension=php_intl.dll в файле php.ini и перезапустить сервер, используя XAMPP панель.

В WAMP, расширение intl установлено, но не включено. Чтобы включить intl Вам необходимо перейти в директорию в которой расположен php C:\wamp\bin\php\php{version}, скопируйте все файлы, которые выглядят как icu*.dll и вставьте а директорию apachebin C:\wamp\bin\apache\apache{version}\bin. После этого перезапустите сервер.

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

MySQL (5.1.10 или выше)

PostgreSQL

Microsoft SQL Server (2008 или выше)

SQLite 3

Примечание

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

Установка CakePHP

Прежде чем вы начнете установку убедитесь что у Вас установлена необходимая минимальная версия PHP (5.6.0):

php -v

У Вас, по крайней мере, должен быть установлен PHP 5.6 (CLI) или выше. Ваш веб-сервер так же должен иметь версию PHP 5.6 или выше, и версия интерфейса командной строки PHP (CLI) 5.6.0 или выше.

Установка Composer

CakePHP использует мендежер зависимостей PHP Composer [http://getcomposer.org], как официально поддерживаемый метод установки.

Установка Composer на Linux и macOS

Запустите скрипт установки, как описано в официальной документации Composer [https://getcomposer.org/download/] и следуйте инструкциям по установке, чтобы установить Composer.

Выполните следующую команду, чтобы переместить composer.phar в директорию по адресу:

mv composer.phar /usr/local/bin/composer

Установка Composer на Windows

Для установки Composer на Windows, вы можете скачать установщик Composer для Windows здесь [https://github.com/composer/windows-setup/releases/]. Более подробные инструкции по установке Composer на Windows можно найти в README здесь [https://github.com/composer/windows-setup].

Создание проекта CakePHP

Теперь, когда вы скачали и установили Composer, предположим вы хотите создать новое приложение CakePHP в папке my_app_name. Для этого просто выполните следующую команду composer:

php composer.phar create-project --prefer-dist cakephp/app:^3.8 my_app_name

Или если Composer установлен глобально:

composer self-update && composer create-project --prefer-dist cakephp/app:^3.8 my_app_name

Как только Composer закончит скачивание каркаса приложения и библиотеки ядра CakePHP, у вас должно появиться полностью работоспособное приложение CakePHP. Убедитесь в том, чтобы в папке вашего приложения всегда были файлы composer.json и composer.lock.

Вы можете перейти по тому пути, куда установлено ваше приложение CakePHP и вы увидите стандартную домашнюю страницу. Чтобы изменить ее содержимое, вы можете отредактировать файл src/Template/Pages/home.ctp.

Несмотря на то, что composer является рекомендуемым методом установки, имеются также готовые сборки доступные для скачивания на Github [https://github.com/cakephp/cakephp/tags]. Эти сборки содержат каркас приложения со всеми установленными пакетами. Также они содержат файл composer.phar, таким образом вы получаете все необходимое для дальнейшей работы.

Обновление CakePHP до актуальной версии

По умолчанию в файле composer.json вашего приложения присутствуют следующие настройки:

"require": {
    "cakephp/cakephp": "3.4.*"
}

Каждый раз при запуске команды php composer.phar update вы будете получать патчи для данной минорной версии. Вы все же можете изменить это значение на ~3.4 для получения последней стабильной версии ветки 3.x.

Если вы хотите получать позднейшие изменения в CakePHP, еще не вошедшие в релиз, установите значение версии dev-master в файле composer.json:

"require": {
    "cakephp/cakephp": "dev-master"
}

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

Установка с помощью Oven

Еще один быстрый способ установки CakePHP это Oven [https://github.com/CakeDC/oven]. Это простой PHP-скрипт, который определяет, что требуется скачать, уcтанавливает каркас приложения CakePHP и выполняет все необходимые предварительные настройки.

После завершения установки вы также получите работоспособное приложение CakePHP.

Примечание

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

Права доступа

CakePHP использует папку tmp для ряда различных операций. Описания модели, кешированные виды, сессионные данные - это лишь некоторые примеры. Папка logs используется для записи лог-файлов стандартным движком FileLog.

Поэтому убедитесь, что в вашем приложении CakePHP для папок logs, tmp и всех их подпапок установлены разрешения для записи данных пользователями. Composer при установке автоматически устанавливает правана запись для папки tmp и ее подпапок для большего удобства, но вы всегда можете перенастроить эти параметры в случае необходимости.

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

HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\  -f1`
setfacl -R -m u:${HTTPDUSER}:rwx tmp
setfacl -R -d -m u:${HTTPDUSER}:rwx tmp
setfacl -R -m u:${HTTPDUSER}:rwx logs
setfacl -R -d -m u:${HTTPDUSER}:rwx logs

Чтобы воспользоваться инструментами консоли CakePHP, вы должны убедиться, что файл bin/cake работоспособен. На *nix или macOS вы можете выполнить команду:

chmod +x bin/cake

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

Если же по какой-либо причине вы не можете изменить разрешения для файла bin/cake, можете запустить консоль CakePHP командой:

php bin/cake.php

Встроенный веб-сервер PHP

Встроенный веб-сервер PHP - это самый быстрый способ настройки CakePHP. В данном примере мы будем использовать консоль CakePHP для запуска встроенного веб-сервера PHP, который сделает ваше приложение доступным по адресу следующего вида - http://host:port. Из папки приложения выполните:

bin/cake server

Без передачи дополнительных параметров эта команда сделает ваше приложение доступным по адресу http://localhost:8765/.

Если у вас имеется что-нибудь конфликтующее с именем localhost или портом 8765, вы можете указать в консоли CakePHP запустить веб-сервер на определенном хосте и/или номере порта используя следующие параметры:

bin/cake server -H 192.168.13.37 -p 5673

Это сделает ваше приложение досупным по адресу http://192.168.13.37:5673/.

Вот и все - ваше приложение работает, и нет необходимости в настройке веб-сервера.

Предупреждение

Встроенный веб-сервер никогда не следует использовать в продакшене. Он предназначается только для тестирования приложения в процессе разработки.

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

Полноценная установка

Полноценная установка - это более гибкий способ настройки CakePHP. Его использование позволяет всему домену действовать как единому приложению CakePHP. Данный пример поможет вам установить CakePHP в вашей файловой системе, и сделать его доступным по адресу http://www.example.com. Учтите, что вам могут понадобиться права для изменения DocumentRoot на веб-серверах Apache.

После установки вашего приложения одним из вышеперечисленных способов в выбранную вами папку - мы предположим, что это будет /cake_install - структура вашего приложения будет следующей:

/cake_install/
    bin/
    config/
    logs/
    plugins/
    src/
    tests/
    tmp/
    vendor/
    webroot/ (папка, соответствующая опции DocumentRoot)
    .gitignore
    .htaccess
    .travis.yml
    composer.json
    index.php
    phpunit.xml.dist
    README.md

Разработчики, использующие Apache, должны установить директиву DocumentRoot для домена в следующее значение:

DocumentRoot /cake_install/webroot

Если ваш веб-сервер настроен корректно, вы должны теперь иметь доступ к вашему приложению по адресу http://www.example.com.

Запускайте

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

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

Переопределение URL

Apache

Хотя CakePHP создан для работы с mod_rewrite «из коробки» - и обычно это так и есть - мы заметили, что некоторые пользователи пытаются получить все для хорошей работы в их системах.

Вот несколько вещей, которые вы можете попробовать сделать, чтобы он работал правильно. Первый взгляд на ваш httpd.conf. (Убедитесь, что вы редактируете системный httpd.conf,а не пользовательский или определенный на сайте httpd.conf.)

Эти файлы могут различаться в разных дистрибутивах и версиях Apache. Вы Может также посмотреть http://wiki.apache.org/httpd/DistrosDefaultLayout для более подробной информации.

Убедитесь, что переопределение .htaccess разрешено и, что AllowOverride установлен в значении All для корректного DocumentRoot. Вы должны наблюдать нечто похожее на:

# Каждая папка, к которой Apache имеет доступ, может быть настроена
# в отношении того, какие сервисы и возможности разрешены и/или запрещены в той
# папке (и ее подпапках).
#
# Для начала, мы настроим параметры "по умолчанию" довольно ограниченными
# возможностями.
<Directory />
    Options FollowSymLinks
    AllowOverride All
#    Order deny,allow
#    Deny from all
</Directory>

Убедитесь, что вы загружаете mod_rewrite корректно. Вы должны видеть нечто подобное:

LoadModule rewrite_module libexec/apache2/mod_rewrite.so

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

После внесения правок перезапустите Apache, чтобы настройки вступили в силу.

Убедитесь, что ваши файлы .htaccess находятся в правильных папках. Некоторые операционные системы скрывают файлы, имена которых начинаются с „.“ и таким образом не позволяют копировать их.

Убедитесь, что ваша копия CakePHP получена из нашего Git-репозитория, и корректно распакована, проврив файлы .htaccess.

Папка app CakePHP (будет скопирована в корневую папку вашего приложения консолью bake):

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteRule    ^$    webroot/    [L]
    RewriteRule    (.*) webroot/$1    [L]
</IfModule>

Папка webroot CakePHP (будет скопирована в корневую папку вашего приложения консолью bake):

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

Если у вашего сайта CakePHP все еще есть проблемы с mod_rewrite, попробуйте изменить настройки Virtual Hosts. На Ubuntu, отредактируйте файл /etc/apache2/доступные-сайты/default (расположение зависит от дистрибутива). В этом файле убедиесь, что опция AllowOverride None изменена на AllowOverride All:

<Directory />
    Options FollowSymLinks
    AllowOverride All
</Directory>
<Directory /var/www>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order Allow,Deny
    Allow from all
</Directory>

На macOS, другое решение - это использовать инструмент virtualhostx [http://clickontyler.com/virtualhostx/], чтобы заставить Virtual Host ссылаться на вашу папку.

Для многих хостингов (GoDaddy, 1and1) ваш веб-сервер изначально обслуживается из папки пользователя, которая изначально использует mod_rewrite. Если вы устанавливаете CakePHP в папку пользователя (http://example.com/~username/cakephp/), или любой другой URL, уже использующий mod_rewrite, вам понадобится добавить блоки RewriteBase в файлы .htaccess, используемые CakePHP (.htaccess, webroot/.htaccess).

Это может быть добавлено в ту же секцию, что и директива RewriteEngine. Так, к примеру ваш файл .htaccess папки webroot может выглядеть так:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /путь/к/app
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

Более детальный разбор необходимых изменений будет зависеть от вашей конкретной установки, и может потребоваться указание параметров, не связанных непосредственно с CakePHP. Для более подробной информации пожалуйста ознакомьтесь с онлайн-документацией Apache.

(Опционально) Чтобы улучшить настройки продакшена, вы должны предотвращать обработку фреймворком CakePHP несуществующих ресурсов. Измените файл .htaccess папки webroot например вот так:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /путь/к/app/
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_URI} !^/(webroot/)?(img|css|js)/(.*)$
    RewriteRule ^ index.php [L]
</IfModule>

Данные настройки предотвратят передачу некорректных параметров файлу index.php и в случае необходимости вернут 404 ошибку.

В дополнение к этому вы можете создать шаблон страницы для 404 ошибки, или же можете использовать встроенный в CakePHP шаблон, добавив директиву ErrorDocument:

ErrorDocument 404 /404-not-found

nginx

nginx не использует файлы .htaccess как Apache, так что необходимо создавать эти переопределенные URL в конфигурации сайта. Обычно все это находится в /etc/nginx/доступные-сайты/файл_конфигурации_вашего_виртуального_хоста. В зависимости от ваших настроек, вам возможно придется внести некоторые правки в данные параметры, но как правило небольшие, вам понадобится PHP на FastCGI:

server {
    listen   80;
    server_name www.example.com;
    rewrite ^(.*) http://example.com$1 permanent;
}

server {
    listen   80;
    server_name example.com;

    # директива root должна быть глобальной
    root   /var/www/example.com/public/webroot/;
    index  index.php;

    access_log /var/www/example.com/log/access.log;
    error_log /var/www/example.com/log/error.log;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include /etc/nginx/fastcgi_params;
        fastcgi_pass    127.0.0.1:9000;
        fastcgi_index   index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

На некоторых серверах (таких как Ubuntu 14.04) приведенная выше конфигурация не будет работать «из коробки», и документация nginx рекомендует иной подход (http://nginx.org/en/docs/http/converting_rewrite_rules.html). Попробуйте нижеприведенные настройки (вы можете заметить, что в данном случае используется всего один блок server {}, а не два, как бы там ни было, если вы хотите иметь доступ к вашему приложению CakePHP еще и с адреса example.com помимо www.example.com, ознакомьтесь с документацией по ссылке выше):

server {
    listen   80;
    server_name www.example.com;
    rewrite 301 http://www.example.com$request_uri permanent;

    # директива root должна быть глобальной
    root   /var/www/example.com/public/webroot/;
    index  index.php;

    access_log /var/www/example.com/log/access.log;
    error_log /var/www/example.com/log/error.log;

    location / {
        try_files $uri /index.php?$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include /etc/nginx/fastcgi_params;
        fastcgi_pass    127.0.0.1:9000;
        fastcgi_index   index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

IIS7 (Windows хостинги)

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

Используйте Microsoft Web Platform Installer [http://www.microsoft.com/web/downloads/platform.aspx] для установки URL Rewrite Module 2.0 [http://www.iis.net/downloads/microsoft/url-rewrite] или скачайте его (32-bit [http://www.microsoft.com/en-us/download/details.aspx?id=5747] / 64-bit [http://www.microsoft.com/en-us/download/details.aspx?id=7435]).

Создайте новый файл web.config в вашей корневой папке CakePHP.

Используя Блокнот или любой XML-safe редактор, скопируйте следующий код в ваш новый файл web.config:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="Exclude direct access to webroot/*"
                  stopProcessing="true">
                    <match url="^webroot/(.*)$" ignoreCase="false" />
                    <action type="None" />
                </rule>
                <rule name="Rewrite routed access to assets(img, css, files, js, favicon)"
                  stopProcessing="true">
                    <match url="^(img|css|files|js|favicon.ico)(.*)$" />
                    <action type="Rewrite" url="webroot/{R:1}{R:2}"
                      appendQueryString="false" />
                </rule>
                <rule name="Rewrite requested file/folder to index.php"
                  stopProcessing="true">
                    <match url="^(.*)$" ignoreCase="false" />
                    <action type="Rewrite" url="index.php"
                      appendQueryString="true" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

Как только файл web.config будет создан с корректными правилами переопределения, все ссылки на CSS-стили, JavaScript и перенаправление маршрутов CakePHP должны работать корректно.

Я не могу использовать переопределение URL

Если вы не хотите или не можете активировать модуль mod_rewrite (или модуль совместимый с ним) на вашем сервере, вы можете использовать встроенные возможности CakePHP. В файле config/app.php раскомментируйте строку:

'App' => [
    // ...
    // 'baseUrl' => env('SCRIPT_NAME'),
]

И удалите эти файлы .htaccess:

/.htaccess
webroot/.htaccess

Это заставит ваши URL выглядеть как www.example.com/index.php/controllername/actionname/param вместо www.example.com/controllername/actionname/param.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Конфигурация

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

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

Настройка вашего приложения

Конфигурация обычно хранится в PHP или INI файлах и загружается во время загрузки. CakePHP поставляется с одним файлом конфигурации по умолчанию, но при необходимости вы можете добавлять дополнительные файлы конфигурации и загружать их в код начальной загрузки вашего приложения. Cake\Core\Configure используется для глобальной конфигурации, а классы типа Cache предоставляют config() методы, чтобы сделать конфигурацию простой и прозрачной.

Загрузка дополнительных файлов конфигурации

Если ваше приложение имеет множество параметров конфигурации, может быть полезно разделить конфигурацию на несколько файлов. После создания каждого из файлов в вашем каталоге config/, вы можете загрузить их в bootstrap.php:

use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;

Configure::config('default', new PhpConfig());
Configure::load('app', 'default', false);
Configure::load('other_config', 'default');

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

Общая конфигурация

Ниже приведено описание переменных и их влияние на приложение CakePHP.

debug

Изменяет вывод отладки CakePHP. false = Режим производства. Не отображаются сообщения об ошибках, ошибки или предупреждения. true = показаны ошибки и предупреждения.

App.namespace

Пространство имён для поиска классов приложений.

Примечание

При изменении пространства имён в вашей конфигурации, вам также понадобится обновить файл composer.json, чтобы использовать это пространство имён. Кроме того, создайте новый автозагрузчик, запустив php composer.phar dumpautoload.

App.baseUrl

Не раскомментируйте это определение, если не планируете использовать Apache mod_rewrite с CakePHP. Не забудьте также удалить файлы .htaccess.

App.base

Базовый каталог, в котором находится приложение. Если false, то он будет обнаружен автоматически. Если не false, убедитесь, что ваша строка начинается с / и не заканчивается символом /. Например, /basedir является допустимым App.base. В противном случае AuthComponent не будет работать должным образом.

App.encoding

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

App.webroot

Каталог webroot.

App.wwwRoot

Путь к webroot.

App.fullBaseUrl

Полноценное доменное имя (включая протокол) к корню вашего приложения. Это используется при создании абсолютных URL-адресов. По умолчанию это значение генерируется с использованием среды $_SERVER. Однако вы должны определить его вручную для оптимизации производительности или если вас беспокоят люди, манипулирующие заголовком Host. В контексте CLI (из оболочек) fullBaseUrl не может быть прочитан из $_SERVER, так как нет веб-сервера. Вам нужно указать его самостоятельно, если вам нужно создать URL-адреса из оболочки (например, при отправке писем).

App.imageBaseUrl

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

App.cssBaseUrl

Веб-путь к каталогу общих css в webroot. Если вы используете CDN, вы должны установить это значение для местоположения CDN.

App.jsBaseUrl

Веб-путь к каталогу общих js в webroot. Если вы используете CDN, вы должны установить это значение для местоположения CDN.

App.paths

Настроить пути для ресурсов, не связанных с классом. Поддерживает разделы plugins, templates, locales, которые позволяют определять пути для плагинов, просматривать шаблоны и файлы локали соответственно.

Security.salt

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

Asset.timestamp

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

(bool) false - Не делает ничего (по умолчанию)

(bool) true - Добавляет метку времени, когда debug является true

(string) „force“ - Всегда добавляет метку времени.

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

См. раздел Настройка БД для получения информации о настройке соединений с базой данных.

Конфигурация кэширования

См. раздел Настройка кэширования для получения информации о настройке кэширования.

Конфигурация ошибок и исключений

См. раздел Ошибки и настройка исключений для получения информации о настройке ошибок и обработчиков исключений.

Конфигурация логирования

См. раздел Logging Configuration для получения информации о настройке ведения журнала в CakePHP.

Настройка электронной почты

См. раздел Настройка эл. почты для получения информации о настройке пресетов электронной почты в CakePHP.

Настройка сеанса(сессии)

См. раздел Настройка сессии для получения информации о настройке обработки сеанса в CakePHP.

Конфигурация маршрутизации

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

Дополнительные пути классов

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

"autoload": {
    "psr-4": {
        "App\\Controller\\": "/path/to/directory/with/controller/folders/",
        "App\\": "src/"
    }
}

Вышеописанное установило бы пути как для пространства имен App, так и App\Controller. Будет проверен первый ключ, и если этот путь не содержит класс/файл, будет проверен второй ключ. Вы также можете сопоставить одно пространство имен с несколькими каталогами следующими образом:

"autoload": {
    "psr-4": {
        "App\\": ["src/", "/path/to/directory/"]
    }
}

Плагин, Шаблон просмотра и локальные пути

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

return [
    // Дополнительная конфигурация
    'App' => [
        'paths' => [
            'plugins' => [
                ROOT . DS . 'plugins' . DS,
                '/path/to/other/plugins/'
            ],
            'templates' => [
                APP . 'Template' . DS,
                APP . 'Template2' . DS
            ],
            'locales' => [
                APP . 'Locale' . DS
            ]
        ]
    ]
];

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

Настройка интонации

See the Inflection Configuration docs for more information.

Переменные среды (окружения)

Многие современные поставщики облачных вычислений, такие как Heroku, позволяют вам определять переменные среды для данных конфигурации. Вы можете настроить CakePHP через переменные среды в стиле 12factor app style [http://12factor.net/]. Переменные среды позволяют вашему приложению требовать меньшего количества состояний, что упрощает управление вашим приложением при развертывании в нескольких средах.

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

Для локальной разработки CakePHP использует dotenv [https://github.com/josegonzalez/php-dotenv], чтобы обеспечить легкую локальную разработку с использованием переменных среды. В приложении вы увидите config/.env.default. Скопировав этот файл в config/.env и настроив значения, вы можете настроить ваше приложение.

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

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

$debug = env('APP_DEBUG', false);

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

Изменено в версии 3.5.0: Поддержка библиотеки dotenv была добавлена в скелет приложения.

Настроить класс

class Cake\Core\Configure

Класс Configure CakePHP может использоваться для хранения и извлечения определённых приложений или значений времени выполнения. Будьте осторожны, этот класс позволяет хранить что-либо в нём, а затем использовать это в любой другой части вашего кода: это может соблазнить разрушить шаблон MVC, разработанный CakePHP. Основная задача настройки класса - хранить централизованные переменные, которые могут быть разделены между многими объектами. Не забудьте попытаться жить по «конвенции по конфигурации», и вы не разрушите структуру MVC, которую предоставляет CakePHP.

Запись данных конфигурации

static Cake\Core\Configure::write($key, $value)

Используйте write() для хранения данных в конфигурации приложения:

Configure::write('Company.name','Pizza, Inc.');
Configure::write('Company.slogan','Pizza for your body and soul');

Примечание

dot notation, используемый в параметре $key, может использоваться для организации ваших настроек конфигурации в логических группах.

Вышеприведённый пример также может быть записан одним вызовом:

Configure::write('Company', [
    'name' => 'Pizza, Inc.',
    'slogan' => 'Pizza for your body and soul'
]);

Вы можете использовать Configure::write('debug', $bool) для переключения между режимами отладки и производства на лету. Это особенно удобно для взаимодействия JSON, где отладочная информация может вызвать проблемы с анализом.

Чтение данных конфигурации

static Cake\Core\Configure::read($key = null, $default = null)

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

// Возвращает 'Pizza Inc.'
Configure::read('Company.name');

// Возвращает 'Pizza for your body and soul'
Configure::read('Company.slogan');

Configure::read('Company');
// Возвращает:
['name' => 'Pizza, Inc.', 'slogan' => 'Pizza for your body and soul'];

// Возвращает 'fallback' как Company.nope не определен.
Configure::read('Company.nope', 'fallback');

Если $key оставлено в null, все значения в Configure будут возвращены.

Изменено в версии 3.5.0: Параметр $default был добавлен в 3.5.0

static Cake\Core\Configure::readOrFail($key)

Читает данные конфигурации так же, как Cake\Core\Configure::read, но ожидает найти пару ключ/значение. Если запрошенная пара не существует, будет выброшено RuntimeException:

Configure::readOrFail('Company.name');    // Принесёт: 'Pizza, Inc.'
Configure::readOrFail('Company.geolocation');  // Бросает исключение

Configure::readOrFail('Company');

// Принесёт:
['name' => 'Pizza, Inc.', 'slogan' => 'Pizza for your body and soul'];

Добавлено в версии 3.1.7: Configure::readOrFail() был добавлен в 3.1.7

Проверка наличия определенных данных конфигурации

static Cake\Core\Configure::check($key)

Используется для проверки наличия ключа/пути и имеет ненулевое значение:

$exists = Configure::check('Company.name');

Удаление данных конфигурации

static Cake\Core\Configure::delete($key)

Используется для удаления информации из конфигурации приложения:

Configure::delete('Company.name');

Чтение и удаление данных конфигурации

static Cake\Core\Configure::consume($key)

Прочтите и удалите ключ из Configure. Это полезно, если вы хотите объединить чтение и удаление значений в одной операции.

Чтение и запись файлов конфигурации

static Cake\Core\Configure::config($name, $engine)

CakePHP поставляется с двумя встроенными файловыми системами. Cake\Core\Configure\Engine\PhpConfig способен читать файлы конфигурации PHP в том же формате, который исторически читал Configure. Cake\Core\Configure\Engine\IniConfig способен читать ini-файлы конфигурации. Подробнее о спецификациях ini-файлов см. в PHP documentation [http://php.net/parse_ini_file]. Чтобы использовать основной конфигурационный движок, вам необходимо прикрепить его к Configure с помощью Configure::config():

use Cake\Core\Configure\Engine\PhpConfig;

// Чтение конфигурационных файлов из конфигурации
Configure::config('default', new PhpConfig());

// Прочитайте конфигурационные файлы с другого пути
Configure::config('default', new PhpConfig('/path/to/your/config/files/'));

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

// Получите массив псевдонимов для подключенных двигателей.
Configure::configured();

// Проверьте, подключен ли какой-либо конкретный двигатель
Configure::configured('default');

static Cake\Core\Configure::drop($name)

Вы также можете удалить подключенные двигатели. Configure::drop('default') удалит псевдоним двигателя по умолчанию. Любые будущие попытки загрузить файлы конфигурации с этим движком не сработают

Configure::drop('default');

Загрузка файлов конфигурации

static Cake\Core\Configure::load($key, $config = 'default', $merge = true)

После того, как вы подключили конфигурационный движок к Configure, вы можете загрузить файлы конфигурации:

// Загрузите файл my_file.php с помощью объекта 'default', движка.
Configure::load('my_file', 'default');

Загруженные файлы конфигурации объединяют свои данные с существующей конфигурацией времени выполнения в Configure. Это позволяет вам перезаписывать и добавлять новые значения в существующую конфигурацию времени выполнения. Установив $merge в true, значения не будут перезаписывать существующую конфигурацию.

Создание или изменение файлов конфигурации

static Cake\Core\Configure::dump($key, $config = 'default', $keys = [])

Сбрасывает все или некоторые данные в Configure в файл или систему хранения, поддерживаемые механизмом конфигурации. Формат сериализации определяется механизмом конфигурации, прикрепленным в виде $config. Например, если „по умолчанию“ движком является Cake\Core\Configure\Engine\PhpConfig, сгенерированный файл будет файлом конфигурации PHP, загружаемым с помощью Cake\Core\Configure\Engine\PhpConfig.

Учитывая, что движок „по умолчанию“ является экземпляром PhpConfig, сохраните все данные в Configure в файле my_config.php:

Configure::dump('my_config', 'default');

Сохранять только конфигурацию обработки ошибок:

Configure::dump('error', 'default', ['Error', 'Exception']);

Configure::dump() можно использовать для изменения или перезаписывания файлов конфигурации, которые читаются с помощью Configure::load().

Сохранение конфигурации времени выполнения

static Cake\Core\Configure::store($name, $cacheConfig = 'default', $data = null)

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

// Сохраните текущую конфигурацию в ключе 'user_1234' в кеше 'default'.
Configure::store('user_1234', 'default');

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

Восстановление конфигурации времени выполнения

static Cake\Core\Configure::restore($name, $cacheConfig = 'default')

Как только вы сохраните конфигурацию во время выполнения, вам, вероятно, потребуется восстановить её, чтобы вы снова могли получить к ней доступ. Configure::restore() именно это и делает:

// Восстановить конфигурацию времени выполнения из кеша.
Configure::restore('user_1234', 'default');

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

Конфигурационные двигатели

CakePHP предоставляет возможность загружать файлы конфигурации из нескольких разных источников и включает в себя подключаемую систему для создания собственных механизмов конфигурации [https://api.cakephp.org/3.x/class-Cake.Core.Configure.ConfigEngineInterface.html]. Встроенные двигатели конфигурации:

JsonConfig [https://api.cakephp.org/3.x/class-Cake.Core.Configure.Engine.JsonConfig.html]

IniConfig [https://api.cakephp.org/3.x/class-Cake.Core.Configure.Engine.IniConfig.html]

PhpConfig [https://api.cakephp.org/3.x/class-Cake.Core.Configure.Engine.PhpConfig.html]

По умолчанию ваше приложение будет использовать PhpConfig.

Загрузочный CakePHP

Если у вас есть какие-либо дополнительные потребности в настройке, вы должны добавить их в файл config/bootstrap.php вашего приложения. Этот файл подключается перед каждым запросом и командой CLI.

Этот файл идеально подходит для ряда обычных задач начальной загрузки:

Определение удобных функций.

Объявление констант.

Определение конфигурации кеша.

Определение конфигурации ведения журнала.

Загрузка пользовательских интонаций (inflections).

Загрузка файлов конфигурации.

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

Application::bootstrap()

В дополнение к config/bootstrap.php, который должен использоваться для настройки проблем низкого уровня вашего приложения, вы также можете использовать хук-метод Application::bootstrap() для загрузки/инициализации плагинов, и присоединить глобальных слушателей событий:

// в src/Application.php
namespace App;

use Cake\Core\Plugin;
use Cake\Http\BaseApplication;

class Application extends BaseApplication
{
    public function bootstrap()
    {
        // Вызовите родителя для `require_once` config/bootstrap.php
        parent::bootstrap();

        Plugin::load('MyPlugin', ['bootstrap' => true, 'routes' => true]);
    }
}

Загрузка плагинов/событий в Application::bootstrap() делает Controller Integration Testing проще, поскольку события и маршруты будут переработаны для каждого метода тестирования.

Отключение общих таблиц

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

Вы можете проверить, был ли какой-либо запрос из класса Generic через DebugKit выпущен через панель SQL в DebugKit. Если у вас по-прежнему возникают проблемы с диагностикой проблемы, которая может быть вызвана автоматическими таблицами, вы можете создать исключение, когда CakePHP неявно использует общий Cake\ORM\Table вместо вашего конкретного класса:

// В вашем bootstrap.php
use Cake\Event\EventManager;
use Cake\Network\Exception\InternalErrorException;

$isCakeBakeShellRunning = (PHP_SAPI === 'cli' && isset($argv[1]) && $argv[1] === 'bake');
if (!$isCakeBakeShellRunning) {
    EventManager::instance()->on('Model.initialize', function($event) {
        $subject = $event->getSubject();
        if (get_class($subject === 'Cake\ORM\Table') {
            $msg = sprintf(
                'Missing table class or incorrect alias when registering table class for database table %s.',
                $subject->getTable());
            throw new InternalErrorException($msg);
        }
    });
}

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Маршрутизация

class Cake\Routing\Router

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

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

Быстрый тур

В этом разделе вам расскажут о наиболее распространённых применениях CakePHP Router. Как правило, вы хотите отображать что-то в качестве целевой страницы, поэтому вы добавляете это в свой файл routes.php:

use Cake\Routing\Router;

// Использование области построителя маршрутов.
Router::scope('/', function ($routes) {
    $routes->connect('/', ['controller' => 'Articles', 'action' => 'index']);
});

// Использование статического метода.
Router::connect('/', ['controller' => 'Articles', 'action' => 'index']);

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

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

$routes->connect('/articles/*', ['controller' => 'Articles', 'action' => 'view']);

Вышеуказанный маршрут примет любой URL-адрес, похожий на /articles/15, и вызовет метод view(15) в ArticlesController. Однако, это не позволит получить доступ к URL-адресам, похожим на /articles/foobar. Если вы захотите, то вы можете ограничить некоторые параметры, чтобы они соответствовали регулярному выражению:

$routes->connect(
    '/articles/:id',
    ['controller' => 'Articles', 'action' => 'view'],
)
->setPatterns(['id' => '\d+'])
->setPass(['id']);

// До 3.5 используйте массив опций
$routes->connect(
    '/articles/:id',
    ['controller' => 'Articles', 'action' => 'view'],
    ['id' => '\d+', 'pass' => ['id']]
);

В предыдущем примере „сменщик звёзд“ был заменён новым заполнителем :id. Использование заполнителей позволяет нам проверять части URL-адреса, в этом случае мы использовали регулярное выражение \d+, чтобы сопоставлялись только цифры. Наконец, мы сказали Router обработать id в качестве аргумента функции view(), указав опцию pass. Подробнее об использовании этого параметра расскажем попозже.

Маршрутизатор CakePHP также может изменять маршруты соответствия. Это означает, что из массива, содержащего соответствующие параметры, он способен генерировать строку URL:

use Cake\Routing\Router;

echo Router::url(['controller' => 'Articles', 'action' => 'view', 'id' => 15]);
// Выйдет
/articles/15

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

// В routes.php
$routes->connect(
    '/login',
    ['controller' => 'Users', 'action' => 'login'],
    ['_name' => 'login']
);

use Cake\Routing\Router;

echo Router::url(['_name' => 'login']);
// Выйдет
/login

Чтобы сохранить код маршрутизации DRY, маршрутизатор имеет концепцию „scopes“ (области). Область определяет общий сегмент пути и, при необходимости, маршрутизирует значения по умолчанию. Любые маршруты, связанные внутри области, наследуют пути/значения по умолчанию из обёртки их областей:

Router::scope('/blog', ['plugin' => 'Blog'], function ($routes) {
    $routes->connect('/', ['controller' => 'Articles']);
});

Вышеуказанный маршрут будет соответствовать /blog/ и отправлять в Blog\Controller\ArticlesController::index().

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

Подключение маршрутов

Cake\Routing\Router::connect($route, $defaults = [], $options = [])

Чтобы сохранить код DRY вы должны использовать „области маршрутизации“. Области маршрутизации не только позволяют сохранить код DRY, но и помогают маршрутизатору оптимизировать его работу. Этот метод по умолчанию использует область /. Чтобы создать область и подключить некоторые маршруты, мы будем использовать метод scope():

// В config/routes.php
use Cake\Routing\Route\DashedRoute;

Router::scope('/', function ($routes) {
    // Подключите общие резервные маршруты.
    $routes->fallbacks(DashedRoute::class);
});

Метод connect() принимает до трёх параметров: шаблон URL, который вы хотите сопоставить, значения по умолчанию для ваших элементов маршрута и параметры маршрута. Параметры часто включают правила регулярного выражения, чтобы помочь элементам сопоставления маршрутизатора в URL-адресе.

Основным форматом определения маршрута является:

$routes->connect(
    '/url/template',
    ['default' => 'defaultValue'],
    ['option' => 'matchingRegex']
);

Первый параметр используется для указания маршрутизатору, какой URL-адрес вы пытаетесь контролировать. URL-адрес - это строка с нормальным символом слэша, но может также содержать подстановочный знак (*) или Элементы маршрута. Использование подстановочного знака указывает маршрутизатору, что вы готовы принять любые дополнительные аргументы. Маршруты без символа * соответствуют только указанному патерну шаблона.

После того, как вы указали URL-адрес, вы используете последние два параметра connect(), чтобы сообщить CakePHP, что делать с запросом после его сопоставления. Второй параметр - ассоциативный массив. Ключи массива должны быть названы после элементов маршрута, которые представляет шаблон URL. Значения в массиве являются значениями по умолчанию для этих ключей. Давайте рассмотрим некоторые базовые примеры, прежде чем мы начнём использовать третий параметр connect():

$routes->connect(
    '/pages/*',
    ['controller' => 'Pages', 'action' => 'display']
);

Этот маршрут находится в файле routes.php, распространяемом с помощью CakePHP. Он соответствует любому URL, начинающемуся с /pages/, и передает его в экшен display() контроллера PagesController. Запрос /pages/products будет отображён в PagesController->display('products').

В дополнение к жадной звезде /* есть также /** синтаксис задней звезды. Используя конечную двойную звезду, она будет отображать оставшуюся часть URL-адреса в виде одного переданного аргумента. Это полезно, если вы хотите использовать аргумент, включающий в нём /:

$routes->connect(
    '/pages/**',
    ['controller' => 'Pages', 'action' => 'show']
);

Входящий URL-адрес /pages/the-example-/-and-proof приведёт к передаче одного аргумента the-example-/-and-proof.

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

$routes->connect(
    '/government',
    ['controller' => 'Pages', 'action' => 'display', 5]
);

В этом примере показано, как вы можете использовать второй параметр connect() для определения параметров по умолчанию. Если вы создали сайт с продуктами для разных категорий клиентов, вы можете подумать о создании маршрута. Это позволяет вам ссылаться на /government, а не /pages/display/5.

Обычным использованием для маршрутизации является создание сегментов URL, которые не соответствуют вашим контроллерам или именам моделей. Предположим, что вместо доступа к нашему регулярному URL-адресу в /users/some_action/5, мы хотели бы иметь доступ к нему же, но с помощью /cooks/some_action/5. Следующий маршрут позаботится об этом:

$routes->connect(
    '/cooks/:action/*', ['controller' => 'Users']
);

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

При создании URL-адресов используются также маршруты. Использование ['controller' => 'Users', 'action' => 'some_action', 5] в качестве URL-адреса, будет выводить /cooks/some_action/5, если указанный маршрут указан в первом совпадении.

Маршруты, которые мы подключали до сих пор, будут соответствовать любому HTTP-глаголу. Если вы создаёте REST API, вам часто нужно сопоставлять действия HTTP с различными методами управления. RouteBuilder предоставляет вспомогательные методы, упрощающие определение маршрутов для определенных HTTP-глаголов:

// Создайте маршрут, который будет отвечать только на GET запросы.
$routes->get(
    '/cooks/:id',
    ['controller' => 'Users', 'action' => 'view'],
    'users:view'
);

// Создайте маршрут, который будет отвечать только на PUT запросы.
$routes->put(
    '/cooks/:id',
    ['controller' => 'Users', 'action' => 'update'],
    'users:update'
);

Вышеуказанные маршруты отображают один и тот же URL-адрес для разных действий контроллера на основе используемого HTTP глагола. Запросы GET перейдут к действию „view“ в то время как запросы PUT перейдут на „update“. Существуют HTTP-помощники для:

GET

POST

PUT

PATCH

DELETE

OPTIONS

HEAD

Все эти методы возвращают экземпляр маршрута, позволяющий вам использовать fluent setters для дальнейшей настройки маршрута.

Добавлено в версии 3.5.0: Методы вспомогательных HTTP-глаголов были добавлены в 3.5.0

Элементы маршрута

Вы можете указать свои собственные элементы маршрута, и это даёт вам возможность определять места в URL-адресе, где должны находиться параметры для действий контроллера. Когда запрос сделан, значения для этих элементов маршрута находятся в $this->request->getParam() в контроллере. Когда вы определяете настраиваемый элемент маршрута, вы можете дополнительно указать регулярное выражение - это говорит CakePHP, как узнать, правильно ли сформирован URL-адрес. Если вы решите не предоставлять регулярное выражение, любой символ / будет рассматриваться как часть параметра:

$routes->connect(
    '/:controller/:id',
    ['action' => 'view']
)->setPatterns(['id' => '[0-9]+']);

// До 3.5 используйте массив опций
$routes->connect(
    '/:controller/:id',
    ['action' => 'view'],
    ['id' => '[0-9]+']
);

В приведённом выше примере показано, как создать быстрый способ просмотра моделей с любого контроллера, создав URL-адрес, который выглядит как /controllername/:id. URL, предоставленный connect(), указывает два элемента маршрута : :controller и :id. Элемент :controller является элементом маршрута по умолчанию CakePHP, поэтому маршрутизатор знает, как сопоставлять и идентифицировать имена контроллеров в URL-адресах. Элемент :id является настраиваемым элементом маршрута, и его необходимо уточнить, указав подходящее регулярное выражение в третьем параметре connect().

CakePHP автоматически не создаёт нижние и пунктирные URL-адреса при использовании параметра :controller. Если вам это нужно, приведённый выше пример можно переписать так:

use Cake\Routing\Route\DashedRoute;

// Создайте конструктор с другим классом маршрутов.
$routes->scope('/', function ($routes) {
    $routes->setRouteClass(DashedRoute::class);
    $routes->connect('/:controller/:id', ['action' => 'view'])
        ->setPatterns(['id' => '[0-9]+']);

    // До 3.5 использовать опцию array
    $routes->connect(
        '/:controller/:id',
        ['action' => 'view'],
        ['id' => '[0-9]+']
    );
});

Класс DashedRoute гарантирует, что параметры :controller и :plugin будут корректно приведены к строчному виду и разделены пунктиром.

Если для переноса из приложения CakePHP 2.x вам нужны нижние и подчеркнутые URL-адреса, вы можете вместо этого использовать класс InflectedRoute.

Примечание

Шаблоны, используемые для элементов маршрута, не должны содержать группы захвата. Если они это сделают, маршрутизатор будет работать неправильно.

Как только этот маршрут определён, запрос /apples/5 вызовет метод view() ApplesController. Внутри метода view() вам нужно будет получить доступ к переданному идентификатору в $this->request->getParam('id').

Если в вашем приложении имеется один контроллер, и вы не хотите, чтобы имя контроллера отображалось в URL-адресе, вы можете сопоставить все URL-адреса с действиями в вашем контроллере. Например, чтобы сопоставить все URL-адреса с действиями контроллера home, например, иметь URL-адреса, такие как /demo вместо /home/demo, вы можете сделать следующее:

$routes->connect('/:action', ['controller' => 'Home']);

Если вы хотите указать нечувствительный к регистру URL-адрес, вы можете использовать регулярные выражения inline-модификаторов:

// До 3.5 используовался массив параметров вместо setPatterns()
$routes->connect(
    '/:userShortcut',
    ['controller' => 'Teachers', 'action' => 'profile', 1],
)->setPatterns(['userShortcut' => '(?i:principal)']);

Еще один пример, и вы будете профессионалом маршрутизации:

// До 3.5 используовался массив параметров вместо setPatterns()
$routes->connect(
    '/:controller/:year/:month/:day',
    ['action' => 'index']
)->setPatterns([
    'year' => '[12][0-9]{3}',
    'month' => '0[1-9]|1[012]',
    'day' => '0[1-9]|[12][0-9]|3[01]'
]);

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

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

Затем мы укажем некоторые значения по умолчанию. Независимо от контроллера, мы хотим вызвать экшен index().

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

После определения этот маршрут будет соответствовать /articles/2007/02/01, /articles/2004/11/16, передавая запросы в экшен index() их соответствующих контроллеров, с параметрами даты в $this->request->getParam().

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

controller Используется для обозначения контроллера для маршрута.

action Используется для обозначения действия(экшена) контроллера для маршрута.

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

prefix Используется для Префиксная маршрутизация

_ext Используется для File extentions routing.

_base Установите false, чтобы удалить базовый путь из сгенерированного URL. Если ваше приложение не находится в корневом каталоге, это может быть использовано для создания URL-адресов, которые являются „относительными кексами“.

_scheme Установите для создания ссылок на разные схемы, такие как webcal или ftp. По умолчанию используется текущая схема.

_host Установите хост для связи. По умолчанию используется текущий хост.

_port Установите порт, если вам нужно создать ссылки на нестандартных портах.

_full Если true, константа FULL_BASE_URL будет добавлена к сгенерированным URL-адресам.

# Позволяет установить фрагменты хеша URL.

_ssl Установите значение true, чтобы преобразовать сгенерированный URL-адрес в https или false, чтобы заставить http.

_method Определите HTTP-глагол/метод для использования. Полезно при работе с Создание RESTful маршрутов.

_name Если вы настроили именованный маршрут, вы можете использовать этот ключ, чтобы указать его.

Настройка параметров маршрута

Существует несколько параметров маршрута, которые могут быть установлены на каждом маршруте. После подключения маршрута вы можете использовать свои быстрые методы построения для дальнейшей настройки маршрута. Эти методы заменяют многие ключи в параметре $options метода connect():

$routes->connect(
    '/:lang/articles/:slug',
    ['controller' => 'Articles', 'action' => 'view'],
)
// Разрешить запросы GET и POST.
->setMethods(['GET', 'POST'])

// Только совпадение в субдомене блога.
->setHost('blog.example.com')

// Установите элементы маршрута, которые должны быть преобразованы
// в переданные аргументы
->setPass(['slug'])

// Установите соответствующие шаблоны для элементов маршрута
->setPatterns([
    'slug' => '[a-z0-9-_]+',
    'lang' => 'en|fr|es',
])

// Также разрешить расширения файлов JSON
->setExtensions(['json'])

// Установите lang как постоянный параметр
->setPersist(['lang']);

Добавлено в версии 3.5.0: Свободные методы построения были добавлены в 3.5.0

Передача параметров в действие(экшен)

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

// src/Controller/BlogsController.php
public function view($articleId = null, $slug = null)
{
    // Здесь код экшена
}

// config/routes.php
Router::scope('/', function ($routes) {
    $routes->connect(
        '/blog/:id-:slug', // Например /blog/3-CakePHP_Rocks
        ['controller' => 'Blogs', 'action' => 'view']
    )
    // Определите элементы маршрута в шаблоне маршрута для
    // передачи в качестве аргументов функции. Вопросы заказа,
    // так как это просто отобразит ":id" в $articleId в вашем действии
    ->setPass(['id', 'slug'])
    // Определите шаблон, который должен совпадать с идентификатором `id`.
    ->setPatterns([
        'id' => '[0-9]+',
    ]);
});

Теперь благодаря возможностям обратной маршрутизации вы можете перейти в массив URL, как показано ниже, и CakePHP будет знать, как формировать URL-адрес, определённый в маршрутах:

// view.ctp
// Это вернёт ссылку на /blog/3-CakePHP_Rocks
echo $this->Html->link('CakePHP Rocks', [
    'controller' => 'Blog',
    'action' => 'view',
    'id' => 3,
    'slug' => 'CakePHP_Rocks'
]);

// Вы также можете использовать числовые индексированные параметры.
echo $this->Html->link('CakePHP Rocks', [
    'controller' => 'Blog',
    'action' => 'view',
    3,
    'CakePHP_Rocks'
]);

Использование именованных маршрутов

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

// Соедините маршрут с именем.
$routes->connect(
    '/login',
    ['controller' => 'Users', 'action' => 'login'],
    ['_name' => 'login']
);

// Назовите конкретный маршрут глагола (3.5.0+)
$routes->post(
    '/logout',
    ['controller' => 'Users', 'action' => 'logout'],
    'logout'
);

// Создайте URL-адрес, используя именованный маршрут.
$url = Router::url(['_name' => 'logout']);

// Создайте URL-адрес, используя именованный маршрут,
// с некоторыми аргументами строки запроса.
$url = Router::url(['_name' => 'login', 'username' => 'jimmy']);

Если ваш шаблон маршрута содержит какие-либо элементы маршрута, такие как :controller, вам нужно будет предоставить их как часть параметров для Router::url().

Примечание

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

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

Router::scope('/api', ['_namePrefix' => 'api:'], function ($routes) {
    // Имя этого маршрута будет `api:ping`
    $routes->get('/ping', ['controller' => 'Pings'], 'ping');
});
// Создайте URL-адрес для маршрута пинга
Router::url(['_name' => 'api:ping']);

// Используйте namePrefix с plugin()
Router::plugin('Contacts', ['_namePrefix' => 'contacts:'], function ($routes) {
    // Подключите маршруты.
});

// Или с prefix()
Router::prefix('Admin', ['_namePrefix' => 'admin:'], function ($routes) {
    // Подключите маршруты.
});

Вы также можете использовать опцию _namePrefix внутри вложенных областей, и она работает так, как вы и ожидали:

Router::plugin('Contacts', ['_namePrefix' => 'contacts:'], function ($routes) {
    $routes->scope('/api', ['_namePrefix' => 'api:'], function ($routes) {
        // Имя этого маршрута будет `contacts:api:ping`
        $routes->get('/ping', ['controller' => 'Pings'], 'ping');
    });
});

// Создайте URL-адрес для маршрута пинга
Router::url(['_name' => 'contacts:api:ping']);

Маршруты, связанные в названных областях, будут иметь только имена, если маршрут также назван. Для безымянных маршрутов, _namePrefix применено к ним не будет.

Добавлено в версии 3.1: Добавлена опция _namePrefix в 3.1

Префиксная маршрутизация

static Cake\Routing\Router::prefix($name, $callback)

Для большинства приложений требуется раздел администрирования, в котором привилегированные пользователи могут вносить изменения. Это часто делается с помощью специального URL-адреса, такого как /admin/users/edit/5. В CakePHP префиксную маршрутизацию можно включить с помощью метода prefix() области видимости, в config/routes.php:

use Cake\Routing\Route\DashedRoute;

Router::prefix('admin', function (RouteBuilder $routes) {
    // Все маршруты здесь будут иметь префикс `/admin`,
    // и добавлять элемент prefix => admin маршрутизации.
    $routes->fallbacks(DashedRoute::class);
});

Префиксы сопоставляются с подпространствами имён в пространстве имён Controller вашего приложения. Имея префиксы в качестве отдельных контроллеров, вы можете создавать более мелкие и простые контроллеры. Поведение, которое является общим для префиксных и не префиксных контроллеров, может быть инкапсулировано с использованием наследования Компоненты или трейтов. Используя пример наших пользователей, доступ к URL-адресу /admin/users/edit/5 вызовет метод edit() нашего src/Controller/Admin/UsersController.php, передающий 5 в качестве первого параметра. Используемый файл просмотра будет src/Template/Admin/Users/edit.ctp.

Вы можете сопоставить URL /admin с вашим экшеном index() контроллера страниц, используя следующий маршрут:

Router::prefix('admin', function (RouteBuilder $routes) {
    // Поскольку вы находитесь в области администрирования,
    // вам не нужно включать префикс /admin или элемент маршрута admin.
    $routes->connect('/', ['controller' => 'Pages', 'action' => 'index']);
});

При создании префиксных маршрутов вы можете установить дополнительные параметры маршрута с помощью аргумента $options:

Router::prefix('admin', ['param' => 'value'], function ($routes) {
    // Маршруты, связанные здесь, имеют
    // префикс '/admin' и имеют набор ключей 'param'.
    $routes->connect('/:controller');
});

Вы также можете определить префиксы внутри областей плагинов:

Router::plugin('DebugKit', function ($routes) {
    $routes->prefix('admin', function ($routes) {
        $routes->connect('/:controller');
    });
});

Вышеизложенное создало бы шаблон маршрута, например /debug_kit/admin/:controller. На связанном маршруте будут установлены элементы маршрута plugin и prefix.

При определении префиксов вы можете, при необходимости, вложить несколько префиксов:

Router::prefix('manager', function ($routes) {
    $routes->prefix('admin', function ($routes) {
        $routes->connect('/:controller');
    });
});

Вышеизложенное создало бы шаблон маршрута, например /manager/admin/:controller. Связанный маршрут будет иметь элемент маршрута prefix, заданный manager/admin.

Текущий префикс будет доступен из методов контроллера через $this->request->getParam('prefix').

При использовании префиксных маршрутов важно установить префиксную опцию. Вот как построить эту ссылку с помощью HTML-помощника:

// Перейдите в префиксный маршрут.
echo $this->Html->link(
    'Manage articles',
    ['prefix' => 'manager', 'controller' => 'Articles', 'action' => 'add']
);

// Оставить префикс
echo $this->Html->link(
    'View Post',
    ['prefix' => false, 'controller' => 'Articles', 'action' => 'view', 5]
);

Примечание

Вы должны подключить префиксные маршруты до подключения резервных маршрутов.

Маршрутизация плагинов

static Cake\Routing\Router::plugin($name, $options = [], $callback)

Маршруты для Плагины должны создаваться с использованием метода plugin(). Этот метод создаёт новую область маршрутизации для маршрутов плагина:

Router::plugin('DebugKit', function ($routes) {
    // Маршруты, связанные здесь, имеют префикс '/debug_kit' и
    // имеют элемент маршрута плагина, установленный в 'DebugKit'.
    $routes->connect('/:controller');
});

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

Router::plugin('DebugKit', ['path' => '/debugger'], function ($routes) {
    // Маршруты, связанные здесь, имеют префикс '/debugger' и
    // имеют элемент маршрута плагина, установленный в 'DebugKit'.
    $routes->connect('/:controller');
});

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

Router::prefix('admin', function ($routes) {
    $routes->plugin('DebugKit', function ($routes) {
        $routes->connect('/:controller');
    });
});

Вышеизложенное создало бы маршрут, который выглядит как /admin/debug_kit/:controller. Он будет иметь элементы prefix и plugin. Раздел Маршрутизация плагина содержит дополнительную информацию о построении маршрутов плагинов.

Создание ссылок на плагины

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

echo $this->Html->link(
    'New todo',
    ['plugin' => 'Todo', 'controller' => 'TodoItems', 'action' => 'create']
);

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

echo $this->Html->link(
    'New todo',
    ['plugin' => null, 'controller' => 'Users', 'action' => 'profile']
);

Установив 'plugin' => null, вы указываете маршрутизатору, что хотите создать ссылку, которая не является частью плагина.

Маршрутизация, ориентированная на SEO

Некоторые разработчики предпочитают использовать тире в URL-адресах, так как они воспринимаются как лучший рейтинг в поисковых системах. Класс DashedRoute может использоваться в вашем приложении с возможностью маршрутизации плагинов, контроллеров „верблюжьих“ имён в пунктирный URL.

Например, если у вас есть плагин ToDo, с контроллером TodoItems и экшеном showItems(), его можно получить в разделе /to-do/todo-items/show-items со следующим подключением маршрутизатора:

use Cake\Routing\Route\DashedRoute;

Router::plugin('ToDo', ['path' => 'to-do'], function ($routes) {
    $routes->fallbacks(DashedRoute::class);
});

Соответствие определенным HTTP-методам

Маршруты могут соответствовать определенным HTTP-методам с помощью методов HTTP-хелпера:

Router::scope('/', function($routes) {
    // Этот маршрут соответствует только POST запросам.
    $routes->post(
        '/reviews/start',
        ['controller' => 'Reviews', 'action' => 'start']
    );

    // Сопоставьте несколько глаголов.
    // До 3.5 используйте $options['_method'] для установки метода
    $routes->connect(
        '/reviews/start',
        [
            'controller' => 'Reviews',
            'action' => 'start',
        ]
    )->setMethods(['POST', 'PUT']);
});

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

$url = Router::url([
    'controller' => 'Reviews',
    'action' => 'start',
    '_method' => 'POST',
]);

Соответствие определённым именам хостов

Маршруты могут использовать опцию _host только для соответствия определенным хостам. Вы можете использовать подстановочный знак *. для соответствия любому поддомену:

Router::scope('/', function($routes) {
    // Этот маршрут встречается только на http://images.example.com
    // До 3.5 использовалась только опция _host
    $routes->connect(
        '/images/default-logo.png',
        ['controller' => 'Images', 'action' => 'default']
    )->setHost('images.example.com');

    // Этот маршрут соответствует только http: //*.example.com.
    $routes->connect(
        '/images/old-log.png',
        ['controller' => 'Images', 'action' => 'oldLogo']
    )->setHost('*.example.com');
});

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

// Если у вас есть этот маршрут
$routes->connect(
    '/images/old-log.png',
    ['controller' => 'Images', 'action' => 'oldLogo']
)->setHost('images.example.com');

// Вам необходимо создать такой URL
echo Router::url([
    'controller' => 'Images',
    'action' => 'oldLogo',
    '_host' => 'images.example.com',
]);

Добавлено в версии 3.4.0: Опция _host была добавлена в 3.4.0

Расширения файлов маршрутизации

static Cake\Routing\Router::extensions(string|array|null $extensions, $merge = true)

Чтобы обрабатывать различные расширения файлов с помощью ваших маршрутов, вы можете определять расширения как на глобальном, так и на уровне охвата. Определение глобальных расширений может быть достигнуто с помощью метода Router::extensions():

Router::extensions(['json', 'xml']);
// ...

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

Чтобы ограничить расширения конкретными областями, вы можете определить их, используя метод Cake\Routing\RouteBuilder::setExtensions():

Router::scope('/', function ($routes) {
    // До 3.5.0 использовать `extensions()`
    $routes->setExtensions(['json', 'xml']);
});

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

Примечание

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

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

Используя расширения, вы указываете маршрутизатору удалить любые соответствующие расширения файлов, а затем проанализировать, что осталось. Если вы хотите создать URL-адрес, такой как /page/title-of-page.html, вам нужно создать свой маршрут, используя:

Router::scope('/page', function ($routes) {
    // До 3.5.0 использовать `extensions()`
    $routes->setExtensions(['json', 'xml', 'html']);
    $routes->connect(
        '/:title',
        ['controller' => 'Pages', 'action' => 'view']
    )->setPass(['title']);
});

Затем для создания ссылок, которые вернутся к маршрутам, просто используйте:

$this->Html->link(
    'Link title',
    ['controller' => 'Pages', 'action' => 'view', 'title' => 'super-article', '_ext' => 'html']
);

Расширения файлов используются Обработка запроса для автоматического переключения вида на основе типов контента.

Соединение с областью Middleware (промежуточного ПО)

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

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

// в config/routes.php
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Http\Middleware\EncryptedCookieMiddleware;

Router::scope('/', function ($routes) {
    $routes->registerMiddleware('csrf', new CsrfProtectionMiddleware());
    $routes->registerMiddleware('cookies', new EncryptedCookieMiddleware());
});

После регистрации промежуточное ПО может быть применено к определенным областям:

$routes->scope('/cms', function ($routes) {
    // Включим CSRF & cookies middleware
    $routes->applyMiddleware('csrf', 'cookies');
    $routes->get('/articles/:action/*', ['controller' => 'Articles'])
});

В ситуациях, когда у вас есть вложенные области, внутренние области наследуют промежуточное ПО, применяемое в области содержимого:

$routes->scope('/api', function ($routes) {
    $routes->applyMiddleware('ratelimit', 'auth.api');
    $routes->scope('/v1', function ($routes) {
        $routes->applyMiddleware('v1compat');
        // Здесь определите маршруты.
    });
});

В приведённом выше примере маршруты, определенные в /v1, будут иметь промежуточное ПО „ratelimit“, „auth.api“ и „v1compat“. Если вы повторно откроете область действия, промежуточное ПО, применяемое к маршрутам в каждой области, будет изолировано:

$routes->scope('/blog', function ($routes) {
    $routes->applyMiddleware('auth');
    // Подключите аутентифицированные действия для этого блога.
});
$routes->scope('/blog', function ($routes) {
    // Подключите здесь публичные действия для блога.
});

В приведённом выше примере два использования области /blog не используют промежуточное ПО. Однако обе эти области будут наследовать промежуточное ПО, определённое в их областях видимости.

Группировка промежуточного ПО

Чтобы сохранить код маршрута DRY промежуточное ПО может быть объединено в группы. Когда объединённые группы могут применяться как промежуточное ПО, возможно:

$routes->registerMiddleware('cookie', new EncryptedCookieMiddleware());
$routes->registerMiddleware('auth', new AuthenticationMiddleware());
$routes->registerMiddleware('csrf', new CsrfProtectionMiddleware());
$routes->middlewareGroup('web', ['cookie', 'auth', 'csrf']);

// Применить группу
$routes->applyMiddleware('web');

Добавлено в версии 3.5.0: Промежуточное ПО и группы промежуточного ПО были добавлены в 3.5.0

Создание RESTful маршрутов

Маршрутизатор упрощает создание маршрутов RESTful для ваших контроллеров. Маршруты RESTful полезны, когда вы создаёте конечные точки API для своего приложения. Если бы мы хотели разрешить доступ REST к контроллеру рецептов, мы бы сделали что-то вроде этого:

// В config/routes.php...

Router::scope('/', function ($routes) {
    // До 3.5.0 использовать `extensions()`
    $routes->setExtensions(['json']);
    $routes->resources('Recipes');
});

Первая строка устанавливает несколько маршрутов по умолчанию для простого доступа REST, где метод определяет желаемый формат результата (например, xml, json, rss). Эти маршруты чувствительны к методу HTTP запросов.

HTTP format

URL.format

Controller action invoked

GET

/recipes.format

RecipesController::index()

GET

/recipes/123.format

RecipesController::view(123)

POST

/recipes.format

RecipesController::add()

PUT

/recipes/123.format

RecipesController::edit(123)

PATCH

/recipes/123.format

RecipesController::edit(123)

DELETE

/recipes/123.format

RecipesController::delete(123)

В классе Router CakePHP используется ряд различных индикаторов для определения используемого HTTP метода. Здесь они указаны в порядке предпочтения:

The _method POST variable

The X_HTTP_METHOD_OVERRIDE

The REQUEST_METHOD header

Переменная _method POST полезна при использовании браузера в качестве REST клиента (или всего, что может сделать POST). Просто установите значение _method для имени метода HTTP-запроса, который вы хотите эмулировать.

Создание маршрутов вложенных ресурсов (под-ресурсов)

После того как вы подключили ресурсы в области, вы также можете подключать маршруты для под-ресурсов. Маршруты под-ресурсов будут добавлены к исходному имени ресурса и параметру id. Например:

Router::scope('/api', function ($routes) {
    $routes->resources('Articles', function ($routes) {
        $routes->resources('Comments');
    });
});

Будет генерировать маршруты ресурсов для articles и comments. Маршруты комментариев будут выглядеть так:

/api/articles/:article_id/comments
/api/articles/:article_id/comments/:id

Вы можете получить article_id в CommentController:

$this->request->getParam('article_id');

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

Router::scope('/api', function ($routes) {
    $routes->resources('Articles', function ($routes) {
        $routes->resources('Comments', ['prefix' => 'articles']);
    });
});

Вышеописанное отобразило бы ресурс „Comments“ в App\Controller\Articles\CommentsController. Наличие отдельных контроллеров позволяет упростить логику контроллера. Префиксы, созданные таким образом, совместимы с Префиксная маршрутизация.

Примечание

Хотя вы можете вложить ресурсы так же глубоко, как вам нужно, не рекомендуется вставлять более двух ресурсов вместе.

Добавлено в версии 3.3: Опция prefix была добавлена в resources() в 3.3.

Ограничение созданных маршрутов

По умолчанию CakePHP будет подключать 6 маршрутов для каждого ресурса. Если вы хотите подключить только определенные маршруты ресурсов, вы можете использовать опцию only:

$routes->resources('Articles', [
    'only' => ['index', 'view']
]);

Создал бы маршруты ресурсов только для чтения. Имена маршрутов: create, update, view, index, и delete.

Изменение используемых экшенов контроллера

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

$routes->resources('Articles', [
    'actions' => ['update' => 'put', 'create' => 'add']
]);

Вышеизложенное будет использовать put() для экшена edit() и add() вместо create().

Сопоставление дополнительных маршрутов ресурсов

Вы можете сопоставить дополнительные методы ресурсов, используя опцию map:

$routes->resources('Articles', [
   'map' => [
       'deleteAll' => [
           'action' => 'deleteAll',
           'method' => 'DELETE'
       ]
   ]
]);
// Это соединило бы /articles/deleteAll

В дополнение к маршрутам по умолчанию, это также свяжет маршрут для /articles/delete_all. По умолчанию сегмент пути будет соответствовать имени ключа. Вы можете использовать ключ „path“ внутри определения ресурса для настройки имени пути:

$routes->resources('Articles', [
    'map' => [
        'updateAll' => [
            'action' => 'updateAll',
            'method' => 'DELETE',
            'path' => '/update_many'
        ],
    ]
]);
// Это соединило бы /articles/update_many

Если вы определяете „only“ и „map“, убедитесь, что ваши сопоставленные методы также находятся в списке „only“.

Пользовательские классы маршрутов для маршрутов ресурсов

Вы можете предоставить ключ connectOptions в массиве $options для resources() для предоставления настраиваемых параметров, используемых connect():

Router::scope('/', function ($routes) {
    $routes->resources('Books', [
        'connectOptions' => [
            'routeClass' => 'ApiRoute',
        ]
    ];
});

URL-инфлексия для ресурсных маршрутов

По умолчанию фрагменты URL многоуровневых контроллеров являются подчеркнутой формой имени контроллера. Например, фрагмент URL-адреса BlogPostsController будет /blog_posts.

Вы можете указать альтернативный тип инфлексии, используя опцию `` inflect``:

Router::scope('/', function ($routes) {
    $routes->resources('BlogPosts', [
        'inflect' => 'dasherize' // Буду использовать ``Inflector::dasherize()``
    ]);
});

Вышеизложенное приведёт к созданию URL-адресов, таких как: /blog-posts.

Примечание

Начиная с CakePHP 3.1, официальный скелет приложения использует DashedRoute в качестве класса маршрута по умолчанию. Использование параметра 'inflect' => 'dasherize' при подключении маршрутов ресурсов рекомендуется для согласованности URL.

Изменение пути элемента

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

Router::scope('/', function ($routes) {
    $routes->resources('BlogPosts', ['path' => 'posts']);
});

Добавлено в версии 3.5.0: Добавлена опция path в 3.5.0

Переданные аргументы

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

http://localhost/calendars/view/recent/mark

В приведённом выше примере как recent, так и mark передаются как аргументы в CalendarsController::view(). Переданные аргументы передаются вашим контроллерам тремя способами. Сначала в качестве аргументов для вызываемого экшена, во-вторых, они доступны в $this->request->getParam('pass') как числовой индексный массив. При использовании настраиваемых маршрутов вы также можете принудительно ввести определенные параметры в переданные аргументы.

Если бы вы посетили вышеупомянутый URL-адрес, и у вас бы был экшен контроллера, который выглядел как:

class CalendarsController extends AppController
{
    public function view($arg1, $arg2)
    {
        debug(func_get_args());
    }
}

Вы получили бы следующий результат:

Array
(
    [0] => recent
    [1] => mark
)

Эти же данные также доступны в $this->request->getParam('pass') в ваших контроллерах, представлениях и помощниках(хелперах). Значения в массиве pass численно индексируются в зависимости от порядка их появления в вызываемом URL:

debug($this->request->getParam('pass'));

Любой из приведённых выше:

Array
(
    [0] => recent
    [1] => mark
)

При создании URL-адресов с использованием routing array вы добавляете переданные аргументы как значения без строковых ключей в массиве:

['controller' => 'Articles', 'action' => 'view', 5]

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

Создание URL-адресов

static Cake\Routing\Router::url($url = null, $full = false)

Создание URL-адресов или обратная маршрутизация - это функция CakePHP, которая позволяет изменять структуру URL-адресов без изменения всего вашего кода. Используя routing arrays для определения ваших URL-адресов, вы можете позже настроить маршруты, и сгенерированные URL-адреса будут автоматически обновляться.

Если вы создаёте URL-адреса, используя строки типа:

$this->Html->link('View', '/articles/view/' . $id);

А потом позже решите, что /articles следует вместо этого называть „posts“ , вам придётся переименовать все URL-адреса приложения. Однако, если вы определили свою ссылку как:

$this->Html->link(
    'View',
    ['controller' => 'Articles', 'action' => 'view', $id]
);

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

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

Router::url([
    'controller' => 'Articles',
    'action' => 'index',
    '?' => ['page' => 1],
    '#' => 'top'
]);

// Будет генерировать URL-адрес, похожий на
/articles/index?page=1#top

Маршрутизатор также преобразует любые неизвестные параметры в массиве маршрутизации в параметры запроса. ? предлагается для обратной совместимости со старыми версиями CakePHP.

Вы также можете использовать любой из специальных элементов маршрута при создании URL-адресов:

_ext Используется для Расширения файлов маршрутизации маршрутизации.

_base Установите false, чтобы удалить базовый путь из сгенерированного URL. Если ваше приложение не находится в корневом каталоге, это может быть использовано для создания URL-адресов, которые являются „относительными кексами“.

_scheme Установите для создания ссылок на разные схемы, такие как webcal или ftp. По умолчанию используется текущая схема.

_host Установите хост для связи. По умолчанию используется текущий хост.

_port Установите порт, если вам нужно создать ссылки на нестандартных портах.

_method Определите HTTP-глагол для URL-адреса.

_full Если true, константа FULL_BASE_URL будет добавлена к сгенерированным URL-адресам.

_ssl Установите значение true, чтобы преобразовать сгенерированный URL-адрес в https или false, чтобы установить http.

_name Название маршрута. Если вы настроили именованные маршруты, вы можете использовать этот ключ, чтобы указать его.

Перенаправление маршрутизации

Перенаправление маршрутизации позволяет вам отправлять HTTP-статус 30x-перенаправления для входящих маршрутов и указывать их на разные URL-адреса. Это полезно, если вы хотите сообщить клиентским приложениям, что ресурс перемещён, и вы не хотите показывать два URL-адреса для одного и того же контента.

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

Router::scope('/', function ($routes) {
    $routes->redirect(
        '/home/*',
        ['controller' => 'Articles', 'action' => 'view'],
        ['persist' => true]
        // Или ['persist' => ['id']] для маршрутизации по умолчанию,
        // где экшен view ожидает $id в качестве аргумента.
    );
})

Перенаправляет /home/* в `/articles/view и передаёт параметры /articles/view. Использование массива в качестве адресата перенаправления позволяет вам использовать другие маршруты, чтобы определить, куда должна быть перенаправлена строка URL. Вы можете перенаправить на внешние местоположения, используя строковые URL-адреса в качестве адресата:

Router::scope('/', function ($routes) {
    $routes->redirect('/articles/*', 'http://google.com', ['status' => 302]);
});

Это перенаправит /articles/* на hat can be resolved i с HTTP-статусом 302.

Пользовательские классы маршрутов

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

Ожидается, что классы маршрутов будут найдены в пространстве имён Routing\\Route вашего приложения или плагина.

Маршрутные классы должны расширять Cake\Routing\Route.

Классы маршрутов должны реализовывать один или оба из match() и/или parse().

Метод parse() используется для анализа входящего URL-адреса. Он должен генерировать массив параметров запроса, которые могут быть разрешены в контроллер и экшене. Верните false из этого метода, чтобы указать неудачу совпадения.

Метод match() используется для сопоставления массива параметров URL и создания строкового URL-адреса. Если параметры URL не соответствуют маршруту, должен быть возвращён false.

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

$routes->connect(
     '/:slug',
     ['controller' => 'Articles', 'action' => 'view'],
     ['routeClass' => 'SlugRoute']
);

// Или, установив routeClass в своей области.
$routes->scope('/', function ($routes) {
    // До 3.5.0 использовался `routeClass()`
    $routes->setRouteClass('SlugRoute');
    $routes->connect(
         '/:slug',
         ['controller' => 'Articles', 'action' => 'view']
    );
});

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

Класс маршрута по умолчанию

static Cake\Routing\Router::defaultRouteClass($routeClass = null)

Если вы хотите использовать альтернативный класс маршрута для всех своих маршрутов, помимо Route, который определён по умолчанию, вы можете сделать это, вызвав Router::defaultRouteClass() перед настройкой любых маршрутов и не указывать опцию routeClass для каждого маршрута. Например:

use Cake\Routing\Route\InflectedRoute;

Router::defaultRouteClass(InflectedRoute::class);

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

Метод откатов

Cake\Routing\Router::fallbacks($routeClass = null)

Метод fallbacks - это простой ярлык для определения маршрутов по умолчанию. Метод использует пройденный класс маршрутизации для определённых правил или если класс не указан, используется класс, возвращаемый Router::defaultRouteClass().

Вызов таких резервов:

use Cake\Routing\Route\DashedRoute;

$routes->fallbacks(DashedRoute::class);

Является эквивалентом следующих явных вызовов:

use Cake\Routing\Route\DashedRoute;

$routes->connect('/:controller', ['action' => 'index'], ['routeClass' => DashedRoute::class]);
$routes->connect('/:controller/:action/*', [], ['routeClass' => DashedRoute::class]);

Примечание

Использование класса маршрута (Route) по умолчанию с резервными ошибками или любого маршрута с элементами маршрута :plugin и/или :controller приведёт к несогласованному URL адресу.

Создание постоянных параметров URL

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

Функции фильтра обратного вызова должны ожидать следующие параметры:

$params Обработанные параметры URL.

$request Текущий запрос.

Функция фильтра URL должна всегда возвращать параметры, даже если немодифицирована.

Фильтры URL позволяют реализовать такие функции, как постоянные параметры:

Router::addUrlFilter(function ($params, $request) {
    if ($request->getParam('lang') && !isset($params['lang'])) {
        $params['lang'] = $request->getParam('lang');
    }
    return $params;
});

Функции фильтра применяются в том порядке, в котором они подключены.

Другой вариант использования - это изменение определенного маршрута во время выполнения (например, маршруты плагина):

Router::addUrlFilter(function ($params, $request) {
    if (empty($params['plugin']) || $params['plugin'] !== 'MyPlugin' || empty($params['controller'])) {
        return $params;
    }
    if ($params['controller'] === 'Languages' && $params['action'] === 'view') {
        $params['controller'] = 'Locations';
        $params['action'] = 'index';
        $params['language'] = $params[0];
        unset($params[0]);
    }
    return $params;
});

Это изменит следующий маршрут:

Router::url(['plugin' => 'MyPlugin', 'controller' => 'Languages', 'action' => 'view', 'es']);

на этот:

Router::url(['plugin' => 'MyPlugin', 'controller' => 'Locations', 'action' => 'index', 'language' => 'es']);

Обработка именованных параметров в URL-адресах

Хотя именованные параметры были удалены в CakePHP 3.0, приложения могут иметь опубликованные URL-адреса, содержащие их. Вы можете продолжать принимать URL-адреса, содержащие именованные параметры.

В методе beforeFilter() вашего контроллера вы можете вызвать parseNamedParams() для извлечения любых именованных параметров из переданных аргументов:

public function beforeFilter(Event $event)
{
    parent::beforeFilter($event);
    Router::parseNamedParams($this->request);
}

Это заполнит $this->request->getParam('named') любыми иными параметрами, найденными в переданных аргументах. Любой переданный аргумент, который интерпретируется как именованный параметр, будет удалён из списка переданных аргументов.

Dispatcher Filters

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Dispatcher Filters

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Объекты запроса и ответа

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

Запрос

class Cake\Http\ServerRequest

ServerRequest является объектом запроса по умолчанию, используемым в CakePHP. Он централизует ряд функций для опроса и взаимодействия с данными запроса. По каждому запросу создаётся один Request, который затем передаётся по ссылке на различные уровни приложения, использующего данные запроса. По умолчанию запрос присваивается $this->request и доступен в контроллерах, ячейках, представлениях и помощниках. Вы также можете получить доступ к нему в компонентах, используя ссылку на контроллер. Некоторые из функций ServerRequest включают в себя:

Обработка массивов GET, POST и FILES в структурах данных, с которыми вы знакомы.

Обеспечение интроспекции среды, относящейся к запросу. Информация, такая как: отправленные заголовки, IP-адрес клиента и субдомен/домен, на сервере, на котором запущено ваше приложение.

Предоставление доступа к параметрам запроса как индексов массивов, так и свойств объекта.

Начиная с версии 3.4.0, объект запроса CakePHP реализует PSR-7 ServerRequestInterface [http://www.php-fig.org/psr/psr-7/], что упрощает использование библиотек вне CakePHP.

Параметры запроса

Запрос предоставляет параметры маршрутизации с помощью метода getParam():

$controllerName = $this->request->getParam('controller');

// До 3.4.0
$controllerName = $this->request->param('controller');

Все Элементы маршрута доступны через этот интерфейс.

В дополнение к Элементы маршрута, вам также часто необходим доступ к Переданные аргументы. Они доступны и для объекта запроса:

// Переданные аргументы
$passedArgs = $this->request->getParam('pass');

Все предоставят вам доступ к переданным аргументам. Существует несколько важных/полезных параметров, которые CakePHP использует внутренне, все они также находятся в параметрах маршрутизации:

plugin Плагин обрабатывает запрос. Будет null, если нет плагина.

controller Контроллер обрабатывает текущий запрос.

action Действие, обрабатывающее текущий запрос.

prefix Префикс для текущего действия. См. Префиксная маршрутизация для получения дополнительной информации.

Параметры строки запроса

Cake\Http\ServerRequest::getQuery($name)

Параметры строки запроса можно прочитать с помощью метода getQuery():

// URL является /posts/index?page=1&sort=title
$page = $this->request->getQuery('page');

// До 3.4.0
$page = $this->request->query('page');

Вы можете либо напрямую получить доступ к свойству запроса, либо использовать метод getQuery() для беспроблемного чтения массива URL запросов. Любые ключи, которые не существуют, вернут null:

$foo = $this->request->getQuery('value_that_does_not_exist');
// $foo === null

// Вы также можете указать значения по умолчанию
$foo = $this->request->getQuery('does_not_exist', 'default val');

Если вы хотите получить доступ ко всем параметрам запроса, вы можете использовать getQueryParams():

$query = $this->request->getQueryParams();

Добавлено в версии 3.4.0: getQueryParams() и getQuery() были добавлены в 3.4.0

Данные в теле запроса

Cake\Http\ServerRequest::getData($name, $default = null)

Доступ ко всем данным POST можно получить с помощью Cake\Http\ServerRequest::getData(). Любые данные формы, содержащие префикс data, будут иметь префикс данных. Например:

// Input с атрибутом имени, равным 'MyModel[title]', доступен в
$title = $this->request->getData('MyModel.title');

Любые ключи, которые не существуют, вернут null:

$foo = $this->request->getData('Value.that.does.not.exist');
// $foo == null

PUT, PATCH или DELETE данные

Cake\Http\ServerRequest::input($callback[, $options])

При создании REST служб вы часто принимаете данные запроса на запросы PUT и DELETE. Любые данные тела запроса application/x-www-form-urlencoded автоматически анализируются и устанавливаются в $this->data для PUT и DELETE запросов. Если вы принимаете данные JSON или XML, см. ниже как вы можете получить доступ к этим телам запросов.

При доступе к входным данным вы можете декодировать их с помощью дополнительной функции. Это полезно при взаимодействии с содержимым тела запроса XML или JSON. Дополнительные параметры для функции декодирования могут передаваться в качестве аргументов в input():

$jsonData = $this->request->input('json_decode');

Переменные среды (из $_SERVER и $_ENV)

Cake\Http\ServerRequest::env($key, $value = null)

ServerRequest::env() является оболочкой для env() глобальной функции и действует как геттер/сеттер для переменных среды без необходимости изменять глобальные значения $_SERVER и $_ENV:

// Получить хост
$host = $this->request->env('HTTP_HOST');

// Установите значение, обычно полезно при тестировании.
$this->request->env('REQUEST_METHOD', 'POST');

Чтобы получить доступ ко всем переменным среды в запросе, используйте getServerParams():

$env = $this->request->getServerParams();

Добавлено в версии 3.4.0: getServerParams() был добавлен в 3.4.0

XML или JSON данные

Приложения, использующие REST, часто обмениваются данными, не используя URL-кодирование. Вы можете читать входные данные в любом формате, используя Http\ServerRequest::input(). Используя функцию декодирования, вы можете получить контент в десериализованном формате:

// Получить JSON-кодированные данные, представленные в экшене PUT/POST
$jsonData = $this->request->input('json_decode');

Некоторые методы десериализации требуют дополнительных параметров при вызове, таких как параметр „array“ для json_decode. Если вы хотите преобразовать XML в объект DOMDocument, то Http\ServerRequest::input() также поддерживает передачу дополнительных параметров:

//Получить XML-кодированные данные, представленные в экшене PUT/POST
$data = $this->request->input('Cake\Utility\Xml::build', ['return' => 'domdocument']);

Информация о пути

Объект запроса также предоставляет полезную информацию о путях в вашем приложении. Атрибуты base и webroot полезны для создания URL-адресов и определения того, находится ли ваше приложение в подкаталоге. Атрибутами, которые вы можете пользоваться, являются:

// Предположим, что текущий URL-адрес запроса /subdir/articles/edit/1?page=1

// Получаем /subdir/articles/edit/1?page=1
$here = $request->getRequestTarget();

// Получаем /subdir
$base = $request->getAttribute('base');

// Получаем /subdir/
$base = $request->getAttribute('webroot');

// До 3.4.0
$webroot = $request->webroot;
$base = $request->base;
$here = $request->here();

Проверка условий запроса

Cake\Http\ServerRequest::is($type, $args...)

Объект запроса обеспечивает простой способ проверки определённых условий в заданном запросе. Используя метод is(), вы можете проверить ряд общих условий, а также проверить другие критерии запроса конкретного приложения:

$isPost = $this->request->is('post');

Вы также можете расширить доступные детекторы запросов, используя Cake\Http\ServerRequest::addDetector() для создания новых типов детекторов. Существует четыре разных типа детекторов, которые вы можете создать:

Сравнение значений среды - сравнивает значение, полученное из env() для равенства с предоставленным значением.

Сравнение значений шаблонов - Сравнение значений шаблонов позволяет сравнить значение, полученное из env() c регулярным выражением.

Сравнение на основе опций - Сравнение на основе опций использует список вариантов для создания регулярного выражения. Последующие вызовы для добавления уже определённого опционного детектора объединяют параметры.

Детекторы обратного вызова - Детекторы обратного вызова позволяют вам выдавать „callback“ тип для обработки проверки. Обратный вызов получит объект запроса в качестве единственного параметра.

Cake\Http\ServerRequest::addDetector($name, $options)

Некоторые примеры:

// Добавьте детектор среды.
$this->request->addDetector(
    'post',
    ['env' => 'REQUEST_METHOD', 'value' => 'POST']
);

// Добавить детектор значений шаблона.
$this->request->addDetector(
    'iphone',
    ['env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i']
);

// Добавьте детектор параметров.
$this->request->addDetector('internalIp', [
    'env' => 'CLIENT_IP',
    'options' => ['192.168.0.101', '192.168.0.100']
]);

// Добавить детектор обратного вызова. Должно быть допустимым.
$this->request->addDetector(
    'awesome',
    function ($request) {
        return $request->getParam('awesome');
    }
);

// Добавьте детектор, который использует дополнительные
// аргументы. Для 3.3.0
$this->request->addDetector(
    'controller',
    function ($request, $name) {
        return $request->getParam('controller') === $name;
    }
);

Request также включает такие методы, как Cake\Http\ServerRequest::domain(), Cake\Http\ServerRequest::subdomains() и Cake\Http\ServerRequest::host() чтобы помочь приложениям с субдоменами и немного облегчить жизнь.

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

is('get') Проверьте, является ли текущий запрос GET.

is('put') Проверьте, является ли текущий запрос PUT.

is('patch') Проверьте, является ли текущий запрос PATCH.

is('post') Проверьте, является ли текущий запрос POST.

is('delete') Проверьте, является ли текущий запрос DELETE.

is('head') Проверьте, является ли текущий запрос HEAD.

is('options') Проверьте, является ли текущий запрос OPTIONS.

is('ajax') Проверьте, пришел ли текущий запрос с помощью X-Requested-With = XMLHttpRequest.

is('ssl') Проверьте, выполняется ли запрос через SSL.

is('flash') Проверьте, имеет ли запрос User-Agent - Flash.

is('requested') Проверьте, имеет ли запрос - параметр запроса „requested“ со значением 1.

is('json') Проверьте, имеет ли запрос расширение json и принимает ли „application/json“ mimetype.

is('xml') роверьте, есть ли запрос с расширением „xml“ и принимающий „application/xml“ или „text/xml“ mimetype.

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

Данные сеанса(сессии)

Для доступа к сессии данного запроса используйте метод session():

$userName = $this->request->session()->read('Auth.User.name');

Для использования объекта сеанса и получения дополнительной информации см. документацию Cессии.

Хост и доменное имя

Cake\Http\ServerRequest::domain($tldLength = 1)

Возвращает имя домена, на котором запущено ваше приложение:

// Напечатает 'example.org'
echo $request->domain();

Cake\Http\ServerRequest::subdomains($tldLength = 1)

Возвращает субдомены, в которых работает ваше приложение, в виде массива:

// Вернёт ['my', 'dev'] для 'my.dev.example.org'
$subdomains = $request->subdomains();

Cake\Http\ServerRequest::host()

Возвращает хост, на котором установлено ваше приложение:

// Напечатает 'my.dev.example.org'
echo $request->host();

Чтение HTTP-метода

Cake\Http\ServerRequest::getMethod()

Возвращает HTTP-метод, с которым был выполнен запрос с помощью:

// Вернёт POST
echo $request->getMethod();

// До 3.4.0
echo $request->method();

Ограничение того, какой HTTP метод принимает экшен

Cake\Http\ServerRequest::allowMethod($methods)

Установите допустимые методы HTTP. Если не согласовано, будет выбрасываться MethodNotAllowedException. Ответ 405 будет включать в себя необходимый Allow заголовок с переданными методами:

public function delete()
{
    // Принимать только запросы POST и DELETE
    $this->request->allowMethod(['post', 'delete']);
    ...
}

Чтение HTTP заголовков

Позволяет получить доступ к любому из HTTP_* заголовков, которые были использованы для запроса. Например:

// Получить заголовок как строку
$userAgent = $this->request->getHeaderLine('User-Agent');

// Получить массив всех значений.
$acceptHeader = $this->request->getHeader('Accept');

// Проверьте, существует ли заголовок
$hasAcceptHeader = $this->request->hasHeader('Accept');

// До 3.4.0
$userAgent = $this->request->header('User-Agent');

Хотя некоторые установки Apache делают заголовок Authorization не доступным, CakePHP сделает его доступным с помощью специальных методов apache по мере необходимости.

Cake\Http\ServerRequest::referer($local = false)

Возвращает ссылочный адрес для запроса.

Cake\Http\ServerRequest::clientIp()

Возвращает IP-адрес текущего посетителя.

Доверенные прокси-заголовки

Если ваше приложение находится за балансировщиком нагрузки или работает в облачном сервисе, вы часто получаете хост, порт и схему балансировки нагрузки в своих запросах. Часто балансировщики нагрузки также отправляют HTTP-X-Forwarded-* заголовки с исходными значениями. Пересылаемые заголовки не будут использоваться CakePHP из коробки. Чтобы объект запроса использовал эти заголовки, установите свойство trustProxy в true:

$this->request->trustProxy = true;

// Эти методы теперь будут использовать проксированные заголовки.
$port = $this->request->port();
$host = $this->request->host();
$scheme = $this->request->scheme();
$clientIp = $this->request->clientIp();

Проверка принятия заголовков

Cake\Http\ServerRequest::accepts($type = null)

Узнайте, какие типы контента принимает клиент, или проверьте, принимает ли он конкретный тип контента.

Получить все типы:

$accepts = $this->request->accepts();

Проверить наличие одного типа:

$acceptsJson = $this->request->accepts('application/json');

Cake\Http\ServerRequest::acceptLanguage($language = null)

Получите все языки, принятые клиентом, или проверьте, принят ли конкретный язык.

Получить список принятых языков:

$acceptsLanguages = $this->request->acceptLanguage();

Проверьте, принят ли конкретный язык:

$acceptsSpanish = $this->request->acceptLanguage('es-es');

Cookies

Запрошенные файлы cookie можно прочитать несколькими способами:

// Получить значение cookie или null, если файл cookie отсутствует.
$rememberMe = $this->request->getCookie('remember_me');

// Прочтите значение или получите значение по умолчанию 0
$rememberMe = $this->request->getCookie('remember_me', 0);

// Получить все куки как хэш
$cookies = $this->request->getCookieParams();

// Получить экземпляр CookieCollection (начиная с 3.5.0)
$cookies = $this->request->getCookieCollection()

См. документацию Cake\Http\Cookie\CookieCollection для работы с коллекцией файлов cookie.

Добавлено в версии 3.5.0: ServerRequest::getCookieCollection() был добавлен в 3.5.0

Ответ

class Cake\Http\Response

Cake\Http\Response - класс ответа по умолчанию в CakePHP. Он инкапсулирует ряд функций и функциональности для генерации HTTP ответов в вашем приложении. Он также помогает в тестировании, поскольку его можно обмануть/пропустить, чтобы вы могли проверять заголовки, которые будут отправлены.

Например Cake\Http\ServerRequest и Cake\Http\Response объединяет ряд ранее найденных методов Controller, RequestHandlerComponent и Dispatcher. Старые методы устарели в пользу использования Cake\Http\Response.

Response предоставляет интерфейс для обёртывания общих задач, связанных с ответом, таких как:

Отправка заголовков для перенаправления.

Отправка типа заголовков контента.

Отправка любого заголовка.

Отправка тела ответа.

Работа с типами контента

Cake\Http\Response::withType($contentType = null)

Вы можете управлять типом Content-Type вашего приложения с помощью Cake\Http\Response::withType(). Если вашему приложению необходимо иметь дело с типами контента, которые не встроены в Response, вы также можете сопоставить их с type():

// Добавить тип vCard
$this->response->type(['vcf' => 'text/v-card']);

// Установите ответ Content-Type на vCard.
$this->response = $this->response->withType('vcf');

// До 3.4.0
$this->response->type('vcf');

Обычно вам нужно отображать дополнительные типы контента в вашем callback-контроллере beforeFilter(), поэтому вы можете использовать функции автоматического переключения вида RequestHandlerComponent, если вы его используете.

Отправка файлов

Cake\Http\Response::withFile($path, $options = [])

Бывают случаи, когда вы хотите отправлять файлы в качестве ответов на запросы. Вы можете это сделать, используя Cake\Http\Response::withFile():

public function sendFile($id)
{
    $file = $this->Attachments->getFile($id);
    $response = $this->response->withFile($file['path']);
    // Верните ответ, чтобы предотвратить попытку контроллера
    // отобразить представление.
    return $response;
}

// До 3.4.0
$file = $this->Attachments->getFile($id);
$this->response->file($file['path']);
// Верните ответ, чтобы предотвратить попытку контроллера
// отобразить представление.
return $this->response;

Как показано в приведённом выше примере, вы должны передать путь к методу. CakePHP отправит соответствующий заголовок типа контента, если это известный тип файла, указанный в Cake\Http\Reponse::$_mimeTypes. Вы можете добавлять новые типы до вызова Cake\Http\Response::withFile() с помощью метода Cake\Http\Response::withType().

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

$response = $this->response->withFile(
    $file['path'],
    ['download' => true, 'name' => 'foo']
);

// До 3.4.0
$this->response->file(
    $file['path'],
    ['download' => true, 'name' => 'foo']
);

Поддерживаемые параметры:

name

Имя - позволяет указать альтернативное имя файла для отправки пользователю.

download

Логическое значение, указывающее, следует ли устанавливать заголовки для принудительной загрузки.

Отправка строки в виде файла

Вы можете ответить файлом, который не существует на диске, например, pdf или ics, сгенерированным «на лету» из строки:

public function sendIcs()
{
    $icsString = $this->Calendars->generateIcs();
    $response = $this->response;
    $response->body($icsString);

    $response = $response->withType('ics');

    // Необязательно принудительно загружать файл
    $response = $response->withDownload('filename_for_download.ics');

    // Возвратить объект ответа, чтобы предотвратить попытку
    // контроллера отобразить представление.
    return $response;
}

Обратные вызовы также могут возвращать тело в виде строки:

$path = '/some/file.png';
$this->response->body(function () use ($path) {
    return file_get_contents($path);
});

Настройка заголовков

Cake\Http\Response::withHeader($header, $value)

Настройка заголовков выполняется с помощью метода Cake\Http\Response::withHeader(). Как и все методы интерфейса PSR-7, этот метод возвращает экземпляр new с новым заголовком:

// Добавить/заменить заголовок
$response = $response->withHeader('X-Extra', 'My header');

// Установка нескольких заголовков
$response = $response->withHeader('X-Extra', 'My header')
    ->withHeader('Location', 'http://example.com');

// Добавить значение в существующий заголовок
$response = $response->withAddedHeader('Set-Cookie', 'remember_me=1');

// До 3.4.0 - Установить заголовок
$this->response->header('Location', 'http://example.com');

Заголовки не отправляются при установке. Вместо этого они сохраняются до тех пор, пока не будет получен ответ Cake\Http\Server.

Теперь вы можете использовать удобный метод Cake\Http\Response::withLocation(), чтобы напрямую установить или получить заголовок местоположения перенаправления.

Настройка body

Cake\Http\Response::withStringBody($string)

Чтобы установить строку в качестве тела ответа, выполните следующие действия:

// Установите строку в тело
$response = $response->withStringBody('My Body');

// Если вам нужен json-ответ
$response = $response->withType('application/json')
    ->withStringBody(json_encode(['Foo' => 'bar']));

Добавлено в версии 3.4.3: withStringBody() был добавлен в 3.4.3

Cake\Http\Response::withBody($body)

Чтобы установить тело ответа, используйте метод withBody(), который предоставляется Zend\Diactoros\MessageTrait:

$response = $response->withBody($stream);

// До 3.4.0 - Установите тело
$this->response->body('My Body');

Убедитесь, что $stream - это Psr\Http\Message\StreamInterface объект. Ниже описано, как создать новый поток.

Вы также можете передавать ответы из файлов с помощью потоков Zend\Diactoros\Stream:

// Поток из файла
use Zend\Diactoros\Stream;

$stream = new Stream('/path/to/file', 'rb');
$response = $response->withBody($stream);

Вы также можете передавать ответы от обратного вызова с помощью CallbackStream. Это полезно, когда у вас есть ресурсы, такие как изображения, файлы CSV или PDF-файлы, которые необходимо передать клиенту:

// Потоковая передача с обратного вызова
use Cake\Http\CallbackStream;

// Создайте изображение.
$img = imagecreate(100, 100);
// ...

$stream = new CallbackStream(function () use ($img) {
    imagepng($img);
});
$response = $response->withBody($stream);

// До 3.4.0 вы можете использовать следующие для создания потоковых ответов.
$file = fopen('/some/file.png', 'r');
$this->response->body(function () use ($file) {
    rewind($file);
    fpassthru($file);
    fclose($file);
});

Установка кодировки символов

Cake\Http\Response::withCharset($charset)

Устанавливает кодировку, которая будет использоваться в ответе:

$this->response = $this->response->withCharset('UTF-8');

// До 3.4.0
$this->response->charset('UTF-8');

Взаимодействие с кэшированием браузера

Cake\Http\Response::withDisabledCache()

Иногда вам необходимо заставить браузеры не кэшировать результаты экшена контроллера. Cake\Http\Response::withDisabledCache() предназначен именно для этого:

public function index()
{
    // Отключить кеширование
    $this->response = $this->response->withDisabledCache();

    // До 3.4.0
    $this->response->disableCache();
}

Предупреждение

Отключение кэширования SSL-доменов, при попытке отправить файлы в Internet Explorer, может привести к ошибкам.

Cake\Http\Response::withCache($since, $time = '+1 day')

Вы также можете сообщить клиентам, что вы хотите, чтобы они кэшировали ответы. Используя Cake\Http\Response::withCache():

public function index()
{
    // Включить кеширование
    $this->response = $this->response->withCache('-1 minute', '+5 days');
}

Вышеизложенное, надеясь ускорить работу ваших посетителей, сообщит клиентам о кешировании полученного ответа в течение 5 дней. Метод withCache() устанавливает значение Last-Modified для первого аргумента. Заголовок Expires и директива max-age устанавливаются на основе второго параметра. Также будет задана директива public Cache-Control.

Точная настройка HTTP-кэша

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

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

Помимо использования Cake\Http\Response::withCache(), вы также можете использовать многие другие методы для тонкой настройки заголовков кэша HTTP, чтобы использовать преимущества кэширования браузера или обратного прокси.

Заголовок управления кэшем

Cake\Http\Response::withSharable($public, $time = null)

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

Cache-Control: private, max-age=3600, must-revalidate

Класс Response позволяет вам установить этот заголовок с помощью некоторых методов утилиты. Эти методы будут создавать окончательный допустимый заголовок Cache-Control. Во-первых, это withSharable() - метод, который указывает, должен ли ответ рассматриваться как доступный для разных пользователей или клиентов. Этот метод фактически управляет частью public или private этого заголовка. Установка ответа как приватный(закрытый) означает, что он весь или часть его предназначена для одного пользователя. Чтобы использовать общие кеши, директива управления должна быть установлена как публичный(общедоступный).

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

public function view()
{
    // ...
    // Установите Cache-Control как общедоступный на 3600 секунд
    $this->response = $this->response->withSharable(true, 3600);
}

public function my_data()
{
    // ...
    // Установите Cache-Control как приватный на 3600 секунд
    $this->response = $this->response->withSharable(false, 3600);
}

Response предоставляет отдельные методы для установки каждой из директив в заголовке Cache-Control.

Заголовок срока действия

Cake\Http\Response::withExpires($time)

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

public function view()
{
    $this->response = $this->response->withExpires('+5 days');
}

Этот метод также принимает экземпляр DateTime или любую строку, которая может быть проанализирована классом DateTime.

Заголовок Etag

Cake\Http\Response::withEtag($tag, $weak = false)

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

Метод withEtag() (называемый тегом сущности) представляет собой строку, которая уникально идентифицирует запрашиваемый ресурс, как контрольная сумма для файла, чтобы определить, соответствует ли он кэшированному ресурсу.

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

public function index()
{
    $articles = $this->Articles->find('all');
    $response = $this->response->withEtag($this->Articles->generateHash($articles));
    if ($response->checkNotModified($this->request)) {
        return $response;
    }
    $this->response = $response;
    // ...
}

Примечание

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

Заголовок Last-Modified

Cake\Http\Response::withModified($time)

В рамках модели проверки кэша HTTP вы можете установить заголовок Last-Modified, чтобы указать дату и время, последнего изменения ресурса. Настройка этого заголовка помогает CakePHP сообщать клиентам кэширования, основываясь на их кэше, был ли изменён ответ.

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

public function view()
{
    $article = $this->Articles->find()->first();
    $response = $this->response->withModified($article->modified);
    if ($response->checkNotModified($this->request)) {
        return $response;
    }
    $this->response;
    // ...
}

Заголовок Vary

Cake\Http\Response::withVary($header)

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

$response = $this->response->withVary('User-Agent');
$response = $this->response->withVary('Accept-Encoding', 'User-Agent');
$response = $this->response->withVary('Accept-Language');

Отправка ответов 304 Not Modified

Cake\Http\Response::checkNotModified(Request $request)

Сравнивает заголовки кэша для объекта запроса с заголовком кэша от ответа и определяет, может ли он всё еще считаться свежим. Если это так, удаляет контент ответа и отправляет заголовок 304 Not Modified:

// В экшене контроллера
if ($this->response->checkNotModified($this->request)) {
    return $this->response;
}

Настройка файлов cookie

Сookie-файлы могут быть добавлены в ответ, используя либо массив, либо объект Cake\Http\Cookie\Cookie:

// Добавление файла cookie в виде массива с использованием неизменяемого API (начиная с 3.4.0+)
$this->response = $this->response->withCookie('remember_me', [
    'value' => 'yes',
    'path' => '/',
    'httpOnly' => true,
    'secure' => false,
    'expire' => strtotime('+1 year')
]);

// До 3.4.0
$this->response->cookie('remember', [
    'value' => 'yes',
    'path' => '/',
    'httpOnly' => true,
    'secure' => false,
    'expire' => strtotime('+1 year')
]);

См. раздел Создание файлов cookie для использования объекта cookie. Вы можете использовать withExpiredCookie() для отправки истёкшего файла cookie в ответе. Это заставит браузер удалить локальный файл cookie:

// Начиная с 3.5.0
$this->response = $this->response->withExpiredCookie('remember_me');

Настройка Cross Origin Request Headers (CORS)

Начиная с 3.2 вы можете использовать метод cors(), для определения связанных заголовков HTTP Access Control [https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS], со свободным интерфейсом:

$this->response->cors($this->request)
    ->allowOrigin(['*.cakephp.org'])
    ->allowMethods(['GET', 'POST'])
    ->allowHeaders(['X-CSRF-Token'])
    ->allowCredentials()
    ->exposeHeaders(['Link'])
    ->maxAge(300)
    ->build();

Связанные заголовки CORS будут применяться только к ответу, если будут выполнены следующие критерии:

В запросе есть заголовок Origin.

Значение Origin запроса соответствует одному из допустимых значений Origin.

Добавлено в версии 3.2: CorsBuilder был добавлен в 3.2

Общие ошибки с неизменяемыми ответами

Начиная с CakePHP 3.4.0, объекты ответа предлагают ряд методов, которые обрабатывают ответы как неизменяемые объекты. Неизменяемые объекты помогают предотвратить трудное отслеживание случайных побочных эффектов и уменьшить ошибки, вызванные вызовами методов или рефакторингом, которые меняют порядок. Хотя они предлагают ряд преимуществ, неизменяемые объекты могут „вызвать привыкание“. Любой метод, начинающийся с with, неизменно действует на ответ, и всегда возвращает новый экземпляр. Забыть сохранить изменённый экземпляр - является наиболее частой ошибкой, которую люди совершают при работе с неизменяемыми объектами:

$this->response->withHeader('X-CakePHP', 'yes!');

В приведённом выше коде в ответе будет отсутствовать заголовок X-CakePHP, поскольку возвращаемое значение метода withHeader() не было сохранено. Чтобы исправить вышеуказанный код, вы должны написать:

$this->response = $this->response->withHeader('X-CakePHP', 'yes!');

Коллекции файлов cookie

class Cake\Http\Cookie\CookieCollection

Объекты CookieCollection доступны из объектов запроса и ответа. Они позволяют взаимодействовать с группами cookie с использованием неизменяемых шаблонов, которые позволяют сохранить неизменность запроса и ответа.

Создание файлов cookie

class Cake\Http\Cookie\Cookie

Объекты Cookie могут быть определены через объекты-конструкторы или с использованием свободного интерфейса, который следует за неизменными шаблонами:

use Cake\Http\Cookie\Cookie;

// Все аргументы в конструкторе
$cookie = new Cookie(
    'remember_me', // имя
    1, // значение
    new DateTime('+1 year'), // срок действия, если применимо
    '/', // путь, если применимо
    'example.com', // domain, если применимо
    false, // безопасно?
    true // только http?
);

// Использование методов построителя
$cookie = (new Cookie('remember_me'))
    ->withValue('1')
    ->withExpiry(new DateTime('+1 year'))
    ->withPath('/')
    ->withDomain('example.com')
    ->withSecure(false)
    ->withHttpOnly(true);

Когда вы создали файл cookie, вы можете добавить его в новый или существующий CookieCollection:

use Cake\Http\Cookie\CookieCollection;

// Создать новую коллекцию
$cookies = new CookieCollection([$cookie]);

// Добавить в существующую коллекцию
$cookies = $cookies->add($cookie);

// Удалить файл cookie по имени
$cookies = $cookies->remove('remember_me');

Примечание

Помните, что коллекции неизменяемы и добавляют файлы cookie или удаляются их из коллекции, создают new объект коллекции.

Вы должны использовать метод withCookie() для добавления файлов cookie к объектам Response, поскольку их проще использовать:

$response = $this->response->withCookie($cookie);

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

Чтение файлов cookie

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

// Проверьте, существует ли файл cookie
$cookies->has('remember_me');

// Получить количество файлов cookie в коллекции
count($cookies);

// Получить экземпляр cookie
$cookie = $cookies->get('remember_me');

Когда у вас есть объект Cookie, вы можете взаимодействовать с его состоянием и изменять его. Имейте в виду, что файлы cookie неизменяемы, поэтому вам нужно обновить коллекцию, если вы измените файл cookie:

// Получить значение
$value = $cookie->getValue()

// Доступ к данным внутри значения JSON
$id = $cookie->read('User.id');

// Проверить состояние
$cookie->isHttpOnly();
$cookie->isSecure();

Добавлено в версии 3.5.0: CookieCollection и Cookie были добавлены в 3.5.0.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Контроллеры

class Cake\Controller\Controller

Контроллеры - это то, что скрывается за буквой „C“ в понятии MVC. После того, как срабатывает маршрутизация, и обнаруживается правильный котроллер, происходит вызов нужного экшена. Ваш контроллер должен произвести верную интерпретацию данных запроса, убедившись, что будет вызвана необходимая модель, и возвращен верный вид и результат запроса. Контроллеры можно представить себе, как промежуточное звено между Моделью и Видом. Старайтесь сохранять ваши контроллеры компактными, помещая всю бизнес-логику внутри модели. Это сделает ваш код более гибким и облегчит его повторное использование и тестирование.

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

Все контроллеры вашего приложения наследуются от класса AppController, который в свою очередь наследуется от класса ядра Controller. Класс AppController может быть объявлен в файле src/Controller/AppController.php, и он должен содержать методы, которые будут общими для всех контроллеров вашего приложения.

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

Контроллер уровня приложения (AppController)

Как уже упоминалось во введении, класс AppController - это родительский класс для всех контроллеров вашего приложения. Сам по себе класс AppController наследуется от класса Cake\Controller\Controller, входящего в состав CakePHP. Класс AppController объявлен в файле src/Controller/AppController.php следующим образом:

namespace App\Controller;

use Cake\Controller\Controller;

class AppController extends Controller
{
}

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

Вы можете использовать ваш AppController для загрузки компонентов, которые будут использоваться в каждом контроллере вашего приложения. CakePHP предоставляет метод initialize(), который вызывается в конце коструктора Контроллера:

namespace App\Controller;

use Cake\Controller\Controller;

class AppController extends Controller
{

    public function initialize()
    {
        // Компонент CSRF всегда включен.
        $this->loadComponent('Csrf');
    }

}

В дополнение к методу initialize(), более старое свойство $components также позволит вам объявлять обязательные к загрузке компоненты. В то время, как применяются стандартные правила наследования ООП, компоненты и хелперы, используемые контроллером обрабатываются по-особому. В данном случае значения свойств класса AppController объединяются с массивами дочерних классов контроллеров. Значения дочерних классов всегда переопределяют значения из AppController.

Поток запросов

Когда к приложению CakePHP делается запрос, классы CakePHP Cake\Routing\Router и Cake\Routing\Dispatcher используют Подключение маршрутов для обнаружения и создания верного экземпляра класса контроллера. Данные запроса инкапсулируются в объект запроса. CakePHP помещает всю важную информацию из запроса в свойство $this->request. Для более подробной информации об объекте запроса смотрите раздел Запрос.

Экшены контроллера

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

По соглашению, CakePHP обрабатывает вид с именем, соответствующим имени экшена. Возвращаясь к нашему примеру онлайн пекарни, наш контроллер может содержать экшены view(), share(), и search(). Контроллер может быть найден в файле src/Controller/RecipesController.php и содержать следующий код:

// src/Controller/RecipesController.php

class RecipesController extends AppController
{
    public function view($id)
    {
        // Здесь описывается вся логика экшена.
    }

    public function share($customerId, $recipeId)
    {
        // Здесь описывается вся логика экшена.
    }

    public function search($query)
    {
        // Здесь описывается вся логика экшена.
    }
}

Файлы шаблонов для этих экшенов, назывались бы src/Template/Recipes/view.ctp, src/Template/Recipes/share.ctp, и src/Template/Recipes/search.ctp. По соглашениям имя файла вида соответствует имени экшена контроллера, записанном в нижнем регистре с использованием подчеркиваний для разделения слов.

Экшены контроллера обычно используют метод Controller::set() для создания контекста, используемого View для обработки слоя вида. Благодаря соглашениям, используемым CakePHP, вам не нужно самим создавать и обрабатывать виды. Вместо этого как только сработает экшен контроллера, CakePHP сам произведет обработку и передачу Вида.

Если по каким-либо причинам вам захочется изменить стандартное поведение, вы можете вернуть объект Cake\Http\Response из экшена с полностью созданным ответом.

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

Взаимодействие с видами

Контроллеры взаимодействуют с видами несколькими способами. Во-первых, они способны передавать данные видам, используя метод Controller::set(). Вы также можете решить, какой класс вида использовать, какой файл вида должен быть выведен контроллером.

Назначение переменных вида

Cake\Controller\Controller::set(string $var, mixed $value)

Метод Controller::set() - это основной способ передачи данных от вашего контроллера в ваш вид. Как только вы используете Controller::set(), переменная становится доступной в вашем виде:

// Сначала передаете данные от контроллера:

$this->set('color', 'розовая');

// После этого, в виде вы можете использовать данные:
?>

Вы выбрали <?= h($color) ?> глазурь для торта.

Метод Controller::set() также принимает ассоциативный массив в качестве своего первого параметра. Это часто может быть быстрым способом назначить набор сведений для представления:

$data = [
    'color' => 'pink',
    'type' => 'sugar',
    'base_price' => 23.95
];

// Делаем $color, $type, и $base_price
// доступными в виде:

$this->set($data);

Установка параметров вида

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

$this->viewBuilder()
    ->helpers(['MyCustom'])
    ->theme('Modern')
    ->className('Modern.Admin');

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

Добавлено в версии 3.1: ViewBuilder был добавлен в версии 3.1

Обработка вида

Cake\Controller\Controller::render(string $view, string $layout)

Метод Controller::render() автоматически вызывается в конце каждого запрошенного экшена контроллера. Этот метод выполняет всю логику вида (используя данные, переданные методом Controller::set()), помещает вид в его макет View::$layout, и отдает обратно пользователю.

Стандартный используемый файл вида определяется соглашениями. Если запрашивается экшен search() из RecipesController, будет обработан файл вида src/Template/Recipes/search.ctp:

namespace App\Controller;

class RecipesController extends AppController
{
// ...
    public function search()
    {
        // Обработка вида из src/Template/Recipes/search.ctp
        $this->render();
    }
// ...
}

Хотя CakePHP будет автоматически вызывать его после логики каждого экшена (если вы не задали параметру $this->autoRender значение false), вы можете использовать его для указания альтернативного файла вида, указав имя файла в качестве первого аргумента метода Controller::render().

Если $view начинается с „/“, предполагается, что путь к этому файлу вида или элементу задан относительно папки src/Template. Это упрощает обращение к элементам, к примеру в вызовах AJAX:

// Обработка элемента из src/Template/Element/ajaxreturn.ctp
$this->render('/Element/ajaxreturn');

Второй параметр $layout метода Controller::render() позволяет вам определить макет внутри которого вид будет отображаться.

Обработка определенного шаблона

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

namespace App\Controller;

class PostsController extends AppController
{
    public function my_action()
    {
        $this->render('custom_file');
    }
}

Это отобразило бы src/Template/Posts/custom_file.ctp вместо src/Template/Posts/my_action.ctp.

Вы также можете отображать виды из плагинов следующим образом: $this->render('НазваниеПлагина.КонтроллерПлагина/custom_file'). К примеру:

namespace App\Controller;

class PostsController extends AppController
{
    public function my_action()
    {
        $this->render('Users.UserDetails/custom_file');
    }
}

Это отобразило бы plugins/Users/src/Template/UserDetails/custom_file.ctp

Перенаправление на другие страницы

Cake\Controller\Controller::redirect(string|array $url, integer $status)

Метод управления потоком, который вы будете использовать чаще всего это Controller::redirect(). Этот метод принимает свой первый параметр в виде CakePHP-относительного URL. Когда пользователь успешно разместил заказ, вы можете перенаправить его на экран квитанции.

public function place_order()
{
    // Логика для завершения заказа размещается здесь
    if ($success) {
        return $this->redirect(
            ['controller' => 'Orders', 'action' => 'thanks']
        );
    }
    return $this->redirect(
        ['controller' => 'Orders', 'action' => 'confirm']
    );
}

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

Вы также можете использовать относительный или абсолютный URL в качестве значения аргумента $url:

return $this->redirect('/orders/thanks');
return $this->redirect('http://www.example.com');

Также вы можете передавать данные в экшен:

return $this->redirect(['action' => 'edit', $id]);

Второй параметр метода Controller::redirect() позволяет вам определять код HTTP-статуса сопровождающий перенаправление. Возможно вы захотите использовать код 301 (moved permanently) или 303 (see other), в зависимости от природы перенапрвления.

Если вы хотите осуществить перенаправление на страницу-источник запроса, вы можете сделать это следующим образом:

return $this->redirect($this->referer());

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

return $this->redirect([
    'controller' => 'Orders',
    'action' => 'confirm',
    '?' => [
        'product' => 'pizza',
        'quantity' => 5
    ],
    '#' => 'top'
]);

Это создаст следующий URL:

http://www.example.com/orders/confirm?product=pizza&quantity=5#top

Перенаправление на другой экшен внутри того же контроллера

Cake\Controller\Controller::setAction($action, $args...)

Если вам нужно перенаправить текущий экшен на другой в том же контроллере, вы можете использовать Controller::setAction() для обновления объекта запроса, изменить шаблон вида, который будет отображаться, и выполнить переадресацию к указанному экшену:

// Из экшена delete, вы можете вывести обновленную
// страницу списка.
$this->setAction('index');

Загрузка дополнительных моделей

Cake\Controller\Controller::loadModel(string $modelClass, string $type)

Функция loadModel() удобна, когда вам нужно использовать таблицу/коллекцию модели, которая не является стандартной для контроллера:

// В методе контроллера.
$this->loadModel('Articles');
$recentArticles = $this->Articles->find('all', [
    'limit' => 5,
    'order' => 'Articles.created DESC'
]);

Если вы используете поставщика таблиц, отличного от встроенной ORM, вы можете привязать эту систему таблиц к контроллерам CakePHP, подключив ее фабричный метод:

// В методе контроллера.
$this->modelFactory(
    'ElasticIndex',
    ['ElasticIndexes', 'factory']
);

После этого вы также сможете использовать loadModel для загрузки экземпляров:

// В методе контроллера.
$this->loadModel('Locations', 'ElasticIndex');

Примечание

TableRegistry встроенной ORM подключена по умолчанию как провайдер „Table“.

Пагинация модели

Cake\Controller\Controller::paginate()

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

Атрибут $paginate дает вам простой способ настройки поведения метода paginate():

class ArticlesController extends AppController
{
    public $paginate = [
        'Articles' => [
            'conditions' => ['published' => 1]
        ]
    ];
}

Настройка загружаемых компонентов

Cake\Controller\Controller::loadComponent($name, $config = [])

В методе initialize() вашего Контроллера вы можете определить любые компоненты, которые вы хотите загрузить, а также любые данные об их конфигурации:

public function initialize()
{
    parent::initialize();
    $this->loadComponent('Csrf');
    $this->loadComponent('Comments', Configure::read('Comments'));
}

property Cake\Controller\Controller::$components

Свойство $components в ваших контроллерах позволяет вам настроить компоненты. Конфигурируемые компоненты и их зависимости будут созданы CakePHP для вас. Прочтите раздел Настройка компонентов для более подробной информации. Как упоминалось ранее, свойство $components будет объединено со свойством, определенным в каждом из родительских классов вашего контроллера.

Настройка загружаемых хелперов

property Cake\Controller\Controller::$helpers

Давайте посмотрим, как сказать CakePHP, что мы хотим использовать дополнительные классы MVC:

class RecipesController extends AppController
{
    public $helpers = ['Form'];
}

Каждая из этих переменных объединяется с их унаследованными значениями, поэтому нет необходимости (к примеру) объявлять заново FormHelper или что-либо, что объявлено в вашем AppController.

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

Коллбеки жизненного цикла запроса

Контроллеры CakePHP запускают несколько событий/коллбеков, которые можно использовать для вставки логики вокруг жизненного цикла запроса:

Список событий

Controller.initialize

Controller.startup

Controller.beforeRedirect

Controller.beforeRender

Controller.shutdown

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

По умолчанию следующие методы обратного вызова связаны с соответствующими событиями, если методы реализуются вашими контроллерами

Cake\Controller\Controller::beforeFilter(Event $event)

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

Примечание

Метод beforeFilter() будет вызываться для отсутствующих экшенов.

Возврат ответа от метода beforeFilter() не будет препятствовать вызову других слушателей того же события. Вы должны явно stop the event.

Cake\Controller\Controller::beforeRender(Event $event)

Вызывается во время события Controller.beforeRender, которое происходит после логики экшена контроллера, но перед визуализацией вида. Этот коллбек не используется часто, но может потребоваться, если вы вызываете вручную метод Controller\Controller::render() перед окончанием экшена.

Cake\Controller\Controller::afterFilter(Event $event)

Вызывается во время события Controller.shutdown, которое запускается после каждого экшена контроллера и после завершения рендеринга. Это последний метод контроллера для запуска.

В дополнение к коллбекам жизненного цикла контроллера Компоненты также предоставляют схожий набор коллбеков.

Не забудьте вызвать коллбеки AppController в коллбек-методах дочерних контроллеров для лучших результатов:

//use Cake\Event\Event;
public function beforeFilter(Event $event)
{
    parent::beforeFilter($event);
}

Подробнее о контроллерах

Контроллер страниц

Компоненты

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Контроллер страниц

Официальное приложение скелета CakePHP поставляется с контроллером по умолчанию PagesController.php. Это простой и дополнительный контроллер для обслуживания статического контента. Домашняя страница, которую вы видите после установки, создается с помощью этого контроллера и файла вида src/Template/Pages/home.ctp. Если вы создадите файл src src/Template/Pages/about_us.ctp, вы можете получить к нему доступ, используя URL http://example.com/pages/about_us. Вы можете изменять контроллер страниц в соответствии с вашими потребностями.

Когда вы «запекаете» приложение с помощью Composer, контроллер страниц создается в вашей папке src src/Controller/.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Компоненты

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

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

Аутентификация

Куки

Межсайтовая подделка запроса

Флэш

Безопасность

Пагинация

Обработка запроса

Настройка компонентов

Многие из основных компонентов требуют конфигурации. Вот некоторые примеры компонентов требующие конфигурации Аутентификация и Куки. Конфигурация для этих компонентов, и для компонентов в целом обычно выполняется с помощью loadComponent() в методе initialize() вашего контроллера или в массиве $components:

class PostsController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Auth', [
            'authorize' => 'Controller',
            'loginAction' => ['controller' => 'Users', 'action' => 'login']
        ]);
        $this->loadComponent('Cookie', ['expires' => '1 day']);
    }

}

Вы можете настроить компоненты во время выполнения с помощью метода config(). Часто, это делается в методе beforeFilter() вашего контроллера. Вышеизложенное может выражаться так:

public function beforeFilter(Event $event)
{
    $this->Auth->config('authorize', ['controller']);
    $this->Auth->config('loginAction', ['controller' => 'Users', 'action' => 'login']);

    $this->Cookie->config('name', 'CookieMonster');
}

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

// Чтение данных конфигурации.
$this->Auth->config('loginAction');

// Установка конфигурации
$this->Csrf->config('cookieName', 'token');

Как и с помощниками, компоненты автоматически объединяют своё свойство $_defaultConfig с конфигурацией конструктора для создания свойства $_config, которое доступно с помощью метода config().

Псевдонимы компонентов

Одной из общих настроек для использования является опция className, которая позволяет вам использовать псевдонимы. Эта функция полезна, если вы хотите заменить $this->Auth или другую общую ссылку на компонент - своей пользовательской реализацией:

// src/Controller/PostsController.php
class PostsController extends AppController
{
    public function initialize()
    {
        $this->loadComponent('Auth', [
            'className' => 'MyAuth'
        ]);
    }
}

// src/Controller/Component/MyAuthComponent.php
use Cake\Controller\Component\AuthComponent;

class MyAuthComponent extends AuthComponent
{
    // Добавьте сюда свой код для переопределения ядра AuthComponent
}

Вышеупомянутый псевдоним MyAuthComponent для $this->Auth бедет доступен во всех ваших контроллерах.

Примечание

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

Загрузка компонентов на лету

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

// В определённом методе контроллера
$this->loadComponent('OneTimer');
$time = $this->OneTimer->getTime();

Примечание

Имейте в виду, что компоненты, загруженные «на лету», не будут пропущены через обратные вызовы. Если вы нуждаетесь в обратных вызовах (колбэках) beforeFilter или startup, вам может потребоваться вызвать их вручную, в зависимости от того, когда вы загружаете свой компонент.

Использование компонентов

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

class PostsController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Flash');
    }

    public function delete()
    {
        if ($this->Post->delete($this->request->getData('Post.id')) {
            $this->Flash->success('Post deleted.');
            return $this->redirect(['action' => 'index']);
        }
    }

Примечание

Поскольку как модель, так и компонент добавляются к контроллерам как свойства и имеют одно и то же „namespace“ (пространство имён), важно не дать компоненту и модели одинаковое имя.

Создание компонента

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

Первый шаг - создать новый компонентный файл и класс. Создайте файл в src/Controller/Component/MathComponent.php. Основная структура компонента будет выглядеть примерно так:

namespace App\Controller\Component;

use Cake\Controller\Component;

class MathComponent extends Component
{
    public function doComplexOperation($amount1, $amount2)
    {
        return $amount1 + $amount2;
    }
}

Примечание

Все компоненты должны размещаться в Cake\Controller\Component. Ошибка размещения компонента вызовет исключение.

Подключение вашего компонента в контроллеры

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

// В контроллере
// Создаем новый компонент в $this->Math,
// а также стандартный $this->Csrf
public function initialize()
{
    parent::initialize();
    $this->loadComponent('Math');
    $this->loadComponent('Csrf');
}

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

// В вашем контроллере
public function initialize()
{
    parent::initialize();
    $this->loadComponent('Math', [
        'precision' => 2,
        'randomGenerator' => 'srand'
    ]);
    $this->loadComponent('Csrf');
}

Вышеизложенное передало бы массив, содержащий точность и randomGenerator в метод MathComponent::initialize(), в параметре $config.

Использование других компонентов в вашем компоненте

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

// src/Controller/Component/CustomComponent.php
namespace App\Controller\Component;

use Cake\Controller\Component;

class CustomComponent extends Component
{
    // Другой компонент, используемый вашим компонентом
    public $components = ['Existing'];

    // Выполните любую другую дополнительную настройку для своего компонента.
    public function initialize(array $config)
    {
        $this->Existing->foo();
    }

    public function bar()
    {
        // ...
    }
}

// src/Controller/Component/ExistingComponent.php
namespace App\Controller\Component;

use Cake\Controller\Component;

class ExistingComponent extends Component
{

    public function foo()
    {
        // ...
    }
}

Примечание

В отличие от компонента, включенного в контроллер, никакие обратные вызовы не будут инициированы на компоненте компонента.

Доступ к компонентам контроллера

Внутри компонента вы можете получить доступ к текущему контроллеру через „реестр“:

$controller = $this->_registry->getController();

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

$controller = $event->getSubject();

Обратные вызовы компонента

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

beforeFilter(Event $event)

Вызывается перед методом beforeFilter() контроллеров, но после метода initialize().

startup(Event $event)

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

beforeRender(Event $event)

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

shutdown(Event $event)

Вызывается до вывода в браузер.

beforeRedirect(Event $event, $url, Response $response)

Вызывается когда методом контроллера осуществляет перенаправление, но перед любым дальнейшим действием. Если этот метод возвращает false, контроллер не будет продолжать перенаправлять запрос. Параметры $url и $response позволяют вам проверять и изменять местоположение или любые другие заголовки в ответе.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Аутентификация

class AuthComponent(ComponentCollection $collection, array $config = [])

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

Рекомендуется к прочтению

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

Это все описывается по шагам в Руководстве по созданию CMS.

Если вы ищете готовые решения по аутентификации и/или авторизации для CakePHP, взгляните на раздел Аутентификация и Авторизация [https://github.com/FriendsOfCake/awesome-cakephp/blob/master/README.md#authentication-and-authorization] списка Awesome CakePHP.

Аутентификация

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

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

BasicAuthenticate предоставляет возможности Базовой HTTP-аутентификации.

DigestAuthenticate предоставляет возможности Дайджест-аутентификации.

По умолчанию AuthComponent использует FormAuthenticate.

Выбор типа аутентификации

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

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

Настройка обработчиков аутентификации

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

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

// Простая настройка
$this->Auth->config('authenticate', ['Form']);

// Передача параметров
$this->Auth->config('authenticate', [
    'Basic' => ['userModel' => 'Members'],
    'Form' => ['userModel' => 'Members']
]);

Во втором примере вы возможно обратили внимание, что ключ userModel был объявлен дважды. Чтобы ваш код соответствовал принципам DRY (не повторяйся), вы можете использовать ключ all. Этот специальный ключ позволяет вам устанавливать параметры, которые вы передаете к каждому прикрепленному объекту. Ключ all также доступен в качестве статического свойства AuthComponent::ALL:

// Передача параметров с помощию 'all'
$this->Auth->config('authenticate', [
    AuthComponent::ALL => ['userModel' => 'Members'],
    'Basic',
    'Form'
]);

В приведенном выше примере и Form и Basic будут получать настройки, объявленные в ключе „all“. Любые настройки, переданные конкретному объекту аутентификации будут переопределять соответствующие ключи внутри ключа „all“. Объекты аутентификации ядра поддерживают следующие ключи конфигурации.

fields Поля, используемые для аутентификации пользователя. Вы можете использовать ключи username и password, чтобы указать поля для имени пользователя и пароля соответственно.

userModel Имя модели для таблицы пользователей; По умолчанию - Users.

finder Метод-файндер для получения записи из таблицы пользователей. По умолчанию устновлен в „all“.

passwordHasher Класс хешера паролей; По умолчанию Default.

Опции scope и contain являются устаревшими в версии 3.1. Используйте вместо них пользовательские файндеры для изменения запроса на выборку записи пользователя.

Опция userFields является устаревшей в версии 3.1. Используйте метод select() в вашем пользовательском файндере.

Чтобы настроить дополнительные поля для записи пользвателя в методе initialize():

public function initialize()
{
    parent::initialize();
    $this->loadComponent('Auth', [
        'authenticate' => [
            'Form' => [
                'fields' => ['username' => 'email', 'password' => 'passwd']
            ]
        ]
    ]);
}

Не помещайте другие ключи конфигурации Auth, такие как authError, loginAction, и т.д. внутрь элементов authenticate или Form. Они должны находиться на одном с ними уровне. Приведенная выше настройка конфигурации компонента Auth с использованием остальных параметров должна выглядеть так:

public function initialize()
{
    parent::initialize();
    $this->loadComponent('Auth', [
        'loginAction' => [
            'controller' => 'Users',
            'action' => 'login',
            'plugin' => 'Users'
        ],
        'authError' => 'Вы правда думали, что вам можно видеть это?',
        'authenticate' => [
            'Form' => [
                'fields' => ['username' => 'email']
            ]
        ],
        'storage' => 'Session'
    ]);
}

В дополнение к стандартной конфигурации, Базовая аутентификация (Basic) также поддерживает следующие ключи:

realm Область, для которой предназначена аутентификация. По умолчанию env('SERVER_NAME').

В дополнение к стандартной конфигурации, Дайджест-аутентификация также поддерживает следующие ключи:

realm Область, для которой предназначена аутентификация. По умолчанию servername (имя сервера).

nonce Значение nonce для аутентификации. По умолчанию uniqid().

qop По умолчанию auth; другие значения пока не поддерживаются.

opaque Строка, которая должна быть возвращена в неизменном виде клиентами. По умолчанию md5($config['realm']).

Примечание

Чтобы найти запись пользователя, запрос к базе данных происходит только с использованием имени пользователя. Проверка пароля производится в PHP. Это связано с тем, что алгоритмы хеширования, такие как bcrypt (алгоритм по умолчанию) генерируют новый хеш каждый раз, даже для неизменной строки, и в данном случае обычное сравнение строк в SQL становится неприменимым для проверки пароля.

Кастомизация поискового запроса

Вы можете кастомизировать запрос на выборку записи пользователя с помощью опции finder в группе параметров authenticate:

public function initialize()
{
    parent::initialize();
    $this->loadComponent('Auth', [
        'authenticate' => [
            'Form' => [
                'finder' => 'auth'
            ]
        ],
    ]);
}

Это потребует наличия поискового метода findAuth() в вашем классе модели UsersTable. В приведенном ниже примере запрос скорректирован для выборки значений только из необходимых полей и добавлено условие выборки значений. Вы должны убедиться, что происходит выборка значений из необходимых полей, таких как username и password:

public function findAuth(\Cake\ORM\Query $query, array $options)
{
    $query
        ->select(['id', 'username', 'password'])
        ->where(['Users.active' => 1]);

    return $query;
}

Примечание

Опция finder доступна только с версии 3.1. В более ранних версиях вы можете использовать опции scope и contain для изменения запроса.

Идентификация и вход пользователей

AuthComponent::identify()

Вам необходимо вручную вызывать $this->Auth->identify(), чтобы идентифицировать пользователя, используя учетные данные предоставленные в запросе. После этого вы должны использовать метод $this->Auth->setUser(), чтобы пользователь вошел в приложение, то есть данные о нем сохранились в сессии.

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

public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $this->Auth->setUser($user);
            return $this->redirect($this->Auth->redirectUrl());
        } else {
            $this->Flash->error(__('Username or password is incorrect'));
        }
    }
}

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

Предупреждение

Метод $this->Auth->setUser($data) авторизует пользователя, независимо от того, какие данные были ему переданы. Он не будет проверять пользовательские данные на соответствие классу аутентификации.

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

AuthComponent::redirectUrl()

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

Если параметр не будет передан, возвращаемый URL будет подчиняться следующим правилам:

Возвращается нормализованный URL из значения redirect строки запроса если он существует и находится в тод же домене, что и текущее приложение. До версии 3.4.0 использовалось значение сессионной переменной Auth.redirect.

Если в строке запроса/сессии нужное значение отсутствует, но присвоено какое-либо значение параметру конфигурации loginRedirect, то будет возвращено это значение.

Если же и в параметре loginRedirect не окажется нужного значения, будет возвращен /.

Создание системы аутентификации без сохранения состояния

Базовая и Дайджест-аутентификация - это системы аутентификации не сохраняющие состояние, и не требующие исходных POST-данных или формы. Если вы используете только эти два способа аутентификации, вашему котроллеру необязательно наличие экшена входа в систему (login). Cистема аутентификации без сохранения состояния перепроверяет данные пользователя при каждом запросе. это создает небольшое количество дополнительных накладных расходов, но позволяет клиентам осуществлять вход без использования куки и делает AuthComponent более гибким при создании API.

Для аутентификаторов без сохранения состояния параметр конфигурации storage следует установить в Memory, чтобы AuthComponent не использовал сеccию для хранения записи пользователя. Вы также можете настроить параметр конфигурации unauthorizedRedirect в false, чтобы AuthComponent выбрасывал ForbiddenException вместо поведения по умолчанию - перенаправления на ссылающуюся страницу.

..note::

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

Объекты аутентификации могут реализовывать метод getUser(), который может использоваться для поддержки систем входа пользователя, независящих от файлов cookie. Типичный метод getUser() рассматривает запрос/среду и использует эту информацию для подтверждения личности пользователя. Например, Базовая HTTP-аутентификация использует $_SERVER['PHP_AUTH_USER'] и $_SERVER['PHP_AUTH_PW'] для полей имени пользователя и пароля.

Примечание

Если аутентификация не работает как ожидается, проверьте, выполняются ли вообще запросы (смотрите BaseAuthenticate::_query($username)). Если запросы не выполняются, проверьте заполняются ли веб-сервером ключи $_SERVER['PHP_AUTH_USER'] и $_SERVER['PHP_AUTH_PW']. Если вы используете Apache с FastCGI-PHP, вам возможно потребуется добавить следующую строку в ваш корневой файл .htaccess:

RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

При каждом запросе данные значения, PHP_AUTH_USER и PHP_AUTH_PW, используются для повторной идентификации пользователя, чтобы убедиться в их подлинности. Как и в случае с методом объекта аутентификации authenticate(), метод getUser() должен возвращать массив с информацией о пользователе, либо false в случае неудачи.:

public function getUser(ServerRequest $request)
{
    $username = env('PHP_AUTH_USER');
    $pass = env('PHP_AUTH_PW');

    if (empty($username) || empty($pass)) {
        return false;
    }
    return $this->_findUser($username, $pass);
}

Пример выше показывает, как вы можете реализовать метод getUser() для Базовой HTTP-аутентификации. Метод _findUser() является частью BaseAuthenticate, и идентифицирует пользователя на основе имени пользователя и пароля.

Использование базовой аутентификации

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

Предупреждение

Базовая аутентификация передает пользовательские данные в виде открытого текста. Вы должны использовать протокол HTTPS при использовании Базовой аутентификации.

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

$this->loadComponent('Auth', [
    'authenticate' => [
        'Basic' => [
            'fields' => ['username' => 'username', 'password' => 'api_key'],
            'userModel' => 'Users'
        ],
    ],
    'storage' => 'Memory',
    'unauthorizedRedirect' => false
]);

Здесь мы используем имя пользователя + ключ API в качестве наших полей, а также используем модель Users.

Создание ключей API для Базовой аутентификации

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

namespace App\Model\Table;

use Cake\Auth\DefaultPasswordHasher;
use Cake\Utility\Text;
use Cake\Event\Event;
use Cake\ORM\Table;

class UsersTable extends Table
{
    public function beforeSave(Event $event)
    {
        $entity = $event->getData('entity');

        if ($entity->isNew()) {
            $hasher = new DefaultPasswordHasher();

            // Генерируем 'токен' API
            $entity->api_key_plain = Security::hash(Security::randomBytes(32), 'sha256', false);

            // Хешируем токен с помощью Bcrypt, чтобы BasicAuthenticate
            // мог его проверить при входе.
            $entity->api_key = $hasher->hash($entity->api_key_plain);
        }
        return true;
    }
}

Приведенный выше код генерирует случайный хеш для каждого пользователя по мере их сохранения. В приведенном выше коде предполагается, что у вас есть два поля api_key - для хранения хэшированного API-ключа и api_key_plain - для открытой текстовой версии ключа API, чтобы мы могли позже отобразить его пользователю. Использование ключа вместо пароля означает, что даже через простой протокол HTTP пользователи могут использовать непрозрачный токен вместо исходного пароля. Также разумно включить логику, позволяющую восстанавливать ключи API по запросу пользователя.

Использование Дайджест-аутентификации

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

Чтобы использовать дайджест-аутентификацию, вам понадобится настроить AuthComponent:

$this->loadComponent('Auth', [
    'authenticate' => [
        'Digest' => [
            'fields' => ['username' => 'username', 'password' => 'digest_hash'],
            'userModel' => 'Users'
        ],
    ],
    'storage' => 'Memory',
    'unauthorizedRedirect' => false
]);

Здесь мы используем имя пользователя + digest_hash в качестве наших полей и используем модель Users.

Хеширование паролей для дайджест-аутентификации

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

namespace App\Model\Table;

use Cake\Auth\DigestAuthenticate;
use Cake\Event\Event;
use Cake\ORM\Table;

class UsersTable extends Table
{
    public function beforeSave(Event $event)
    {
        $entity = $event->getData('entity');

        // Создание пароля для дайджест-аутентификации.
        $entity->digest_hash = DigestAuthenticate::password(
            $entity->username,
            $entity->plain_password,
            env('SERVER_NAME')
        );
        return true;
    }
}

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

Примечание

Третий параметр метода DigestAuthenticate::password() должен совпадать со значением параметра конфигурации „realm“, объявленным, когда DigestAuthentication настраивалось в AuthComponent::$authenticate. По умолчанию его значение - это env('SCRIPT_NAME'). Возможно вам захочется изменить это значение на какую-нибудь статичную строку, если вы например хотите иметь согласованные хеши в различных окружениях.

Создание кастомных объектов аутентификации

Поскольку объекты аутентификации являются подключаемыми, вы можете создавать собственные объекты аутентификации в своем приложении или плагинах. Например, если вы хотите создать объект аутентификации OpenID. В src/Auth/OpenidAuthenticate.php вы можете указать следующее:

namespace App\Auth;

use Cake\Auth\BaseAuthenticate;
use Cake\Http\ServerRequest;
use Cake\Http\Response;

class OpenidAuthenticate extends BaseAuthenticate
{
    public function authenticate(ServerRequest $request, Response $response)
    {
        // Делаем здесь необходимые действия для OpenID.
        // Возвращаем здесь массив с данным о пользователе,
        // либо возвращаем false в случае неудачи.
    }
}

Объекты аутентификации должны возвращать false, если идентификация пользователя не удалась, либо массив с информацией о пользователе в противном случае. Необязательно наследоваться от класса BaseAuthenticate, вы можете просто реализовать интерфейс Cake\Event\EventListenerInterface. Класс BaseAuthenticate предоставляет несколько полезных методов, которые часто используются. Также вы можете реализовать метод getUser(), если ваш объект аутентификации поддерживает аутентификацию без сохранения состояния или же без использования куки-файлов. Смотрите разделы по базовой и дайджест-аутентификации ниже для более полной информации.

AuthComponent запускает два события, Auth.afterIdentify и Auth.logout, после того, как пользователь был идентифицирован и перед его выходом из приложения соответственно. Вы можете назначить коллбэк-функции для этих событий, задав их в качестве значений в ассоциативном массиве внутри метода implementedEvents() вашего класса аутентификации:

public function implementedEvents()
{
    return [
        'Auth.afterIdentify' => 'afterIdentify',
        'Auth.logout' => 'logout'
    ];
}

Использование кастомных объектов аутентификации

После того как вы создали свои собственные объекты аутентификации, вы можете использовать их включая их в массив authenticate компонента AuthComponent:

$this->Auth->config('authenticate', [
    'Openid', // объект аутентификации приложения.
    'AuthBag.Openid', // объект аутентификации плагина.
]);

Примечание

Обратите внимание, что при использовании простых обозначений при инициализации объекта аутентификации нет слова „Authenticate“. Если вы все же используете пространства имен, вам нужно будет установить полное пространство имен класса, включая слово „Authenticate“.

Обработка неаутентифицированных запросов

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

Если аутентификатор возвращает null, то AuthComponent перенаправляет пользователя на экшен входа(login). Если же это AJAX-запрос, и параметр конфигурации ajaxLogin указывает, что элемент визуализируется иначе, то будет возвращен код состояния HTTP 403.

Вывод флэш-сообщений компонента Auth

Чтобы отображать сообщения об ошибках сессии, генерируемые Auth, вам нужно добавить следующий код в свой макет (layout). Добавьте следующие две строки в файл src/Template/Layout/default.ctp в разделе body:

// Все, что необходимо для версий начиная с 3.4.0
echo $this->Flash->render();

// Для версий, предшествующих 3.4.0, потребуется следующее
echo $this->Flash->render('auth');

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

key - Используемый ключ, по умолчанию „default“. В версиях ниже 3.4.0, по умолчанию использовалось значение „auth“.

element - Имя элемента использовать для визуализации, по умолчанию null.

params - Массив дополнительных используемых параметров, по умолчанию [].

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

$this->Auth->config('authError', "Упс, вы не авторизованы для получения доступа в этой области.");

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

В методе beforeFilter() вашего контроллера или в настройках компонента:

if (!$this->Auth->user()) {
    $this->Auth->config('authError', false);
}

Хеширование паролей

Вы несете ответственность за хэширование паролей, перед тем, как они будут сохранены в базе данных, самый простой способ - использовать функцию-сеттер в вашей сущности User:

namespace App\Model\Entity;

use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Entity;

class User extends Entity
{

    // ...

    protected function _setPassword($password)
    {
        if (strlen($password) > 0) {
          return (new DefaultPasswordHasher)->hash($password);
        }
    }

    // ...
}

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

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

Создание пользовательских классов хеширования паролей

Чтобы использовать другой хешер пароля, вам необходимо создать класс в src/Auth/LegacyPasswordHasher.php и реализовать методы hash() и check(). Этот класс должен наследоваться от класса AbstractPasswordHasher:

namespace App\Auth;

use Cake\Auth\AbstractPasswordHasher;

class LegacyPasswordHasher extends AbstractPasswordHasher
{

    public function hash($password)
    {
        return sha1($password);
    }

    public function check($password, $hashedPassword)
    {
        return sha1($password) === $hashedPassword;
    }
}

После чего вам необходимо настроить AuthComponent для использования вашего собственного хешера паролей:

public function initialize()
{
    parent::initialize();
    $this->loadComponent('Auth', [
        'authenticate' => [
            'Form' => [
                'passwordHasher' => [
                    'className' => 'Legacy',
                ]
            ]
        ]
    ]);
}

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

Изменение алгоритмов хэширования

CakePHP предоставляет чистый способ переноса паролей пользователей с одного алгоритма на другой, это достигается с помощью класса FallbackPasswordHasher. Предполагая, что вы переносите приложение с CakePHP 2.x, который использует хэши паролей sha1, вы можете настроить AuthComponent следующим образом:

public function initialize()
{
    parent::initialize();
    $this->loadComponent('Auth', [
        'authenticate' => [
            'Form' => [
                'passwordHasher' => [
                    'className' => 'Fallback',
                    'hashers' => [
                        'Default',
                        'Weak' => ['hashType' => 'sha1']
                    ]
                ]
            ]
        ]
    ]);
}

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

При использовании WeakPasswordHasher` вам нужно будет установить значение ``Security.salt, чтобы гарантировать надежность паролей засчет использования так называемой «соли».

Чтобы обновить пароли старых пользователей на лету, вы можете изменить функцию входа соответствующим образом:

public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $this->Auth->setUser($user);
            if ($this->Auth->authenticationProvider()->needsPasswordRehash()) {
                $user = $this->Users->get($this->Auth->user('id'));
                $user->password = $this->request->getData('password');
                $this->Users->save($user);
            }
            return $this->redirect($this->Auth->redirectUrl());
        }
        ...
    }
}

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

Вход пользователей вручную

AuthComponent::setUser(array $user)

Иногда возникает необходимость, когда вам нужно вручную осуществлять вход пользователя, например, сразу после регистрации в вашем приложении. Вы можете сделать это вызвав метод $this->Auth->setUser() с данными того пользователя, вход которого („login“) вы хотите осуществить:

public function register()
{
    $user = $this->Users->newEntity($this->request->getData());
    if ($this->Users->save($user)) {
        $this->Auth->setUser($user->toArray());
        return $this->redirect([
            'controller' => 'Users',
            'action' => 'home'
        ]);
    }
}

Предупреждение

Убедитесь в том, что вы добавляете вручную id новому пользователю (User) в массиве, передаваемом методу setUser(). В противном случае у вас будет отсутствовать идентификатор пользователя.

Получение доступа к вошедшим пользователям

AuthComponent::user($key = null)

Как только пользователь войдет в систему, вам часто потребуется какая-то конкретная информация о текущем пользователе. Вы можете получить доступ к вошедшему в приложение пользователю с помощью метода AuthComponent::user():

// Вызов из контроллера или другого компонента
$this->Auth->user('id');

Если текущий пользователь не вошел в приложение, или же если ключ не существует, будет возвращено значение null.

Выход пользователей

AuthComponent::logout()

В конце концов вам понадобится быстрый способ разавторизовать кого-то и перенаправить их туда, куда нужно. Этот метод также полезен, если вы хотите предоставить ссылку «Выйти из системы» внутри области вашего приложения для авторизованных пользователей:

public function logout()
{
    return $this->redirect($this->Auth->logout());
}

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

Когда выполнять аутентификацию

В некоторых случаях вы можете захотеть вызвать метод $this->Auth->user() внутри метода beforeFilter(Event $event). Это осуществимо при использовании ключа конфигурации checkAuthIn. Для этого внесите следующие изменения для сопоставления, в каких событиях какие проверки аутентификации должны быть осуществлены:

//Настройка AuthComponent для аутентификации в методе initialize()
$this->Auth->config('checkAuthIn', 'Controller.initialize');

По умолчанию значение checkAuthIn - 'Controller.startup' - но при использовании 'Controller.initialize' первоначальная аутентификация осуществляется перед методом beforeFilter().

Авторизация

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

ControllerAuthorize Вызывает метод isAuthorized() в активном контроллере, и использует возвращаемое значение для авторизации пользователя. Чаще всего это наиболее простой способ авторизовать пользователей.

Примечание

Адаптеры ActionsAuthorize и CrudAuthorize доступные в CakePHP 2.x теперь перемещены в отдельный плагин cakephp/acl [https://github.com/cakephp/acl].

Настройка обработчиков авторизации

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

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

// Простейшая настройка
$this->Auth->config('authorize', ['Controller']);

// Передача параметров
$this->Auth->config('authorize', [
    'Actions' => ['actionPath' => 'controllers/'],
    'Controller'
]);

По аналогии с authenticate, authorize помогает вам соблюдать принцип DRY (не повторяйся), с помощью ключа all. Этот специальный ключ позволяет вам назначать передаваемые параметры каждому прикрепленному объекту. Ключ all также можно представить в виде статического свойства AuthComponent::ALL:

// Передаем настройки с помощью 'all'
$this->Auth->config('authorize', [
    AuthComponent::ALL => ['actionPath' => 'controllers/'],
    'Actions',
    'Controller'
]);

В примере, приведенном выше, как Actions так и Controller получат все настройки, объявленные для ключа „all“. Все настройки, переданные конкретному объекту авторизации, переопределят значения соответствующих ключей, имеющихся внутри ключа „all“.

Если аутентифицированный пользователь пытается перейти к URL-адресу, к которому у него нет прав доступа, он перенаправляется обратно к странице-источнику запроса. Если вы не хотите такого перенаправления (в основном, при использовании адаптера аутентификации без учета состояния), вы можете установить параметр конфигурации unauthorizedRedirect в значение false. Это вынудит AuthComponent выбросить исключение ForbiddenException вместо того, чтобы выполнить перенаправление.

Создание пользовательских объектов авторизации

Поскольку объекты авторизации подключаемы, вы можете создавать пользовательские объекты авторизации в своем приложении или плагинах. Например, если вы хотите создать объект авторизации LDAP. В src/Auth/LdapAuthorize.php вы могли бы поместить следующий код:

namespace App\Auth;

use Cake\Auth\BaseAuthorize;
use Cake\Http\ServerRequest;

class LdapAuthorize extends BaseAuthorize
{
    public function authorize($user, ServerRequest $request)
    {
        // Здесь вы производите все операции, необходимые для LDAP.
    }
}

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

Использование пользовательских объектов авторизации

Как только вы создали ваш объект авторизации, вы можете подключить его, передав его в массив authorize вашего компонента AuthComponent:

$this->Auth->config('authorize', [
    'Ldap', // объект авторизации уровня приложения.
    'AuthBag.Combo', // объект авторизации плагина.
]);

Отказ от использования авторизации

Если вы все же пожелали не использовать какой-либо из встроенных объектов авторизации, и захотели обрабатывать события за пределами AuthComponent, в таком случае вы можете установить значение $this->Auth->config('authorize', false);. По умолчанию AuthComponent использует для authorize значение false. Если вы не используете схему авторизации, убедитесь, что проверка авторизации производится либо вручную внутри метода beforeFilter() вашего контроллера, либо с помощью какого-нибудь другого компонента.

Создание общедоступных экшенов

AuthComponent::allow($actions = null)

Часто у вас может возникать необходимость оставлять общедоступными некоторые экшены контроллера (не требующие авторизации). AuthComponent пессимистичен и по умолчанию закрывает доступ. Вы можете пометить экшены как общедоступные с помощью метода AuthComponent::allow(). AuthComponent не будет проверять авторизацию пользователей, обращающихся к таким экшенам, так же, как не будет проверять объекты авторизации:

// Разрешить все экшены
$this->Auth->allow();

// Разрешить только экшен index.
$this->Auth->allow('index');

// Разрешить только экшены view и index.
$this->Auth->allow(['view', 'index']);

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

Примечание

Вы не должны открывать доступ к экшену «login» вашего контроллера UsersController. Подобные действия могут нарушить нормальную работу компонента AuthComponent.

Создание экшенов требующих авторизации

AuthComponent::deny($actions = null)

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

// Закрыть все экшены.
$this->Auth->deny();

// Закрыть один экшен
$this->Auth->deny('add');

// Закрыть группу экшенов.
$this->Auth->deny(['add', 'edit']);

По аналогии с методом allow(), если не передается никаких параметров, то доступ закрыватся ко всем экшенам. Если передается строковое значение, то доступ закрывается именно к указанному экшену. Для закрытия нескольких экшенов в метод передается массив

Использование ControllerAuthorize

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

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

class AppController extends Controller
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Auth', [
            'authorize' => 'Controller',
        ]);
    }

    public function isAuthorized($user = null)
    {
        // Любой зарегистрированный пользователь может иметь доступ
        // к общедоступным функциям
        if (!$this->request->getParam('prefix')) {
            return true;
        }

        // Только администоры могут иметь доступ к админке
        if ($this->request->getParam('prefix') === 'admin') {
            return (bool)($user['role'] === 'admin');
        }

        // По умолчанию отказ в доступе
        return false;
    }
}

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

Параметры конфигурации

Приведенные ниже параметры могут все быть объявлены либо в методе initialize() вашего контроллера, либо с помощью метода $this->Auth->config(), вызванного внутри вашего метода beforeFilter():

ajaxLogin

Имя опционального элемента вида, который выводится при совершении AJAX-запроса с недействительной или истекшей сессией.

allowedActions

Экшены контроллера, для которых валидация пользователя необязательна.

authenticate

Представляет собой массив объектов Аутентификации, которые вы хотите использовать при входе пользователей в приложение. Имеется несколько объектов аутентификации, встроенных в ядро; подробности смотрите в разделе Рекомендуется к прочтению.

authError

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

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

authorize

Представляет собой массив объектов Авторизации, которые вы хотите использовать, когда выавторизуете пользователей при каждом запросе; смотрите раздел authorization-objects для более подробной информации.

flash

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

element - Используемый элемент; по умолчанию „default“.

key - Используемый ключ; по умолчанию „auth“.

params - Массив дополнительных параметров; по умолчанию „[]“.

loginAction

URL (объявленный как строка либо как массив) для экшена контроллера, обрабатывающего вход в приложение. По умолчанию /users/login.

loginRedirect

URL (объявленный как строка либо как массив) для экшена контроллера, на который пользователь должен быть перенаправлен после входа. Это значение будет проигнорировано, если у пользователя внутри сессии имеется значение Auth.redirect.

logoutRedirect

Экшен по умолчанию, на который пользователь перенаправляется после выхода из приложения. В то время, AuthComponent не обрабатывает перенаправление пользователя после выхода, URL будет возвращен из метода AuthComponent::logout().Значение по умолчанию - loginAction.

unauthorizedRedirect

Контролирует обработку несанкционированного доступа. По умолчанию неавторизованный пользователь перенаправляется на URL-адрес страницы-источника запроса либо на loginAction либо на „/“. Если установлено значение false, вместо перенаправления выбрасывается исключение ForbiddenException.

storage

Класс хранения, используемый для сохранения записи пользователя. При использовании аутентификатора без учета состояния вы должны установить значение Memory. По умолчанию используется Session. Вы можете передавать параметры конфигурации в класс хранения, используя формат массива. К примеру для назначения пользовательского ключа сессии вы можете установить значение storage в ['className' => 'Session', 'key' => 'Auth.Admin'].

checkAuthIn

Название события, в котором должны выполняться первоначальные проверки подлинности. По умолчанию используется Controller.startup. Вы можете установить его в Controller.initialize, если вы хотите, чтобы проверка выполнялась до того, как запустится метод контроллера beforeFilter().

Вы можете получить текущие значения настроек, вызвав метод $this->Auth->config() с единственным переданным параметром конфигурации:

$this->Auth->config('loginAction');

$this->redirect($this->Auth->config('loginAction'));

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

Тестирование экшенов защищенных AuthComponent

Смотрите раздел Testing Actions That Require Authentication для получения более полной информации по тестированию экшенов контроллера, защищенных компонентом AuthComponent.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Куки

class Cake\Controller\Component\CookieComponent(ComponentRegistry $collection, array $config = [])

CookieComponent - это оболочка вокруг собственного метода PHP setcookie(). Это упрощает манипулирование файлами cookie и автоматически шифрует данные cookie. Куки, добавленные через CookieComponent, будут отправляться только в том случае, если экшен контроллера завершен.

Не рекомендуется, начиная с версии 3.5.0: Вы должны использовать Промежуточное ПО для шифрования файлов cookie вместо CookieComponent.

Настройка куки

Файлы cookie можно настроить как глобально, так и для каждого имени верхнего уровня. Данные глобальной конфигурации будут объединены с конфигурацией верхнего уровня. Поэтому нужно только переопределить те части, которые отличаются друг от друга. Чтобы настроить глобальные параметры, используйте метод config():

$this->Cookie->config('path', '/');
$this->Cookie->config([
    'expires' => '+10 days',
    'httpOnly' => true
]);

Для настройки определенного ключа используйте метод configKey():

$this->Cookie->configKey('User', 'path', '/');
$this->Cookie->configKey('User', [
    'expires' => '+10 days',
    'httpOnly' => true
]);

Существует некоторое количество параметров конфигурации для файлов куки:

expires

Как долго куки должен существовать. По умолчанию 1 месяц.

path

Путь на сервере, по которому куки будет доступен. Если, к примеру, путь задан как „/foo/“, то файлы куки будут доступны только внутри каталога /foo/ и всех его подкаталогах, например в папке домена /foo/bar/. Значением по умлчанию является базовый путь приложения.

domain

Домен, для которого куки доступны. Чтобы сделать куки доступными на всех поддоменах example.com, установите значение „.example.com“.

secure

Показывает, что куки должны передаваться только по защищенному соединению, через протокол HTTPS. Когда установлено значение true, куки будут созданы только в случае если защищенное соединение существует.

key

Ключ шифрования, используемый в случае если включено шифрование куки. По умолчанию Security.salt.

httpOnly

Установите в true, если хотите сделать куки доступными только по протоколу HTTP. В таком случае куки будут недоступны из JavaScript. По умолчанию false.

encryption

Тип используемого шифрования. Значение по умолчанию „aes“. Можно также установить „rijndael“ для обратной совместимости.

Использование компонента

Компонент CookieComponent предлагает некоторое количество методов для работы с куки-файлами.

Cake\Controller\Component\CookieComponent::write(mixed $key, mixed $value = null)

Метод write() является сердцем компонента куки. $key - это имя переменной куки, которое вы хотите использовать, а $value - информация, которая должна быть сохранена:

$this->Cookie->write('name', 'Larry');

Вы также можете группировать переменные с помощью точечной нотации в параметре key:

$this->Cookie->write('User.name', 'Larry');
$this->Cookie->write('User.role', 'Lead');

Если вы хотите записать несколько значений в файл cookie за один раз, вы можете передать массив:

$this->Cookie->write('User',
    ['name' => 'Larry', 'role' => 'Lead']
);

Все значения в куки зашифрованы с помощью AES по умолчанию. Если вы хотите хранить значения в виде обычного текста, обязательно оставьте ключ пустым:

$this->Cookie->configKey('User', 'encryption', false);

Cake\Controller\Component\CookieComponent::read(mixed $key = null)

Этот метод используется для чтения значения переменной куки с именем, определенном в параметре $key.

// Выведется "Larry"
echo $this->Cookie->read('name');

// Вы также можете использовать точечную нотацию
echo $this->Cookie->read('User.name');

// Чтобы получить переменные, сгруппированные вами с помощью
// точечной нотации в виде массива, используйте следующий формат
$this->Cookie->read('User');

// Это выведет что-то наподобии ['name' => 'Larry', 'role' => 'Lead']

Предупреждение

CookieComponent не может взаимодействовать со строковыми значениями, содержащими ,. Компонент попытается интерпретировать эти значения как массивы, что приведет к неправильным результатам. Вместо этого вам лучше воспользоваться методом $request->getCookie().

Cake\Controller\Component\CookieComponent::check($key)

Параметры

$key (string) – Ключ для проверки.

Используется для проверки наличия ключа/пути и не равно ли его значение null.

Cake\Controller\Component\CookieComponent::delete(mixed $key)

Удаляет куки-переменную с именем $key. Работает с точечной нотацией:

// Удаление переменной
$this->Cookie->delete('bar');

// Удаляет куки-переменную ``bar``, но не трогает все остальные
// значения, принадлежащие ``foo``
$this->Cookie->delete('foo.bar');

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Межсайтовая подделка запроса

Активируя компонент CSRF вы получаете защиту от атак. CSRF [http://en.wikipedia.org/wiki/Cross-site_request_forgery] или Межсайтовая подделка запросов является распространенной уязвимостью в веб-приложениях. Он позволяет злоумышленнику захватывать и воспроизводить предыдущий запрос, а иногда и отправлять запросы данных с использованием тегов изображений или ресурсов в других доменах.

Компонент CsrfComponent работает, устанавливая куки в браузер пользователя. При создании формы с помощью Cake\View\Helper\FormHelper, добавляется скрытое поле с токеном CSRF. Во время события Controller.startup если запрос является POST, PUT, DELETE, PATCH запросом, компонент сравнит данные запроса со значением хранящимся в куки. Если какое-либо из этих значений будет отсутствовать или же два значения не совпадут, компонент выбросит исключение Cake\Network\Exception\InvalidCsrfTokenException.

Примечание

Вы всегда должны проверять используемый HTTP-метод перед выполнением во избежание побочных эффектов. Вы должны проверять HTTP-vtnjl или использовать Cake\Http\ServerRequest::allowMethod() чтобы удостовериться, что используется правильный HTTP-метод.

Добавлено в версии 3.1: Изменен тип выбрасываемого исключения с Cake\Network\Exception\ForbiddenException на Cake\Network\Exception\InvalidCsrfTokenException.

Не рекомендуется, начиная с версии 3.5.0: Вы должны использовать Cross Site Request Forgery (CSRF) Middleware вместо CsrfComponent.

Использование CsrfComponent

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

public function initialize()
{
    parent::initialize();
    $this->loadComponent('Csrf');
}

Параметры могут быть переданы в компонент через настройки вашего компонента. Доступные параметры конфигурации:

cookieName Имя отправляемой куки-переменной. По умолчанию csrfToken.

expiry Продолжительность жизни токена CSRF. По умолчанию равен продолжительности сессии браузера. Принимает значения strtotime с версии 3.1

secure Будет ли cookie установлен с флагом Secure. То есть, cookie будет установлен только при HTTPS-соединении, и любая попытка по обычному HTTP не удастся. По умолчанию используется false.

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

Когда включено, вы можете получить доступ к текущему токену CSRF в объекте запроса:

$token = $this->request->getParam('_csrfToken');

Интеграция с FormHelper

CsrfComponent легко интегрируется с FormHelper. Каждый раз, когда вы создаете форму с помощью FormHelper, она вставляет скрытое поле, содержащее токен CSRF.

Примечание

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

CSRF-защита и AJAX-запросы

Помимо параметров запроса данных, токены CSRF могут быть отправлены через специальный заголовок X-CSRF-Token. Использование заголовка часто упрощает интеграцию токена CSRF с тяжелыми приложениями JavaScript или конечными точками API на основе XML/JSON.

Отключение компонента CSRF для определенных экшенов

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

public function beforeFilter(Event $event)
{
    $this->eventManager()->off($this->Csrf);
}

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Флэш

class Cake\Controller\Component\FlashComponent(ComponentCollection $collection, array $config = [])

FlashComponent предоставляет способ установки одноразовых уведомлений, которые будут отображаться после обработки формы или подтверждения данных. CakePHP ссылается на эти сообщения как «флэш-сообщения». FlashComponent записывает флэш-сообщения в $_SESSION для дальнейшего вывода в Виде с помощью хелпера FlashHelper.

Настройка флэш-сообщений

FlashComponent предоставляет два способа установки флеш-сообщений: его магический метод __call() и его метод set(). Чтобы предотвратить многословность в приложении, магический метод __call() компонента FlashComponent позволяет использовать имя метода, которое сопоставляется с элементом, расположенным в каталоге src/Template/Element/Flash. В соответствии соглашениями, имена методов в верблюжьем регистре будут соотносится с именем элемента, написанном в нижнем регистре с разделением слов с помощью знака подчеркивания:

// Использует src/Template/Element/Flash/success.ctp
$this->Flash->success('This was successful');

// Использует src/Template/Element/Flash/great_success.ctp
$this->Flash->greatSuccess('This was greatly successful');

В качестве альтернативы вы можете задавать текстовое сообщение без использования заготовленного элемента вида, используя метод set():

$this->Flash->set('Это текст сообщения');

Добавлено в версии 3.1: Флэш-сообщения теперь складываются в стопку. Последовательные вызовы set() или __call() с тем же ключом будут добавлять сообщения в $_SESSION. Если вы хотите сохранить прежнее поведение (одно сообщение даже после последовательных вызовов), установите параметр clear в true при настройке Компонента.

Методы __call() и set() компонента FlashComponent опционально могут принимать второй параметр - массив опций:

key По умолчанию „flash“. Ключ массива, который можно найти в ключе Flash в сессии.

element По умолчанию null, но будет автоматически назначен при использовании магического метода __call(). Передает имя элемента для вывода.

params Опциональный массив ключей/значений для задания переменных, к которым можно обращаться из элемента.

Добавлено в версии 3.1: Добавлен новый ключ clear. Этот ключ ожидает булево значение и позволяет вам удалить все сообщения в текущей стопке, начав тем самым новую.

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

// В вашем Контроллере
$this->Flash->success('Пользователь сохранен', [
    'key' => 'positive',
    'params' => [
        'name' => $user->name,
        'email' => $user->email
    ]
]);

// В вашем Виде
<?= $this->Flash->render('positive') ?>

<!-- В src/Template/Element/Flash/success.ctp -->
<div id="flash-<?= h($key) ?>" class="message-info success">
    <?= h($message) ?>: <?= h($params['name']) ?>, <?= h($params['email']) ?>.
</div>

Обратите внимание, что параметр element будет всегда переопределен при использовании метода __call(). Чтобы получить определенный элемент из плагина, вы должны установить значение параметра plugin. Например:

// В вашем Контроллере
$this->Flash->warning('Мое сообщение', ['plugin' => 'ИмяПлагина']);

Приведенный выше код будет использовать элемент warning.ctp находящийся в plugins/PluginName/src/Template/Element/Flash для вывода флэш-сообщения.

Примечание

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

By default, CakePHP escapes the content in flash messages to prevent cross site scripting. User data in your flash messages will be HTML encoded and safe to be printed. If you want to include HTML in your flash messages, you need to pass the escape option and adjust your flash message templates to allow disabling escaping when the escape option is passed.

HTML во флэш-сообщениях

Добавлено в версии 3.3.3.

Возможен вывод HTML во флэш-сообщениях с помощью опции 'escape':

$this->Flash->info(sprintf('<b>%s</b> %s', h($highlight), h($message)), ['escape' => false]);

Убедитесь в том, что вы вручную экранируете ввод в подобном случае. В примере выше $highlight и $message не являются HTML-разметкой и благодаря этому экранируются.

Для более подробной информации о выводе ваших флэш-сообщений, пожалуйста просмотрите раздел FlashHelper.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Безопасность

class SecurityComponent(ComponentCollection $collection, array $config = [])

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

Ограничение принимаемых вашим приложением HTTP-методов.

Защита от несанкционированного доступа к формам

Требование использования SSL.

Ограничение межконтроллерного взаимодействия.

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

Используя компонент Security, вы автоматически получаете защиту от несанкционированного доступа. Скрытые поля токена автоматически вставляются в формы и проверяются компонентом Security.

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

Примечание

При использовании компонента Security вы должны использовать FormHelper для создания ваших форм. Кроме того, вы не должны переопределять любые атрибуты «name» полей формы. Компонент Security ищет определенные индикаторы, которые создаются и управляются FormHelper (особенно те, которые создаются в View\Helper\FormHelper::create() и View\Helper\FormHelper::end()). Динамическое изменение полей, отправленных в запросе POST (например, отключение, удаление или создание новых полей через JavaScript), скорее всего, приведет к отправке запроса на blackhole-коллбэк.

Вы всегда должны проверять используемый HTTP-метод перед выполнением во избежание возможных побочных эффектов. Вы должны проверять HTTP-метод либо использовать Cake\Http\ServerRequest::allowMethod(), чтобы убедиться в том, что используется правильный HTTP-метод.

Обработка Blackhole-коллбэков

SecurityComponent::blackHole(object $controller, string $error = '', SecurityException $exception = null)

Если запрос ограничен компонентом Security, то он будет поглощен („black-holed“) как неверный запрос, который будет возвращать ошибку 400 по умолчанию. Вы можете настроить это поведение установив в качестве значения параметра blackHoleCallback имя коллбэк-функции в контроллере.

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

public function beforeFilter(Event $event)
{
    $this->Security->setConfig('blackHoleCallback', 'blackhole');
}

public function blackhole($type)
{
    // Здесь обрабатываются ошибки.
}

Примечание

Используйте метод $this->Security->config() для версий CakePHP ниже 3.4.

Параметр $type может иметь следующие значения:

„auth“ Указывает на ошибку валидации формы, или на несоответствие контроллера/экшена.

„secure“ Указывает на сбой SSL-ограничения метода.

Добавлено в версии cakephp/cakephp: 3.2.6

C версии 3.2.6 к коллбэку blackHole добавлен дополнительный параметр. В качестве второго параметра передается экземпляр класса Cake\Controller\Exception\SecurityException.

Доступ к экшенам через SSL

SecurityComponent::requireSecure()

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

SecurityComponent::requireAuth()

Sets the actions that require a valid Security Component generated token. Takes any number of arguments. Can be called with no arguments to force all actions to require a valid authentication.

Ограничение межконтроллерных взаимодействий

allowedControllers

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

allowedActions

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

Эти параметры конфигурации позволяют вам ограничить коммуникацию между контроллерами. Установите их с помощью метода setConfig(), либо config() если вы используете CakePHP версии ниже 3.4.

Предотвращение подделки форм

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

* Неизвестные поля не могут быть добавлены в форму.
* Поля не могут быть удалены из формы.
* Значения скрытых полей не могут быть изменены.

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

Примечание

SecurityComponent не предотвратит добавление/изменение элементов в выпадающих списках. Также он не предотвратит добавление/изменение радио-кнопок.

unlockedFields

Указываются поля формы, которые необходимо исключить из POST-валидации. Поля могут быть разблокированы (unlocked) как в Компоненте, так и в FormHelper::unlockField(). Разблокированные поля не обязаны быть частью POST, в то время как значения разблокированных скрытых полей не подвергаются проверке.

validatePost

Установите в false для полной отмены валидации POST-запросов, по сути, отключает валидацию форм.

Указанные выше опции могут быть настроены с помощью метода setConfig(), или же config(), если вы используете CakePHP версии ниже 3.4.

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

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

namespace App\Controller;

use App\Controller\AppController;
use Cake\Event\Event;

class WidgetsController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Security');
    }

    public function beforeFilter(Event $event)
    {
        if ($this->request->getParam('admin')) {
            $this->Security->requireSecure();
        }
    }
}

Приведенный выше пример, заставит все экшены внутри маршрута admin требовать безопасных SSL-запросов:

namespace App\Controller;

use App\Controller\AppController;
use Cake\Event\Event;

class WidgetsController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Security', ['blackHoleCallback' => 'forceSSL']);
    }

    public function beforeFilter(Event $event)
    {
        if ($this->request->getParam('admin')) {
            $this->Security->requireSecure();
        }
    }

    public function forceSSL()
    {
        return $this->redirect('https://' . env('SERVER_NAME') . $this->request->getRequestTarget());
    }
}

Примечание

Use $this->request->here() for CakePHP версий ниже 3.4.0.

Данный пример заставит все экшены внутри маршрута admin требовать безопасных SSL-запросов. Когда запрос поглощен (black holed), будет вызван назначенный коллбэк forceSSL(), который автоматически перенаправит небезопасные запросы на безопасные.

CSRF Protection

CSRF или Межсайтовая Подделка Запросов (Cross Site Request Forgery) - распространенная уязвимость в веб-приложениях. Она позволяет злоумышленнику захватывать и воспроизводить предыдущий запрос, а иногда и отправлять запросы данных с использованием тегов изображений или ресурсов в других доменах. Чтобы включить функции защиты CSRF, используйте Межсайтовая подделка запроса.

Отключение компонента Security для определенных экшенов

Могут быть случаи, когда вы захотите отключить все проверки безопасности для экшена (например, AJAX-запросы). Вы можете «разблокировать» эти действия, указав их в $this->Security->unlockedActions в beforeFilter(). Свойство unlockedActions не будет влиять на другие функции SecurityComponent:

namespace App\Controller;

use App\Controller\AppController;
use Cake\Event\Event;

class WidgetController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Security');
    }

    public function beforeFilter(Event $event)
    {
         $this->Security->setConfig('unlockedActions', ['edit']);
    }
}

Примечание

Используйте $this->Security->config() для CakePHP версии ниже 3.4.0.

Данный пример отключит все проверки безопасности для экшена edit.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Пагинация

class Cake\Controller\Component\PaginatorComponent

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

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

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

Использование Controller::paginate()

В контроллере мы начинаем с определения условий запроса по умолчанию, которые будут использоваться в переменной $paginate. Эти условия служат основой для ваших запросов с разбивкой по страницам. Они дополняются параметрами sort, direction, limit и page, переданными из URL-адреса. Важно отметить, что ключ order должен быть определён в структуре массива, как показано ниже:

class ArticlesController extends AppController
{
    public $paginate = [
        'limit' => 25,
        'order' => [
            'Articles.title' => 'asc'
        ]
    ];

    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Paginator');
    }
}

Вы также можете включить любой из параметров, поддерживаемых ORM\Table::find(), например fields:

class ArticlesController extends AppController
{
    public $paginate = [
        'fields' => ['Articles.id', 'Articles.created'],
        'limit' => 25,
        'order' => [
            'Articles.title' => 'asc'
        ]
    ];

    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Paginator');
    }
}

Хотя вы можете передать большинство параметров запроса из свойства paginate, часто бывает проще объединить свои варианты разбиения на страницы в Custom Finder Methods. Вы можете определить использование разбиения на страницы поиска, установив опцию finder:

class ArticlesController extends AppController
{
    public $paginate = [
        'finder' => 'published',
    ];
}

Поскольку методы пользовательского поиска также могут принимать параметры, вы можете передать параметры в пользовательский метод поиска в свойстве paginate:

class ArticlesController extends AppController
{
    // Найти статьи по тегу
    public function tags()
    {
        $tags = $this->request->getParam('pass');

        $customFinderOptions = [
            'tags' => $tags
        ];
        // Метод пользовательского поиска вызывает findTagged из ArticlesTable.php,
        // он должен выглядеть следующим образом:
        // public function findTagged(Query $query, array $options) {
        // поэтому мы используетем tagged как ключ
        $this->paginate = [
            'finder' => [
                'tagged' => $customFinderOptions
            ]
        ];
        $articles = $this->paginate($this->Articles);
        $this->set(compact('articles', 'tags'));
    }
}

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

class ArticlesController extends AppController
{
    public $paginate = [
        'Articles' => [],
        'Authors' => [],
    ];
}

Значения ключей Articles и Authors могут содержать все свойства, которые мог бы использовать массив модели/ключа без $paginate.

Как только свойство $paginate определено, мы можем использовать метод Controller\Controller::paginate() для создания данных разбивки на страницы и добавить PaginatorHelper если он еще не добавлен. Метод paginate контроллера возвращает результирующий набор запроса разбитого на страницы, и устанавливает метаданные разбиения на страницы для запроса. Вы можете получить доступ к метаданным разбиения на страницы с помощью $this->request->getParam('paging'). Более полным примером использования paginate() будет следующее:

class ArticlesController extends AppController
{
    public function index()
    {
        $this->set('articles', $this->paginate());
    }
}

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

public function index()
{
   $query = $this->Articles->find('popular')->where(['author_id' => 1]);
   $this->set('articles', $this->paginate($query));
}

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

// Использование запроса
$comments = $this->paginate($commentsTable->find());

// Использование имени модели
$comments = $this->paginate('Comments');

// Использование объекта таблицы
$comments = $this->paginate($commentTable);

Непосредственное использование пагинатора

Если вам нужно разделить данные из другого компонента, вы можете напрямую использовать PaginatorComponent. Он имеет аналогичный API для метода контроллера:

$articles = $this->Paginator->paginate($articleTable->find(), $config);

// Или
$articles = $this->Paginator->paginate($articleTable, $config);

Первым параметром должен быть объект запроса метода find в таблицу объекта, из которого вы хотите выполнить разбиение на страницы. При желании вы можете передать объект таблицы и создать запрос. Второй параметр должен быть массивом параметров, используемый для разбиения на страницы. Этот массив должен иметь ту же структуру, что и свойство $paginate в контроллере. При разбиении на страницы объекта Query параметр finder будет проигнорирован. Предполагается, что вы передаёте запрос, который требуется разбивать на страницы.

Пагинация по нескольким запросам

Вы можете разбивать на страницы несколько моделей в одном экшене контроллера, используя опцию scope как в свойстве $paginate контроллера, так и в вызове метода paginate():

// Свойство Paginate
public $paginate = [
    'Articles' => ['scope' => 'article'],
    'Tags' => ['scope' => 'tag']
];

// В экшене контроллера
$articles = $this->paginate($this->Articles, ['scope' => 'article']);
$tags = $this->paginate($this->Tags, ['scope' => 'tag']);
$this->set(compact('articles', 'tags'));

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

/dashboard?article[page]=1&tag[page]=3

См. раздел Разбиение на несколько результатов для создания области видимости HTML-элементов и URL-адресов для разбивки на страницы.

Добавлено в версии 3.3.0: Множественное разбиение на страницы было добавлено в 3.3.0

Управление полями используемыми для сортировки

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

public $paginate = [
    'sortWhitelist' => [
        'id', 'title', 'Users.username', 'created'
    ]
];

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

Ограничиние максимального количества строк на страницу

Количество результатов, полученных на странице, отображается пользователю как параметр limit. Как правило, нежелательно разрешать пользователям получать сразу все строки разбитые на страницы. Опция maxLimit ограничит запрос так, что никто не сможет превысить этот предел из вне. По умолчанию CakePHP ограничивает максимальное количество строк, которые могут быть выбраны, до 100. Если это значение по умолчанию не подходит для вашего приложения, вы можете настроить его как часть параметров разбивки на страницы, например, уменьшив это кол-во до 10:

public $paginate = [
    'maxLimit' => 10,
            // ну а здесь другие ключи..
];

Если параметр ограничения запроса больше этого значения, он будет уменьшен до значения maxLimit.

Присоединение дополнительных ассоциаций

Дополнительные ассоциации могут быть загружены в разбитую таблицу с помощью параметра contain:

public function index()
{
    $this->paginate = [
        'contain' => ['Authors', 'Comments']
    ];

    $this->set('articles', $this->paginate($this->Articles));
}

Запросы на заданную страницу

PaginatorComponent будет выбрасывать NotFoundException при попытке доступа к несуществующей странице, например, когда запрошенный номер страницы превышает общий счёт страниц.

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

use Cake\Network\Exception\NotFoundException;

public function index()
{
    try {
        $this->paginate();
    } catch (NotFoundException $e) {
        // Сделайте что-то здесь, например, перенаправление на первую или последнюю страницу.
        // $this->request->getParam('paging') // предоставит вам необходимую информацию
    }
}

Разбиение страницы в Виде

Проверьте документацию View\Helper\PaginatorHelper, чтобы создать ссылки для навигацию по страницам.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Обработка запроса

class RequestHandlerComponent(ComponentCollection $collection, array $config = [])

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

По умолчанию RequestHandler будет автоматически определять AJAX-запросы, основываясь на HTTP-заголовке X-Requested-With, используемый многими JavaScript-бибилиотеками. При использовании в сочетании с Cake\Routing\Router::extensions(), RequestHandler будет автоматически переключать макеты и шаблоны на те, которые соответствуют типам файлов, отличным от HTML. Кроме того, если существует хелпер с тем же именем, что и запрашиваемое расширение, он будет добавлен в массив хелперов контроллера. Наконец, если XML/JSON-данные переданы методом POST вашим Контроллерам, они будут разобраны в массив, который присвоится методу $this->request->getData(), и к нему можно будет получить доступ, как и к стандартным POST-данным. Для того, чтобы сделать возможным использование компонента RequestHandler, вы должны добавить его в ваш метод initialize():

class WidgetsController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('RequestHandler');
    }

    // Остальное содержимое контроллера
}

Получение информации о запросе

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

RequestHandlerComponent::accepts($type = null)

$type может быть строкой, либо массивом, либо null. Если будет строка, accepts() вернет true, если клиент примет тип содержимого. Если указан массив, accepts() вернет true, если один любой из типов содержимого будет принят клиентом. Если будет null, будет возвращен массив типов содержимого, принимаемых клиентом. Например:

class ArticlesController extends AppController
{

    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('RequestHandler');
    }

    public function beforeFilter(Event $event)
    {
        if ($this->RequestHandler->accepts('html')) {
            // Выполнить код только если клиент принимает HTML (text/html)
            // в качестве ответа.
        } elseif ($this->RequestHandler->accepts('xml')) {
            // Выполняет только XML-код
        }
        if ($this->RequestHandler->accepts(['xml', 'rss', 'atom'])) {
            // Выполнить код, если клиент принимает любой из типов: XML, RSS
            // или Atom.
        }
    }
}

Другие методы определения „типа“ запроса включают в том числе:

RequestHandlerComponent::isXml()

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

RequestHandlerComponent::isRss()

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

RequestHandlerComponent::isAtom()

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

RequestHandlerComponent::isMobile()

Возвращает true, если строка user agent совпадает с мобильным веб-браузером или если клиент принимает WAP-контент. Поддерживаемые строки мобильных User Agent:

Android

AvantGo

BlackBerry

DoCoMo

Fennec

iPad

iPhone

iPod

J2ME

MIDP

NetFront

Nokia

Opera Mini

Opera Mobi

PalmOS

PalmSource

portalmmm

Plucker

ReqwirelessWeb

SonyEricsson

Symbian

UP.Browser

webOS

Windows CE

Windows Phone OS

Xiino

RequestHandlerComponent::isWap()

Возвращает true если клиент принимает WAP-контент.

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

if ($this->request->is('ajax')) {
    $this->response->disableCache();
}
// Остальной код экшена Контроллера

Автоматическое декодирование данных запроса

Добавьте декодер данных запроса. Обработчик должен содержать метод обратного вызова и любые дополнительные аргументы для него. Метод обратного вызова должен возвращать массив данных, содержащихся в запросе. Например, добавление обработчика CSV может выглядеть так:

class ArticlesController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $parser = function ($data) {
            $rows = str_getcsv($data, "\n");
            foreach ($rows as &$row) {
                $row = str_getcsv($row, ',');
            }
            return $rows;
        };
        $this->loadComponent('RequestHandler', [
            'inputTypeMap' => [
                'csv' => [$parser]
            ]
        ]);
    }
}

Вы можете использовать любой callable [http://php.net/callback] для функции обработки. Вы также можете передать дополнительные аргументы для функции обратного вызова, это полезно для таких функций, как json_decode:

$this->RequestHandler->addInputType('json', ['json_decode', true]);

// После версии 3.1.0 вы должны использовать
$this->RequestHandler->config('inputTypeMap.json', ['json_decode', true]);

Приведенный выше пример сделает результатом $this->request->getData() массив из входящих JSON-данных, без указания дополнительного true, вы бы получили набор объектов класса stdClass.

Не рекомендуется, начиная с версии 3.1.0: С версии 3.1.0 метод addInputType() является устаревшим. Вы должны использовать метод config() для добавления типов ввода во время выполнения.

Проверка предпочтений типов содержимого

RequestHandlerComponent::prefers($type = null)

Определяет, какие типы контента предпочитает клиент. Если параметр не задан, возвращается наиболее вероятный тип содержимого. Если $type - массив, возвращается первый тип, который принимает клиент. Предпочтение определяется главным образом расширением файла, проанализированным Маршрутизатором, если оно было предоставлено, а во-вторых, списком типов контента в HTTP_ACCEPT:

$this->RequestHandler->prefers('json');

Ответ на запросы

RequestHandlerComponent::renderAs($controller, $type)

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

// Принудительная обработка ответа контроллером в виде xml.
$this->RequestHandler->renderAs($this, 'xml');

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

RequestHandlerComponent::respondAs($type, $options)

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

$this->RequestHandler->respondAs('xml', [
    // Forse download
    'attachment' => true,
    'charset' => 'UTF-8'
]);

RequestHandlerComponent::responseType()

Возвращает заголовок Content-type текущего типа ответа, либо null, если еще ни один не был установлен.

Использование преимуществ валидации кэша HTTP

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

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

Если ответ оценивается как неизмененный, то процесс рендеринга вида прекращается, экономя время обработки и полосу пропускания, а содержимое не возвращается клиенту. Код состояния ответа затем устанавливается на 304 Not Modified.

Вы можете отказаться от этой автоматической проверки, установив параметр checkHttpCache в false:

public function initialize()
{
    parent::initialize();
    $this->loadComponent('RequestHandler', [
        'checkHttpCache' => false
    ]);
}

Использование кастомных ViewClass’ов

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

Вы можете сопоставить существующие и новые типы с вашими пользовательскими классами. Вы также можете автоматизировать процесс сопоставления с помощью параметра viewClassMap:

public function initialize()
{
    parent::initialize();
    $this->loadComponent('RequestHandler', [
        'viewClassMap' => [
            'json' => 'ApiKit.MyJson',
            'xml' => 'ApiKit.MyXml',
            'csv' => 'ApiKit.Csv'
        ]
    ]);
}

Не рекомендуется, начиная с версии 3.1.0: С версии 3.1.0 метод viewClassMap() является устаревшим. Вместо этого вы должны использовать метод config() для изменения viewClassMap во время работы приложения.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Представления

class Cake\View\View

Представления (Виды) - это то, что скрывается за буквой V в понятии MVC. Представления отвечают за вывод данных возвращаемых запросом в определенной форме. Часто это происходит в виде HTML, XML или JSON, но потоковые файлы и создание PDF-файлов, которые пользователи могут скачивать, также входят в круг обязанностей уровня Представления.

CakePHP поставляется с несколькими встроенными классами Представлений для обработки наиболее распростаненных сценариев рендеринга:

Для создания веб-сервисов использующих XML или JSON вы можете использовать представления JSON и XML.

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

Для создания видов, использующих несколько тем оформления вы можете использовать темы.

Представление уровня приложения

AppView - это класс Представления по умолчанию вашего приложения. Сам по себе AppView наследуется от Cake\View\View, который включен в CakePHP, и определен в src/View/AppView.php следующим образом:

<?php
namespace App\View;

use Cake\View\View;

class AppView extends View
{
}

Вы можете использовать AppView для загрузки хелперов, которые будут использоваться для каждого вида, отображаемого в вашем приложении. CakePHP предоставляет метод initialize(), который вызывается в конце конструктора класса View для такого использования:

<?php
namespace App\View;

use Cake\View\View;

class AppView extends View
{

    public function initialize()
    {
        // Всегда активировать хелпер MyUtils
        $this->loadHelper('MyUtils');
    }

}

Шаблоны Представления

Слой представления CakePHP - это то, как вы общаетесь с вашими пользователями. Большую часть времени ваши представления будут выводить HTML/XHTML-документы в браузеры, но вам так же может понадобиться генерировать ответ удаленному клиентскому приложению посредством формаиа JSON, либо вывести пользователю CSV-файл.

Файлы шаблонов CakePHP имеют стандартное расширение .ctp (CakePHP Template) и используют альтернативный синтаксис управляющих структур PHP [http://php.net/manual/en/control-structures.alternative-syntax.php] для управляющих структур и вывода. Эти файлы содержат логику, необходимую для подготовки данных, полученных от контроллера, в формат представления, подготовленный для вашей аудитории.

Варианты вывода значений переменных

C помщью стандартных чзыковых конструкций echo или print:

<?php echo $variable; ?>

С использованием сокращенного синтаксиса:

<?= $variable ?>

Альтернативные управляющие конструкции

Управляющие конструкции, такие как if, for, foreach, switch, и while могут быть записаны в упрощенном формате. Заметьте, что в этом случае не используются фигурные скобки. Вместо этого, к примеру для закрытия конструкции foreach закрывающая фигурная скобка заменяется на endforeach. Каждая из перечисленных выше управляющих конструкций, имеет схожий синтаксис закрытия: endif, endfor, endforeach, и endwhile.Также заметьте, что вместо использования точки с запятой после каждой структуры (за исключением последней) используется знак двоеточие.

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

<ul>
<?php foreach ($todo as $item): ?>
  <li><?= $item ?></li>
<?php endforeach; ?>
</ul>

Еще пример, использование if/elseif/else. Обратите внимание на двоеточия:

<?php if ($username === 'салли'): ?>
   <h3>Привет Салли</h3>
<?php elseif ($username === 'джо'): ?>
   <h3>Привет Джо</h3>
<?php else: ?>
   <h3>Привет неизвестный пользователь</h3>
<?php endif; ?>

Если же вы предпочтете использовать шаблонизаторы, подобные Twig [http://twig.sensiolabs.org], подкласс Представления будет посредничать между вашим шаблонизатором и CakePHP.

Файлы шаблонов хранятся в src/Template/ в папке с именем, соответствующим имени контроллера, использующего файлы, и имеют названия в честь экшенов, которые им соответствуют. Например, файл представления для экшена view() контроллера Products, обычно располагается по следующему пути - src/Template/Products/view.ctp.

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

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

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

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

хелперы: эти классы инкапсулируют логику представления, которая необходима во многих местах слоя представления. Наряду с другими вещами, хелперы в CakePHP могут помочь вам создать формы, построить AJAX-функциональность, разбить на страницы данные модели или транслировать RSS-каналы.

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

Переменные Представления

Любые переменные, заданные в вашем контроллере с помощью метода set(), будут доступны как в представлении, так и в макете вашего экшена. Кроме того, любые заданные переменные также будут доступны в любом элементе. Если вам нужно передать дополнительные переменные из представления в макет, вы можете либо вызвать set() в шаблоне представления, либо использовать блоки представления.

Помните, вы всегда должны экранировать любые пользовательские данные перед их выводом. Вы можете сделать это с помощью функции h():

<?= h($user->bio); ?>

Назначение переменных Представления

Cake\View\View::set(string $var, mixed $value)

Представления имеют метод set() аналогичный методу set() в объектах Контроллера. Использование метода set() из вашего файла представления добавит переменные в макет и элементы, которые будут отображаться позже. См. Назначение переменных вида для более подробной информации.

Вы можете сделать следующее в вашем файле представления:

$this->set('activeMenuButton', 'posts');

Тогда в вашем макете будет доступна переменная $activeMenuButton со значением „posts“.

Расширение Представлений

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

<!-- src/Template/Common/view.ctp -->
<h1><?= $this->fetch('title') ?></h1>
<?= $this->fetch('content') ?>

<div class="actions">
    <h3>Related actions</h3>
    <ul>
    <?= $this->fetch('sidebar') ?>
    </ul>
</div>

Приведенный выше файл представления может быть использован в качестве родительского представления. Он ожидает, что представление, расширяющее его определит блоки sidebar и title. Блок content - это специальный блок, создаваемый CakePHP. Он будет содержать весь неохваченный контент из расширяющегося представления. Предположим, что в нашем файле представления имеется переменная $post, содержащая данные о нашем посте, в таком случае файл представления выглядел бы следующим образом:

<!-- src/Template/Posts/view.ctp -->
<?php
$this->extend('/Common/view');

$this->assign('title', $post->title);

$this->start('sidebar');
?>
<li>
<?php
echo $this->Html->link('edit', [
    'action' => 'edit',
    $post->id
]);
?>
</li>
<?php $this->end(); ?>

// Остальное содержимое будет доступно как блок 'content'
// в родительском представлении.
<?= h($post->body) ?>

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

$this->extend('/Common/view');
$this->extend('/Common/index');

В результате исполнения кода приведенного выше, для текущего представления родительским станет /Common/index.ctp.

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

Примечание

Вы должны избегать использование слова content в качестве имени блока в вашем приложении. CakePHP использует данное имя для вывода неохваченного содержимого в расширенных представлениях.

Вы можете получить перечень всех заполненных блоков с помощью метода blocks():

$list = $this->blocks();

Использование блоков Представления

Блоки представления предоставляют гибкий API, позволяющий вам определять блоки вашего представления/макета, которые будут определены где-либо еще. Например, блоки идеально подходят для реализации таких вещей, как сайдбар или области для загрузки контента в нижней/верхней части макета. Методы start(), append(), prepend(), assign(), fetch(), и end() позволяют работать с захватом блоков:

// Создание блока сайдбара.
$this->start('sidebar');
echo $this->element('sidebar/recent_topics');
echo $this->element('sidebar/recent_comments');
$this->end();

// Добавление новых блоков в конец сайдбара.
$this->start('sidebar');
echo $this->fetch('sidebar');
echo $this->element('sidebar/popular_topics');
$this->end();

Так же вы можете добавить новое содержимое в конец имеющегося блока с помощью метода append():

$this->append('sidebar');
echo $this->element('sidebar/popular_topics');
$this->end();

// То же самое что и в примере выше
$this->append('sidebar', $this->element('sidebar/popular_topics'));

Если вам нужно очистить или переписать блок, существует пара альтернатив. Метод reset() очистит или перепишет блок в любое время. Метод assign(), в который передается пустая строка также может быть использован в этом случае.:

// Очистить старое содержимое блока сайдбара.
$this->reset('sidebar');

// Присвоение пустой строки также очистит содержимое блока.
$this->assign('sidebar', '');

Добавлено в версии 3.2: Метод View::reset() был добавлен в версии 3.2

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

// В файле представления или макета выше строки $this->fetch('title')
$this->assign('title', $title);

Метод prepend() позволяет вам вставить содержимое перед существующим блоком:

// Добавить перед блоком сайдбара
$this->prepend('sidebar', 'это содержимое попадет в верхнюю часть сайдбара');

Отображение блоков

Вы можете выводить блоки, используя метод fetch(). Данный метод выведет блок, возвращающий „“ если указанный блок не будет существовать:

<?= $this->fetch('sidebar') ?>

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

// В макете src/Template/Layout/default.ctp
<?php if ($this->fetch('menu')): ?>
<div class="menu">
    <h3>Опции меню</h3>
    <?= $this->fetch('menu') ?>
</div>
<?php endif; ?>

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

<div class="shopping-cart">
    <h3>Your Cart</h3>
    <?= $this->fetch('cart', 'Your cart is empty') ?>
</div>

Использование блоков для скриптов и CSS-файлов

HtmlHelper связывается с блоками представления, а каждый из его методов script(), css() и meta() обновляет блок с соответствующим именем при использовании с параметром block = true:

<?php
// В вашем файле представления
$this->Html->script('carousel', ['block' => true]);
$this->Html->css('carousel', ['block' => true]);
?>

// В вашем файле макета.
<!DOCTYPE html>
<html lang="en">
    <head>
    <title><?= $this->fetch('title') ?></title>
    <?= $this->fetch('script') ?>
    <?= $this->fetch('css') ?>
    </head>
    // Остальная часть макета

Cake\View\Helper\HtmlHelper также позволяет вам контролировать в каких именно блоках будут размещены скрипты и файлы CSS:

// В вашем файле представления
$this->Html->script('carousel', ['block' => 'scriptBottom']);

// В вашем файле макета.
<?= $this->fetch('scriptBottom') ?>

Макеты

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

Макет, используемый в CakePHP по умолчанию располжен в файле src/Template/Layout/default.ctp. Если вы хотите полностью изменить внешний вид вашего приложения, это то место, с которого вам следует начать, так как выводимый контроллером код представлений располагается внутри этого стандартного макета при отображении страницы.

Все файлы макетов должны располагаться в папке src/Template/Layout. Когда вы создаете макет, вы должны указать, где CakePHP должен выводить ваши представления. Для этого выдолжны разместить в файле макета строку $this->fetch('content'). Вот пример того, как может выглядеть макет по умолчанию:

<!DOCTYPE html>
<html lang="en">
<head>
<title><?= h($this->fetch('title')) ?></title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<!-- Здесь подключаются внешние файлы и скрипты (См. HTML-хелпер для более полной информации.) -->
<?php
echo $this->fetch('meta');
echo $this->fetch('css');
echo $this->fetch('script');
?>
</head>
<body>

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

<!-- Место, в котором я хочу отображать свои представления -->
<?= $this->fetch('content') ?>

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

</body>
</html>

Блоки script, css и meta содержат любой контент, объявленный в представлениях с помощью встроенного HTML-хелпера. Это удобно для подключения JavaScript и CSS-файлов из представлений.

Примечание

При использовании HtmlHelper::css() или HtmlHelper::script() в шаблоне установите 'block' => true, чтобы расместить исходный HTML в одноименном блоке. (См. API чтобы узнать подробнее)

Блок content содержит контент выводимого представления.

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

$this->assign('title', 'View Active Users');

Вы можете создать столько макетов, сколько пожелаете: просто поместите их в папке src/Template/Layout и переключайтесь между ними в экшенах вашего контроллера, используя свойство $layout предствления или контроллера:

// Из контроллера
public function view()
{
    // Назначение макета.
    $this->viewBuilder()->setLayout('admin');

    // До версии 3.4
    $this->viewBuilder()->layout('admin');

    // До версии 3.1
    $this->layout = 'admin';
}

// Из файла представления
$this->layout = 'loggedin';

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

namespace App\Controller;

class UsersController extends AppController
{
    public function viewActive()
    {
        $this->set('title', 'View Active Users');
        $this->viewBuilder()->setLayout('default_small_ad');

        // в версиях ниже 3.4
        $this->viewBuilder()->layout('default_small_ad');

        // в версиях ниже 3.1
        $this->layout = 'default_small_ad';
    }

    public function viewImage()
    {
        $this->viewBuilder()->setLayout('image');

        // Вывод изображения пользователя
    }
}

Помимо стандартного макета, каркас приложения CakePHP также имеет макет „ajax“. Макет Ajax удобен для обработки ответов AJAX - это пустой макет. (Для большинства вызовов AJAX требуется только немного разметки взамен, а не полностью отображаемый интерфейс.)

Каркас приложения также включает стандартный макет для создания RSS.

Использование макетов из плагинов

Если вы хотите использовать макет, который находится внутри плагина, вы можете использовать plugin syntax. Например, чтобы использовать макет контакта из плагина Contacts:

namespace App\Controller;

class UsersController extends AppController
{
    public function view_active()
    {
        $this->viewBuilder()->layout('Contacts.contact');
        // или для версий ниже 3.1
        $this->layout = 'Contacts.contact';
    }
}

Элементы

Cake\View\View::element(string $elementPath, array $data, array $options = [])

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

Элементы находятся в папке src/Template/Element/, и имеют расширение .ctp. Они выводятся с помощью метода представления element():

echo $this->element('helpbox');

Передача переменных в элемент

Вы можете передавать данные в элемент, используя второй аргумент метода element():

echo $this->element('helpbox', [
    "helptext" => "О, этот текст очень полезен."
]);

Внутри файла элемента все переданные переменные дступны как элементы массива параметров (аналогично работе метода Controller::set() в контроллере, работающемс файлами шаблонов). В примере выше файл src/Template/Element/helpbox.ctp может использовать переменную $helptext:

// Внутри файла src/Template/Element/helpbox.ctp
echo $helptext; // Выводит "О, этот текст очень полезен."

Метод View::element() также поддерживает опции для элемента. Это опции „cache“ и „callbacks“. Пример:

echo $this->element('helpbox', [
        "helptext" => "Это передано в элемент как $helptext",
        "foobar" => "Это передано в элемент как $foobar",
    ],
    [
        // использует настройку кэша "long_view"
        "cache" => "long_view",
        // установлен в true, чтобы вызывались методы before/afterRender для элемента
        "callbacks" => true
    ]
);

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

$this->element('helpbox', [], [
        "cache" => ['config' => 'short', 'key' => 'уникальное значение']
    ]
);

Если вам требуется реализовать больше логики в вашем элементе, такие как динамически обновляемые данные, используйте для этого ячейки представления (View Cell) вместо элементов. Узнайте больше о Ячейках.

Кэширование элементов

Вы можете использовать преимущества кэширования представления CakePHP, если вы задаете параметр cache. Если установлено значение `` true``, он будет кэшировать элемент в конфигурации кэша „по умолчанию“. В противном случае вы можете назначить, какую конфигурацию кэша следует использовать. Смотрите Кэширование для получения дополнительной информации о настройке Cache. Простым примером кэширования элемента будет:

echo $this->element('helpbox', [], ['cache' => true]);

Если вы выводите один и тот же элемент более одного раза в представлении при включенном кешировании, обязательно устанавливайте параметр „key“ в другое значение каждый раз. Это предотвратит переопределение кэшированных результатов при каждом новом вызове метода element(). Например:

echo $this->element(
    'helpbox',
    ['var' => $var],
    ['cache' => ['key' => 'first_use', 'config' => 'view_long']]
);

echo $this->element(
    'helpbox',
    ['var' => $differenVar],
    ['cache' => ['key' => 'second_use', 'config' => 'view_long']]
);

Приведенный выше пример обеспечит независимое кэширование результатов для обоих элементов. Если вы хотите использовать общие параметры кэширования для всех элементов, вы можете избежать некоторых повторов в коде, указав желаемые параметры кэширования в View::$elementCache. CakePHP будет использовать эти параметры по умолчанию, если другие настройки не будут указаны явно.

Запрос элементов из плагина

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

echo $this->element('Contacts.helpbox');

Если ваше представление - это часть плагина, вы можете не указывать явно имя плагина. Например, если вы находитесь в контроллере ContactsController плагина Contacts, то следующие строки:

echo $this->element('helpbox');
// и
echo $this->element('Contacts.helpbox');

будут равнозначны по смыслу и выведут один и тот же элемент.

Для элементов внутри вложенных папок плагина (например, plugins/Contacts/Template/Element/sidebar/helpbox.ctp), используйте следующий синтаксис:

echo $this->element('Contacts.sidebar/helpbox');

Префикс маршрута и элементы

Добавлено в версии 3.0.1.

Если у вас в маршруте используется префикс, область видимости пути Элемента может переключиться на местоположение соответствующее префиксу, так же как это делают Макеты и Представления. Предположим у вас настроен префикс «Admin», и вы делаете следующий вызов:

echo $this->element('my_element');

Сначала элемент будет искаться в src/Template/Admin/Element/. Если его не окажется в той папке, то поиск продолжится по стандартному пути.

Кэширование секций вашего представления

Cake\View\View::cache(callable $block, array $options = [])

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

// Предполагаются некоторые локальные переменные
echo $this->cache(function () use ($user, $article) {
    echo $this->cell('UserProfile', [$user]);
    echo $this->cell('ArticleFull', [$article]);
}, ['key' => 'my_view_key']);

По умолчанию кэшированное содержимое представления будет передаваться в параметр конфигурации View::$elementCache, но вы можете использовать опцию config, чтобы изменить это.

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

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

Список событий

View.beforeRender

View.beforeRenderFile

View.afterRenderFile

View.afterRender

View.beforeLayout

View.afterLayout

Вы можете прикреплять обработчики приложения к указанным событиям, или использовать Коллбэки Хелпера.

Создание ваших собственных классов представления

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

Файлы классов представления должны быть расположены в папке src/View. Например: src/View/PdfView.php

Имена классов представления должны оканчиваться на View. Например: PdfView.

Когда вы ссылаетесь имена классов представления, вы должны опускать суффикс View. Например: $this->viewBuilder()->className('Pdf');.

Также, для того, чтобы все работало правильно, вы должны убдиться, что наследуетесь от класса View:

// В src/View/PdfView.php
namespace App\View;

use Cake\View\View;

class PdfView extends View
{
    public function render($view = null, $layout = null)
    {
        // Здесь находится ваша логика.
    }
}

Переопределение метода render() позволяет вам полностью контролировать то, как отображается ваш контент.

Подробнее о Представлениях

Ячейки Представления

Темы

Представления JSON и XML

Хелперы

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Ячейки Представления

Ячейки Представления - это небольшие мини-контроллеры, которые могут вызывать логику представления и выводить результат в шаблоны. Данная идея позаимствована от ячеек в Ruby [https://github.com/apotonick/cells], где они играют схожую роль и приследуют схожие цели.

Когда использовать ячейки

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

Создание ячеек

Чтобы создать ячейку, определите класс в src/View/Cell и шаблон в src/Template/Cell/. В этом примере мы создадим ячейку для отображения количества сообщений в почтовом ящике уведомлений пользователя. Сначала создайте файл класса. Его содержимое должно выглядеть так:

namespace App\View\Cell;

use Cake\View\Cell;

class InboxCell extends Cell
{

    public function display()
    {
    }

}

Сохраните этот файл как src/View/Cell/InboxCell.php. Как вы можете заметить, подобно другим классам в CakePHP, Ячейки имеют несколько соглашений:

Ячейки располагаются в пространстве имен App\View\Cell. Если вы создаете ячейку в плагине, пространство имен должно быть вида PluginName\View\Cell.

Имена классов должны оканчиваться на Cell.

Классы должны наследоваться от Cake\View\Cell.

Мы добавили пустой метод display() в нашу ячейку; это традиционный метод по умолчанию при рендеринге ячейки. Мы рассмотрим, как использовать другие методы позже в документации. Теперь создайте файл src/Template/Cell/Inbox/display.ctp. Это будет наш шаблон для нашей новой ячейки.

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

bin/cake bake cell Inbox

Это создаст код, аналогичный тому, что мы описали выше.

Реализация Ячеек

Предположим, что мы работаем над приложением, которое позволяет пользователям отправлять сообщения друг другу. У нас есть модель Messages, и мы хотим показать количество непрочитанных сообщений, не засоряя AppController. Это идеальный вариант использования для ячейки. В классе, который мы только что создали, добавьте следующее:

namespace App\View\Cell;

use Cake\View\Cell;

class InboxCell extends Cell
{

    public function display()
    {
        $this->loadModel('Messages');
        $unread = $this->Messages->find('unread');
        $this->set('unread_count', $unread->count());
    }

}

Поскольку ячейки используют ModelAwareTrait и ViewVarsTrait, их поведение очень схоже с поведением контроллера. Мы можем использовать методы loadModel() и set() точно так же, как в контроллере. В нашем файле шаблона добавьте следующее:

<!-- src/Template/Cell/Inbox/display.ctp -->
<div class="notification-icon">
    У вас <?= $unread_count ?> непрочитанных сообщений.
</div>

Примечание

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

Загрузка ячеек

Ячейки могут быть загружены из представлений с помощью метода cell(), и работают аналогично в обоих контекстах:

// Загрузка ячейки на уровне приложения
$cell = $this->cell('Inbox');

// Загрузка ячейки из плагина
$cell = $this->cell('Messaging.Inbox');

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

// Выполнить метод expanded() ячейки Inbox
$cell = $this->cell('Inbox::expanded');

Если вам понадобится логика контроллера, чтобы решить какие ячейки загружать в запросе, вы можете использовать CellTrait в вашем контроллере, чтобы сделать доступным метод cell():

namespace App\Controller;

use App\Controller\AppController;
use Cake\View\CellTrait;

class DashboardsController extends AppController
{
    use CellTrait;

    // Остальной код.
}

Передача аргументов ячейке

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

$cell = $this->cell('Inbox::recent', ['-3 days']);

Это будет соответствовавть следующей сигнатуре функции:

public function recent($since)
{
}

Отображение ячейки

Как только ячейка будет загружена и выполнена, вы вероятно захотите отобразить её. Простейший способ отобразить ячейку - использовать echo:

<?= $cell ?>

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

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

Примечание

Вывод ячейки с помощью echo использует магический метод PHP __toString(), который предотвращает вывод имени файла и номера строки для появляющихся ошибок. Для получения более информативных сообщений об ошибках рекомендуется использовать метод Cell::render(), например

Отображение альтернативных шаблонов

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

// Явный вызов метода render()
echo $this->cell('Inbox::recent', ['-3 days'])->render('messages');

// Установка шаблона перед выводом ячейки.
$cell = $this->cell('Inbox');
$cell->viewBuilder()->setTemplate('messages');
// До версии 3.4
$cell->viewBuilder()->template('messages');
// До версии 3.1
$cell->template = 'messages';
echo $cell;

Кэширование выводимых в ячейках данных

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

// Кэширование с использованием стандартной конфигурации и со сгенерированным ключом
$cell = $this->cell('Inbox', [], ['cache' => true]);

// Кэширование с определенной конфигурацией и со сгенерированным ключом
$cell = $this->cell('Inbox', [], ['cache' => ['config' => 'cell_cache']]);

// Определены и ключ и конфигурация кэширования
$cell = $this->cell('Inbox', [], [
    'cache' => ['config' => 'cell_cache', 'key' => 'inbox_' . $user->id]
]);

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

Примечание

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

Постраничная навигация внутри ячеек

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

namespace App\View\Cell;

use Cake\View\Cell;
use Cake\Datasource\Paginator;

class FavoritesCell extends Cell
{
    public function display($user)
    {
        $this->loadModel('Messages');

        // Создание пагинатора
        $paginator = new Paginator();

        // Разбиение модели на страницы
        $results = $paginator->paginate(
            $this->Messages,
            $this->request->getQueryParams(),
            [
                // Use a parameterized custom finder.
                'finder' => ['favorites' => [$user]],

                // Use scoped query string parameters.
                'scope' => 'favorites',
            ]
        );

        $paging = $paginator->getPagingParams() + (array)$request->getParam('paging');
        $this->request = $this->request->withParam('paging', $paging));

        $this->set('favorites', $results);
    }
}

Описанная выше ячейка разбивает на страницы модель Messages, используя scoped pagination parameters.

Добавлено в версии 3.5.0: Cake\Datasource\Paginator был добавлен в версии 3.5.0.

Параметры ячеек

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

namespace App\View\Cell;

use Cake\View\Cell;
use Cake\Datasource\Paginator;

class FavoritesCell extends Cell
{
    protected $_validCellOptions = ['limit'];

    protected $limit = 3;

    public function display($userId)
    {
        $this->loadModel('Users');
        $result = $this->Users->find('friends', ['for' => $userId]);
        $this->set('favorites', $result);
    }
}

Здесь мы определили свойство $limit и добавили параметр ячейки limit. Это позволит нам определять параметр при создании ячейки:

$cell = $this->cell('Favorites', [$user->id], ['limit' => 10])

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

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Темы

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

Чтобы использовать темы, установите название темы в экшене вашего контроллера, либо в коллбэк-методе beforeRender():

class ExamplesController extends AppController
{
    // Для CakePHP ниже версии 3.1
    public $theme = 'Modern';

    public function beforeRender(\Cake\Event\Event $event)
    {
        $this->viewBuilder()->setTheme('Modern');

        // Для CakePHP ниже версии 3.5
        $this->viewBuilder()->theme('Modern');
    }
}

Файлы шаблона темы должны находиться в паке плагина с соответствующим названием. К примеру, использованная выше тема может быть найдена в plugins/Modern/src/Template. Важно помнить, что CakePHP ожидает имена плагинов/тем в стиле PascalCase. В остальном, структура папки plugins/Modern/src/Template аналогична используемой в src/Template/.

Например, файл представления для экшена edit контроллера Posts будет расположен в файле plugins/Modern/src/Template/Posts/edit.ctp. Файлы макетов будут находиться в plugins/Modern/src/Template/Layout/. Вы также можете создавать кастомизированные шаблоны для плагинов с помощью тем. Если бы у вас был бы плагин под названием „Cms“, который бы содержал контроллер TagsController, тема Modern могла бы предоставить шаблон plugins/Modern/src/Template/Plugin/Cms/Tags/edit.ctp, заменяющий шаблон edit плагина.

Если файл представления не может быть найден внутри темы, CakePHP попытается обнаружить файл представления в папке src/Template/. Таким образом, вы можете создать файлы главного шаблона, и просто переопределять их в папке с вашей темой.

Если ваша тема также действует еще и в качестве плагина, не забудьте проверить, что она прописана в файле config/bootstrap.php. Например:

/**
 * Загружаем наш плагин с темой в папке /plugins/Modern
 */
Plugin::load('Modern');

Ресурсы темы

Так как темы - это стандартные плагины CakePHP, они могут включать в себя все необходимые ресурсы в своей папке webroot. Это позволяет с легкостью комплектовать и распространять темы. Во время разработки запросы к ресурсам темы бутдут обабатываться Cake\Routing\Dispatcher. Для повышения производительности на стадии продакшена рекомендуется использовать symlink-assets.

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

// В теме под названием 'purple_cupcake'
$this->Html->css('main.css');

// создается путь
/purple_cupcake/css/main.css

// и ссылается на
plugins/PurpleCupcake/webroot/css/main.css

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Представления JSON и XML

JsonView и XmlView позволяют вам создавать ответы JSON и XML, и интегрировать их с Cake\Controller\Component\RequestHandlerComponent.

Активируя RequestHandlerComponent в вашем приложении, а также поддержку расширений json и/или xml, вы можете автоматически использовать новые классы представлений. JsonView и XmlView будут далее именоваться просто как представления данных в этом разделе.

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

Активация представлений данных в вашем приложении

Прежде чем вы сможете использовать классы представлений данных, вам нужно сначала загрузить Cake\Controller\Component\RequestHandlerComponent в вашем контроллере:

public function initialize()
{
    ...
    $this->loadComponent('RequestHandler');
}

Это может быть сделано в вашем AppController, и активирует автоматическое переключение классов представлений, в зависимости от типа содержимого. Вы также можете настроить компонент с помощью опции viewClassMap, чтобы сопоставить типы вашим кастомным классам и/или добавить сопоставления также и для других типов данных (отличных от JSON и XML).

Вы опционально можете включить поддержку расширений json и/или xml, используя маршрутизацию по расширениям файлов. Это позволит вам получать доступ к данным в определенном формате, будь то JSON, XML или какой-либо другой формат, используя URL, оканчивающийся на имя типа ответа, как если бы это было расширением файла. К примеру http://example.com/articles.json.

По умолчанию, когда не включена маршрутизацию по расширениям файлов, запрос, для которого использовался заголовок Accept, определяет какого типа формат должен быть выведен пользователю. Например, формат Accept, используемый для вывода ответа в формате JSON - application/json.

Использование представлений данных с ключом Serialize

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

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

namespace App\Controller;

class ArticlesController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('RequestHandler');
    }

    public function index()
    {
        // Установить переменные представления, которые должны
        // быть сериализованы.
        $this->set('articles', $this->paginate());
        // Указываем, какие переменные представления JsonView
        // должен сериализовать.
        $this->set('_serialize', 'articles');
    }
}

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

namespace App\Controller;

class ArticlesController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('RequestHandler');
    }

    public function index()
    {
        // Какой-то код, создавший $articles и $comments

        // Установливаем переменные представления, которые должны
        // быть сериализованы.
        $this->set(compact('articles', 'comments'));

        // Указываем, какие переменные представления JsonView
        // должен сериализовать.
        $this->set('_serialize', ['articles', 'comments']);
    }
}

Определяя _serialize как массив, мы получаем преимущество автоматического добавления элемента верхнего уровня <response> при использовании XmlView. Если вы используете строковое значение для _serialize и XmlView, убедитесь в том, что ваша переменная представления имеет единственный элемент верхнего уровня. Если элементов верхнего уровня будет больше одного, Xml не сможет сгенерироваться.

Использование представления данных с файлами шаблонов

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

// Код контроллера
class ArticlesController extends AppController
{
    public function index()
    {
        $articles = $this->paginate('Articles');
        $this->set(compact('articles'));
    }
}

// Код представления - src/Template/Articles/json/index.ctp
foreach ($articles as &$article) {
    unset($article->generated_html);
}
echo json_encode(compact('articles'));

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

Примечание

С версии 3.1.0 AppController в каркасе приложения автоматически добавляет '_serialize' => true ко всем запросам XML/JSON. Вам придется удалить этот код из коллбэк-метода beforeRender(), либо установить значение '_serialize' => false в экшенах вашего контроллера, если вы хотите использовать файлы представлений.

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

class XmlView

По умолчанию при использовании _serialize XmlView обернет ваши сериализованные переменные в узел <response>. Вы можете задать свое собственное имя для этого узла, используя переменную представления _rootNode.

Класс XmlView поддерживает переменную _xmlOptions, которая позволяет вам изменять опции, используемые при генерировании XML

The XmlView class supports the _xmlOptions variable that allows you to customize the options used to generate XML, например tags вместо attributes.

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

class JsonView

Класс JsonView поддерживает переменную _jsonOptions, которая позволяет вам настроить битовую маску, используемую для генерирования JSON. См. json_encode [http://php.net/json_encode] для более полной информации о валидных значениях данной опции.

Например, чтобы сериализовать вывод ошибок валидации сущностей (entities) CakePHP в совместимой с JSON форме, сделайте следующее:

// Вэкшене вашего контроллера при ошибке сохранения
$this->set('errors', $articles->errors());
$this->set('_jsonOptions', JSON_FORCE_OBJECT);
$this->set('_serialize', ['errors']);

Ответы JSONP

При использовании JsonView вы можете использовать специальную переменную представления _jsonp для активации возможности получения ответа JSONP. Если установить значение true, класс представления проверяет, задан ли параметр строки запроса с именем «callback» и, если это так, следует обернуть ответ json в функцию с указанным именем. Если вы хотите использовать собственное имя параметра строки запроса вместо «callback», установите _jsonp на требуемое имя вместо true.

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

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

// src/Controller/VideosController.php
namespace App\Controller;

use App\Controller\AppController;
use Cake\Network\Exception\NotFoundException;

class VideosController extends AppController
{
    public function export($format = '')
    {
        $format = strtolower($format);

        // Формат сопоставления представлений
        $formats = [
          'xml' => 'Xml',
          'json' => 'Json',
        ];

        // Ошибка на неизвестных типах
        if (!isset($formats[$format])) {
            throw new NotFoundException(__('Unknown format.'));
        }

        // Установка формата вывода представления
        $this->viewBuilder()->className($formats[$format]);

        // Получение данных
        $videos = $this->Videos->find('latest');

        // Назначение представления данных
        $this->set(compact('videos'));
        $this->set('_serialize', ['videos']);

        // Принудительная загрузка
        // До версии 3.4.0
        // $this->response->download('report-' . date('YmdHis') . '.' . $format);
        return $this->response->withDownload('report-' . date('YmdHis') . '.' . $format);
    }
}

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Хелперы

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

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

Хлебные крошки

Мгновенные сообщения

Form

Html

Number

Paginator

Rss

Сессия

Text

Time

Url

Настройка хелперов

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

class AppView extends View
{
    public function initialize()
    {
        parent::initialize();
        $this->loadHelper('Html');
        $this->loadHelper('Form');
        $this->loadHelper('Flash');
    }
}

Для загрузки хелперов из плагинов используйте plugin syntax, используемый повсеместно в CakePHP:

$this->loadHelper('Blog.Comment');

Вы не обязаны явно загружать хелперы, входящие в состав CakePHP или вашего приложения. Эти хелперы могут быть лениво загружены при первом использовании. Например:

// Загружается FormHelper если он еще не был загружен.
$this->Form->create($article);

Из представлений, входящих в состав плагинов, хелперы плагинов также могут быть лениво загружены. Например, шаблоны представления в плагине „Blog“ могут лениво загружать хелперы из этого же плагина.

Загрузка хелперов по условию

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

class AppView extends View
{
    public function initialize()
    {
        parent::initialize();
        if ($this->request->getParam('action') === 'index') {
            $this->loadHelper('ListPage');
        }
    }
}

Вы можете также использовать метод beforeRender() вашего контроллера для загрузки хелперов:

class ArticlesController extends AppController
{
    public function beforeRender(Event $event)
    {
        parent::beforeRender($event);
        $this->viewBuilder()->helpers(['MyHelper']);
    }
}

Параметры конфигурации

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

namespace App\View\Helper;

use Cake\View\Helper;
use Cake\View\View;

class AwesomeHelper extends Helper
{

    // Хук initialize() доступен с версии 3.2. Для более ранних версий
    // вы можете переопределить конструктор, если это необходимо.
    public function initialize(array $config)
    {
        debug($config);
    }
}

Параметры могут быть определены при объявлении хелперов в контроллере, как показано ниже:

namespace App\Controller;

use App\Controller\AppController;

class AwesomeController extends AppController
{
    public $helpers = ['Awesome' => ['option1' => 'value1']];
}

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

namespace App\View\Helper;

use Cake\View\Helper;
use Cake\View\StringTemplateTrait;

class AwesomeHelper extends Helper
{

    use StringTemplateTrait;

    protected $_defaultConfig = [
        'errorClass' => 'error',
        'templates' => [
            'label' => '<label for="{{for}}">{{content}}</label>',
        ],
    ];
}

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

// Чтение параметра конфигурации errorClass.
$class = $this->Awesome->config('errorClass');

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

class PostsController extends AppController
{
    public function beforeRender(Event $event)
    {
        parent::beforeRender($event);
        $builder = $this->viewBuilder();
        $builder->helpers([
            'CustomStuff' => $this->_getCustomStuffConfig(),
        ]);
    }
}

Создание псевдонимов для хелперов

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

// src/View/AppView.php
class AppView extends View
{
    public function initialize()
    {
        $this->loadHelper('Html', [
            'className' => 'MyHtml'
        ]);
    }
}

// src/View/Helper/MyHtmlHelper.php
namespace App\View\Helper;

use Cake\View\Helper\HtmlHelper;

class MyHtmlHelper extends HtmlHelper
{
    // Добавьте сюда ваш код для переопределения функций HtmlHelper
}

Приведенный выше пример установит псевдоним MyHtmlHelper для вызова $this->Html в ваших представлениях.

Примечание

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

Использование хелперов

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

echo $this->Html->css('styles');

Это вызовет метод css() хелпера HtmlHelper. Вы можете получить доступ к любому хелперу используя конструкцию $this->{$имяХелпера}.

Загрузка хелперов на лету

Иногда могут возникнуть ситуации, когда вам понадобится динамически загружать хелперы из представления. Вы можете использовать для этого класс представления Cake\View\HelperRegistry:

// Можете использовать любой из вариантов.
$mediaHelper = $this->loadHelper('Media', $mediaConfig);
$mediaHelper = $this->helpers()->load('Media', $mediaConfig);

HelperRegistry - это реестр и он поддерживает API реестра используемый повсеместно в CakePHP.

Коллбэк-методы

Помощники имеют несколько коллбэк-методов, которые позволяют вам управлять процессом визуализации представления. Смотрите разделы документации Класс Helper и Events System для получения более подробной информации.

Создание хелперов

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

Файлы классов хелперов должны располагаться в папке src/View/Helper. Например: src/View/Helper/LinkHelper.php

Имена классов хелперов должны оканчиваться на Helper. Например: LinkHelper.

Если вы ссылаетесь на имена классов хелперов, вы должны опускать суффикс Helper. Например: $this->loadHelper('Link');.

Также вы должны наследовать класс Helper, чтобы у вас все работало корректно:

/* src/View/Helper/LinkHelper.php */
namespace App\View\Helper;

use Cake\View\Helper;

class LinkHelper extends Helper
{
    public function makeEdit($title, $url)
    {
        // Здесь размещается логика создания ссылок с особым
        // форматированием
    }
}

Встраивание других хелперов

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

/* src/View/Helper/LinkHelper.php (using other helpers) */

namespace App\View\Helper;

use Cake\View\Helper;

class LinkHelper extends Helper
{
    public $helpers = ['Html'];

    public function makeEdit($title, $url)
    {
        // Используйте HTML-хелпер для вывода
        // форматированных данных:

        $link = $this->Html->link($title, $url, ['class' => 'edit']);

        return '<div class="editOuter">' . $link . '</div>';
    }
}

Использование вашего хелпера

Как только вы создали ваш хелпер и разместили его в папке src/View/Helper/, вы можете загрузить его в ваших представлениях:

class AppView extends View
{
    public function initialize()
    {
        parent::initialize();
        $this->loadHelper('Link');
    }
}

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

<!-- создание ссылки с помощью нового хелпера -->
<?= $this->Link->makeEdit('Change this Recipe', '/recipes/edit/5') ?>

Примечание

HelperRegistry будет пытаться лениво загрузить любые хелперы, не определенные явно в вашем Контроллере.

Доступ к переменным представления внутри вашего хелпера

Если вы захотите получить доступ к переменной Представления внутри хелпера, вы можете использовать $this->_View->get():

class AwesomeHelper extends Helper
{

    public $helpers = ['Html'];

    public someMethod()
    {
        // установить мета-тег description
        echo $this->Html->meta(
            'description', $this->_View->get('metaDescription'), ['block' => 'meta']
        );
    }
}

Вывод элемента Представления внутри вашего хелпера

Если вы захотите вывести Элемент внутри вашего Хелпера, вы можете использовать $this->_View->element() следующим образом:

class AwesomeHelper extends Helper
{
    public someFunction()
    {
        // вывод непосредственно в вашем хелпере
        echo $this->_View->element(
            '/path/to/element',
            ['foo'=>'bar','bar'=>'foo']
        );

        // или возаращаем результат в
        // ваше представление
        return $this->_View->element(
            '/path/to/element',
            ['foo'=>'bar','bar'=>'foo']
        );
    }
}

Класс Helper

class Helper

Коллбэки

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

Helper::beforeRenderFile(Event $event, $viewFile)

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

Helper::afterRenderFile(Event $event, $viewFile, $content)

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

Helper::beforeRender(Event $event, $viewFile)

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

Helper::afterRender(Event $event, $viewFile)

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

Helper::beforeLayout(Event $event, $layoutFile)

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

Helper::afterLayout(Event $event, $layoutFile)

Вызывается после завершения обработки макета. Получает в качестве аргумента имя файла макета.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Хлебные крошки

class Cake\View\Helper\BreadcrumbsHelper(View $view, array $config = [])

Добавлено в версии 3.3.6.

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

Создание навигационных цепочек

Вы можете добавлять звенья в список, используя метод add(). Он принимает три аргумента:

title Строка для отображения в качестве заголовка хлебной крошки

url Строка или массив параметров, передаваемых хелперу Url

options Массив атрибутов для шаблонов item и itemWithoutLink. Смотрите раздел определение атрибутов для элемента для более полной информации.

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

// Добавить элемен в конец цепочки
$this->Breadcrumbs->add(
    'Products',
    ['controller' => 'products', 'action' => 'index']
);

// Добавить несколько элементов в конец цепочки
$this->Breadcrumbs->add([
    ['title' => 'Products', 'url' => ['controller' => 'products', 'action' => 'index']],
    ['title' => 'Product name', 'url' => ['controller' => 'products', 'action' => 'view', 1234]]
]);

// Элементы будут добавлены в начало списка
$this->Breadcrumbs->prepend(
    'Products',
    ['controller' => 'products', 'action' => 'index']
);

// Добавить несколько элементов в начало списка в том порядке,
// в котором они указаны
$this->Breadcrumbs->prepend([
    ['title' => 'Products', 'url' => ['controller' => 'products', 'action' => 'index']],
    ['title' => 'Product name', 'url' => ['controller' => 'products', 'action' => 'view', 1234]]
]);

// Вставка в определенную позицию. Если позиция находится вне диапазона
// допустимых значений, будет выброшено исключение.
$this->Breadcrumbs->insertAt(
    2,
    'Products',
    ['controller' => 'products', 'action' => 'index']
);

// Вставка перед другим элементом, в соответствии с заголовком.
// Если указанный элемент не будет обнаружен,
// будет выброшено исключение.
$this->Breadcrumbs->insertBefore(
    'A product name', // заголовок элемента, перед которым вставлять новый
    'Products',
    ['controller' => 'products', 'action' => 'index']
);

// Вставка после другого элемента, в соответствии с заголовком.
// Если указанный элемент не будет обнаружен,
// будет выброшено исключение.
$this->Breadcrumbs->insertAfter(
    'A product name', // заголовок элемента, после которого вставлять новый
    'Products',
    ['controller' => 'products', 'action' => 'index']
);

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

Вывод цепочки хлебных крошек

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

$attributes : Массив атрибутов, которые будут применены к шаблону wrapper. Это дает вам возможность добавлять атрибуты тегам HTML. Он принимает специальный ключ templateVars, чтобы позволить вставку пользовательских переменных в шаблон.

$separator : Массив атрибутов для шаблона separator. Доступные свойства:

separator Строка для отображения в качестве разделителя

innerAttrs Предоставление атрибутов в случае, если разделитель разбит на две части

templateVars Позволяет добавить пользовательские переменные в шаблон

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

Вот пример того, как выводится навигационная цепочка:

echo $this->Breadcrumbs->render(
    ['class' => 'breadcrumbs-trail'],
    ['separator' => '<i class="fa fa-angle-right"></i>']
);

Кастомизация вывода

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

[
    'wrapper' => '<ul{{attrs}}>{{content}}</ul>',
    'item' => '<li{{attrs}}><a href="{{url}}"{{innerAttrs}}>{{title}}</a></li>{{separator}}',
    'itemWithoutLink' => '<li{{attrs}}><span{{innerAttrs}}>{{title}}</span></li>{{separator}}',
    'separator' => '<li{{attrs}}><span{{innerAttrs}}>{{separator}}</span></li>'
]

Вы можете с легкостью изменять их, используя метод templates(), из StringTemplateTrait:

$this->Breadcrumbs->templates([
    'wrapper' => '<nav class="breadcrumbs"><ul{{attrs}}>{{content}}</ul></nav>',
]);

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

$this->Breadcrumbs->templates([
    'item' => '<li{{attrs}}>{{icon}}<a href="{{url}}"{{innerAttrs}}>{{title}}</a></li>{{separator}}'
]);

Чтобы определить параметр {{icon}}, просто пропишите его при добавлении элементов в навигационную цепочку:

$this->Breadcrumbs->add(
    'Products',
    ['controller' => 'products', 'action' => 'index'],
    [
        'templateVars' => [
            'icon' => '<i class="fa fa-money"></i>'
        ]
    ]
);

Определение атрибутов для элементов

Если вы хотите применить специфические HTML-атрибуты к элементу цепочки, а также к его дочернему элементу, вы можете использовать ключ innerAttrs, предлагаемый аргументом $options. Все кроме параметров innerAttrs и templateVars будет выведено как HTML-атрибуты элемента цепочки:

$this->Breadcrumbs->add(
    'Products',
    ['controller' => 'products', 'action' => 'index'],
    [
        'class' => 'products-crumb',
        'data-foo' => 'bar',
        'innerAttrs' => [
            'class' => 'inner-products-crumb',
            'id' => 'the-products-crumb'
        ]
    ]
);

// В соответствии с шаблоном по умолчанию, это сформирует следующий HTML:
<li class="products-crumb" data-foo="bar">
    <a href="/products/index" class="inner-products-crumb" id="the-products-crumb">Products</a>
</li>

Очистка хлебных крошек

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

$crumbs = $this->Breadcrumbs->getCrumbs();
$crumbs = collection($crumbs)->map(function ($crumb) {
    $crumb['options']['class'] = 'breadcrumb-item';
    return $crumb;
})->toArray();

$this->Breadcrumbs->reset()->add($crumbs);

Добавлено в версии 3.4.0: Метод reset() был добавлен в версии 3.4.0

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Мгновенные сообщения

class Cake\View\Helper\FlashHelper(View $view, array $config = [])

FlashHelper предоставляет способ визуализации мгновенных сообщений, которые сохраняются в $_SESSION с использованием FlashComponent. FlashComponent и FlashHelper в основном используют элементы для отображения мгновенных сообщений. Элементы мгновенных сообщений находятся в папке src/Template/Element/Flash. Заметьте, что шаблон приложения CakePHP предоставляется с тремя готовыми элементами мгновенных сообщений: success.ctp, default.ctp, и error.ctp.

Отображение мгновенных сообщений

Для отображения мгновенного сообщения используется метод render() из FlashHelper в файле шаблона:

<?= $this->Flash->render() ?>

По умолчанию CakePHP использует ключ «flash» для мгновенных сообщений в сессии. Но, если был указан ключ при установке мгновенного сообщения в FlashComponent, можно указать какой из ключей мгновенного сообщения отображать:

<?= $this->Flash->render('other') ?>

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

// в Контроллере
$this->Flash->set('The user has been saved.', [
    'element' => 'success'
]);

// В файле шаблона: будет использован great_success.ctp вместо succcess.ctp
<?= $this->Flash->render('flash', [
    'element' => 'great_success'
]);

Примечание

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

Добавлено в версии 3.1: FlashComponent теперь ставит сообщения в очередь. Если установлены несколько мгновенных сообщений, при вызове render() каждое сообщение будет отображать свои собственные элементы в том порядке, в каком они были установлены.

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

Префикс маршрутизации и мгновенные сообщения

Добавлено в версии 3.0.1.

Если у вас сконфигурирован префикс маршрутизации, Flash элементы могут храниться в src/Template/{Prefix}/Element/Flash. Таким образом, вы можете хранить конкретные шаблоны сообщений для каждой части вашего приложения. Например, используя различные щаблоны для пользовательского интерфейса и интерфейса администратора.

Мгновенные сообщения и темы

FlashHelper использует обычные элементы для отображения сообщений и таким образом будет соответствовать любой теме, которая будет указана. Таким образом, когда у темы есть файл src/Template/Element/Flash/error.ctp, он будет использоваться так же, как и другие Элементы и Представления.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Form

class Cake\View\Helper\FormHelper(View $view, array $config = [])

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

Запуск формы

Cake\View\Helper\FormHelper::create(mixed $context = null, array $options = [])

$context - Контекст, для которого определяется форма. Может быть объектом ORM, набором результатов ORM, массивом метаданных или false/null (чтобы сделать форму без модели).

$options - Массив параметров и/или атрибутов HTML.

Первым методом, который вам нужно использовать для использования FormHelper, является create(). Этот метод выводит тег открытия формы.

Все параметры являются необязательными. Если create() вызывается без заданных параметров, предполагается, что вы создаёте форму, которая отправляется текущему контроллеру через текущий URL-адрес. Метод отправки по умолчанию - POST. Если вы вызвали create() внутри представления для UsersController::add(), вы увидите что-то вроде следующего вывода в визуализированном представлении:

<form method="post" action="/users/add">

Аргумент $context используется как „контекст“ формы. Существует несколько встроенных контекстов формы, и вы даже можете добавить свои собственные контексты, которые мы рассмотрим ниже, в следующем разделе. Встроенные провайдеры отображают следующие значения $context:

Экземпляр Entity или итератор будут отображаться в EntityContext [https://api.cakephp.org/3.x/class-Cake.View.Form.EntityContext.html]; этот контекстный класс позволяет FormHelper работать с результатами встроенной ORM.

Массив, содержащий ключ schema, будет отображаться в ArrayContext [https://api.cakephp.org/3.x/class-Cake.View.Form.ArrayContext.html], который позволяет вам создание простых структур данных для ваших форм.

null и false будут отображаться в NullContext [https://api.cakephp.org/3.x/class-Cake.View.Form.NullContext.html]; этот контекстный класс требуется интерфейсу FormHelper. Этот контекст полезен, если вы хотите создать короткую форму, которая не требует постоянной ORM.

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

Когда форма создана с контекстом, все созданные вами элементы управления будут использовать активный контекст. В случае поддерживаемой формы ORM FormHelper может обращаться к связанным данным, ошибкам проверки и метаданным схемы. Вы можете закрыть активный контекст с помощью метода end() и снова его вызвать create().

Чтобы создать форму для объекта, выполните следующие действия:

// Если вы находитесь на /articles/add
// $article должна быть пустым элементом статьи
echo $this->Form->create($article);

Вывод:

<form method="post" action="/articles/add">

Это будут POST данные формы для метода add() экшена в ArticlesController. И вы также можете использовать ту же логику для создания формы редактирования. FormHelper использует объект Entity для автоматического определения того, следует ли создавать форму добавления или редактирования. Если предоставленный объект не является новым, форма будет создана как форма для редактирования.

Например, если мы перейдем к http://example.org/articles/edit/5, мы можем сделать следующее:

// src/Controller/ArticlesController.php:
public function edit($id = null)
{
    if (empty($id)) {
        throw new NotFoundException;
    }
    $article = $this->Articles->get($id);
    // Здесь логика сохранения
    $this->set('article', $article);
}

// View/Articles/edit.ctp:
// Поскольку $article->isNew() является ложным, мы получим форму редактирования
<?= $this->Form->create($article) ?>

Вывод:

<form method="post" action="/articles/edit/5">
<input type="hidden" name="_method" value="PUT" />

Примечание

Поскольку это форма редактирования, то создаётся скрытое поле input для переопределения метода HTTP по умолчанию.

Варианты создания формы

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

'type' - Позволяет выбрать тип создаваемой формы. Если тип не указан, он будет определён автоматически на основе контекста формы. Допустимые значения:

'get' - Будет установлен метод формы HTTP GET.

'file' - Будет установлен метод формы HTTP POST и 'enctype' в «multipart/form-data».

'post' - Будет установлен метод формы HTTP POST.

'put', 'delete', 'patch' - При передаче формы будет переопределять метод HTTP с помощью PUT, DELETE или PATCH соответственно.

'method' - Допустимые значения такие же, как указано выше. Позволяет явно переопределить метод формы.

'url' - Укажите URL-адрес, в который будут отправляться данные из формы. Может быть строкой или массивом URL.

'encoding' - Устанавливает кодировку accept-charset для формы. По умолчанию используется Configure::read('App.encoding').

'enctype' - Позволяет явно установить кодировку формы.

'templates' - Шаблоны, которые вы хотите использовать для этой формы. Любые предоставленные шаблоны будут объединены поверх уже загруженных шаблонов. Может быть либо именем файла (без расширения) из /config, либо массивом шаблонов для использования.

'context' - Дополнительные параметры для класса контекста формы. (Например, EntityContext принимает параметр 'table', который позволяет вам установить конкретный класс таблицы, на котором должна быть основана форма.)

'idPrefix' - Префикс для генерирования атрибутов ID.

'templateVars' - Позволяет предоставить шаблонные переменные для шаблона formStart.

Совет

Помимо указанных выше опций вы можете указать в аргументе $options любые допустимые атрибуты HTML, которые вы хотите передать созданному элементу form.

Получение значений формы из строки запроса

Добавлено в версии 3.4.0.

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

По умолчанию FormHelper извлекает значения из контекста. Контексты по умолчанию, такие как EntityContext, будут извлекать данные из текущего объекта или из $request->getData().

Однако, если, вы создаёте форму, которая должна читать данные из строки запроса, вы можете использовать valueSource() для изменения того, где FormHelper считывает вводимые данные:

// Строка запроса приоритета по контексту:
echo $this->Form->create($article, [
    'valueSources' => ['query', 'context']
]);

// Тот же эффект:
echo $this->Form
    ->setValueSources(['query', 'context'])
    ->create($articles);

// Чтение данных только из строки запроса:
echo $this->Form->create($article);
$this->Form->setValueSources('query');

// Тот же эффект:
echo $this->Form->create($article, ['valueSources' => 'query']);

Поддерживаемые источники - context, data и query. Вы можете использовать один или несколько источников. Любые виджеты, сгенерированные FormHelper, собирают свои значения из источников в том порядке, в котором вы это настроили.

Источники значений будут сброшены до значения по умолчанию (['context']), когда вызывается end().

Изменение метода HTTP для формы

С помощью опции type вы можете изменить метод HTTP, который будет использоваться формой:

echo $this->Form->create($article, ['type' => 'get']);

Вывод:

<form method="get" action="/articles/edit/5">

Указание значения 'file' для type, изменяет метод отправки формы на „post“, и включает в себя enctype из «multipart/form-data» в теге формы. Это должно использоваться, если в форме есть какие-либо элементы файла. Отсутствие правильного атрибута enctype приведёт к тому, что загрузка файла не будет функционировать.

Например:

echo $this->Form->create($article, ['type' => 'file']);

Вывод:

<form enctype="multipart/form-data" method="post" action="/articles/add">

При использовании значений 'put', 'patch' или 'delete' как значение 'type', ваша форма будет функционально эквивалентна форме „post“, но тогда представленный, метод HTTP-запроса будет переопределён как „PUT“, „PATCH“ или „DELETE“, соответственно. Это позволяет CakePHP эмулировать правильную поддержку REST в веб-браузерах.

Установка URL-адреса для формы

Использование опции 'url' позволяет указать путь до определенного экшена в текущем контроллере или другом контроллере в вашем приложении.

Для примера, если вы хотите указать форму для метода login() текущего контроллера, вы должны написать массив $options, например следующий:

echo $this->Form->create($article, ['url' => ['action' => 'login']]);

Вывод:

<form method="post" action="/users/login">

Если желаемое действие формы отсутствует в текущем контроллере, вы можете указать полный URL-адрес до экшена для формы. Приведенный URL-адрес может относиться к вашему приложению CakePHP:

echo $this->Form->create(null, [
    'url' => ['controller' => 'Articles', 'action' => 'publish']
]);

Вывод:

<form method="post" action="/articles/publish">

Или вы можете указать внешний домен:

echo $this->Form->create(null, [
    'url' => 'http://www.google.com/search',
    'type' => 'get'
]);

Вывод:

<form method="get" action="http://www.google.com/search">

Используйте 'url' => false, если вы не хотите использовать URL-адрес в качестве экшена формы.

Использование пользовательских валидаторов

Часто модели будут иметь множество наборов проверки, и вы захотите, чтобы FormHelper маркировал необходимые поля, на основе определенных правил проверки, которые будет действовать для ваших контроллеров. Например, таблица „users“ имеет специальные правила проверки, которые применяются только при регистрации учетной записи:

echo $this->Form->create($user, [
    'context' => ['validator' => 'register']
]);

Вышеизложенное будет использовать правила, определенные в валидаторе register, которые определяются UsersTable::validationRegister(), для $user и всех связанных ассоциаций. Если вы создаёте форму для связанных объектов, вы можете определить правила проверки для каждой ассоциации, используя массив:

echo $this->Form->create($user, [
    'context' => [
        'validator' => [
            'Users' => 'register',
            'Comments' => 'default'
        ]
    ]
]);

Вышеизложенное будет использовать register для пользователя и default для комментариев пользователя.

Создание классов контекста

Хотя встроенные классы контекста предназначены для охвата основных случаев, с которыми вы столкнетесь, вам может понадобиться построить новый класс контекста, если вы используете другую ORM. В этих ситуациях вам нужно реализовать Cake\View\Form\ContextInterface [https://api.cakephp.org/3.x/class-Cake.View.Form.ContextInterface.html] . После того как вы внедрили этот интерфейс, вы можете связать свой новый контекст с FormHelper. Часто бывает лучше сделать это в прослушивателе событий View.beforeRender или в классе представления приложения:

$this->Form->addContextProvider('myprovider', function ($request, $data) {
    if ($data['entity'] instanceof MyOrmClass) {
        return new MyProvider($request, $data);
    }
});

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

Создание элементов управления формы

Cake\View\Helper\FormHelper::control(string $fieldName, array $options = [])

$fieldName - Имя поля в форме 'Modelname.fieldname'.

$options - Необязательный массив, который может включать как Опции для Control, так и опции других методов (которые control() использует внутри для генерации различных элементов HTML), а также любые допустимые атрибуты HTML.

Метод control() позволяет создавать полные(законченные) элементы управления формой. Эти элементы управления будут включать в себя обёртку div, label, виджет управления и ошибки проверки(валидации), если это необходимо. Используя метаданные в контексте формы, этот метод выберет соответствующий тип управления для каждого поля. Внутри себя метод control() использует другие методы FormHelper.

Совет

Обратите внимание, что, хотя поля, созданные методом control(), называются в общем «inputs» на этой странице, технически говоря, метод control() может генерировать не только одни input элементы HTML, но также и другие элементы HTML-формы (Например select, button, textarea).

По умолчанию метод control() будет использовать следующие шаблоны виджетов:

'inputContainer' => '<div class="input {{type}}{{required}}">{{content}}</div>'
'input' => '<input type="{{type}}" name="{{name}}"{{attrs}}/>'

В случае ошибок проверки он также будет использовать:

'inputContainerError' => '<div class="input {{type}}{{required}} error">{{content}}{{error}}</div>'

Тип созданного элемента управления (когда мы не предоставляем никаких дополнительных опций для указания типа сгенерированного элемента) выводится с помощью интроспекции модели и зависит от типа столбца:

Column Type

Resulting Form Field

string, uuid (char, varchar, etc.)

text

boolean, tinyint(1)

checkbox

decimal

number

float

number

integer

number

text

textarea

text, with name of password, passwd

password

text, with name of email

email

text, with name of tel, telephone, or phone

tel

date

day, month, and year selects

datetime, timestamp

day, month, year, hour, minute, and meridian selects

time

hour, minute, and meridian selects

binary

file

Параметр $options позволяет вам выбрать конкретный тип управления, который вам нужен:

echo $this->Form->control('published', ['type' => 'checkbox']);

Совет

Как небольшая тонкость, генерирование определенных элементов с помощью метода формы control() всегда будет по умолчанию генерировать обёртку div. Генерирование одного и того же типа элемента с помощью одного из методов конкретной формы (например, $this->Form->('published');) в большинстве случаев не будет генерировать обёртку div. В зависимости от ваших потребностей вы можете использовать тот или иной способ.

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

echo $this->Form->control('title', ['required' => false]);

Чтобы пропустить запуск проверки браузера для всей формы, вы можете установить опцию 'formnovalidate' => true для кнопки ввода, которую вы создаёте, используя View\Helper\FormHelper::submit() или установите 'novalidate' => true в опции для View\Helper\FormHelper::create().

Например, предположим, что модель ваших пользователей содержит поля для username (varchar), password (varchar), approved (datetime) и quote (text). Вы можете использовать метод control() FormHelper для создания соответствующих элементов управления для всех этих полей формы:

echo $this->Form->create($user);
// Генерируем текстовый input
echo $this->Form->control('username');
// Генерируем парольный input
echo $this->Form->control('password');
// Предполагается, что 'approved' - это поле даты или времени,
// генерирующее: День, Месяц, Год, Час, Минуту
echo $this->Form->control('approved');
// Генерируем элемент textarea (текстовую область)
echo $this->Form->control('quote');
// Генерируем кнопку
echo $this->Form->button('Add');
echo $this->Form->end();

Более подробный пример, показывающий некоторые параметры для поля даты:

echo $this->Form->control('birth_dt', [
    'label' => 'Date of birth',
    'minYear' => date('Y') - 70,
    'maxYear' => date('Y') - 18,
]);

Помимо специфических Опции для Control, вы также можете указать любую опцию, принятую соответствующим специальным методом для выбранного типа управления (или предполагаемого CakePHP) и любого атрибута HTML (например, onfocus).

Если вы хотите создать поле формы select с использованием отношения belongsTo (или hasOne), вы можете добавить следующее в свой UserController (при условии, что ваш пользователь belongsTo - принадлежит группе):

$this->set('groups', $this->Users->Groups->find('list'));

После этого добавьте в шаблон вида следующее:

echo $this->Form->control('group_id', ['options' => $groups]);

Чтобы сделать поле select для ассоциации belongsToMany групп, вы можете добавить следующее в свой UserController:

$this->set('groups', $this->Users->Groups->find('list'));

После этого, добавьте в шаблон вида следующее:

echo $this->Form->control('groups._ids', ['options' => $groups]);

Если имя вашей модели состоит из двух или более слов (например, «UserGroups»), при передаче данных с помощью set() вы должны называть свои данные в множественном числе и нижнем camelCased и lower camelCased [https://en.wikipedia.org/wiki/Camel_case#Variations_and_synonyms] следующим образом:

$this->set('userGroups', $this->UserGroups->find('list'));

Примечание

Вы не должны использовать FormHelper::control() для создания кнопок отправки. Используйте вместо этого: View\Helper\FormHelper::submit().

Соглашения об именах полей

При создании виджетов control вы должны указывать свои поля после соответствующих атрибутов в объекте форм. Например, если вы создали форму для объекта $article, вы должны создать поля, названные в честь его свойств. Например. title, body и published.

Вы можете создавать элементы управления для связанных моделей или произвольных моделей, передавая association.fieldname первым параметром:

echo $this->Form->control('association.fieldname');

Любые точки в именах полей будут преобразованы в данные вложенных запросов. Например, если вы создали поле с именем 0.comments.body, вы получили бы атрибут имени, который выглядит как 0[comments][body]. Это соглашение упрощает сохранение данных с помощью ORM. Подробности для различных типов ассоциаций можно найти в разделе Создание инпутов для связанных данных section.

При создании элементов управления, связанных с датой, FormHelper добавит field-suffix (суфик поля). Вы можете заметить дополнительные поля с именем year, month, day, hour, minute или meridian. Эти поля автоматически преобразуются в объекты DateTime, когда сортируются эти объекты.

Опции для Control

FormHelper::control() поддерживает большое количество опций с помощью аргумента $options. В дополнение к своим собственным опциям control() принимает опции для выведенных/выбранных сгенерированных типов control (например, для checkbox или textarea), а также для атрибутов HTML. В этом подразделе будут рассмотрены варианты, специфичные (исключительно) для FormHelper::control().

$options['type'] - Строка, определяющая тип виджета, который будет сгенерирован. В дополнение к типам полей, найденным в Создание элементов управления формы, вы также можете создавать 'file', 'password' и любой другой тип, поддерживаемый HTML5. Указав 'type', вы будете вынуждать тип сгенерированного элемента control, переопределять интроспекцию модели. По умолчанию используется значение null.

Например:

echo $this->Form->control('field', ['type' => 'file']);
echo $this->Form->control('email', ['type' => 'email']);

Вывод:

<div class="input file">
    <label for="field">Field</label>
    <input type="file" name="field" value="" id="field" />
</div>
<div class="input email">
    <label for="email">Email</label>
    <input type="email" name="email" value="" id="email" />
</div>

$options['label'] - Либо заголовок строки, либо массив ссылок options for the label. Вы можете установить этот ключ в строку, которую вы хотите отобразить в label, который обычно сопровождает HTML элемент input. По умолчанию используется значение null.

Например:

echo $this->Form->control('name', [
    'label' => 'The User Alias'
]);

Вывод:

<div class="input">
    <label for="name">The User Alias</label>
    <input name="name" type="text" value="" id="name" />
</div>

Кроме того, можно установить этот ключ в false, чтобы отключить генерацию элемента label.

Например:

echo $this->Form->control('name', ['label' => false]);

Вывод:

<div class="input">
    <input name="name" type="text" value="" id="name" />
</div>

Установите это в массив, чтобы предоставить дополнительные параметры для элемента `` label``. Если вы это сделаете, вы можете использовать ключ 'text' в массиве для настройки текста label.

Например:

echo $this->Form->control('name', [
    'label' => [
        'class' => 'thingy',
        'text' => 'The User Alias'
    ]
]);

Вывод:

<div class="input">
    <label for="name" class="thingy">The User Alias</label>
    <input name="name" type="text" value="" id="name" />
</div>

$options['options'] - Здесь вы можете указать массив, содержащий элементы, которые должны быть сгенерированы для виджетов, таких как radio или select, для которых требуется массив элементов в качестве аргумента (см Создание radio кнопок и Создание селектов (выборщиков) для получения более подробной информации). По умолчанию используется значение null.

$options['error'] - Использование этого ключа позволяет переопределить сообщения об ошибках модели по умолчанию и может использоваться, например, для установки сообщений i18n. Чтобы отключить вывод сообщения об ошибках и полевые классы, установите ключ 'error' в false. По умолчанию используется значение null.

Например:

echo $this->Form->control('name', ['error' => false]);

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

Например:

$this->Form->control('name', [
    'error' => ['Not long enough' => __('This is not long enough')]
]);

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

$options['nestedInput'] - Используется с флажками и переключателями. Контролирует, генерируется ли элемент ввода внутри или вне элемента label. Когда control() генерирует флажок или переключатель, вы можете установить это значение в false, чтобы заставить генерировать HTML элемент input вне элемента label.

С другой стороны, вы можете установить это значение в true для любого типа control, чтобы принудительно сгенерировать элемент ввода внутри label. Если вы измените это для переключателей, вам также необходимо изменить шаблон по умолчанию radioWrapper. В зависимости от сгенерированного типа control, он по умолчанию имеет значение true или false.

$options['templates'] - Шаблоны, которые вы хотите использовать для этого input. Любые указанные шаблоны будут объединены поверх уже загруженных шаблонов. Этот параметр может быть либо именем файла (без расширения) в /config, который содержит шаблоны, которые вы хотите загрузить, так и массивом шаблонов.

$options['labelOptions'] - Установите для этого параметра значение false, чтобы отключить метки (label) вокруг nestedWidgets или установить его в массив атрибутов, который будет передан тегу label.

Создание специализированных элементов управления

В дополнение к общему методу control(), FormHelper имеет специальные методы для создания нескольких различных типов элементов управления. Они могут быть использованы для создания собственно виджета управления и в сочетании с другими методами, такими как View\Helper\FormHelper::label() и View\Helper\FormHelper::error() для создания полностью настраиваемых макетов форм.

Общие параметры для специализированных элементов управления

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

'id' - Установите этот ключ, чтобы принудительно установить значение идентификатора DOM для элемента управления. Это переопределит 'idPrefix', который может быть установлен.

'default' - Используется для установки значения по умолчанию для поля управления. Значение используется, если данные, переданные в форму, не содержат значения для поля (или если данные вообще не передаются). Явное значение по умолчанию переопределит любые значения по умолчанию, определенные в схеме.

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

echo $this->Form->text('ingredient', ['default' => 'Sugar']);

Пример с полем select («Medium» размер будет выбран по умолчанию):

$sizes = ['s' => 'Small', 'm' => 'Medium', 'l' => 'Large'];
echo $this->Form->select('size', $sizes, ['default' => 'm']);

Примечание

Вы не можете использовать default для проверки checkbox - вместо этого вы можете установить значение в $this->request->getData() в своём контроллере или установить параметр управления 'checked' в true.

Остерегайтесь использования false для назначения значения по умолчанию. Значение false используется для отключения/исключения опций поля управления, поэтому 'default' => false не будет устанавливать никакого значения вообще. Вместо этого используйте 'default' => 0.

'value' - Form, Entity или request->getData() например, используется для установки определенного значения для поля управления. Это переопределит любое значение, которое может быть добавлено из контекста, например Form, Entity или request->getData() и т.д.

Примечание

Если вы хотите, чтобы поле не отображало его значение из контекста или valuesSource, вам нужно установить 'value' в '' (вместо того, чтобы устанавливать его в null).

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

Изменено в версии 3.3.0: Начиная с версии 3.3.0, FormHelper автоматически будет использовать любые значения по умолчанию, определенные в схеме базы данных. Вы можете отключить это поведение, установив опцию schemaDefault в false.

Создание элементов input

Остальные методы, доступные в FormHelper, предназначены для создания определенных элементов формы. Многие из этих методов также используют специальный параметр $options или $attributes. Однако, в этом случае, этот параметр используется в основном для указания атрибутов HTML-тегов (таких как значение или DOM-идентификатор элемента в форме).

Создание текстовых инпутов

Cake\View\Helper\FormHelper::text(string $name, array $options)

$name - Имя поля в форме 'Modelname.fieldname'.

$options - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, а также любые допустимые атрибуты HTML.

Создаёт простой HTML элемент input, text (текстового) типа.

Например:

echo $this->Form->text('username', ['class' => 'users']);

Вывод:

<input name="username" type="text" class="users">

Создание input типа password

Cake\View\Helper\FormHelper::password(string $fieldName, array $options)

$fieldName - Имя поля в форме 'Modelname.fieldname'.

$options - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, а также любые допустимые атрибуты HTML.

Создаёт простой HTML элемент input, типа password.

Например:

echo $this->Form->password('password');

Вывод:

<input name="password" value="" type="password">

Создание скрытого input

Cake\View\Helper\FormHelper::hidden(string $fieldName, array $options)

$fieldName - Имя поля в форме 'Modelname.fieldname'.

$options - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, а также любые допустимые атрибуты HTML.

Создаёт скрытый input для формы.

Например:

echo $this->Form->hidden('id');

Вывод:

<input name="id" type="hidden" />

Создание текстовых полей

Cake\View\Helper\FormHelper::textarea(string $fieldName, array $options)

$fieldName - Имя поля в форме 'Modelname.fieldname'.

$options - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, определенных параметров текстовой области (см. ниже), а также любых допустимых атрибутов HTML.

Создаёт поле управления textarea. Используемый шаблон виджета по умолчанию:

'textarea' => '<textarea name="{{name}}"{{attrs}}>{{value}}</textarea>'

Для примера:

echo $this->Form->textarea('notes');

Вывод:

<textarea name="notes"></textarea>

Если форма редактируется (т.е. массив $this->request->getData() содержит информацию, ранее сохранённую для объекта User), значение, соответствующее полю notes, будет автоматически добавляться в сгенерированный HTML-код.

Пример:

<textarea name="notes" id="notes">
    Этот текст подлежит редактированию.
</textarea>

Опции для textarea

В дополнение к Общие параметры для специализированных элементов управления, textarea() поддерживает несколько специфичных опций:

'escape' - Определяет, следует ли экранировать содержимое текстового поля. По умолчанию используется true.

Например:

echo $this->Form->textarea('notes', ['escape' => false]);
// OR....
echo $this->Form->control('notes', ['type' => 'textarea', 'escape' => false]);

'rows', 'cols' - Вы можете использовать эти два ключа для установки атрибутов HTML, которые определяют количество строк и столбцов для поля textarea.

Например:

echo $this->Form->textarea('comment', ['rows' => '5', 'cols' => '5']);

Вывод:

<textarea name="comment" cols="5" rows="5">
</textarea>

Создание элементов select, checkbox и radio

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

Опции элементов select, checkbox и radio

Ниже вы можете найти опции, которые являются одинаковыми для методов select(), checkbox() и radio() (параметры же, относящиеся только к одному из методов, описаны в отдельном разделе каждого метода).

'value' - Устанавливает или выбирает значение затронутого элемента(ов):

Для чекбоксов, он устанавливает HTML атрибут 'value', назначенный элементу input, независимо от того, что вы предоставляете в качестве значения.

Для переключателей(radio) или select он определяет, какой элемент будет выбран при визуализации формы (в этом случае 'value' должно быть присвоено действительное значение существующего элемента). Может также использоваться в сочетании с любым типом управления select-type, таким как date(), time(), dateTime():

echo $this->Form->time('close_time', [
    'value' => '13:30:00'
]);

Примечание

Ключ 'value' для элементов date() и dateTime() может также иметь значение как временной метки UNIX или объекта DateTime.

Для элемента select, где вы устанавливаете атрибут 'multiple' в true, вы можете предоставить массив со значениями, которые вы хотите выбрать по умолчанию:

// Элементы HTML <option> со значениями 1 и 3 будут отображаться предварительно выбранными
echo $this->Form->select(
    'rooms',
    [1, 2, 3, 4, 5],
    [
        'multiple' => true,
        'value' => [1, 3]
    ]
);

'empty' - Используется для radio() и select(). По умолчанию используется false.

Когда он передаётся в radio() и устанавливается в true, он создаёт дополнительный входной элемент в качестве первого переключателя со значением '' и надпись метки, равную строке 'empty'. Если вы хотите управлять надписью ярлыка, установите этот параметр вместо строки.

Когда он передаётся методу select(), это создаёт пустой HTML элемент option с пустым значением в раскрывающемся списке. Если вы хотите иметь пустое значение с отображаемым текстом, а не просто пустым option, передайте строку 'empty':

echo $this->Form->select(
    'field',
    [1, 2, 3, 4, 5],
    ['empty' => '(choose one)']
);

Вывод:

<select name="field">
    <option value="">(choose one)</option>
    <option value="0">1</option>
    <option value="1">2</option>
    <option value="2">3</option>
    <option value="3">4</option>
    <option value="4">5</option>
</select>

'hiddenField' - Для флажков и переключателей по умолчанию также создаётся скрытый элемент input вместе с основным элементом, так что ключ в $this->request->getData() будет существовать даже без указанния его значения. Для флажков(radio) его значение по умолчанию равно 0, а для переключателей(select) - ''.

Пример вывода по умолчанию:

<input type="hidden" name="published" value="0" />
<input type="checkbox" name="published" value="1" />

Это можно отключить, установив 'hiddenField' в false:

echo $this->Form->checkbox('published', ['hiddenField' => false]);

Результат:

<input type="checkbox" name="published" value="1">

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

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

<h2>Primary Colors</h2>
<input type="hidden" name="color" value="0" />
<label for="color-red">
    <input type="checkbox" name="color[]" value="5" id="color-red" />
    Red
</label>

<label for="color-blue">
    <input type="checkbox" name="color[]" value="5" id="color-blue" />
    Blue
</label>

<label for="color-yellow">
    <input type="checkbox" name="color[]" value="5" id="color-yellow" />
    Yellow
</label>

<h2>Tertiary Colors</h2>
<input type="hidden" name="color" value="0" />
<label for="color-green">
    <input type="checkbox" name="color[]" value="5" id="color-green" />
    Green
</label>
<label for="color-purple">
    <input type="checkbox" name="color[]" value="5" id="color-purple" />
    Purple
</label>
<label for="color-orange">
    <input type="checkbox" name="color[]" value="5" id="color-orange" />
    Orange
</label>

Отключение 'hiddenField' во второй контрольной группе предотвратит это поведение.

Вы можете установить значение для скрытого поля, отличное от 0, например в „N“:

echo $this->Form->checkbox('published', [
    'value' => 'Y',
    'hiddenField' => 'N',
]);

Создание переключателей (чекбоксов)

Cake\View\Helper\FormHelper::checkbox(string $fieldName, array $options)

$fieldName - Имя поля в форме 'Modelname.fieldname'.

$options - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, или Опции элементов select, checkbox и radio, специфические опции для переключателей (см. ниже), а также любые допустимые HTML атрибуты.

Создаёт элемент checkbox формы . Используемый шаблон виджета:

'checkbox' => '<input type="checkbox" name="{{name}}" value="{{value}}"{{attrs}}>'

Опции для переключателей

'checked' - Boolean, чтобы указать, будет ли этот флажок установлен. По умолчанию используется false.

'disabled' - Создать отключенный флажок.

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

Например:

echo $this->Form->checkbox('done');

Вывод:

<input type="hidden" name="done" value="0">
<input type="checkbox" name="done" value="1">

Можно указать значение флажка, используя массив $options.

Например:

echo $this->Form->checkbox('done', ['value' => 555]);

Вывод:

<input type="hidden" name="done" value="0">
<input type="checkbox" name="done" value="555">

Если вы не хотите, чтобы FormHelper создавал скрытый input, используйте „false“ для 'hiddenField'.

Например:

echo $this->Form->checkbox('done', ['hiddenField' => false]);

Вывод:

<input type="checkbox" name="done" value="1">

Создание radio кнопок

Cake\View\Helper\FormHelper::radio(string $fieldName, array $options, array $attributes)

$fieldName - Имя поля в форме 'Modelname.fieldname'.

$options - Необязательный массив, содержащий как минимум метки для переключателей. Может также содержать значения и атрибуты HTML. Когда этот массив отсутствует, метод будет генерировать только скрытый input (если 'hiddenField' в true) или вообще не элемент (если 'hiddenField' в false).

$attributes - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, или Опции элементов select, checkbox и radio, определенных атрибутов переключателя (см. ниже), а также любых допустимых атрибутов HTML.

Создаёт набор инпутов для радиокнопки. Используемые шаблоны виджетов:

'radio' => '<input type="radio" name="{{name}}" value="{{value}}"{{attrs}}>'
'radioWrapper' => '{{label}}'

Аттрибуты для радиокнопок

'label' - Boolean, чтобы указать, должны ли отображаться метки для виджетов. По умолчанию true.

'hiddenField' - Если установлено значение true, то будет включён скрытый ввод со значением ''. Это полезно для создания наборов радиокнопок, которые не являются непрерывными (бесконечными). По умолчанию true.

'disabled' - Установите true или 'disabled', чтобы отключить все переключатели. По умолчанию используется false.

Вы должны предоставить подписи ярлыков(label) для переключателей с помощью аргумента $options.

Для примера:

$this->Form->radio('gender', ['Masculine','Feminine','Neuter']);

Вывод:

<input name="gender" value="" type="hidden">
<label for="gender-0">
    <input name="gender" value="0" id="gender-0" type="radio">
    Masculine
</label>
<label for="gender-1">
    <input name="gender" value="1" id="gender-1" type="radio">
    Feminine
</label>
<label for="gender-2">
    <input name="gender" value="2" id="gender-2" type="radio">
    Neuter
</label>

Обычно $options содержит простые пары key => value. Однако, если вам нужно поместить пользовательские атрибуты на свои переключатели, вы можете использовать расширенный формат.

Например:

echo $this->Form->radio(
    'favorite_color',
    [
        ['value' => 'r', 'text' => 'Red', 'style' => 'color:red;'],
        ['value' => 'u', 'text' => 'Blue', 'style' => 'color:blue;'],
        ['value' => 'g', 'text' => 'Green', 'style' => 'color:green;'],
    ]
);

Вывод:

<input type="hidden" name="favorite_color" value="">
<label for="favorite-color-r">
    <input type="radio" name="favorite_color" value="r" style="color:red;" id="favorite-color-r">
    Red
</label>
<label for="favorite-color-u">
    <input type="radio" name="favorite_color" value="u" style="color:blue;" id="favorite-color-u">
    Blue
</label>
<label for="favorite-color-g">
    <input type="radio" name="favorite_color" value="g" style="color:green;" id="favorite-color-g">
    Green
</label>

Создание селектов (выборщиков)

Cake\View\Helper\FormHelper::select(string $fieldName, array $options, array $attributes)

$fieldName - Имя поля в форме 'Modelname.fieldname'. Это даст атрибут name элемента select.

$options - Необязательный массив, содержащий список элементов для select. Когда этот массив отсутствует, метод будет генерировать только пустой HTML элемент select без каких-либо опционных элементов внутри него.

$attributes - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, или Опции элементов select, checkbox и radio, определенных атрибутов переключателя (см. ниже), а также любых допустимых атрибутов HTML.

Создаёт элемент select, заполненный элементами из массива $options. Если задано $attributes['value'], то HTML элемент(ы) option, которые имеют указанное значение(я), будут отображаться как выбранные.

По умолчанию select использует следующие шаблоны виджета:

'select' => '<select name="{{name}}"{{attrs}}>{{content}}</select>'
'option' => '<option value="{{value}}"{{attrs}}>{{text}}</option>'

Может также использовать:

'optgroup' => '<optgroup label="{{label}}"{{attrs}}>{{content}}</optgroup>'
'selectMultiple' => '<select name="{{name}}[]" multiple="multiple"{{attrs}}>{{content}}</select>'

Аттрибуты для select

'multiple' - Если установлено значение true, вы можете выбрать несколько вариантов select. Если установлен флажок 'checkbox', вместо этого будут созданы несколько чекбоксов. По умолчанию используется значение null.

'escape' - Boolean. Если true, содержимое элементов option внутри select будет кодироваться в HTML-объекте. По умолчанию используется true.

'val' - Позволяет предварительно выбрать значение в элементе select.

'disabled' - Управляет атрибутом disabled. Если установлено значение true, отключается весь select. Если он установлен в массив, он отключит только те конкретные элементы option, значения которых указаны в массиве.

Аргумент $options позволяет вам вручную указывать содержимое элементов option элемента select.

Например:

echo $this->Form->select('field', [1, 2, 3, 4, 5]);

Вывод:

<select name="field">
    <option value="0">1</option>
    <option value="1">2</option>
    <option value="2">3</option>
    <option value="3">4</option>
    <option value="4">5</option>
</select>

Массив для $options также может быть представлен как пары ключ-значение.

Например:

echo $this->Form->select('field', [
    'Value 1' => 'Label 1',
    'Value 2' => 'Label 2',
    'Value 3' => 'Label 3'
]);

Вывод:

<select name="field">
    <option value="Value 1">Label 1</option>
    <option value="Value 2">Label 2</option>
    <option value="Value 3">Label 3</option>
</select>

Если вы хотите сгенерировать select с группами опций, просто передайте данные в иерархическом формате (вложенный массив). Это работает также с несколькими флажками и переключателями, но вместо optgroup он обёртывает элементы в элементах fieldset.

For example:

$options = [
    'Group 1' => [
        'Value 1' => 'Label 1',
        'Value 2' => 'Label 2'
    ],
    'Group 2' => [
        'Value 3' => 'Label 3'
    ]
];
echo $this->Form->select('field', $options);

Вывод:

<select name="field">
    <optgroup label="Group 1">
        <option value="Value 1">Label 1</option>
        <option value="Value 2">Label 2</option>
    </optgroup>
    <optgroup label="Group 2">
        <option value="Value 3">Label 3</option>
    </optgroup>
</select>

Чтобы генерировать HTML-атрибуты в теге option:

$options = [
    ['text' => 'Description 1', 'value' => 'value 1', 'attr_name' => 'attr_value 1'],
    ['text' => 'Description 2', 'value' => 'value 2', 'attr_name' => 'attr_value 2'],
    ['text' => 'Description 3', 'value' => 'value 3', 'other_attr_name' => 'other_attr_value'],
];
echo $this->Form->select('field', $options);

Вывод:

<select name="field">
    <option value="value 1" attr_name="attr_value 1">Description 1</option>
    <option value="value 2" attr_name="attr_value 2">Description 2</option>
    <option value="value 3" other_attr_name="other_attr_value">Description 3</option>
</select>

Управление селектами с помощью атрибутов

Используя определенные параметры в параметре $attributes, вы можете управлять определенным поведением метода select().

'empty' - Установите ключ 'empty' в аргументе $attributes в true (значение по умолчанию false), чтобы добавить пустой параметр с пустым значением в верхней части вашего выпадающего списка.

Например:

$options = ['M' => 'Male', 'F' => 'Female'];
echo $this->Form->select('gender', $options, ['empty' => true]);

Вывод:

<select name="gender">
    <option value=""></option>
    <option value="M">Male</option>
    <option value="F">Female</option>
</select>

'escape' - Метод select() позволяет использовать атрибут 'escape', который принимает логическое значение и определяет, кодирует ли объект select HTML содержимое элементов option.

Например:

// Это предотвратит HTML-кодирование содержимого опции каждого элемента
$options = ['M' => 'Male', 'F' => 'Female'];
echo $this->Form->select('gender', $options, ['escape' => false]);

'multiple' - Если установлено значение true, select позволит выбрать несколько вариантов.

Например:

echo $this->Form->select('field', $options, ['multiple' => true]);

Кроме того, установите 'multiple' в 'checkbox', чтобы вывести список связанных чекбоксов(флажков):

$options = [
    'Value 1' => 'Label 1',
    'Value 2' => 'Label 2'
];
echo $this->Form->select('field', $options, [
    'multiple' => 'checkbox'
]);

Вывод:

<input name="field" value="" type="hidden">
<div class="checkbox">
    <label for="field-1">
        <input name="field[]" value="Value 1" id="field-1" type="checkbox">
        Label 1
    </label>
</div>
<div class="checkbox">
    <label for="field-2">
        <input name="field[]" value="Value 2" id="field-2" type="checkbox">
        Label 2
    </label>
</div>

'disabled' - Эта опция может быть установлена для того, чтобы отключить все или некоторые элементы option в select . Чтобы отключить все элементы, установите 'disabled' в true. Чтобы отключить только определенные элементы, назначьте 'disabled' массив, содержащий ключи элементов, которые необходимо отключить.

Например:

$options = [
    'M' => 'Masculine',
    'F' => 'Feminine',
    'N' => 'Neuter'
];
echo $this->Form->select('gender', $options, [
    'disabled' => ['M', 'N']
]);

Вывод:

<select name="gender">
    <option value="M" disabled="disabled">Masculine</option>
    <option value="F">Feminine</option>
    <option value="N" disabled="disabled">Neuter</option>
</select>

Эта опция также работает, когда 'multiple' установлен в 'checkbox':

$options = [
    'Value 1' => 'Label 1',
    'Value 2' => 'Label 2'
];
echo $this->Form->select('field', $options, [
    'multiple' => 'checkbox',
    'disabled' => ['Value 1']
]);

Вывод:

<input name="field" value="" type="hidden">
<div class="checkbox">
    <label for="field-1">
        <input name="field[]" disabled="disabled" value="Value 1" type="checkbox">
        Label 1
    </label>
</div>
<div class="checkbox">
    <label for="field-2">
        <input name="field[]" value="Value 2" id="field-2" type="checkbox">
        Label 2
    </label>
</div>

Создание input для файла

Cake\View\Helper\FormHelper::file(string $fieldName, array $options)

$fieldName - Имя поля в форме 'Modelname.fieldname'.

$options - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, а также любых допустимых HTML атрибутов.

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

'file' => '<input type="file" name="{{name}}"{{attrs}}>'

Чтобы добавить поле загрузки файла в форму, вы должны сначала убедиться, что в enctype формы установлено значение 'multipart/form-data'.

Поэтому начните с метода create(), например, так:

echo $this->Form->create($document, ['enctype' => 'multipart/form-data']);
// Или
echo $this->Form->create($document, ['type' => 'file']);

Затем добавьте строку, которая выглядит как одна из двух следующих строк в файле шаблона вида формы:

echo $this->Form->control('submittedfile', [
    'type' => 'file'
]);

// Или
echo $this->Form->file('submittedfile');

Примечание

Из-за ограничений самого HTML, невозможно поместить значения по умолчанию в поля ввода типа „file“. Каждый раз, когда отображается форма, значение внутри будет пустым.

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

Для приведённого выше примера, значения в представленном массиве данных будут организованы следующим образом, если CakePHP был установлен на сервере Windows (ключ 'tmp_name' будет содержать другой путь в среде Unix):

$this->request->data['submittedfile']

// будет содержать следующий массив:
[
    'name' => 'conference_schedule.pdf',
    'type' => 'application/pdf',
    'tmp_name' => 'C:/WINDOWS/TEMP/php1EE.tmp',
    'error' => 0, // В Windows это может быть строка.
    'size' => 41737,
];

Этот массив генерируется самим PHP, поэтому для более подробной информации о том, как PHP обрабатывает данные, переданные через поля файла прочитайте раздел руководства PHP по загрузке файлов [http://php.net/features.file-upload].

Примечание

При использовании $this->Form->file(), не забудьте установить тип кодировки формы, установив опцию 'type' для 'file' в $this->Form->create().

Создание элементов управления связанных с датой и временем

Методы, связанные с датой и временем, разделяют ряд общих черт и опций и поэтому группируются вместе в этот подраздел.

Общие параметры для элементов управления даты и времени

Эти параметры являются общими для элементов управления, связанных с датой и временем:

'empty' - Если true лишний , пустой, HTML элемент option добавится внутри select в верхнюю часть списка. Если строка, то эта строка отображается как пустой элемент. По умолчанию используется true.

'default' | value - Используйте одно из двух, чтобы установить значение по умолчанию для поля. Значение в $this->request->getData(), соответствующее имени поля, переопределит это значение. Если не задано значение по умолчанию, будет использоваться time().

'year', 'month', 'day', 'hour', 'minute', 'second', 'meridian' - Эти параметры позволяют вам контролировать, какие элементы управления будут сгенерированы, а какие нет. Установив любую из этих опций в false, вы можете отключить генерацию конкретного параметра (если по умолчанию он будет отображаться в используемом методе). Кроме того, каждый параметр позволяет передавать HTML атрибуты к этому конкретному элементу select.

Опции для элементов управления, связанных с датой

Эти варианты относятся к методам, связанным с датой - тоесть year(), month(), day(), dateTime() и date():

'monthNames' - Если false, вместо текста для отображения месяцев в select будут использоваться 2 цифры. Если задано значение массива (например, ['01' => 'Jan', '02' => 'Feb', ...]), будет использоваться данный массив.

'minYear' - Самое низкое значение, которое нужно использовать в выборе года.

'maxYear' - Самое большое значение, которое нужно использовать в выборе года.

'orderYear' - Порядок значений года в select. Возможные значения: 'asc' и 'desc'. По умолчанию используется 'desc'.

Опции для управления временем

Эти варианты касаются методов времени - hour(), minute(), second(), dateTime() и time():

'interval' - Интервал в минутах между значениями, отображаемыми в элементах option. По умолчанию 1.

'round' - Установите значение up или down, если вы хотите заставить округлять минуты в любом направлении, когда значение не соответствует аккуратности в интервале. По умолчанию используется значение null.

timeFormat - Используется для dateTime() и time(). Формат времени для использования в элементе select: либо 12, либо 24. Если для этой опции установлено что угодно, кроме 24, формат будет автоматически установлен в 12, а селектор выбора meridian будет отображаться автоматически справа от выбора секунд. По умолчанию - 24.

format - Применяется к hour(). Используемый формат времени: либо 12, либо 24. Если он установлен в 12, meridian не будет отображаться автоматически. Вам решать либо добавить его, либо предоставить средства для вывода из контекста формы правильного периода дня. По умолчанию - 24.

second - Используется для dateTime() и time(). Установите значение true, чтобы отключить секунды. По умолчанию используется false.

Создание элементов управления DateTime

Cake\View\Helper\FormHelper::dateTime($fieldName, $options = [])

$fieldName - Строка, которая будет использоваться в качестве префикса для атрибута name элементов select.

$options - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, или конкретные параметры даты и времени (см. выше), а также любые допустимые атрибуты HTML.

Создаёт набор элементов select для даты и времени.

Чтобы контролировать порядок элементов управления и любые элементы/содержимое между элементами управления, вы можете переопределить шаблон DateWidget. По умолчанию шаблон DateWidget:

{{year}}{{month}}{{day}}{{hour}}{{minute}}{{second}}{{meridian}}

Вызов метода без дополнительных опций приведёт к созданию по умолчанию 5-ти элементов select для: года (4 цифры), месяца (полное английское имя), дня (числа), часа (число), минут (число).

Для примера:

<?= $this->form->dateTime('registered') ?>

Вывод:

<select name="registered[year]">
    <option value="" selected="selected"></option>
    <option value="2022">2022</option>
    ...
    <option value="2012">2012</option>
</select>
<select name="registered[month]">
    <option value="" selected="selected"></option>
    <option value="01">January</option>
    ...
    <option value="12">December</option>
</select>
<select name="registered[day]">
    <option value="" selected="selected"></option>
    <option value="01">1</option>
    ...
    <option value="31">31</option>
</select>
<select name="registered[hour]">
    <option value="" selected="selected"></option>
    <option value="00">0</option>
    ...
    <option value="23">23</option>
</select>
<select name="registered[minute]">
    <option value="" selected="selected"></option>
    <option value="00">00</option>
    ...
    <option value="59">59</option>
</select>

Чтобы создать элементы управления datetime с пользовательскими классами/атрибутами в определённом поле выбора, вы можете предоставить их как массивы параметров для каждого компонента в аргументе $options.

Для примера:

echo $this->Form->dateTime('released', [
    'year' => [
        'class' => 'year-classname',
    ],
    'month' => [
        'class' => 'month-class',
        'data-type' => 'month',
    ],
]);

Что создаст следующие два селекта:

<select name="released[year]" class="year-class">
    <option value="" selected="selected"></option>
    <option value="00">0</option>
    <option value="01">1</option>
    <!-- .. snipped for brevity .. -->
</select>
<select name="released[month]" class="month-class" data-type="month">
    <option value="" selected="selected"></option>
    <option value="01">January</option>
    <!-- .. snipped for brevity .. -->
</select>

Создание элементов управления датой

Cake\View\Helper\FormHelper::date($fieldName, $options = [])

$fieldName - Имя поля, которое будет использоваться в качестве префикса для атрибута name элемента select.

$options - Необязательный массив, включающий любые Общие параметры для специализированных элементов управления, или the Общие параметры для элементов управления даты и времени, или любые применимые Опции для управления временем, а также любые допустимые атрибуты HTML.

Создаёт по умолчанию три элемента select, заполненных значениями: год (4 цифры), месяц (полное английское имя) и день (число), соответственно.

Вы можете дополнительно управлять сгенерированными элементами select, предоставляя дополнительные параметры.

Для примера:

    // Предполагая, что текущий год - 2017 год; это отключает выбор дня,
    // удаляет пустую опцию для года, выбирает минимальный год, добавляет HTML
    // атрибуты для года, добавляет строку 'empty' вариант для месяца, меняет
    // месяц на числовой
<?php
    echo $this->Form->date('registered', [
        'minYear' => 2018,
        'monthNames' => false,
        'empty' => [
            'year' => false,
            'month' => 'Choose month...'
        ],
        'day' => false,
        'year' => [
            'class' => 'cool-years',
            'title' => 'Registration Year'
        ]
    ]);
?>

Вывод:

<select class= "cool-years" name="registered[year]" title="Registration Year">
    <option value="2022">2022</option>
    <option value="2021">2021</option>
    ...
    <option value="2018">2018</option>
</select>
<select name="registered[month]">
    <option value="" selected="selected">Choose month...</option>
    <option value="01">1</option>
    ...
    <option value="12">12</option>
</select>

Создание элементов управления временем

Cake\View\Helper\FormHelper::time($fieldName, $options = [])

$fieldName - Имя поля, которое будет использоваться в качестве префикса для атрибута name элемента select.

$options - Необязательный массив, включающий любые Общие параметры для специализированных элементов управления, или the Общие параметры для элементов управления даты и времени, или любые применимые Опции для управления временем, а также любые допустимые атрибуты HTML.

Создаёт по умолчанию два элемента select (hour и minute), заполненные значениями из 24 часов и 60 минут, соответственно. Кроме того, атрибуты HTML могут быть предоставлены в $options для каждого конкретного компонента. Если $options['empty'] в false, select не будет содержать пустую опцию по умолчанию.

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

echo $this->Form->time('released', [
    'interval' => 15,
    'hour' => [
        'class' => 'foo-class',
    ],
    'minute' => [
        'class' => 'bar-class',
    ],
]);

Что создаст следующие два элемента выбора:

<select name="released[hour]" class="foo-class">
    <option value="" selected="selected"></option>
    <option value="00">0</option>
    <option value="01">1</option>
    <!-- .. snipped for brevity .. -->
    <option value="22">22</option>
    <option value="23">23</option>
</select>
<select name="released[minute]" class="bar-class">
    <option value="" selected="selected"></option>
    <option value="00">00</option>
    <option value="15">15</option>
    <option value="30">30</option>
    <option value="45">45</option>
</select>

Создание элементов управления годом

Cake\View\Helper\FormHelper::year(string $fieldName, array $options = [])

$fieldName - Имя поля, которое будет использоваться в качестве префикса для атрибута name элемента select.

$options - Необязательный массив, включающий любые Общие параметры для специализированных элементов управления, или the Общие параметры для элементов управления даты и времени, или любые применимые Опции для элементов управления, связанных с датой, а также любые допустимые атрибуты HTML.

Создаёт элемент select, заполненный годами от minYear до maxYear (если эти параметры предоставлены), а также значения от -5 лет до +5 лет, отсчитываемые от сегодняшнего дня. Кроме того, атрибуты HTML могут быть предоставлены в $options. Если $options['empty'] в false, то элемент select не будет содержать пустой элемент в списке.

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

echo $this->Form->year('purchased', [
    'minYear' => 2000,
    'maxYear' => date('Y')
]);

If it was 2009, you would get the following:

<select name="purchased[year]">
    <option value=""></option>
    <option value="2009">2009</option>
    <option value="2008">2008</option>
    <option value="2007">2007</option>
    <option value="2006">2006</option>
    <option value="2005">2005</option>
    <option value="2004">2004</option>
    <option value="2003">2003</option>
    <option value="2002">2002</option>
    <option value="2001">2001</option>
    <option value="2000">2000</option>
</select>

Создание элементов управления месяцем

Cake\View\Helper\FormHelper::month(string $fieldName, array $attributes)

$fieldName - Имя поля, которое будет использоваться в качестве префикса для атрибута name элемента select.

$attributes - Необязательный массив, включающий любые Общие параметры для специализированных элементов управления, или the Общие параметры для элементов управления даты и времени, или любые применимые Опции для элементов управления, связанных с датой, а также любые допустимые атрибуты HTML.

Создает элемент select, заполненный именами месяцев.

Для примера:

echo $this->Form->month('mob');

Вывод:

<select name="mob[month]">
    <option value=""></option>
    <option value="01">January</option>
    <option value="02">February</option>
    <option value="03">March</option>
    <option value="04">April</option>
    <option value="05">May</option>
    <option value="06">June</option>
    <option value="07">July</option>
    <option value="08">August</option>
    <option value="09">September</option>
    <option value="10">October</option>
    <option value="11">November</option>
    <option value="12">December</option>
</select>

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

Например:

echo $this->Form->month('mob', ['monthNames' => false]);

Примечание

Месяцы по умолчанию могут быть локализованы с помощью функций CakePHP Internationalization & Localization.

Создание элементов управления днями

Cake\View\Helper\FormHelper::day(string $fieldName, array $attributes)

$fieldName - Имя поля, которое будет использоваться в качестве префикса для атрибута name элемента select.

$attributes - Необязательный массив, включающий любые Общие параметры для специализированных элементов управления, или the Общие параметры для элементов управления даты и времени, или любые применимые Опции для элементов управления, связанных с датой, а также любые допустимые атрибуты HTML.

Создаёт элемент select, заполненный (числовыми) днями месяца.

Чтобы создать пустой элемент option с пригласительным текстом по вашему выбору (например, первый вариант - „День“), вы можете предоставить текст в параметре 'empty'.

Для примера:

echo $this->Form->day('created', ['empty' => 'День']);

Вывод:

<select name="created[day]">
    <option value="" selected="selected">День</option>
    <option value="01">1</option>
    <option value="02">2</option>
    <option value="03">3</option>
    ...
    <option value="31">31</option>
</select>

Создание элементов управления часами

Cake\View\Helper\FormHelper::hour(string $fieldName, array $attributes)

$fieldName - Имя поля, которое будет использоваться в качестве префикса для атрибута name элемента select.

$attributes - Необязательный массив, включающий любые Общие параметры для специализированных элементов управления, или the Общие параметры для элементов управления даты и времени, или любые применимые Опции для управления временем, а также любые допустимые атрибуты HTML.

Создает элемент select, заполненный дневными часами.

Вы можете создать 12 или 24-часовой элемент select, используя опцию 'format':

echo $this->Form->hour('created', [
    'format' => 12
]);
echo $this->Form->hour('created', [
    'format' => 24
]);

Создание минутных элементов управления

Cake\View\Helper\FormHelper::minute(string $fieldName, array $attributes)

$fieldName - Имя поля, которое будет использоваться в качестве префикса для атрибута name элемента select.

$attributes - Необязательный массив, включающий любые Общие параметры для специализированных элементов управления, или the Общие параметры для элементов управления даты и времени, или любые применимые Опции для управления временем, а также любые допустимые атрибуты HTML.

Создает элемент select, заполненный значениями минут для часа. Вы можете создать элемент select, который содержит только определенные значения, используя опцию 'interval'.

Например, если вы хотите 10-минутные приращения, вы бы сделали следующее:

// В вашем файле шаблона Вида
echo $this->Form->minute('arrival', [
    'interval' => 10
]);

Это приведет к выводу:

<select name="arrival[minute]">
    <option value="" selected="selected"></option>
    <option value="00">00</option>
    <option value="10">10</option>
    <option value="20">20</option>
    <option value="30">30</option>
    <option value="40">40</option>
    <option value="50">50</option>
</select>

Создание элементов управления Meridian

Cake\View\Helper\FormHelper::meridian(string $fieldName, array $attributes)

$fieldName - Имя поля, которое будет использоваться в качестве префикса для атрибута name элемента select.

$attributes - Необязательный массив, включающий Общие параметры для специализированных элементов управления, а также любые допустимые атрибуты HTML.

Создаёт элемент select, заполненный „am“ и „pm“. Это полезно, когда в часовом формате установлено значение 12 вместо 24, так как это позволяет указать период дня, к которому относится час.

Создание ярлыков

Cake\View\Helper\FormHelper::label(string $fieldName, string $text, array $options)

$fieldName - Имя поля в форме 'Modelname.fieldname'.

$text - Необязательная строка, содержащая текст подписи ярлыка.

$options - Необязательный. Строка или массив, содержащий любой из Общие параметры для специализированных элементов управления, а также любые допустимые атрибуты HTML.

Создаёт элемент label. Аргумент $fieldName используется для генерации HTML атрибута for для элемента; если $text не определено, $fieldName также будет использоваться для изменения атрибута text.

Например:

echo $this->Form->label('User.name');
echo $this->Form->label('User.name', 'Your username');

Вывод:

<label for="user-name">Name</label>
<label for="user-name">Your username</label>

Когда вы устанавливаете $options как строку, она будет использоваться как имя класса:

echo $this->Form->label('User.name', null, ['id' => 'user-label']);
echo $this->Form->label('User.name', 'Your username', 'highlight');

Вывод:

<label for="user-name" id="user-label">Name</label>
<label for="user-name" class="highlight">Your username</label>

Отображение и проверка ошибок

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

Отображение ошибок

Cake\View\Helper\FormHelper::error(string $fieldName, mixed $text, array $options)

$fieldName - Имя поля в форме 'Modelname.fieldname'.

$text - Необязательный. Строка или массив, содержащие сообщения об ошибках. Если массив, то он должен быть хэшем имён ключей => сообщений. По умолчанию используется значение null.

$options - Необязательный массив, который может содержать только логическое значение с ключом 'escape', который определит, будет ли HTML выводить содержимое сообщения об ошибке. По умолчанию используется true.

Показывает сообщение об ошибке проверки, заданное в $text, для данного поля в случае ошибки проверки. Если $text не указан, будет использовано сообщение об ошибке проверки по умолчанию для этого поля.

Использует следующие шаблоны виджетов:

'error' => '<div class="error-message">{{content}}</div>'
'errorList' => '<ul>{{content}}</ul>'
'errorItem' => '<li>{{text}}</li>'

Шаблоны 'errorList' и 'errorItem' используются для форматирования нескольких сообщений об ошибках в поле.

Пример:

// Если в TicketsTable у вас есть правило проверки 'notEmpty':
public function validationDefault(Validator $validator)
{
    $validator
        ->requirePresence('ticket', 'create')
        ->notEmpty('ticket');
}

// И внутри Templates/Tickets/add.ctp у вас есть:
echo $this->Form->text('ticket');

if ($this->Form->isFieldError('ticket')) {
    echo $this->Form->error('ticket', 'Полностью настраиваемое сообщение об ошибке!');
}

Если вы нажмёте кнопку Отправить своей формы, не указав значение поля Ticket, ваша форма выведет:

<input name="ticket" class="form-error" required="required" value="" type="text">
<div class="error-message">Полностью настраиваемое сообщение об ошибке!</div>

Примечание

При использовании View\Helper\FormHelper::control(), ошибки отображаются по умолчанию, поэтому вам не нужно использовать isFieldError() или вручную вызывать error().

Совет

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

Проверка ошибок

Cake\View\Helper\FormHelper::isFieldError(string $fieldName)

$fieldName - Имя поля в форме 'Modelname.fieldname'.

Возвращает true, если в $fieldName найдена ошибка, иначе возвращает false.

Example:

if ($this->Form->isFieldError('gender')) {
    echo $this->Form->error('gender');
}

Создание кнопок и элементов submit

Создание элементов Submit

Cake\View\Helper\FormHelper::submit(string $caption, array $options)

$caption - Необязательная строка, содержащая текстовую подпись кнопки или путь к изображению. По умолчанию используется 'Submit'.

$options - Необязательный массив, включающий любые Общие параметры для специализированных элементов управления, или конкретные параметры отправки (см. ниже), а также любые допустимые атрибуты HTML.

Создаёт элемент input типа submit, с $caption в качестве значения. Если представленная $caption является URL-адресом, указывающим на изображение (т е. если строка содержит „://“ или содержит какие-либо расширения „.jpg, .jpe, .jpeg, .gif“), изображение кнопки submit будет сгенерировано с использованием указанного изображения, если оно существует. Если первым символом является „/“, то путь изображения относится к webroot, иначе если первый символ не является „/“, то путь изображения относится к webroot/img.

По умолчанию будут использоваться следующие шаблоны виджета:

'inputSubmit' => '<input type="{{type}}"{{attrs}}/>'
'submitContainer' => '<div class="submit">{{content}}</div>'

Опции для Submit

'type' - Установите эту опцию на 'reset', чтобы создать кнопки сброса. По умолчанию используется 'submit'.

'templateVars' - Установите этот массив для предоставления дополнительных переменных шаблона для входного элемента и его контейнера.

Любые другие предоставленные атрибуты будут назначены элементу input.

Следующее:

echo $this->Form->submit('Click me');

Выведет:

<div class="submit"><input value="Click me" type="submit"></div>

Вы можете передать относительный или абсолютный URL-адрес изображения в параметр caption вместо текста подписи:

echo $this->Form->submit('ok.png');

Вывод:

<div class="submit"><input type="image" src="/img/ok.png"></div>

Submit полезен, когда вам нужен только базовый текст или изображения. Если вам нужно более сложное содержимое кнопки, вы должны использовать button().

Создание элементов кнопок

Cake\View\Helper\FormHelper::button(string $title, array $options = [])

$title - Обязательная строка, содержащая текстовую подпись кнопки.

$options - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, или отдельных опций кнопок (см. ниже), а также любых допустимых атрибутов HTML.

Создаёт кнопку HTML с указанным заголовком и по умолчанию типом 'button'.

Опции кнопки

$options['type'] - Вы можете установить это для одного из следующих трёх возможных значений:

'submit' - Подобно методу $this->Form->submit() он создаст кнопку отправки. Однако это не создаст обёртку div, как в submit(). Это тип по умолчанию.

'reset' - Создаёт кнопку сброса формы.

'button' - Создаёт стандартную кнопку.

$options['escape'] - Boolean. Если установлено значение true, HTML кодирует значение, указанное внутри $title. По умолчанию используется false.

Для примера:

echo $this->Form->button('Кнопка');
echo $this->Form->button('Другая кнопка', ['type' => 'button']);
echo $this->Form->button('Сбросить форму', ['type' => 'reset']);
echo $this->Form->button('Отправить форму', ['type' => 'submit']);

Вывод:

<button type="submit">Кнопка</button>
<button type="button">Другая кнопка</button>
<button type="reset">Сбросить форму</button>
<button type="submit">Отправить форму</button>

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

// Выведет экранированный HTML.
echo $this->Form->button('<em>Отправить форму</em>', [
    'type' => 'submit',
    'escape' => true
]);

Закрытие формы

Cake\View\Helper\FormHelper::end($secureAttributes = [])

$secureAttributes - Необязательный. Позволяет предоставлять защищенные атрибуты, которые будут передаваться как атрибуты HTML в скрытые элементы ввода, созданные для SecurityComponent.

Метод end() закрывает и завершает форму. Часто end() выводит только тег закрывающей формы, но использование end() является хорошей практикой, поскольку позволяет FormHelper вставлять скрытые элементы формы, которые требует Cake\Controller\Component\SecurityComponent:

<?= $this->Form->create(); ?>

<!-- Элементы формы идут сюда -->

<?= $this->Form->end(); ?>

Если вам нужно добавить дополнительные атрибуты к сгенерированным скрытым полям input, вы можете использовать аргумент $secureAttributes.

Например:

echo $this->Form->end(['data-type' => 'hidden']);

Вывод:

<div style="display:none;">
    <input type="hidden" name="_Token[fields]" data-type="hidden"
        value="2981c38990f3f6ba935e6561dc77277966fabd6d%3AAddresses.id">
    <input type="hidden" name="_Token[unlocked]" data-type="hidden"
        value="address%7Cfirst_name">
</div>

Примечание

Если вы используете Cake\Controller\Component\SecurityComponent в вашем приложении вы всегда должны заканчивать свои формы с помощью end().

Создание автономных кнопок и POST-ссылок

Создание POST-кнопок

Cake\View\Helper\FormHelper::postButton(string $title, mixed $url, array $options = [])

$title - Обязательная строка, содержащая текстовую подпись кнопки. По умолчанию не кодируется HTML.

$url - URL-адрес формы, представленной в виде строки или массива.

$options - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, или конкретных параметров (см. ниже), а также любых допустимых атрибутов HTML.

Создаёт тег <button> окружённый элементами <form>, который отправляется через POST по умолчанию. Кроме того, по умолчанию он генерирует скрытые поля ввода для SecurityComponent.

Опции для кнопки POST

'data' - Массив с ключом/значением для передачи скрытого ввода.

'method' - Используемый метод запроса. Например, для 'delete' для имитации HTTP/1.1 DELETE запроса. По умолчанию используется 'post'.

'form' - Массив с любой опцией, которую FormHelper::create() может принимать.

Кроме того, метод postButton() принимает параметры, которые действительны для метода button().

Для примера:

// В Templates/Tickets/index.ctp
<?= $this->Form->postButton('Delete Record', ['controller' => 'Tickets', 'action' => 'delete', 5]) ?>

Выведет HTML, похожий на:

<form method="post" accept-charset="utf-8" action="/Rtools/tickets/delete/5">
    <div style="display:none;">
        <input name="_method" value="POST" type="hidden">
    </div>
    <button type="submit">Delete Record</button>
    <div style="display:none;">
        <input name="_Token[fields]" value="186cfbfc6f519622e19d1e688633c4028229081f%3A" type="hidden">
        <input name="_Token[unlocked]" value="" type="hidden">
        <input name="_Token[debug]" value="%5B%22%5C%2FRtools%5C%2Ftickets%5C%2Fdelete%5C%2F1%22%2C%5B%5D%2C%5B%5D%5D" type="hidden">
    </div>
</form>

Поскольку этот метод генерирует элемент form, не используйте этот метод в уже открытой форме. Вместо этого используйте Cake\View\Helper\FormHelper::submit() or Cake\View\Helper\FormHelper::button() для создания кнопок внутри открытых форм.

Создание POST-ссылок

Cake\View\Helper\FormHelper::postLink(string $title, mixed $url = null, array $options = [])

$title - Обязательная строка, предоставляющая текст, который будет обёрнут в теги <a>.

$url - Необязательный. Строка или массив, содержащий URL-адрес формы (Cake-relative или внешний URL-адрес, начинающийся с http://).

$options - Необязательный массив, включающий любой из Общие параметры для специализированных элементов управления, или конкретных параметров (см. ниже), а также любых допустимых атрибутов HTML.

Создает HTML-ссылку, но обращается к URL-адресу, используя указанный вами метод (по умолчанию - POST). Требуется включить JavaScript в браузере.

Опции для POST-ссылки

'data' - Массив с ключом/значением для передачи скрытого ввода.

'method' - Используемый метод запроса. Например, для 'delete' для имитации HTTP/1.1 DELETE запроса. По умолчанию используется 'post'.

'confirm' - Сообщение подтверждения отображается при нажатии. По умолчанию null.

'block' - Установите этот параметр в true, чтобы добавить форму для просмотра блока 'postLink' или указать имя настраиваемого блока. По умолчанию используется значение `` null``.

Кроме того, метод postLink() будет принимать параметры, которые действительны для метода link().

Этот метод создаёт элемент <form>. Если вы хотите использовать этот метод внутри существующей формы, вы должны использовать опцию block, чтобы новая форма была установлена в view block, которые могут отображаться вне основной формы.

Если всё, что вы ищете, это кнопка для отправки вашей формы, то вы должны использовать вместо этого Cake\View\Helper\FormHelper::button() или Cake\View\Helper\FormHelper::submit().

Примечание

Не ставьте postLink в открытую форму. Вместо этого используйте параметр block для буферизации формы в view block

Настройка использования шаблонов FormHelper

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

Чтобы изменить шаблоны при загрузке хелпера, вы можете установить опцию 'templates', когда включаете помощника в ваш контроллер:

// В классе View
$this->loadHelper('Form', [
    'templates' => 'app_form',
]);

Это загрузило бы теги, найденные в config/app_form.php. Этот файл должен содержать массив шаблонов индексированный по имени:

// в config/app_form.php
return [
    'inputContainer' => '<div class="form-control">{{content}}</div>',
];

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

Вы также можете изменить шаблоны во время выполнения с помощью метода setTemplates():

$myTemplates = [
    'inputContainer' => '<div class="form-control">{{content}}</div>',
];
$this->Form->setTemplates($myTemplates);
// До 3.4
$this->Form->templates($myTemplates);

Предупреждение

Строки шаблонов, содержащие знак процента (%), требуют особого внимания; вы должны приписать этому символу еще один процент, чтобы он выглядел как %%. Причина в том, что внутренние шаблоны скомпилированы для использования с sprintf(). Пример: '<div style="width:{{size}}%%">{{content}}</div>'

Список шаблонов

Список шаблонов по умолчанию, их формат по умолчанию и ожидаемые переменные можно найти в FormHelper API documentation [https://api.cakephp.org/3.x/class-Cake.View.Helper.FormHelper.html#%24_defaultConfig].

Использование специальных контейнеров для пользовательского контроля

В дополнение к этим шаблонам метод control() будет пытаться использовать отдельные шаблоны для каждого контейнера управления. Например, при создании элемента управления datetime используется datetimeContainer, если он присутствует. Если этот контейнер отсутствует, будет использоваться шаблон inputContainer.

Для примера:

// Add custom radio wrapping HTML
$this->Form->setTemplates([
    'radioContainer' => '<div class="form-radio">{{content}}</div>'
]);

// Create a radio set with our custom wrapping div.
echo $this->Form->control('User.email_notifications', [
    'options' => ['y', 'n'],
    'type' => 'radio'
]);

Использование отдельных групп настраиваемых форм

Подобно управлению контейнерами, метод control() также пытается использовать отдельные шаблоны для каждой группы форм. Группа форм представляет собой комбинацию меток и элементов управления. Например, при создании radio control будет использоваться radioFormGroup, если он присутствует. Если этот шаблон отсутствует по умолчанию, каждый набор label и input отображается с использованием шаблона formGroup по умолчанию.

Для примера:

// Add custom radio form group
$this->Form->setTemplates([
    'radioFormGroup' => '<div class="radio">{{label}}{{input}}</div>'
]);

Добавление дополнительных шаблонных переменных в шаблоны

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

Например:

// Добавьте шаблон с помощью заполнителя.
$this->Form->setTemplates([
    'inputContainer' => '<div class="input {{type}}{{required}}">
        {{content}} <span class="help">{{help}}</span></div>'
]);

// Создайте ввод и заполните переменную справки
echo $this->Form->control('password', [
    'templateVars' => ['help' => 'At least 8 characters long.']
]);

Вывод:

<div class="input password">
    <label for="password">
        Password
    </label>
    <input name="password" id="password" type="password">
    <span class="help">At least 8 characters long.</span>
</div>

Добавлено в версии 3.1: Опция templateVars была добавлена в 3.1.0

Перемещение флажков и радио за пределами метки

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

$this->Form->setTemplates([
    'nestingLabel' => '{{hidden}}{{input}}<label{{attrs}}>{{text}}</label>',
    'formGroup' => '{{input}}{{label}}',
]);

Это заставит переключатели и флажки отображаться вне их меток.

Создание цельных форм

Создание нескольких элементов управления

Cake\View\Helper\FormHelper::controls(array $fields = [], $options = [])

$fields - Массив полей для генерации. Позволяет настраивать настраиваемые типы, метки и другие параметры для каждого указанного поля.

$options - Необязательный. Множество опций. Допустимые ключи:

'fieldset' - Установите для этого параметра значение false, чтобы отключить набор полей. Если пуст, набор полей будет включен. Также может быть массив параметров, которые будут применяться как HTML атрибуты к тегу fieldset.

legend - Строка используется для настройки текста legend. Установите это в false, чтобы отключить легенду для сгенерированного набора входных данных.

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

echo $this->Form->controls([
    'name',
    'email'
]);

Вы можете настроить текст легенды, используя опцию:

echo $this->Form->controls($fields, ['legend' => 'Update news post']);

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

echo $this->Form->controls([
    'name' => ['label' => 'custom label']
]);

При настройке $fields вы можете использовать параметр $options для управления сгенерированным набором легенд/полей.

Для примера:

echo $this->Form->controls(
    [
        'name' => ['label' => 'custom label']
    ],
    ['legend' => 'Update your post']
);

Если вы отключите fieldset, legend не будет напечатан.

Создание элементов управления для всего объекта

Cake\View\Helper\FormHelper::allControls(array $fields, $options = [])

$fields - Необязательный. Массив настроек для полей, которые будут сгенерированы. Позволяет настраивать настраиваемые типы, метки и другие параметры.

$options - Необязательный. Множество опций. Допустимые ключи:

'fieldset' - Установите для этого параметра значение false, чтобы отключить набор полей. Если пуст, набор полей будет включен. Также может быть массив параметров, которые будут применяться в качестве атрибутов HTML для тега fieldset.

legend - Строка используется для настройки текста legend. Установите её в false, чтобы отключить легенду для сгенерированного набора управления.

Этот метод тесно связан с controls(), однако аргумент $fields по умолчанию имеет все поля в текущем объекте верхнего уровня. Чтобы исключить определенные поля из созданных элементов управления, установите их в false в параметре $fields:

echo $this->Form->allControls(['password' => false]);
// Или до 3.4.0:
echo $this->Form->allInputs(['password' => false]);

Создание инпутов для связанных данных

Создание форм для связанных данных является простым и тесно связано с путями в данных вашего объекта. Предполагая следующие табличные соотношения:

Authors HasOne Profiles

Authors HasMany Articles

Articles HasMany Comments

Articles BelongsTo Authors

Articles BelongsToMany Tags

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

$this->Form->create($article);

// Article controls.
echo $this->Form->control('title');

// Author controls (belongsTo)
echo $this->Form->control('author.id');
echo $this->Form->control('author.first_name');
echo $this->Form->control('author.last_name');

// Author profile (belongsTo + hasOne)
echo $this->Form->control('author.profile.id');
echo $this->Form->control('author.profile.username');

// Tags controls (belongsToMany)
echo $this->Form->control('tags.0.id');
echo $this->Form->control('tags.0.name');
echo $this->Form->control('tags.1.id');
echo $this->Form->control('tags.1.name');

// Multiple select element for belongsToMany
echo $this->Form->control('tags._ids', [
    'type' => 'select',
    'multiple' => true,
    'options' => $tagList,
]);

// Inputs for the joint table (articles_tags)
echo $this->Form->control('tags.0._joinData.starred');
echo $this->Form->control('tags.1._joinData.starred');

// Comments controls (hasMany)
echo $this->Form->control('comments.0.id');
echo $this->Form->control('comments.0.comment');
echo $this->Form->control('comments.1.id');
echo $this->Form->control('comments.1.comment');

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

$article = $this->Articles->patchEntity($article, $this->request->getData(), [
    'associated' => [
        'Authors',
        'Authors.Profiles',
        'Tags',
        'Comments'
    ]
]);

Добавление пользовательских виджетов

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

Создание класса виджета

Классы виджетов имеют очень простой интерфейс. Они должны реализовать Cake\View\Widget\WidgetInterface. Для этого интерфейса необходимы методы render(array $data) и secureFields(array $data). Метод render() ожидает массив данных для создания виджета и, как ожидается, вернёт строку HTML для виджета. Метод secureFields() ожидает также массив данных, и ожидается, что он вернёт массив, содержащий список полей для защиты этого виджета. Если CakePHP создаёт ваш виджет, вы можете получить экземпляр Cake\View\StringTemplate в качестве первого аргумента, за которым следуют любые зависимости, которые вы определяете. Если вы захотите создать виджет Autocomplete, вы можете сделать следующее:

namespace App\View\Widget;

use Cake\View\Form\ContextInterface;
use Cake\View\Widget\WidgetInterface;

class AutocompleteWidget implements WidgetInterface
{

    protected $_templates;

    public function __construct($templates)
    {
        $this->_templates = $templates;
    }

    public function render(array $data, ContextInterface $context)
    {
        $data += [
            'name' => '',
        ];
        return $this->_templates->format('autocomplete', [
            'name' => $data['name'],
            'attrs' => $this->_templates->formatAttributes($data, ['name'])
        ]);
    }

    public function secureFields(array $data)
    {
        return [$data['name']];
    }
}

Очевидно, что это очень простой пример, но он демонстрирует, как можно создать пользовательский виджет. Этот виджет будет отображать шаблон строки «autocomplete», такой как:

$this->Form->setTemplates([
    'autocomplete' => '<input type="autocomplete" name="{{name}}" {{attrs}} />'
]);

Для получения дополнительной информации о строковых шаблонах, смотрите Настройка использования шаблонов FormHelper.

Использование виджетов

Вы можете загружать пользовательские виджеты при загрузке FormHelper или с помощью метода addWidget(). При загрузке FormHelper виджеты определяются как настройка:

// В классе View
$this->loadHelper('Form', [
    'widgets' => [
        'autocomplete' => ['Autocomplete']
    ]
]);

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

$this->loadHelper('Form', [
    'widgets' => [
        'autocomplete' => [
            'App\View\Widget\AutocompleteWidget',
            'text',
            'label'
        ]
    ]
]);

В приведенном выше примере виджет autocomplete будет зависеть от виджетов text и label. Если вашему виджету нужен доступ к представлению, вы должны использовать „widget“ _view. Когда будет создан виджет autocomplete, ему будут переданы объекты виджетов, которые связаны с именами text и label. Чтобы добавить виджеты с помощью addWidget(), метод будет выглядеть так:

// Использование имени класса.
$this->Form->addWidget(
    'autocomplete',
    ['Autocomplete', 'text', 'label']
);

// Использование экземпляра - требует разрешения зависимостей.
$autocomplete = new AutocompleteWidget(
    $this->Form->getTemplater(),
    $this->Form->widgetRegistry()->get('text'),
    $this->Form->widgetRegistry()->get('label'),
);
$this->Form->addWidget('autocomplete', $autocomplete);

После добавления/замены виджеты могут использоваться в качестве „type“ (типа) контроля:

echo $this->Form->control('search', ['type' => 'autocomplete']);

Это создаст пользовательский виджет с label и обёрткой div так же, как всегда делает controls(). Кроме того, вы можете создать виджет управления, используя даже только магический метод:

echo $this->Form->autocomplete('search', $options);

Работа с SecurityComponent

Cake\Controller\Component\SecurityComponent предлагает несколько функций, которые делают ваши формы более защищёнными и безопасными. Просто включив в свой контроллер SecurityComponent, вы автоматически воспользуетесь функциями защиты от несанкционированного доступа.

Как упоминалось ранее, при использовании SecurityComponent, вы всегда должны закрывать свои формы, используя View\Helper\FormHelper::end(). Это обеспечит создание специальных _Token инпутов.

Cake\View\Helper\FormHelper::unlockField($name)

$name - Необязательный. Имя поля, разделенное точками.

Разблокирует поле, освобождая его от хэширования SecurityComponent. Это также позволяет манипулировать полями с помощью JavaScript. Параметр $name должен быть именем свойства объекта для поля:

$this->Form->unlockField('id');

Cake\View\Helper\FormHelper::secure(array $fields = [], array $secureAttributes = [])

$fields - Необязательный. Массив, содержащий список полей для использования при генерации хеша. Если не указано, то будут использоваться $this->fields.

$secureAttributes - Необязательный. Массив атрибутов HTML для передачи в сгенерированные скрытые элементы ввода.

Создаёт скрытое поле input с хэшем безопасности на основе полей, используемых в форме или пустой строке, когда защищенные формы не используются. Если установлены $secureAttributes, то эти HTML атрибуты будут объединены в скрытые теги ввода, созданные для SecurityComponent. Это особенно полезно для установки атрибутов HTML5, таких как 'form'.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Html

class Cake\View\Helper\HtmlHelper(View $view, array $config = [])

Роль HtmlHelper в CakePHP заключается в создании простых HTML-связей, быстрых и более устойчивых к изменениям. При его использовании, помощник позволит вашему приложению быть более подвижным, и более гибким, там где он помещен относительно корня домена.

Многие методы HtmlHelper включают параметр $attributes, который позволяет вам использовать любые дополнительные атрибуты ваших тегов. Вот несколько примеров использования параметра $attributes:

Желаемые атрибуты: <tag class="someClass" />
Массив параметров: ['class' => 'someClass']

Желаемые атрибуты: <tag name="foo" value="bar" />
Массив параметров:  ['name' => 'foo', 'value' => 'bar']

Вставка хорошо отформатированных элементов

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

Создание тегов Charset

Cake\View\Helper\HtmlHelper::charset($charset=null)

Используется для создания метатега с указанием кодировки документа. Значением по умолчанию является кодировка UTF-8. Пример использования:

echo $this->Html->charset();

Получаем:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

Альтернативное использование:

echo $this->Html->charset('ISO-8859-1');

Получаем:

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />

Ссылки на CSS файлы

Cake\View\Helper\HtmlHelper::css(mixed $path, array $options = [])

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

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

Если для ключа „rel“ в $options установлено значение „import“, то таблица стилей будет импортирована.

Этот метод подключения CSS предполагает, что указанный файл CSS находится внутри каталога webroot/css, если путь не начинается с символа „/“.

echo $this->Html->css('forms');

Получаем:

<link rel="stylesheet" href="/css/forms.css" />

Первым параметром может быть массив для подключения нескольких файлов.

echo $this->Html->css(['forms', 'tables', 'menu']);

Получаем:

<link rel="stylesheet" href="/css/forms.css" />
<link rel="stylesheet" href="/css/tables.css" />
<link rel="stylesheet" href="/css/menu.css" />

Вы можете включать файлы CSS из любого загруженного плагина, используя синтаксис плагина plugin syntax. Чтобы подключить plugins/DebugKit/webroot/css/toolbar.css вы можете сделать следующее:

echo $this->Html->css('DebugKit.toolbar.css');

Если вы хотите добавить файл CSS, который имеет такое же имя, как и загруженный плагин, вы можете сделать следующее. Например, если у вас есть плагин Blog, и вы хотите также подключить webroot/css/Blog.common.css, вы можете сделать следующее:

echo $this->Html->css('Blog.common.css', ['plugin' => false]);

Программируемое создание CSS

Cake\View\Helper\HtmlHelper::style(array $data, boolean $oneline = true)

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

echo $this->Html->style([
    'background' => '#633',
    'border-bottom' => '1px solid #000',
    'padding' => '10px'
]);

Получаем:

background:#633; border-bottom:1px solid #000; padding:10px;

Создание мета-тегов

Cake\View\Helper\HtmlHelper::meta(string|array $type, string $url = null, array $options = [])

Этот метод удобен для связи с внешними ресурсами, такими как RSS/Atom-каналы и фав-иконки. Как и в методе css(), здесь вы можете указать, хотите ли вы использовать этот тег в строке или внутри блока meta. Для вставки внутрь блока, установите параметр $attributes в true, то есть - ['block' => true].

Если вы установите атрибут «type» с помощью параметра $attributes, то CakePHP создаст несколько ярлыков:

type

translated value

html

text/html

rss

application/rss+xml

atom

application/atom+xml

icon

image/x-icon

<?= $this->Html->meta(
    'favicon.ico',
    '/favicon.ico',
    ['type' => 'icon']
);
?>
// Вывод (с добавленным разрывом строк)
<link
    href="http://example.com/favicon.ico"
    title="favicon.ico" type="image/x-icon"
    rel="alternate"
/>
<?= $this->Html->meta(
    'Comments',
    '/comments/index.rss',
    ['type' => 'rss']
);
?>
// Вывод (с добавленным разрывом строк)
<link
    href="http://example.com/comments/index.rss"
    title="Comments"
    type="application/rss+xml"
    rel="alternate"
/>

Этот метод также можно использовать для добавления meta keywords (ключевых слов) и описания. например:

<?= $this->Html->meta(
    'keywords',
    'здесь вводим любые ключевые слова'
);
?>
// Вывод
<meta name="keywords" content="здесь вводим любые ключевые слова" />

<?= $this->Html->meta(
    'description',
    'здесь вводим любое мета-описание'
);
?>
// Вывод
<meta name="description" content="здесь вводим любое мета-описание" />

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

<?= $this->Html->meta([
    'link' => 'http://example.com/manifest',
    'rel' => 'manifest'
]);
?>
// Вывод
<link href="http://example.com/manifest" rel="manifest"/>

Любые атрибуты, преданные в метод meta(), будут добавлены в сгенерированный тег.

Создание DOCTYPE

Cake\View\Helper\HtmlHelper::docType(string $type = 'html5')

Возвращает (X)HTML DOCTYPE (объявление типа документа). Предоставляются типы документа в соответствии со следующей таблицей:

type

translated value

html4-strict

HTML 4.01 Strict

html4-trans

HTML 4.01 Transitional

html4-frame

HTML 4.01 Frameset

html5 (default)

HTML5

xhtml-strict

XHTML 1.0 Strict

xhtml-trans

XHTML 1.0 Transitional

xhtml-frame

XHTML 1.0 Frameset

xhtml11

XHTML 1.1

echo $this->Html->docType();
// Вывод: <!DOCTYPE html>

echo $this->Html->docType('html4-trans');
// Вывод:
// <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
//    "http://www.w3.org/TR/html4/loose.dtd">

Ссылки на изображения

Cake\View\Helper\HtmlHelper::image(string $path, array $options = [])

Создает отформатированный тег изображения. Указанный путь должен быть относительным по отношению к webroot/img/.

echo $this->Html->image('cake_logo.png', ['alt' => 'CakePHP']);

Получаем:

<img src="/img/cake_logo.png" alt="CakePHP" />

Чтобы создать ссылку на изображение, укажите назначение ссылки, используя url в $attributes.

echo $this->Html->image("recipes/6.jpg", [
    "alt" => "Brownies",
    'url' => ['controller' => 'Recipes', 'action' => 'view', 6]
]);

Получаем:

<a href="/recipes/view/6">
    <img src="/img/recipes/6.jpg" alt="Brownies" />
</a>

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

echo $this->Html->image("logo.png", ['fullBase' => true]);

Получаем:

<img src="http://example.com/img/logo.jpg" alt="" />

Вы можете включать файлы изображений из любого загруженного плагина, используя синтаксис плагина plugin syntax. Подключить plugins/DebugKit/webroot/img/icon.png вы можете следующим образом:

echo $this->Html->image('DebugKit.icon.png');

Если вы хотите включить файл изображения, который имеет одинаковое имя с загруженным плагином, вы можете сделать следующее. Например, если у вас есть плагин Blog, и вы хотите подключить webroot/img/Blog.icon.png, вы можете сделать так:

echo $this->Html->image('Blog.icon.png', ['plugin' => false]);

Создание ссылок

Cake\View\Helper\HtmlHelper::link(string $title, mixed $url = null, array $options = [])

Метод общего назначения для создания ссылок HTML. Для указания атрибутов к элементу $title, используйте $options и не забудьте об экранировании.

echo $this->Html->link(
    'Enter',
    '/pages/home',
    ['class' => 'button', 'target' => '_blank']
);

Получаем:

<a href="/pages/home" class="button" target="_blank">Enter</a>

Используйте опцию '_full'=>true для абсолютных URL-ов:

echo $this->Html->link(
    'Панель приборов',
    ['controller' => 'Dashboards', 'action' => 'index', '_full' => true]
);

Получаем:

<a href="http://www.yourdomain.com/dashboards/index">Панель приборов</a>

Укажите ключ confirm в параметрах, для отображения confirm() диалога JavaScript:

echo $this->Html->link(
    'Удалить',
    ['controller' => 'Recipes', 'action' => 'delete', 6],
    ['confirm' => 'Вы точно уверены, что хотите удалить этот рецепт?']
);

Получаем:

<a href="/recipes/delete/6"
    onclick="return confirm(
        'Вы точно уверены, что хотите удалить этот рецепт?'
    );">
    Удалить
</a>

Строки запроса также могут быть созданы с помощью link().

echo $this->Html->link('Посмотреть изображение', [
    'controller' => 'Images',
    'action' => 'view',
    1,
    '?' => ['height' => 400, 'width' => 500]
]);

Получаем:

<a href="/images/view/1?height=400&width=500">Посмотреть изображение</a>

Специальные символы HTML в $title будут преобразованы в HTML сущности. Чтобы отключить это преобразование, установите опцию escape в false в массиве $options.

echo $this->Html->link(
    $this->Html->image("recipes/6.jpg", ["alt" => "Brownies"]),
    "recipes/view/6",
    ['escape' => false]
);

Получаем:

<a href="/recipes/view/6">
    <img src="/img/recipes/6.jpg" alt="Brownies" />
</a>

Установка escape в false также отключит экранирование атрибутов у ссылки. Вы можете использовать опцию escapeTitle, чтобы отключить экранирование заголовка, а не атрибутов.

echo $this->Html->link(
    $this->Html->image('recipes/6.jpg', ['alt' => 'Brownies']),
    'recipes/view/6',
    ['escapeTitle' => false, 'title' => 'hi "howdy"']
);

Получаем:

<a href="/recipes/view/6" title="hi &quot;howdy&quot;">
    <img src="/img/recipes/6.jpg" alt="Brownies" />
</a>

Также посмотрите метод Cake\View\Helper\UrlHelper::build() для получения дополнительных примеров различных типов URL-адресов.

Ссылки на видео и аудио файлы

Cake\View\Helper\HtmlHelper::media(string|array $path, array $options)

Опции:

type Тип элемента мультимедиа для генерации, допустимого значения - «audio» или «видео». Если тип не указан, то тип носителя будет установлен на основе mime заголовка файла.

text Текст для включения в тег к видео

pathPrefix Префикс пути, для использования в относительных URL-адресах, по умолчанию используется „files/“

fullBase Значит, что атрибут src получит полный адрес включая доменное имя

Возвращаем отформатированный audio/video тег:

 <?= $this->Html->media('audio.mp3') ?>

 // Вывод
 <audio src="/files/audio.mp3"></audio>

 <?= $this->Html->media('video.mp4', [
     'fullBase' => true,
     'text' => 'Fallback text'
 ]) ?>

 // Вывод
 <video src="http://www.somehost.com/files/video.mp4">Fallback text</video>

<?= $this->Html->media(
     ['video.mp4', ['src' => 'video.ogg', 'type' => "video/ogg; codecs='theora, vorbis'"]],
     ['autoplay']
 ) ?>

 // Вывод
 <video autoplay="autoplay">
     <source src="/files/video.mp4" type="video/mp4"/>
     <source src="/files/video.ogg" type="video/ogg;
         codecs='theora, vorbis'"/>
 </video>

Ссылки на файлы Javascript

Cake\View\Helper\HtmlHelper::script(mixed $url, mixed $options)

Подключает файл(ы) сценария, содержащий(ие) либо локальный(ые), либо удаленный(ые) URL(-ы).

По умолчанию, теги script будут вставляться в документ как строка. Вы можете переопределить это поведение, установив $options['block'] в true, тогда вместо тегов script, будет добавлен блок script, который вы можете распечатать в любом другом месте документа. Если вы захотите переопределить имя блока, вы можете сделать это, установив это в настройке $options['block'].

$options['once'] управляет тем, включать или нет этот сценарий один раз за запрос или больше, чем один раз. По умолчанию используется значение `` true``.

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

Этот метод подключения файла JavaScript предполагает, что указанный файл JavaScript находится внутри каталога webroot/js:

echo $this->Html->script('scripts');

Получаем:

<script src="/js/scripts.js"></script>

Вы можете ссылаться на файлы с абсолютными путями, а также на ссылки файлов которые не входят в webroot/js:

echo $this->Html->script('/otherdir/script_file');

Вы также можете сослаться на удаленный URL-адрес:

echo $this->Html->script('http://code.jquery.com/jquery.min.js');

Получаем:

<script src="http://code.jquery.com/jquery.min.js"></script>

Первым параметром может быть массив для включения нескольких файлов.

echo $this->Html->script(['jquery', 'wysiwyg', 'scripts']);

Получаем:

<script src="/js/jquery.js"></script>
<script src="/js/wysiwyg.js"></script>
<script src="/js/scripts.js"></script>

Вы можете добавить тег скрипта к определенному блоку с помощью опции block:

echo $this->Html->script('wysiwyg', ['block' => 'scriptBottom']);

В вашем макете вы можете вывести все теги скриптов, добавленные в „scriptBottom“:

echo $this->fetch('scriptBottom');

Вы можете подключать файлы сценариев из любого загруженного плагина, используя plugin syntax. Чтобы подключить plugins/DebugKit/webroot/js/toolbar.js вы можете сделать так:

echo $this->Html->script('DebugKit.toolbar.js');

Если вы хотите подключить файл сценария, который имеет одинаковое имя с загруженным плагином, вы можете сделать следующее. Например, если у вас есть плагин Blog, и также, вы хотите подключить webroot/js/Blog.plugins.js, вы можете сделать так

echo $this->Html->script('Blog.plugins.js', ['plugin' => false]);

Создание встроенных блоков Javascript

Cake\View\Helper\HtmlHelper::scriptBlock($code, $options = [])

Чтобы сгенерировать блоки Javascript для кода представления(Вида) PHP, вы можете использовать один из скриптов блочных методов. Сценарии могут быть выведены на место, или буферизованы в блок:

// Определите блок сценария сразу, с атрибутом defer.
$this->Html->scriptBlock('alert("hi")', ['defer' => true]);

// Буфер-блок сценария, который будет выведен позже.
$this->Html->scriptBlock('alert("hi")', ['block' => true]);

Cake\View\Helper\HtmlHelper::scriptStart($options = [])

Cake\View\Helper\HtmlHelper::scriptEnd()

Вы можете использовать метод scriptStart() для создания блока захвата, который будет выводить в тег <script>. Захваченные фрагменты сценария могут выводиться встроенными, или забуферироваными в блок:

// Добавление сценария в блок.
$this->Html->scriptStart(['block' => true]);
echo "alert('Я нахожусь в JavaScript');";
$this->Html->scriptEnd();

После того, как вы забуферировали javascript, вы можете вывести его, как и любой другой блок представления View Block:

// В ващем layout-е
echo $this->fetch('script');

Создание вложенных списков

Cake\View\Helper\HtmlHelper::nestedList(array $list, array $options = [], array $itemOptions = [])

Создадим вложенный список (UL/OL) из ассоциативного массива:

$list = [
    'Languages' => [
        'English' => [
            'American',
            'Canadian',
            'British',
        ],
        'Spanish',
        'German',
    ]
];
echo $this->Html->nestedList($list);

Вывод:

// Вывод (минус пробелы)
<ul>
    <li>Languages
        <ul>
            <li>English
                <ul>
                    <li>American</li>
                    <li>Canadian</li>
                    <li>British</li>
                </ul>
            </li>
            <li>Spanish</li>
            <li>German</li>
        </ul>
    </li>
</ul>

Создание заголовков таблиц

Cake\View\Helper\HtmlHelper::tableHeaders(array $names, array $trOptions = null, array $thOptions = null)

Создаёт строку заголовка ячеек таблицы, для размещения внутри тегов <table></table>:

echo $this->Html->tableHeaders(['Date', 'Title', 'Active']);

Вывод:

<tr>
    <th>Date</th>
    <th>Title</th>
    <th>Active</th>
</tr>

echo $this->Html->tableHeaders(
    ['Date', 'Title','Active'],
    ['class' => 'status'],
    ['class' => 'product_table']
);

Вывод:

<tr class="status">
     <th class="product_table">Date</th>
     <th class="product_table">Title</th>
     <th class="product_table">Active</th>
</tr>

Вы можете установить атрибуты для столбцов в $thOptions. Они будут использованы вместо значений указанных по умолчанию:

echo $this->Html->tableHeaders([
    'id',
    ['Name' => ['class' => 'highlight']],
    ['Date' => ['class' => 'sortable']]
]);

Вывод:

<tr>
    <th>id</th>
    <th class="highlight">Name</th>
    <th class="sortable">Date</th>
</tr>

Создание ячеек таблицы

Cake\View\Helper\HtmlHelper::tableCells(array $data, array $oddTrOptions = null, array $evenTrOptions = null, $useCount = false, $continueOddEven = true)

Создает ячейки таблицы в строках, назначая атрибуты <tr> по-разному для строк с нечётными и чётными номерами. Обернём каждую ячейку таблицы массивом, для каждого конкретного <td>-атрибута.

echo $this->Html->tableCells([
    ['Jul 7th, 2007', 'Best Brownies', 'Yes'],
    ['Jun 21st, 2007', 'Smart Cookies', 'Yes'],
    ['Aug 1st, 2006', 'Anti-Java Cake', 'No'],
]);

Вывод:

<tr><td>Jul 7th, 2007</td><td>Best Brownies</td><td>Yes</td></tr>
<tr><td>Jun 21st, 2007</td><td>Smart Cookies</td><td>Yes</td></tr>
<tr><td>Aug 1st, 2006</td><td>Anti-Java Cake</td><td>No</td></tr>

echo $this->Html->tableCells([
    ['Jul 7th, 2007', ['Best Brownies', ['class' => 'highlight']] , 'Yes'],
    ['Jun 21st, 2007', 'Smart Cookies', 'Yes'],
    ['Aug 1st, 2006', 'Anti-Java Cake', ['No', ['id' => 'special']]],
]);

Вывод:

<tr>
    <td>
        Jul 7th, 2007
    </td>
    <td class="highlight">
        Best Brownies
    </td>
    <td>
        Yes
    </td>
</tr>
<tr>
    <td>
        Jun 21st, 2007
    </td>
    <td>
        Smart Cookies
    </td>
    <td>
        Yes
    </td>
</tr>
<tr>
    <td>
        Aug 1st, 2006
    </td>
    <td>
        Anti-Java Cake
    </td>
    <td id="special">
        No
    </td>
</tr>

echo $this->Html->tableCells(
    [
        ['Red', 'Apple'],
        ['Orange', 'Orange'],
        ['Yellow', 'Banana'],
    ],
    ['class' => 'darker']
);

Вывод:

<tr class="darker"><td>Red</td><td>Apple</td></tr>
<tr><td>Orange</td><td>Orange</td></tr>
<tr class="darker"><td>Yellow</td><td>Banana</td></tr>

Изменение вывода тегов в HtmlHelper

Cake\View\Helper\HtmlHelper::setTemplates($templates)

Параметр $templates может быть либо строковым файловым путем для PHP файла, содержащим теги, которые вы хотите загрузить, или массивом шаблонов для добавления/замены:

// Загрузка шаблона config/my_html.php
$this->Html->setTemplates('my_html');

// Загрузка определённого шаблона
$this->Html->setTemplates([
    'javascriptlink' => '<script src="{{url}}" type="text/javascript"{{attrs}}></script>'
]);

При загрузке файлов шаблонов, ваш файл должен выглядеть так:

<?php
return [
    'javascriptlink' => '<script src="{{url}}" type="text/javascript"{{attrs}}></script>'
];

Предупреждение

Строки шаблонов, содержащие знак процента (%), требуют особого внимания. Вы должны приписать этому символу еще один процент, чтобы он выглядел так %%. Причина в том, что внутренние шаблоны скомпилированы для использования с методом sprintf(). Пример: <div style="width:{{size}}%%">{{content}}</div>

Создание дорожки из хлебных крошек с помощью HtmlHelper

Cake\View\Helper\HtmlHelper::addCrumb(string $name, string $link = null, mixed $options = null)

Cake\View\Helper\HtmlHelper::getCrumbs(string $separator = '&raquo;', string $startText = false)

Cake\View\Helper\HtmlHelper::getCrumbList(array $options = [], $startText = false)

Во многих приложениях есть «хлебные крошки» для облегчения навигации для конечных пользователей. Вы можете создать «хлебные крошки» в вашем приложении с помощью HtmlHelper. Сделайте сначала хлебные крошки, в макете вашего шаблона:

echo $this->Html->getCrumbs(' > ', 'Home');

Параметр $startText также может принимать массив. Это даёт больший контроль над генерируемой первой ссылкой:

echo $this->Html->getCrumbs(' > ', [
    'text' => $this->Html->image('home.png'),
    'url' => ['controller' => 'Pages', 'action' => 'display', 'home'],
    'escape' => false
]);

Любые ключи, которые не являются text или url, будут переданы link() как параметр $options.

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

$this->Html->addCrumb('Users', '/users');
$this->Html->addCrumb('Add User', ['controller' => 'Users', 'action' => 'add']);

Это добавит вывод «Home > Users > Add User» в вашем макете, где добавлен getCrumbs.

Вы также можете получить крошки, отформатированные внутри HTML-списка:

echo $this->Html->getCrumbList();

В качестве параметров вы можете использовать обычный параметр HTML, который вписывается в <ul> (Ненумерованный список), например class. Для определенных параметров у вас есть: separator (будет находиться между элементами <li>, firstClass и lastClass, например так:

echo $this->Html->getCrumbList(
    [
        'firstClass' => false,
        'lastClass' => 'active',
        'class' => 'breadcrumb'
    ],
    'Home'
);

Этот метод использует: Cake\View\Helper\HtmlHelper::tag() для генерации списка и его элементов. Работает аналогично View\Helper\HtmlHelper::getCrumbs(), поэтому он использует параметры с каждой крошкой. Вы можете использовать параметр $startText чтобы предоставить первую хлебную ссылку/текст. Это полезно, когда вы хотите включить корневую ссылку. Эта опция работает так же, как опция $startText для View\Helper\HtmlHelper::getCrumbs().

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Number

class Cake\View\Helper\NumberHelper(View $view, array $config = [])

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Formatting Currency Values

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Paginator

class Cake\View\Helper\PaginatorHelper(View $view, array $config = [])

PaginatorHelper используется для вывода элементов управления разбиением на страницы, таких как номера страниц и следующие/предыдущие ссылки. Он работает в тандеме с PaginatorComponent.

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

Шаблоны PaginatorHelper

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

Шаблоны используют {{var}} метки стиля. Важно не добавлять никаких пробелов вокруг {{}} иначе замены не будут работать.

Загрузка шаблонов из файла

При добавлении PaginatorHelper в ваш контроллер, вы можете определить „templates“, чтобы определить файл шаблона для загрузки. Это позволяет настроить несколько шаблонов и сохранить код DRY:

// В вашем /src/view/AppView.php
public function initialize()
{
    ...
    $this->loadHelper('Paginator', ['templates' => 'paginator-templates']);
}

Это загрузит файл, расположенный по адресу config/paginator-templates.php. См. пример ниже, как выглядит файл. Вы также можете загрузить шаблоны из плагина с использованием plugin syntax:

// В вашем /src/view/AppView.php
public function initialize()
{
    ...
    $this->loadHelper('Paginator', ['templates' => 'MyPlugin.paginator-templates']);
}

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

return [
    'number' => '<a href="{{url}}">{{text}}</a>',
];

Смена шаблонов во время выполнения

Cake\View\Helper\PaginatorHelper::setTemplates($templates)

Этот метод позволяет вам изменять шаблоны, используемые PaginatorHelper в во время выполнения. Это может быть полезно, если вы хотите настроить шаблоны для специальных вызовов метода:

// Прочитаем текущее значение шаблона.
$result = $this->Paginator->getTemplates('number');
// Prior to 3.4
$result = $this->Paginator->templates('number');

// Сменим шаблон
$this->Paginator->setTemplates([
    'number' => '<em><a href="{{url}}">{{text}}</a></em>'
]);

Предупреждение

Строки шаблонов, содержащие знак процента (%), нуждаются в специальном внимании. Вы должны приписать этому символу еще один процент, чтобы он выглядел как %%. Причина в том, что внутренние шаблоны скомпилированы для использоваться с sprintf(). Пример: „<div style=»width:{{size}}%%»>{{content}}</div>“

Имена шаблонов

PaginatorHelper использует следующие шаблоны:

nextActive Активное состояние для ссылки, сгенерированное методом nextActive().

nextDisabled Состояние отключено, генерируется методом nextDisabled().

prevActive Состояние включено, генерируется методом prevActive().

prevDisabled Состояние отключено, генерируется методом prevDisabled().

counterRange Шаблон счетчика counter() используется в формате format == range, генерируемый методом counterRange().

counterPages Шаблон счетчика counter() используется в формате format == pages, генерируемый методом counterPages().

first Шаблон, используемый для ссылки, генерируемый методом first().

last Шаблон, используемый для ссылки, генерируемый методом last().

number Шаблон, используемый для ссылки, генерируемый методом numbers().

current Шаблон, используемый для текущей страницы, генерируемый методом current().

ellipsis Шаблон, используемый для эллипсов, генерируемый методом ellipsis().

sort Шаблон для сортировки ссылок без направления, генерируемый методом sort().

sortAsc Шаблон для сортировки ссылок по возрастанию, генерируемый методом sortAsc().

sortDesc Шаблон для сортировки ссылок по убыванию, генерируемый методом sortDesc().

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

Cake\View\Helper\PaginatorHelper::sort($key, $title = null, $options = [])

Параметры

$key (string) – Имя столбца, в котором должен быть отсортирован набор записей.

$title (string) – Заголовок для ссылки. Если $title имеет значение null, $key будет использоваться для преобразования в формат «Title Case» и как заголовок.

$options (array) – Опции для сортировки ссылки.

Создаёт сортировку. Устанавливает параметры querystring для сортировки и направления. Ссылки по умолчанию будут сортироваться по asc (возрастанию). После первого клика по ссылке сгенерированный с помощью sort(), автоматически обрабатывает переключение направления. Если resultset сортируется по asc (возрастанию) по указанному ключу, возвращаемая ссылка будет сортироваться по desc (убыванию).

Принятые ключи для $options:

escape Если вы хотите, чтобы содержимое HTML-объекта было закодировано, используйте true.

model Модель используемая по умолчанию для PaginatorHelper::defaultModel().

direction Используется по умолчанию, когда ссылка неактивна.

lock Направление блокировки. Будет использоваться только по умолчанию, по умолчанию false.

Предполагается, что вы размещаете страницы на нескольких страницах и находитесь на первой странице:

echo $this->Paginator->sort('user_id');

Вывод:

<a href="/posts/index?page=1&amp;sort=user_id&amp;direction=asc">User Id</a>

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

echo $this->Paginator->sort('user_id', 'Учетная запись пользователя');

Вывод:

<a href="/posts/index?page=1&amp;sort=user_id&amp;direction=asc">Учетная запись пользователя</a>

Если вы используете HTML-изображения в своих ссылках, не забудьте установить их выход:

echo $this->Paginator->sort(
  'user_id',
  '<em>Учетная запись пользователя</em>',
  ['escape' => false]
);

Вывод:

<a href="/posts/index?page=1&amp;sort=user_id&amp;direction=asc"><em>Учетная запись пользователя</em></a>

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

echo $this->Paginator->sort('user_id', null, ['direction' => 'desc']);

Вывод:

<a href="/posts/index?page=1&amp;sort=user_id&amp;direction=desc">User Id</a>

Опцию блокировки можно использовать для блокировки сортировки в указанном направлении:

echo $this->Paginator->sort('user_id', null, ['direction' => 'asc', 'lock' => true]);

Cake\View\Helper\PaginatorHelper::sortDir(string $model = null, mixed $options = [])

Возвращает текущее направление сортировки записей.

Cake\View\Helper\PaginatorHelper::sortKey(string $model = null, mixed $options = [])

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

Создание ссылок на номера страниц

Cake\View\Helper\PaginatorHelper::numbers($options = [])

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

Поддерживаемые параметры:

before Содержимое должно быть вставлено перед номерами.

after Содержимое вводится после номеров.

model Модель для создания чисел, по умолчанию PaginatorHelper::defaultModel().

modulus Сколько номеров будет включено по обе стороны текущей страницы, по умолчанию 8.

first Если вы хотите, чтобы генерировались первые ссылки, задайте целое число определяющее количество „первых“ ссылок для генерации. По умолчанию используется false. Если строка установлена как ссылка на первую страницу, то она будет сгенерирована со значением как и заглавие:

echo $this->Paginator->numbers(['first' => 'Первая страница']);

last Если вы хотите сгенерировать ссылки на последние страницы, установите целое число для определения числа „последних“ ссылок для генерации. По умолчанию используется false. Следуйте той же логике, как и для опции first. Или, если хотите, используйте last().

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

echo $this->Paginator->numbers();

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

echo $this->Paginator->numbers(['first' => 2, 'last' => 2]);

Создание ссылок для перехода

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

Cake\View\Helper\PaginatorHelper::prev($title = '<< Previous', $options = [])

Параметры

$title (string) – Заголовок для ссылки.

$options (mixed) – Параметры ссылки для разбивки на страницы.

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

$options поддерживает следующие клавиши:

escape Если вы хотите, чтобы содержимое HTML-объекта было закодировано, установите в true.

model Используемая модель, по умолчанию используется PaginatorHelper::defaultModel().

disabledTitle Текст для использования, когда ссылка отключена. По умолчанию используется параметр $title.

Простой пример:

echo $this->Paginator->prev(' << ' . __('previous'));

Если вы в настоящее время находитесь на второй странице постов, вы получите следующее:

<li class="prev">
    <a rel="prev" href="/posts/index?page=1&amp;sort=title&amp;order=desc">
        &lt;&lt; previous
    </a>
</li>

Если бы не было предыдущих страниц, то:

<li class="prev disabled"><span>&lt;&lt; previous</span></li>

Чтобы изменить шаблоны, используемые этим методом, см. Шаблоны PaginatorHelper.

Cake\View\Helper\PaginatorHelper::next($title = 'Next >>', $options = [])

Этот метод идентичен prev(), за несколькими исключениями. Он создаёт ссылки, указывающие на следующую страницу, а не на предыдущую. Это также использует next в качестве значения атрибута rel вместо prev.

Cake\View\Helper\PaginatorHelper::first($first = '<< first', $options = [])

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

echo $this->Paginator->first('< first');

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

echo $this->Paginator->first(3);

Вышеуказанное создаст ссылки на первые 3 страницы, как только вы доберётесь до третьей или большей страницы. До этого ничего не будет выводиться.

Параметр options принимает следующее:

model Модель используемая по умолчанию PaginatorHelper::defaultModel()

escape Следует ли экранировать текст. Установите значение false, если ваш контент содержит HTML.

Cake\View\Helper\PaginatorHelper::last($last = 'last >>', $options = [])

Этот метод очень похож на метод first(). Однако он имеет несколько отличий. Он не будет создавать никаких ссылок для строковых значений $last, если вы находитесь на последней странице. Для целочисленного значения $last никакие ссылки не будут сгенерированы после того, как пользователь окажется в диапазоне последних страницы.

Создание тегов ссылок в заголовке

PaginatorHelper можно использовать для создания тегов ссылок пагинации на вашей странице в элементе <head>:

// Создайте следующие/предыдущие ссылки для текущей модели.
echo $this->Paginator->meta();

// Создайте следующие/предыдущие и первые/последние ссылки для текущей модели.
echo $this->Paginator->meta(['first' => true, 'last' => true]);

Добавлено в версии 3.4.0: The first and last options were added in 3.4.0

Проверка состояния пагинации

Cake\View\Helper\PaginatorHelper::current(string $model = null)

Получает набор записей, на текущей странице, для данной модели:

// Our URL is: http://example.com/comments/view/page:3
echo $this->Paginator->current('Comment');
// Output is 3

Cake\View\Helper\PaginatorHelper::hasNext(string $model = null)

Возвращает true, если данный результирующий набор не находится на последней странице.

Cake\View\Helper\PaginatorHelper::hasPrev(string $model = null)

Возвращает true, если данный результирующий набор не находится на первой странице.

Cake\View\Helper\PaginatorHelper::hasPage(string $model = null, integer $page = 1)

Возвращает true, если заданный набор результатов имеет номер страницы, заданный $page.

Cake\View\Helper\PaginatorHelper::total(string $model = null)

Возвращает общее количество страниц для предоставленной модели.

Добавлено в версии 3.4.0.

Создание счетчика страниц

Cake\View\Helper\PaginatorHelper::counter($options = [])

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

Существует ряд опций для counter(). Поддерживаемые:

format Формат счетчика. Поддерживаемые форматы: „range“ (диапазон), „pages“ (страницы) и пользовательские. По умолчанию страницы, будут выводиться как „1 of 10“. В пользовательском режиме строка с параметрами анализируется, а токены заменяются фактическими значениями. Доступными токенами являются:

{{page}} - отображается текущая страница.

{{pages}} - общее количество страниц.

{{current}} - текущее количество отображаемых записей.

{{count}} - общее количество записей в наборе результатов.

{{start}} - номер первой отображаемой записи.

{{end}} - номер последней отображаемой записи.

{{model}} - Плурализованная человеческая форма названия модели. Если ваша модель была „RecipePage“, {{model}} будет „recipe pages“.

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

echo $this->Paginator->counter(
    'Page {{page}} of {{pages}}, showing {{current}} records out of
     {{count}} total, starting on record {{start}}, ending on {{end}}'
);

Установка „format“ для диапазона будет выводиться как „1 - 3 of 13“:

echo $this->Paginator->counter([
    'format' => 'range'
]);

model Имя модели, разбитой на страницы, по умолчанию является PaginatorHelper::defaultModel(). И используется в соединение с пользовательской строкой в опции „format“.

Создание URL-адресов для пагинации

Cake\View\Helper\PaginatorHelper::generateUrl(array $options = [], $model = null, $full = false)

По умолчанию возвращается строка полного URL-адреса для использования в нестандартных контекстах (например, JavaScript):

echo $this->Paginator->generateUrl(['sort' => 'title']);

Создание элемента управления с ограничениями

Cake\View\Helper\PaginatorHelper::limitControl(array $limits = [], $default = null, array $options = [])

Создание выпадающего элемента управления, который изменяет параметр запроса limit:

// Используется значение по умолчанию.
echo $this->Paginator->limitControl();

// Определите, какие параметры лимита вы хотите.
echo $this->Paginator->limitControl([25 => 25, 50 => 50]);

// Пользовательские лимиты и установка выбранной опции
echo $this->Paginator->limitControl([25 => 25, 50 => 50], $user->perPage);

The generated form and control will automatically submit on change.

Добавлено в версии 3.5.0: The limitControl() method was added in 3.5.0

Настройка параметров пагинации

Cake\View\Helper\PaginatorHelper::options($options = [])

Устанавливает все параметры для PaginatorHelper. Поддерживаемые параметры:

url URL-адрес действия для разбивки на страницы. „url“ имеет несколько дополнительных опций:

sort Ключ к сортировке записей.

direction Направление сортировки. По умолчанию используется „ASC“.

page Номер страницы для отображения.

Вышеупомянутые параметры могут использоваться для принудительного создания определенных страниц/направлений. Вы также можете добавить дополнительный URL-адрес во все URL-адреса, созданные в helper:

$this->Paginator->options([
    'url' => [
        'sort' => 'email',
        'direction' => 'desc',
        'page' => 6,
        'lang' => 'en'
    ]
]);

Приведенное выше добавляет параметр маршрута en ко всем ссылкам, которые создаст хелпер(помощник). Он также создаст ссылки со специфическими значениями сортировки, направления и страницы. По умолчанию PaginatorHelper объединит все текущие переданные аргументы и параметры строки запроса.

escape Определяет, должно ли поле заголовка ссылок ссылаться на HTML. По умолчанию true.

model Имя модели, разбитой на страницы, по умолчанию PaginatorHelper::defaultModel().

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

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

Подробнее см. в API PaginatorHelper [https://api.cakephp.org/3.x/class-Cake.View.Helper.PaginatorHelper.html]. Как уже упоминалось, PaginatorHelper также предлагает функции сортировки, которые могут быть интегрированы в заголовки столбцов таблицы:

<!-- src/Template/Posts/index.ctp -->
<table>
    <tr>
        <th><?= $this->Paginator->sort('id', 'ID') ?></th>
        <th><?= $this->Paginator->sort('title', 'Title') ?></th>
    </tr>
       <?php foreach ($recipes as $recipe): ?>
    <tr>
        <td><?= $recipe->id ?> </td>
        <td><?= h($recipe->title) ?> </td>
    </tr>
    <?php endforeach; ?>
</table>

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

Также можно отсортировать столбец на основе ассоциаций:

<table>
    <tr>
        <th><?= $this->Paginator->sort('title', 'Title') ?></th>
        <th><?= $this->Paginator->sort('Authors.name', 'Author') ?></th>
    </tr>
       <?php foreach ($recipes as $recipe): ?>
    <tr>
        <td><?= h($recipe->title) ?> </td>
        <td><?= h($recipe->name) ?> </td>
    </tr>
    <?php endforeach; ?>
</table>

Конечным ингредиентом, для отображения страниц в виде просмотров является добавление навигации по страницам, также предоставляемый PaginationHelper:

// Показывает номера страниц
<?= $this->Paginator->numbers() ?>

// Показывает следующую и предыдущую ссылки
<?= $this->Paginator->prev('« Previous') ?>
<?= $this->Paginator->next('Next »') ?>

// Печать X из Y, где X - текущая страница, а Y - количество страниц
<?= $this->Paginator->counter() ?>

Вывод, выданный методом counter(), также можно настроить с помощью специальных маркеров:

<?= $this->Paginator->counter([
    'format' => 'Page {{page}} of {{pages}}, showing {{current}} records out of
             {{count}} total, starting on record {{start}}, ending on {{end}}'
]) ?>

Разбиение на несколько результатов

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

// Установим свой вариант модели
echo $this->Paginator->sort('title', ['model' => 'Articles']);

// Установим модель по умолчанию
$this->Paginator->options(['defaultModel' => 'Articles']);
echo $this->Paginator->sort('title');

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

Добавлено в версии 3.3.0: Множественная разбивка на страницы добавлена в 3.3.0

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Rss

class Cake\View\Helper\RssHelper(View $view, array $config = [])

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Сессия

class Cake\View\Helper\SessionHelper(View $view, array $config = [])

Не рекомендуется, начиная с версии 3.0.0: SessionHelper не используется с версии 3.x. Вместо этого необходимо использовать FlashHelper или получить доступ к сессии через request.

Как обычный аналог объекта Session, SessionHelper копирует большинство функций объекта и предоставляет к нему доступ в представлении.

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

Как и в случае с объектом сессии, доступ к данным осуществляется с использованием структуры массива dot notation:

['User' => [
    'username' => 'super@example.com'
]];

Учитывая предыдущую структуру массива, доступ к элементу осуществляется через точку User.username, указывая на вложенный массив. Эта нотация используется для всех методов SessionHelper, где используется $key.

Cake\View\Helper\SessionHelper::read(string $key)

Тип результата

mixed

Читает из сессии. Возвращает строку или массив в зависимости от её содержания.

Cake\View\Helper\SessionHelper::check(string $key)

Тип результата

boolean

Проверяет, есть ли такой ключ в сессии. Возвращает логическое значение, отображая существование ключа.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Text

class Cake\View\Helper\TextHelper(View $view, array $config = [])

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Highlighting Substrings

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Time

class Cake\View\Helper\TimeHelper(View $view, array $config = [])

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Url

class Cake\View\Helper\UrlHelper(View $view, array $config = [])

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Доступ к Базе Данных и ORM

В CakePHP работа с Базой Данных осуществляется с помощью двух основных типов объектов. Первый тип - repositories (репозитории) или по другому table objects (объекты таблицы). Эти объекты предоставляют доступ к наборам данных. Они позволяют сохранять новые записи, редактировать/удалять существующие, определять отношения между данными и выполнять различные массовые операции. Второй тип объектов - entities (сущности). Сущности представляют собой конкретные отдельные записи таблицы и позволяют работать со строкой таблицы как с объектом.

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

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

Объектно-реляционное отображение заимствует идеи и концепции паттернов ActiveRecord и Datamapper. ORM реализует гибрид, который сочетает аспекты обоих паттернов, чтобы создать быстрое и легкое в использовании объектно-реляционное отображение.

Прежде чем начать изучать ORM, убедитесь что вы настроили соединение с БД.

Примечание

Если вы знакомы с предыдущими версиями CakePHP, вам следует прочитать Руководство по миграции на новую ORM чтобы понимать различия между CakePHP 3.0 и более старыми версиями CakePHP.

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

Чтобы начать вам не нужно писать какой либо дополнительный код по настройке Базы данных. Если вы следуете CakePHP соглашениям Модели и Базы данных то вы можете просто начать использовать ORM. Например если вы хотите получить данные из таблицы articles вы можете сделать это так:

use Cake\ORM\TableRegistry;

// Prior to 3.6 use TableRegistry::get('Articles')
$articles = TableRegistry::getTableLocator()->get('Articles');

$query = $articles->find();

foreach ($query as $row) {
    echo $row->title;
}

Обратите внимание, мы не писали код который конфигурирует таблицу articles. Соглашения в CakePHP позволяют нам пропустить написание шаблонного кода фрэймворк будет использовать базовые классы, если в вашем приложении еще не определены конкретные классы. Если вы хотите настроить класс ArticlesTable, отвечающий за таблицу articles добавив в него отношения с другими таблицами или определить дополнительные методы, то необходимо добавить в файл src/Model/Table/ArticlesTable.php после открывающего тега <?php следующий код:

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{

}

Классы таблиц именуются в ВерблюжемРегистре с постфиксом Table после названия таблицы. После того, как класс таблицы создан вы можете ссылаться на него, используя ORM\TableRegistry как и ранее:

use Cake\ORM\TableRegistry;

// Теперь $articles экземпляр класса ArticlesTable.
// Prior to 3.6 use TableRegistry::get('Articles')
$articles = TableRegistry::getTableLocator()->get('Articles');

Теперь, когда у нас есть конкретный класс таблицы, мы возможно захотим использовать конкретный класс одной записи таблицы (сущности). Класс сущности позволяют определить геттеры и сеттеры, добавить дополнительную логику и многое другое. Начнем с создания класса src/Model/Entity/Article.php и добавления после тега <?php следующего кода:

namespace App\Model\Entity;

use Cake\ORM\Entity;

class Article extends Entity
{

}

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

use Cake\ORM\TableRegistry;

// $articles объект класса ArticlesTable.
// Prior to 3.6 use TableRegistry::get('Articles')
$articles = TableRegistry::getTableLocator()->get('Articles');
$query = $articles->find();

foreach ($query as $row) {
    // Каждая строка $row теперь объект класса сущности Article.
    echo $row->title;
}

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

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

Больше инфорации

Основы конфигурации базы данных

Краткий обзор

Настройка

Управление соединениями

Типы данных

Классы подключений

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

Логирование запросов

Использование кавычек в идентификаторах

Кэширование метаданных

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

Query Builder

Using SQL Functions

Adding Calculated Fields

Returning the Total Count of Records

Caching Loaded Results

Filtering by Associated Data

Adding Joins

Табличные объекты

Основное применение

Обратные вызовы жизненного цикла

Поведения

Настройка соединений

Использование TableRegistry

Entities

Creating Virtual Properties

Exposing Virtual Properties

Retrieving Data & Results Sets

Getting the First Result

Finding Key/Value Pairs

Custom Finder Methods

Dynamic Finders

Eager Loading Associations

Modifying Results with Map/Reduce

Validating Data

Using A Different Validation Set For Associations

Saving Data

Converting Request Data into Entities

Deleting Data

Associations - Linking Tables Together

HasOne Associations

BelongsTo Associations

HasMany Associations

BelongsToMany Associations

Behaviors

Using Behaviors

Core Behaviors

Schema System

ORM Cache Shell

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Основы конфигурации базы данных

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

Краткий обзор

Функции, описанные в даннйо главе дают представление о возможностях взаимодействия с API базы данных на низком уровне. Если вместо этого вы хотите узнать больше о нолных возможностях ORM, обратитесь к разделам Query Builder и Табличные объекты.

Самый простой способ создать соединение с базой данных - использовать DSN строку:

use Cake\Datasource\ConnectionManager;

$dsn = 'mysql://root:password@localhost/my_database';
ConnectionManager::config('default', ['url' => $dsn]);

После создания, появится возможность получить доступ к объекту соединения и начать его использовать:

$connection = ConnectionManager::get('default');

Поддерживаемые базы данных

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

MySQL 5.5+

SQLite 3

PostgreSQL 8.3+

SQLServer 2008+

Oracle (с использованием плагина)

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

База данных Oracle поддерживается с использованием плагина: Драйвер для базы данных Oracle [https://github.com/CakeDC/cakephp-oracle-driver]

Выполнение выражений Select

Выполнение сырых SQL запросов осуществляется очень просто:

use Cake\Datasource\ConnectionManager;

$connection = ConnectionManager::get('default');
$results = $connection->execute('SELECT * FROM articles')->fetchAll('assoc');

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

$results = $connection
    ->execute('SELECT * FROM articles WHERE id = :id', ['id' => 1])
    ->fetchAll('assoc');

Также можно использовать сложные типы данных в качестве аргументов:

use Cake\Datasource\ConnectionManager;
use DateTime;

$connection = ConnectionManager::get('default');
$results = $connection
    ->execute(
        'SELECT * FROM articles WHERE created >= :created',
        ['created' => new DateTime('1 day ago')],
        ['created' => 'datetime']
    )
    ->fetchAll('assoc');

Вместо написания SQL запросов вручную, используйте конструктор:

$results = $connection
    ->newQuery()
    ->select('*')
    ->from('articles')
    ->where(['created >' => new DateTime('1 day ago')], ['created' => 'datetime'])
    ->order(['title' => 'DESC'])
    ->execute()
    ->fetchAll('assoc');

Выполнение выражений Insert

Вставка строк в базу данных обычно состоит всего из нескольких строк кода:

use Cake\Datasource\ConnectionManager;
use DateTime;

$connection = ConnectionManager::get('default');
$connection->insert('articles', [
    'title' => 'A New Article',
    'created' => new DateTime('now')
], ['created' => 'datetime']);

Выполнение выражений Update

Обновление строк в базе данных также интуитивно понятно, следующий пример обновляет элемент article с id 10:

use Cake\Datasource\ConnectionManager;
$connection = ConnectionManager::get('default');
$connection->update('articles', ['title' => 'New title'], ['id' => 10]);

Выполнение выражений Delete

Точно так же метод delete() используется для удаления строк из базы данных. В следующем примере удаляется элемент article с id 10:

use Cake\Datasource\ConnectionManager;
$connection = ConnectionManager::get('default');
$connection->delete('articles', ['id' => 10]);

Настройка

Согласно правилу, соединение с базой данных настраивается в config/app.php. Информация о соединении в этом файле используется в классе Cake\Datasource\ConnectionManager, создавая настройку соединения, которая будет использоваться вашим приложением. Пример настройки соединения находится в файле config/app.default.php. Пример конфигурации выглядит следующим образом:

'Datasources' => [
    'default' => [
        'className' => 'Cake\Database\Connection',
        'driver' => 'Cake\Database\Driver\Mysql',
        'persistent' => false,
        'host' => 'localhost',
        'username' => 'my_app',
        'password' => 'sekret',
        'database' => 'my_app',
        'encoding' => 'utf8mb4',
        'timezone' => 'UTC',
        'cacheMetadata' => true,
    ]
],

Вышеуказанный код создаст соединение „default“ с предоставленными параметрами. Вы можете определить столько соединений, сколько нужно в конфигурационном файле. Вы можете также определить дополнительные соединения во время выполнения, используя Cake\Datasource\ConnectionManager::config(). Пример:

use Cake\Datasource\ConnectionManager;

ConnectionManager::config('default', [
    'className' => 'Cake\Database\Connection',
    'driver' => 'Cake\Database\Driver\Mysql',
    'persistent' => false,
    'host' => 'localhost',
    'username' => 'my_app',
    'password' => 'sekret',
    'database' => 'my_app',
    'encoding' => 'utf8mb4',
    'timezone' => 'UTC',
    'cacheMetadata' => true,
]);

Параметры конфигурации также могут быть представлены в виде строки DSN. Это полезно при работе с переменными окружения или провайдерами PaaS:

ConnectionManager::config('default', [
    'url' => 'mysql://my_app:sekret@localhost/my_app?encoding=utf8&timezone=UTC&cacheMetadata=true',
]);

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

По умолчанию, все объекты Таблицы используют default соединение. Для использования соединения не по умолчанию, смотрите Настройка соединений.

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

className

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

driver

Имя класса драйвера, используемого для реализации всех особенностей механизма базы данных. Это может быть как короткое имя класса с использованием синтаксиса плагина, полное имя класса или созданный экземпляр драйвера. Примеры сокращенных наименований класса: Mysql, Sqlite, Postgres, и Sqlserver

persistent

Следует ли использовать постоянное соединение с базой данных. Эта опция не поддерживается SqlServer. Начиная с версии 3.4.13 CakePHP, исключение выбрасывается, если вы пытаетесь установить persistent в true с помощью SqlServer.

host

Имя хоста сервера базы данных (или IP-адрес).

username

Имя пользователя учетной записи.

password

Пароль учетной записи.

database

Имя базы данных для этого соединения. Избегайте использования . названии вашей базы данных. Из-за того, что экранирование символов осложнено, CakePHP не поддерживает . в имени базы данных. Путь к базе данных SQLite должен быть абсолютным (например, ROOT. DS. 'My_app.db'), чтобы избежать неправильного указания пути из-за использования относительного пути.

port (при необходимости)

Порт TCP или сокет Unix, используемый для подключения к серверу.

encoding

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

timezone

Часовой пояс, установленный на сервере.

schema

Настройка в базе данных, используемая в PostgreSQL для указания используемой схемы.

unix_socket

Используется драйверами для подключения через файлы Unix сокетов. Если вы используете PostgreSQL и хотите использовать Unix сокеты, оставьте значение хоста пустым.

ssl_key

Путь до файла SSL ключа. (Поддерживается только MySQL).

ssl_cert

Путь до файла SSL сертификата. (Поддерживается только MySQL).

ssl_ca

Путь до файла издателя SSL сертификата. (Поддерживается только MySQL).

init

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

log

Установите значение на true для включения логирования запросов. При включеннои режиме запросы будут логироваться на уровне debug с областью действий queriesLog.

quoteIdentifiers

Установите значение на true, если используются зарезервированные слова или специальные символы в названиях таблиц или столбцов. Включение этого параметра приведёт к тому, что в запросах, сформированных с использованием Query Builder, будут использоваться идентификаторы, заключенные в кавычки при создании SQL. Необходимо отметить, что это снизит производительность так как каждый запрос должен быть отслежен и обработан до выполнения.

flags

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

cacheMetadata

Либо логическое true, либо строка, содержащая конфигурацию кэша для хранения метаданных. Отключение кэширования метаданных не рекомендуется и может привести к очень низкой производительности. Смотрите раздел: ref: database-metadata-cache для получения дополнительной информации.

mask

Установите разрешения для сгенерированного файла базы данных. (Поддерживается только SQLite)

На данном этапе будет полезно обратить внимание на Соглашения CakePHP. Правильное наименование для таблиц (и добавление некоторых столбцов) может дать некоторые дополнительные функции и избежать настройки. Например, если назвать таблицу базы данных big_boxes, таблицу BigBoxesTable и контроллер BigBoxesController, то все вместе будет работать автоматически. Согласно правилам, необходимо использовать знак нижнего подчеркивания, нижний регистр и множественное число в наименованиях таблиц баз данных - например: bakers, pastry_stores и savory_cakes.

Управление соединениями

class Cake\Datasource\ConnectionManager

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

Доступ к соединениям

static Cake\Datasource\ConnectionManager::get($name)

Настроенные один раз, соединения могут быть получены с использованием Cake\Datasource\ConnectionManager::get(). Данный метод создаст и загрузит соединение, если оно не было создано до этого или возвратит существующее известное соединение:

use Cake\Datasource\ConnectionManager;

$conn = ConnectionManager::get('default');

Попытка загрузить соединения, которые не существуют, приведет к вызову исключения.

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

Используя config() и get() можно создавать новые соединения, которые не определены в файлах конфигурации, во время выполнения приложения:

ConnectionManager::config('my_connection', $config);
$conn = ConnectionManager::get('my_connection');

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

Типы данных

class Cake\Database\Type

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

string

Обычно поддерживается столбцами типа CHAR или VARCHAR. Использование опции fixed преобразует столбец в тип CHAR. Для SQL Server используются типы NCHAR и NVARCHAR.

text

Соответствует типам TEXT.

uuid

Соответствует типу UUID, если база данных имеет таковой, в противном случае генерируется поле CHAR(36).

binaryuuid

Соответствует типу UUID, если база данных имеет таковой, в противном случае генерируется столбец BINARY(16).

integer

Соответствует типу INTEGER, предоставляемому базой данных. BIT на данный момент не поддерживается.

smallinteger

Соответствует типу SMALLINT, предоставляемому базой данных.

tinyinteger

Соответствует типу TINYINT или SMALLINT, предоставляемому базой данных. Для MySQL TINYINT(1) является логическим типом.

biginteger

Соответствует типу BIGINT, предоставляемому базой данных.

float

Соответствует либо DOUBLE, либо FLOAT в зависимости от базы данных. Опция precision используется для определения кол-ва знаков после запятой.

decimal

Соответствует типу DECIMAL. Поддерживаются опции length и precision.

boolean

Соответствует BOOLEAN, кроме MySQL, где TINYINT(1) используется для представления булевых значений. BIT(1) на данный момент не поддерживается.

binary

Соответствует типу BLOB или BYTEA, в зависимости от базы данных.

date

Соответствует простому типу часового пояса столбца DATE. Возвращаемое значение столбца этого типа - Cake\I18n\Date, которое расширяет основной класс DateTime.

datetime

Соответствует простому типу часового пояса столбца DATETIME. В PostgreSQL и SQL Server преобразуется в тип TIMESTAMP. Возвращаемое значение по умолчанию столбца этого типа - Cake\I18n\Time, которое расширяет встроенный класс DateTime и Chronos [https://github.com/cakephp/chronos].

timestamp

Соответствует типу TIMESTAMP.

time

Соответствует типу TIME во всех базах данных.

json

Соответствует типу JSON, если он доступен, иначе преобразуется в TEXT. Тип JSON был добавлен в версии 3.3.0

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

Каждый тип предоставляет функции преобразования между представлениями PHP и SQL. Эти методы вызываются на основе подсказок для типов данных при выполнении запросов. Например, столбец, помеченный как „datetime“ будет автоматически конвертировать входные параметры из экземпляров DateTime во временные метки или форматированные строки представляющие даты. Аналогично, столбцы с „двоичными“ данными принимают файловые дескрипторы и генерируют файловые дескрипторы при чтении данных.

Изменено в версии 3.3.0: Был добавлен тип json.

Изменено в версии 3.5.0: Были добавлены типы smallinteger и tinyinteger.

Изменено в версии 3.6.0: Был добавлен тип binaryuuid.

Добавление пользовательских типов

static Cake\Database\Type::map($name, $class)

Если необходимо использовать особые типы, которые не встроены в CakePHP, можно добавить новые дополнительные типы в систему типов CakePHP. Классы типов имплементируют следующие методы:

toPHP: Преобразует данное значение из типа базы данных в эквивалент PHP.

toDatabase: Преобразует данное значение из типа PHP в один из подходящих базе данных.

toStatement: Преобразует данное значение в эквивалент выражения.

marshal: Сериализирует необработанные данные в объекты PHP.

Простой способ реализовать базовый интерфейс - расширить класс Cake\Database\Type. Например, если необходимо добавить тип JSON, нужно написать следуюий класс для типа:

// в src/Database/Type/JsonType.php

namespace App\Database\Type;

use Cake\Database\Driver;
use Cake\Database\Type;
use PDO;

class JsonType extends Type
{

    public function toPHP($value, Driver $driver)
    {
        if ($value === null) {
            return null;
        }
        return json_decode($value, true);
    }

    public function marshal($value)
    {
        if (is_array($value) || $value === null) {
            return $value;
        }
        return json_decode($value, true);
    }

    public function toDatabase($value, Driver $driver)
    {
        return json_encode($value);
    }

    public function toStatement($value, Driver $driver)
    {
        if ($value === null) {
            return PDO::PARAM_NULL;
        }
        return PDO::PARAM_STR;
    }

}

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

use Cake\Database\Type;

Type::map('json', 'App\Database\Type\JsonType');

Добавлено в версии 3.3.0: Тип Json, описанный в этом примере был добавлен в ядро фреймворка.

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

use Cake\Database\Schema\TableSchema;

class WidgetsTable extends Table
{

    protected function _initializeSchema(TableSchema $schema)
    {
        $schema->columnType('widget_prefs', 'json');
        return $schema;
    }

}

Сопоставление пользовательских типов данных выражениям SQL

Добавлено в версии 3.3.0: Поддержка сопоставления пользовательских типов данных к выражениям SQL была добавлена в версии 3.3.0.

Предыдущий пример сопоставляет пользовательский тип данных для столбца типа „json“, который легко выразить в виде строеи в выражении SQL. Сложные типы данных SQL не могут быть выражены строками/числами в SQL запросах. При работе с этими типами данных класс Типа должен имплементировать интерфейс Cake\Database\Type\ExpressionTypeInterface. Этот интерфейс даёт возможность пользовательскому типу представлять значение в виде SQL выражения. Например, построим простой класс Типа для обработки типа данных POINT из MySQL. Сначала определим объект „value“, который используется для представления данных POINT в PHP:

// в src/Database/Point.php
namespace App\Database;

// Наш объект-значение не изменяем.
class Point
{
    protected $_lat;
    protected $_long;

    // Factory метод.
    public static function parse($value)
    {
        // Парсинг данных из MySQL.
        return new static($value[0], $value[1]);
    }

    public function __construct($lat, $long)
    {
        $this->_lat = $lat;
        $this->_long = $long;
    }

    public function lat()
    {
        return $this->_lat;
    }

    public function long()
    {
        return $this->_long;
    }
}

После создания объекта-значения понадобится класс Типа для сопоставления данных в этот объект-значение и в выражение SQL:

namespace App\Database\Type;

use App\Database\Point;
use Cake\Database\Expression\FunctionExpression;
use Cake\Database\Type as BaseType;
use Cake\Database\Type\ExpressionTypeInterface;

class PointType extends BaseType implements ExpressionTypeInterface
{
    public function toPHP($value, Driver $d)
    {
        return Point::parse($value);
    }

    public function marshal($value)
    {
        if (is_string($value)) {
            $value = explode(',', $value);
        }
        if (is_array($value)) {
            return new Point($value[0], $value[1]);
        }
        return null;
    }

    public function toExpression($value)
    {
        if ($value instanceof Point) {
            return new FunctionExpression(
                'POINT',
                [
                    $value->lat(),
                    $value->long()
                ]
            );
        }
        if (is_array($value)) {
            return new FunctionExpression('POINT', [$value[0], $value[1]]);
        }
        // Обработка других случаев.
    }
}

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

Метод toPHP парсит результаты SQL запроса в объект-значение.

Метод marshal преобразовывает данные запроса в объект-значение. Здесь принимаются такие строковые значения, как '10.24,12.34 и массивы.

Метод toExpression обрабатывает преобразование объект-значения в эквивалент выражения SQL. В этом примере полученный в результате SQL будет выглядеть как POINT(10.24, 12.34).

Как только будет создан пользовательский тип, понадобится подключить тип к таблице классов.

Использование неизменяемых объектов DateTime

Добавлено в версии 3.2: Неизменяемые объекты date/time были добавлены в версии 3.2.

Так как объекты Date/Time легко изменить, CakePHP дает возможность использовать неизменяемые объект-значения. Наилучшим образом это можно сделать в файле config/bootstrap.php:

Type::build('datetime')->useImmutable();
Type::build('date')->useImmutable();
Type::build('time')->useImmutable();
Type::build('timestamp')->useImmutable();

Примечание

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

Классы подключений

class Cake\Database\Connection

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

Выполнение запросов

Cake\Database\Connection::query($sql)

Получив объект подключения, вы вероятно захотите выполнить с ним несколько запросов. Слой абстракции базы данных CakePHP предоставляет функции обёртки поверх PDO и собственных драйверов. Данные обертки предоставляют схожий интерфейс аналогичный PDO. Существует несколько различных способов выполнения запросов в зависимости от его типа, который необходимо выполнить и того какие результаты нужны в ответ. Наиболее простой метод - query(), который позволяет запускать уже выполненные SQL запросы:

$stmt = $conn->query('UPDATE articles SET published = 1 WHERE id = 2');

Cake\Database\Connection::execute($sql, $params, $types)

Метод query() не допускает применение дополнительных параметров. Если нужны дополнительные параметры, необходимо использовать метод execute(), который дает возможность использовать шаблон для подстановки:

$stmt = $conn->execute(
    'UPDATE articles SET published = ? WHERE id = ?',
    [1, 2]
);

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

$stmt = $conn->execute(
    'UPDATE articles SET published_date = ? WHERE id = ?',
    [new DateTime('now'), 2],
    ['date', 'integer']
);

Cake\Database\Connection::newQuery()

Это позволяет использовать расширенные типы данных в ваших приложениях и правильно конвертировать их в SQL выражения. Последний и наиболее гибкий способ создания запросов - использование Query Builder. Данный подход позволяет сконструировать сложные и выразительные запросы без использования платформенно-ориентированного SQL:

$query = $conn->newQuery();
$query->update('articles')
    ->set(['published' => true])
    ->where(['id' => 2]);
$stmt = $query->execute();

При использовании конструктора запросов, ни один SQL запрос не будет послан на сервер базы данных до вызова метода execute() или пока запрос не будет повторён. При итерации запроса, сначала он выполняется, а затем его результатирующий набор данных будет проитерирован:

$query = $conn->newQuery();
$query->select('*')
    ->from('articles')
    ->where(['published' => true]);

foreach ($query as $row) {
    // Сделать что-нибудь со строкой.
}

Примечание

Если есть экземпляр Cake\ORM\Query, можно использовать all() для получения результатирующего набора SELECT запросов.

Использование транзакций

Объекты соединения предоставляют несколько простых способов выполнения транзакций базы данных. Наиболее простой способ выполнения - используя методы begin(), commit() и rollback(), которые преобразуются в их SQL эквиваленты:

$conn->begin();
$conn->execute('UPDATE articles SET published = ? WHERE id = ?', [true, 2]);
$conn->execute('UPDATE articles SET published = ? WHERE id = ?', [false, 4]);
$conn->commit();

Cake\Database\Connection::transactional(callable $callback)

В дополнение к этим экземплярам интерфейсов соединений в том числе предоставляется метод transactional(), который делает обработку вызовов begin/commit/rollback еще проще:

$conn->transactional(function ($conn) {
    $conn->execute('UPDATE articles SET published = ? WHERE id = ?', [true, 2]);
    $conn->execute('UPDATE articles SET published = ? WHERE id = ?', [false, 4]);
});

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

Вызов begin.

Вызов замыкания.

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

Если замыкание возвращает false, будет вызван откат изменений.

Если замыкание выполняется успешно, транзакция будет завершена.

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

При использовании нижнего уровня API базы данных, вы будете часто сталкиваться с объектами выражения. Эти объекты позволяют манипулировать подготовленными выражениями из драйвера. После создания и выполнения объекта запроса или использования execute(), у вас появится экземпляр StatementDecorator. Он оборачивает основной объект выражения и предоставляет несколько дополнительных функций.

Подготовка выражения

Вы можете создать объект выражения, используя execute() или prepare(). Метод execute() возвращает выражение с привязанными к нему значениями. При использовании prepare() возвращается неполное выражение:

// Выражения execute уже имеют связанные значения.
$stmt = $conn->execute(
    'SELECT * FROM articles WHERE published = ?',
    [true]
);

// Выражения из метода prepare будут параметрами полей для подстановки.
// Необходимо привязать параметры до того как их использовать.
$stmt = $conn->prepare('SELECT * FROM articles WHERE published = ?');

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

Привязка значений

После того как подготовленное выражение создано, можно привязть дополнительные ланные. Можно привязать множество значений сразу, используя метод bind() или привязать отдельные элементы, используя bindValue:

$stmt = $conn->prepare(
    'SELECT * FROM articles WHERE published = ? AND created > ?'
);

// Привязка множества значений
$stmt->bind(
    [true, new DateTime('2013-01-01')],
    ['boolean', 'date']
);

// Привязка одного значения
$stmt->bindValue(1, true, 'boolean');
$stmt->bindValue(2, new DateTime('2013-01-01'), 'date');

При создании выражений, можно использовать именованные ключи массива вместо индексных:

$stmt = $conn->prepare(
    'SELECT * FROM articles WHERE published = :published AND created > :created'
);

// Привязка множества значений
$stmt->bind(
    ['published' => true, 'created' => new DateTime('2013-01-01')],
    ['published' => 'boolean', 'created' => 'date']
);

// Привязка одного значения
$stmt->bindValue('published', true, 'boolean');
$stmt->bindValue('created', new DateTime('2013-01-01'), 'date');

Предупреждение

Нельзя смешивать индексные и именованные ключи массива в одном и том же выражении.

Выполнение и получение строк

После подготовки выражения и привязки к нему данных, можно выполнить его и получить строки. Выражения должны быть выполнены с использованием метода execute(). После выполнения, результаты можно получить, используя fetch(), fetchAll() или проитерировать выражение:

$stmt->execute();

// Прочитать одну строку.
$row = $stmt->fetch('assoc');

// Прочитать все строки.
$rows = $stmt->fetchAll('assoc');

// Прочитать строки путем итерации.
foreach ($stmt as $row) {
    // Выполнить задачу
}

Примечание

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

Получение количества строк

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

$rowCount = count($stmt);
$rowCount = $stmt->rowCount();

Коды ошибок

Если запрос не был выполнен успешно, можно получить соответствующую информацию об ошибке, используя методы errorCode() и errorInfo(). Эти методы работают также, как те, что предоставляются PDO:

$code = $stmt->errorCode();
$info = $stmt->errorInfo();

Логирование запросов

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

// До 3.7.0 используйте logQueries()
// Включить логирование.
$conn->enableQueryLogging(true);

// Отключить логирование
$conn->enableQueryLogging(false);

Когда логирование запросов включено, запросы логируются в Cake\Log\Log, используя уровень „debug“ и область действия „queriesLog“. Необхоимо, чтобы логгер был настроен на этот уровень и область действия. Логирование с опцией stderr может быть полезно при работе с юнит тестами и логирование в files/syslog может быть полезно при работе с веб-запросами:

use Cake\Log\Log;

// Консольное логирование
Log::config('queries', [
    'className' => 'Console',
    'stream' => 'php://stderr',
    'scopes' => ['queriesLog']
]);

// Логирование в файле
Log::config('queries', [
    'className' => 'File',
    'path' => LOGS,
    'file' => 'queries.log',
    'scopes' => ['queriesLog']
]);

Примечание

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

Использование кавычек в идентификаторах

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

Снижение производительности - использование кавычек в идентификаторах намного медленнее и сложнее чем их неиспользование.

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

Для унаследованной схемы, в которой требуются кавычки в идентификаторах, можно включить настройку quoteIdentifiers в Настройка. Эта функция включается в том числе и при выполении:

$conn->getDriver()->enableAutoQuoting();

При включенном режиме, использование кавычек в идентификаторах

Когда использование кавычек в идентификаторах включено, вызывается дополнительный проход запроса, который ковертирует все идентификаторы в объекты IdentifierExpression.

Примечание

SQL фрагменты, содержащиеся в объектах QueryExpression не изменяются.

Кэширование метаданных

Функция объектно-реляционного отображения данных CakePHP использует отражение базы данных для определения схемы, индексов и внешних ключей, которые содержит приложение. Так как эти метаданные изменяются нечасто и требуют много ресурсов при доступе, они обычно кэшируются. По умолчанию, метаданные хранятся в конфигурации кэша _cake_model_. Можно определить пользовательскую конфигурацию кэша, используя опцию cacheMetatdata в конфигурации источника данных:

'Datasources' => [
    'default' => [
        // Other keys go here.

        // Use the 'orm_metadata' cache config for metadata.
        'cacheMetadata' => 'orm_metadata',
    ]
],

В том числе можно определить конфигурацию кэширования метаданных во время выполенния с помощью метода cacheMetadata():

// Отключить кэширование
$connection->cacheMetadata(false);

// Включить кэширование
$connection->cacheMetadata(true);

// Использование пользовательской конфигурации кэширования
$connection->cacheMetadata('orm_metadata');

CakePHP вклчюает инструмент CLI для управления кэшированием метаданных. Обратитесь к разделу /console-and-shells/schema-cache для получения дополнительной информации.

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

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

$dsn = 'mysql://root:password@localhost/';

Теперь можно использовать объект соединения для выполнения запросов, которые создают/изменяют базы данных. Например, для создания базы данных:

$connection->query("CREATE DATABASE IF NOT EXISTS my_database");

Примечание

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

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Query Builder

class Cake\ORM\Query

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Using SQL Functions

Adding Calculated Fields

Advanced Conditions

Returning the Total Count of Records

Caching Loaded Results

Filtering by Associated Data

Adding Joins

Updating Data

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Табличные объекты

class Cake\ORM\Table

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

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

Основное применение

Для начала создайте класс Table. Подобные классы располагаются в src/Model/Table. Классы Table это коллекция типовых моделей реляционной базы данных, представляющие основной интерфейс для взаимодействия с вашей базой данных в CakePHP ORM. Самый простой класс таблицы будет выглядеть следующим образом:

// src/Model/Table/ArticlesTable.php
namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
}

Обратите внимание, мы не сообщили ORM какую таблицу использует наш класс. При соблюдение соглашения о базе данных в этом нет необходимости. В приведённом выше примере будет использоваться таблица с именем articles. Если, например, имя таблицы будет blog_posts, то класс, отвечающий за взаимодействие с ней, будет именоваться BlogPostsTable. Вы можете указать другое имя таблицы, воспользовавшись методом setTable():

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{

    public function initialize(array $config)
    {
        $this->setTable('my_table');

        // До 3.4.0
        $this->table('my_table');
    }

}

При явном указание таблицы, ORM будет продолжать следовать соглашениям, ожидая первичного ключа с именем id. Если вам нужно изменить и это, можете воспользоваться методом setPrimaryKey():

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->setPrimaryKey('my_id');
        // До 3.4.0
        $this->primaryKey('my_id');
    }
}

Настройка класса Entity для использования таблиц

По умолчанию, табличные объекты используют класс Entity, основываясь на соглашение об именование моделей. Например, если ваш класс таблицы называется ArticlesTable, то класс Entity должен называться Article. Если класс таблицы с именем PurchaseOrdersTable, то Entity класс PurchaseOrder. Если, однако, вы хотите использовать имя класса Entity идущее вразрез с соглашением, вы можете воспользоваться методом setEntityClass() для смены имени:

class PurchaseOrdersTable extends Table
{
    public function initialize(array $config)
    {
        $this->setEntityClass('App\Model\Entity\PO');

        // До 3.4.0
        $this->entityClass('App\Model\Entity\PO');
    }
}

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

Получение экземпляров класса таблицы

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

// В контроллере и методе.
use Cake\ORM\TableRegistry;

// Prior to 3.6 use TableRegistry::get('Articles')
$articles = TableRegistry::getTableLocator()->get('Articles');

Класс TableRegistry предоставляет различные зависимости для построения таблицы и ведёт реестр всех созданных экземпляров таблиц, что упрощает построение отношений и настройку ORM. Смотрите раздел Использование TableRegistry, для получения дополнительной информации.

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

// Таблица плагина
// Prior to 3.6 use TableRegistry::get('PluginName.Articles')
$articlesTable = TableRegistry::getTableLocator()->get('PluginName.Articles');

// Таблица плагина с префиксом vendor'а
// Prior to 3.6 use TableRegistry::get('VendorName/PluginName.Articles')
$articlesTable = TableRegistry::getTableLocator()->get('VendorName/PluginName.Articles');

Обратные вызовы жизненного цикла

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

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

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

Список событий

Model.initialize

Model.beforeMarshal

Model.beforeFind

Model.buildValidator

Model.buildRules

Model.beforeRules

Model.afterRules

Model.beforeSave

Model.afterSave

Model.afterSaveCommit

Model.beforeDelete

Model.afterDelete

Model.afterDeleteCommit

initialize

Cake\ORM\Table::initialize(Event $event, ArrayObject $data, ArrayObject $options)

Событие Model.initialize запускается после вызова методов конструктора и инициализации. Классы Table, по умолчанию, не слушают это событие, используя вместо этого метод initialize.

Для прослушивания и ответа на событие Model.initialize, вы можете создать класс слушателя, реализующий интерфейс EventListenerInterface:

use Cake\Event\EventListenerInterface;
class ModelInitializeListener implements EventListenerInterface
{
    public function implementedEvents()
    {
        return array(
            'Model.initialize' => 'initializeEvent',
        );
    }
    public function initializeEvent($event)
    {
        $table = $event->getSubject();
        // сделаем что-нибудь здесь
    }
}

и присоединить слушателя к глобальному диспетчеру EventManager:

use Cake\Event\EventManager;
$listener = new ModelInitializeListener();
EventManager::instance()->attach($listener);

Это вызовет вызов initializeEvent любого класса Table.

beforeMarshal

Cake\ORM\Table::beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options)

Событие Model.beforeMarshal запускается до преобразования данных запроса в объекты. Дополнительная информация, об модификации данных запроса перед преобразованием в объекты, доступна в документации.

beforeFind

Cake\ORM\Table::beforeFind(Event $event, Query $query, ArrayObject $options, $primary)

Событие Model.beforeFind запускается перед каждой операцией поиска. Остановив событие и предоставив возвращаемое значение, вы можете полностью обойти операцию поиска. Любые изменения, сделанные для экземпляра $query, будут сохранены для остальной части поиска. Параметр $primary служит признаком корневого запроса, либо связанного с ним запроса. Все ассоциации, участвующие в запросе, будут запускать событие Model.beforeFind. Для ассоциаций, которые используют объединения, будет предоставлен mock-запрос. В вашем слушателе событий вы можете установить дополнительные поля, условия, или объединения. Эти параметры/функции будут скопированы в корневой запрос.

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

В предыдущих версиях CakePHP присутствовало событие afterFind, заменённое с помощью изменения результатов с помощью Map/Reduce и конструкторов объекта.

buildValidator

Cake\ORM\Table::buildValidator(Event $event, Validator $validator, $name)

Событие Model.buildValidator срабатывает после вызова именованных правил проверки $name, например, validationDefault. Можно использовать эту функцию для добавления методов проверки.

buildRules

Cake\ORM\Table::buildRules(Event $event, RulesChecker $rules)

Событие Model.buildRules срабатывает после создания экземпляра правил проверки и после вызова табличного метода buildRules().

beforeRules

Cake\ORM\Table::beforeRules(Event $event, EntityInterface $entity, ArrayObject $options, $operation)

Событие Model.beforeRules вызывается до применения правил проверки. Остановив событие, вы можете завершить проверку и вернуть результат применения правил.

afterRules

Cake\ORM\Table::afterRules(Event $event, EntityInterface $entity, ArrayObject $options, $result, $operation)

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

beforeSave

Cake\ORM\Table::beforeSave(Event $event, EntityInterface $entity, ArrayObject $options)

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

afterSave

Cake\ORM\Table::afterSave(Event $event, EntityInterface $entity, ArrayObject $options)

Событие Model.afterSave запускается после сохранения объекта.

afterSaveCommit

Cake\ORM\Table::afterSaveCommit(Event $event, EntityInterface $entity, ArrayObject $options)

Событие Model.afterSaveCommit запускается после транзакции, в которой прошла операция сохранения. Оно также срабатывает для неатомарных сохранений, где операции с базой данных неявно совершены. Событие запускается только для первичной таблицы, для которой непосредственно вызывается метод save(). Событие не запускается, если транзакция началась до вызова сохранения.

beforeDelete

Cake\ORM\Table::beforeDelete(Event $event, EntityInterface $entity, ArrayObject $options)

Событие запускается перед удалением объекта. Остановка этого события прервёт операцию удаления. Когда событие остановлено, возвратится её результат выполнения. Остановка событий описано в соответствующем разделе книги.

afterDelete

Cake\ORM\Table::afterDelete(Event $event, EntityInterface $entity, ArrayObject $options)

Событие Model.afterDelete запускается после удаления объекта.

afterDeleteCommit

Cake\ORM\Table::afterDeleteCommit(Event $event, EntityInterface $entity, ArrayObject $options)

Событие Model.afterDeleteCommit запускается после транзакции, в которой прошла операция удаления. Оно также срабатывает для неатомарных удаления, где операции с базой данных неявно совершены. Событие запускается только для первичной таблицы, для которой непосредственно вызывается метод delete(). Событие не запускается, если транзакция началась до вызова удаления.

Поведения

Cake\ORM\Table::addBehavior($name, array $options = [])

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

Для добавления поведения в таблицу, необходимо вызвать метод addBehavior(). Как правило, наилучшим местом для этого является метод initialize():

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Timestamp');
    }
}

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

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Timestamp', [
            'events' => [
                'Model.beforeSave' => [
                    'created_at' => 'new',
                    'modified_at' => 'always'
                ]
            ]
        ]);
    }
}

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

Настройка соединений

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

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public static function defaultConnectionName() {
        return 'replica_db';
    }
}

Примечание

Метод defaultConnectionName() должен быть статическим.

Использование TableRegistry

class Cake\ORM\TableRegistry

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

Настройка объектов таблицы

static Cake\ORM\TableRegistry::get($alias, $config)

При загрузке таблиц из реестра, вы можете изменить их зависимости или использовать mock-объекты, предоставляя массив $options:

$articles = TableRegistry::getTableLocator()->get('Articles', [
    'className' => 'App\Custom\ArticlesTable',
    'table' => 'my_articles',
    'connection' => $connectionObject,
    'schema' => $schemaObject,
    'entityClass' => 'Custom\EntityClass',
    'eventManager' => $eventManager,
    'behaviors' => $behaviorRegistry
]);

Обратите внимание на параметры конфигурации connection и schema. Они принимают не строковые значения, а объекты. Соединение будет принимать объект Cake\Database\Connection и схему Cake\Database\Schema\Collection.

Примечание

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

Вы также можете предварительно настроить реестр с помощью метода config(). Данные конфигурации сохраняются для каждого псевдонима и могут быть переопределены методом объекта initialize():

TableRegistry::config('Users', ['table' => 'my_users']);

Примечание

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

Примечание

Статический API CakeORMTableRegistry устарел в CakePHP 3.6.0. Вместо этого используйте локатор таблицы.

Очистка реестра

static Cake\ORM\TableRegistry::clear

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

TableRegistry::clear();

Настройка пространства имён для расположения ORM классов

Если вы не соблюдали соглашения, вероятней всего CakePHP не сможет найти ваши классы Table и Entity. Чтобы исправить это, вы можете установить пространство имён с помощью метода Cake\Core\Configure::write. Например:

/src
    /App
        /My
            /Namespace
                /Model
                    /Entity
                    /Table

Для схемы выше будет следующая настройка:

Cake\Core\Configure::write('App.namespace', 'App\My\Namespace');

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Entities

class Cake\ORM\Entity

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Creating Virtual Properties

Mass Assignment

Lazy Loading Associations

Exposing Virtual Properties

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Retrieving Data & Results Sets

class Cake\ORM\Table

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Getting the First Result

Finding Key/Value Pairs

Custom Finder Methods

Dynamic Finders

Eager Loading Associations

Filtering by Associated Data

Changing Fetching Strategies

Loading Additional Associations

Modifying Results with Map/Reduce

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Validating Data

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Using A Different Validation Set For Associations

Applying Application Rules

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Saving Data

class Cake\ORM\Table

Converting Request Data into Entities

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Deleting Data

class Cake\ORM\Table

Cake\ORM\Table::delete(Entity $entity, $options = [])

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Associations - Linking Tables Together

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

HasOne Associations

BelongsTo Associations

HasMany Associations

BelongsToMany Associations

Using the „through“ Option

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Behaviors

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Using Behaviors

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

Для добавления поведения в таблицу, необходимо вызвать метод addBehavior(). Как правило, наилучшим местом для этого является метод initialize():

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Timestamp');
    }
}

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

namespace App\Model\Table;

use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Timestamp', [
            'events' => [
                'Model.beforeSave' => [
                    'created_at' => 'new',
                    'modified_at' => 'always'
                ]
            ]
        ]);
    }
}

Core Behaviors

CounterCache

Timestamp

Translate

Дерево

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

CounterCache

class Cake\ORM\Behavior\CounterCacheBehavior

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Примечание

The CounterCache behavior works for belongsTo associations only. For example for «Comments belongsTo Articles», you need to add the CounterCache behavior to the CommentsTable in order to generate comment_count for Articles table.

It is possible though to make this work for belongsToMany associations. You need to enable the CounterCache behavior in a custom through table configured in association options. See how to configure a custom join table Using the „through“ Option.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Timestamp

class Cake\ORM\Behavior\TimestampBehavior

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Translate

class Cake\ORM\Behavior\TranslateBehavior

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Дерево

class Cake\ORM\Behavior\TreeBehavior

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

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

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

Требования

Это поведение требует наличия следующих столбцов в таблице БД:

parent_id (nullable) Столбец, содержащий идентификатор родительской строки

lft (integer, signed) Столбец, используется для поддержания древовидной структуры

rght (integer, signed) Столбец, используется для поддержания древовидной структуры

Вы можете изменить имена этих полей, если это вам нужно.

Более подробную информацию о значении полей и их использовании можно найти в этой статье, описывающей логику MPTT [http://www.sitepoint.com/hierarchical-data-database-2/]

Предупреждение

TreeBehavior, пока что, не поддерживает составные первичные ключи.

Быстрый тур

Активируется TreeBehavior, добавлением его в таблицу, в которой вы хотите сохранить иерархические данных, следующим образом:

class CategoriesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Tree');
    }
}

После добавления, вы уже можете позволить CakePHP построить внутреннюю структуру, если в вашей таблице уже есть несколько строк:

// Prior to 3.6 use TableRegistry::get('Categories')
$categories = TableRegistry::getTableLocator()->get('Categories');
$categories->recover();

Вы можете проверить, работает ли TreeBehavior, например, выбрав из таблицы любую строку и запросив количество её потомков:

$node = $categories->get(1);
echo $categories->childCount($node);

Получение „плоского“ списка потомков для узла так же просто:

$descendants = $categories->find('children', ['for' => 1]);

foreach ($descendants as $category) {
    echo $category->name . "\n";
}

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

$descendants = $categories
    ->find('children', ['for' => 1])
    ->where(['name LIKE' => '%Foo%']);

foreach ($descendants as $category) {
    echo $category->name . "\n";
}

Если вам нужен „потоковый“ список, где дети для каждого узла вложены в иерархии, то вы можете использовать „threaded“ («резьбовой») поиск:

$children = $categories
    ->find('children', ['for' => 1])
    ->find('threaded')
    ->toArray();

foreach ($children as $child) {
    echo "{$child->name} has " . count($child->children) . " direct children";
}

Для прохождения по результам возвращённым с использованием „threaded“ поиска, обычно требуются рекурсивные функции, но если вам требуется только набор результатов, содержащий одно поле с каждого уровня, чтобы вы могли отобразить список, например, в HTML-элементе, лучше использовать поиск „treeList“ („Список деревьев“):

$list = $categories->find('treeList');

// В файле шаблона CakePHP:
echo $this->Form->control('categories', ['options' => $list]);

// Или вы можете выводить его в виде обычного текста, например, в скрипте CLI
foreach ($list as $categoryName) {
    echo $categoryName . "\n";
}

Вывод будет аналогичен:

My Categories
_Fun
__Sport
___Surfing
___Skating
_Trips
__National
__International

Инструмент treeList принимает несколько параметров:

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

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

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

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

$query = $categories->find('treeList', [
    'keyPath' => 'url',
    'valuePath' => 'id',
    'spacer' => ' '
]);

Одна очень общая задача - найти путь дерева от определенного узла к корню дерева. Это полезно, например, для добавления списка „breadcrumbs“ („Хлебных крошек“) для показа структуры меню:

$nodeId = 5;
$crumbs = $categories->find('path', ['for' => $nodeId]);

foreach ($crumbs as $crumb) {
    echo $crumb->name . ' > ';
}

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

$node = $categories->get(5);

// Переместим узел, чтобы он отображался на одну позицию вверх, при отображении дочерних элементов.
$categories->moveUp($node);

// Переместим узел в верхнюю часть списка, текущего уровня.
$categories->moveUp($node, true);

// Переместим узел в самый низ.
$categories->moveDown($node, true);

Конфигурация

Если имена столбцов, по умолчанию, используемые TreeBehavior, не соответствуют вашим собственным схемам, вы можете указать им псевдонимы:

public function initialize(array $config)
{
    $this->addBehavior('Tree', [
        'parent' => 'ancestor_id', // Используйте это вместо parent_id
        'left' => 'tree_left', // Используйте это вместо lft
        'right' => 'tree_right' // Используйте это вместо rght
    ]);
}

Уровень узла (Глубина)

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

$this->addBehavior('Tree', [
    'level' => 'level', // По умолчанию значение null, то есть без сохранения уровня
]);

Если вы не хотите кэшировать уровень с помощью поля db, вы можете использовать TreeBehavior::getLevel() - метод для получения уровня узла.

Область видимости и несколько деревьев

Иногда у вас есть необходимость сохранить более одной древовидной структуры внутри одной и той же таблицы, и вы можете это сделать, используя конфигурацию „scope“. Например, для таблицы „locations“ (местоположений), вы можете создать по одному дереву на страну:

class LocationsTable extends Table
{

    public function initialize(array $config)
    {
        $this->addBehavior('Tree', [
            'scope' => ['country_name' => 'Brazil']
        ]);
    }

}

В предыдущем примере, все операции дерева будут привязаны только к строкам столбца country_name и привязаны к „Brazil“. Но вы можете изменить область охвата на лету, используя функцию „config“:

$this->behaviors()->Tree->config('scope', ['country_name' => 'France']);

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

$this->behaviors()->Tree->config('scope', function ($query) {
    $country = $this->getConfigureContry(); // Выделенная функция
    return $query->where(['country_name' => $country]);
});

Восстановление с помощью настраиваемого поля сортировки

По умолчанию, функция restore() сортирует элементы с использованием первичного ключа. Это отлично работает если это числовой (auto increment) столбец, но может привести к странным результатам, если вы использовали UUID.

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

$this->addBehavior('Tree', [
    'recoverOrder' => ['country_name' => 'DESC'],
]);

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

При использовании поведения Tree вам обычно не нужно беспокоиться о внутреннем представление иерархической структуры. Позиции, где узлы помещаются в дерево, выводятся из столбца „parent_id“ в каждом из ваших объектов:

$aCategory = $categoriesTable->get(10);
$aCategory->parent_id = 5;
$categoriesTable->save($aCategory);

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

Вы можете сделать узел корнем дерева, установив столбец „parent_id“ в ноль:

$aCategory = $categoriesTable->get(10);
$aCategory->parent_id = null;
$categoriesTable->save($aCategory);

Дети, для нового корневого узла, будут сохранены.

Удаление узлов

Удаление узла и всего его поддерева (любые дочерние элементы, которые он может иметь на любой глубине в нутри дерева) тривиально:

$aCategory = $categoriesTable->get(10);
$categoriesTable->delete($aCategory);

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

$aCategory = $categoriesTable->get(10);
$categoriesTable->removeFromTree($aCategory);
$categoriesTable->delete($aCategory);

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

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

$descendants = $teams->find('children', ['for' => 1]);

foreach ($descendants as $descendant) {
    $team = $teams->get($descendant->id); // поиск обновленной сущности объекта
    if ($team->expired) {
        $teams->delete($team); // удаление изменяет порядок слева и справа от записей в базе данных
    }
}

TreeBehavior переупорядочивает значения lft и rght записей в таблице, когда узел удаляется. Таким образом, значения lft и rght сущностей внутри $descendants (сохраненный до операции удаления) будут неточными. Объекты должны быть загружены и изменены «на лету», чтобы предотвратить несоответствия в таблице.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Schema System

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

ORM Cache Shell

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Консоль Bake

эта страница переехала [https://book.cakephp.org/bake/1.x/ru/].

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Кэширование

class Cake\Cache\Cache

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

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

Встроенные механизмы кеширования:

FileCache (Файловый кеш) - это простой кэш, который использует локальные файлы. Он является самым медленным механизмом кэширования и не предоставляет функций для атомарных операций. Однако, поскольку дисковое хранилище часто довольно дешёвое, хранение больших объектов или элементов, которые нередко записываются в файлы, хорошо работает.

ApcCache - пользовательский кеш APC - использует расширение PHP APCu [http://php.net/apcu]. Это расширение использует общую память на веб-сервере для хранения объектов. Это делает его очень быстрым и способным обеспечивать атомарные функции чтения/записи.

Wincache - использует расширение Wincache [http://php.net/wincache]. Wincache похож на APC по функциям и производительности, но оптимизирован для Windows и IIS.

XcacheEngine - Xcache [http://xcache.lighttpd.net/] - является расширением PHP, которое предоставляет функции аналогичные APC.

MemcachedEngine - использует расширение - Memcached [http://php.net/memcached].

RedisEngine (Redis) - использует расширение - phpredis [https://github.com/nicolasff/phpredis]. Redis обеспечивает быструю и постоянную систему кэширования, похожую на Memcached и также обеспечивает атомарные операции.

Независимо от используемого вами CacheEngine, ваше приложение взаимодействует с Cake\Cache\Cache согласованным образом. Вы можете менять ваш CacheEngine, по мере того, как растёт ваше приложение.

Настройка класса кэша

static Cake\Cache\Cache::config($key, $config = null)

Настройка класса Cache может выполняться в любом месте, но в целом вы захотите настроить кеш во время начальной загрузки. Файл config/app.php сделан именно для таких нужд. Вы можете настроить столько кешей, сколько вам нужно, и использовать любую смесь движков кеша. CakePHP, внутри себя, использует две конфигурации кэша. _cake_core_ - используется для хранения файла карты и проанализированных результатов в файле Internationalization & Localization. _cake_model_ - используется для хранения описаний схем для моделей ваших приложений. Если вы используете APC или Memcached, вы должны установить уникальные ключи для основных кешей. Это предотвратит перезапись данных кеширования нескольких приложений друг-другом.

Использование нескольких конфигураций, также позволяет вам постепенно изменять хранилище как вам это нужно. Например, в вашем config/app.php вы можете указать следующее:

// ...
'Cache' => [
    'short' => [
        'className' => 'File',
        'duration' => '+1 hours',
        'path' => CACHE,
        'prefix' => 'cake_short_'
    ],
    // Использование пространства имён в качестве имени
    'long' => [
        'className' => 'Cake\Cache\Engine\FileEngine',
        'duration' => '+1 week',
        'probability' => 100,
        'path' => CACHE . 'long' . DS,
    ]
]
// ...

Параметры конфигурации также могут предоставляться как строка DSN. Это полезно при работе с переменными окружения или провайдерами PaaS:

Cache::config('short', [
    'url' => 'memcached://user:password@cache-host/?timeout=3600&prefix=myapp_',
]);

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

Вы также можете настроить механизмы кэширования, во время выполнения:

// Использование короткого имени
Cache::config('short', [
    'className' => 'File',
    'duration' => '+1 hours',
    'path' => CACHE,
    'prefix' => 'cake_short_'
]);

// Использование пространства имён в качестве имени, т.е. использование длинного имени
Cache::config('long', [
    'className' => 'Cake\Cache\Engine\FileEngine',
    'duration' => '+1 week',
    'probability' => 100,
    'path' => CACHE . 'long' . DS,
]);

// Использование построенного объекта.
$object = new FileEngine($config);
Cache::config('other', $object);

Название этих конфигураций „short“ или „long“ используется в $onfig как параметр для метода записи Cake\Cache\Cache::write() и чтения Cake\Cache\Cache::read(). При настройке CacheEngine вы можете обратится к имени класса, используя следующие варианты синтаксисов:

Короткое имя класса без „Engine“ или пространства имен. Это будет означать, что вы хотите использовать движок кэша Cake\Cache\Engine или App\Cache\Engine.

Использование plugin syntax, который позволяет загружать движки из определенного плагина.

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

Использование объекта, который расширяет класс CacheEngine.

Примечание

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

Настройка откатов кэша

В случае, если движок недоступен, например, FileEngine пытается сделать запись в недоступную для записи папку, или RedisEngine, не может подключиться к Redis, движок NullEngine вернётся к noop („No OPeration“) и вызовет логическую ошибки. Это не позволит приложению отбросить неперехваченное исключение из-за кэша.

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

Cache::config('redis', [
    'className' => 'Redis',
    'duration' => '+1 hours',
    'prefix' => 'cake_redis_',
    'host' => '127.0.0.1',
    'port' => 6379,
    'fallback' => 'default',
]);

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

Добавлено в версии 3.5.0: Добавлены резервные копии кэша.

Удаление сконфигурированных кеш-двигателей

static Cake\Cache\Cache::drop($key)

После создания конфигурации вы не сможете её изменить. Вместо этого вам следует удалить конфигурацию и воссоздать её, используя удаление - Cake\Cache\Cache::drop() и новое конфигурирование - Cake\Cache\Cache::config(). Удаление механизма кеша удалит config и уничтожит адаптер, если он был сконструирован.

Запись в кэш

static Cake\Cache\Cache::write($key, $value, $config = 'default')

Cache::write() будет писать $value для кэша. Вы можете прочесть или удалить это значение позже, обратившись к нему с помощью $key. Вы можете указать дополнительную конфигурацию для хранения кэша. Если нет $config, используется значение по умолчанию. Cache::write() может хранить любой тип объекта и идеально подходит для хранения истории результатов поиска моделей:

if (($posts = Cache::read('posts')) === false) {
    $posts = $someService->getAllPosts();
    Cache::write('posts', $posts);
}

Методы Cache::write() и Cache::read() могут использоваться для уменьшения числа запросов, сделанных в базу данных.

Примечание

Если вы планируете кэшировать результат запросов, созданных с помощью CakePHP ORM, лучше использовать встроенные возможности кеша объекта Query как описано в разделе Caching Loaded Results

Написание нескольких ключей одновременно

static Cake\Cache\Cache::writeMany($data, $config = 'default')

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

$result = Cache::writeMany([
    'article-' . $slug => $article,
    'article-' . $slug . '-comments' => $comments
]);

// Будет содержать $result
['article-first-post' => true, 'article-first-post-comments' => true]

Чтение через кеширование

static Cake\Cache\Cache::remember($key, $callable, $config = 'default')

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

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

class IssueService
{
    public function allIssues($repo)
    {
        return Cache::remember($repo . '-issues', function () use ($repo) {
            return $this->fetchAll($repo);
        });
    }
}

Чтение из кеша

static Cake\Cache\Cache::read($key, $config = 'default')

Cache::read() используется для чтения закэшированного значения, хранящегося в $key из $config. Если $config равно null, то будет использоваться значение по умолчанию. Cache::read() вернёт кешированное значение если это действительный кеш или false, если срок жизни кеша истёк или запрошенный файл кэша не существует. Содержимое кеша может быть false, поэтому убедитесь, что вы используете строгие операторы сравнения: === или !==.

Например:

$cloud = Cache::read('cloud');

if ($cloud !== false) {
    return $cloud;
}

// Создание данных облака
// ...

// Сохраним данные в кеше
Cache::write('cloud', $cloud);
return $cloud;

Одновременное чтение нескольких ключей

static Cake\Cache\Cache::readMany($keys, $config = 'default')

После того, как вы сразу записали несколько ключей, вы, вероятно, захотите их прочитать. Хотя вы и можете использовать несколько вызовов read(), но всётаки readMany() позволяет CakePHP использовать более эффективные API хранения, если они доступны. Например, используя readMany() мы можем сохранить несколько сетевых подключений, при использовании Memcached:

$result = Cache::readMany([
    'article-' . $slug,
    'article-' . $slug . '-comments'
]);
// Будет содержать $result
['article-first-post' => '...', 'article-first-post-comments' => '...']

Удаление из кэша

static Cake\Cache\Cache::delete($key, $config = 'default')

Cache::delete() позволит вам полностью удалить закешированный объект из истории:

// Удаление ключа
Cache::delete('my_key');

Одновременное удаление нескольких ключей

static Cake\Cache\Cache::deleteMany($keys, $config = 'default')

После того, как вы написали сразу несколько ключей, вы можете удалить их. В то время как вы можете использовать несколько вызовов delete(), deleteMany() - позволяет CakePHP использовать более эффективный API хранения данных, если он доступен. Например, используем deleteMany() для удаления некольких сохранённых сетевых подключений, при использовании Memcached:

$result = Cache::deleteMany([
    'article-' . $slug,
    'article-' . $slug . '-comments'
]);
// Будет содержать $result
['article-first-post' => true, 'article-first-post-comments' => true]

Очистка закэшированных данных

static Cake\Cache\Cache::clear($check, $config = 'default')

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

// Очищаем только истёкшие ключи
Cache::clear(true);

// Очищаем все ключи
Cache::clear(false);

static Cake\Cache\Cache::gc($config)

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

Примечание

Поскольку APC и Wincache используют изолированные кеши для веб-сервера и CLI, они должны быть очищены отдельно (CLI не может почистить веб-сервер и наоборот).

Использование кеша для хранения счетчиков

static Cake\Cache\Cache::increment($key, $offset = 1, $config = 'default')

static Cake\Cache\Cache::decrement($key, $offset = 1, $config = 'default')

Счетчики в вашем приложении являются хорошими кандидатами для хранения в кеше. Так, например, может быть сохранен в кеше простой обратный отсчёт для оставшихся „слотов“ в конкурсе. Класс Cache предоставляет атомарные действия для увеличения/уменьшения(инкремента/дикремента) счетчика ценностей, в простой форме. Атомарные операции важны для этих значений, поскольку они снижают риск конкуренции и не позволят двум пользователям одновременно опустить значение на единицу, что могло бы привести к неправильному результату.

После установки целочисленного значения вы можете манипулировать ими с помощью методов increment() и decrement():

Cache::write('initial_count', 10);

// Later on
Cache::decrement('initial_count');

// Or
Cache::increment('initial_count');

Примечание

Инкремент и дикремент не работают с FileEngine. Вы должны использовать APC, Wincache, Redis или Memcached.

Использование кеша для хранения общих результатов запроса

Вы можете значительно повысить производительность своего приложения, добавив в кеш результаты которые редко меняются или являются ресурсоёмкими для чтения(получения). Прекрасным примером этого являются результаты Cake\ORM\Table::find(). Объект Query позволяет кэшировать результаты с использованием метода cache(). Чтобы получить больше информации см. раздел Caching Loaded Results.

Использование групп

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

Cache::config('site_home', [
    'className' => 'Redis',
    'duration' => '+999 days',
    'groups' => ['comment', 'article']
]);

Cake\Cache\Cache::clearGroup($group, $config = 'default')

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

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

// src/Model/Table/ArticlesTable.php
public function afterSave($event, $entity, $options = [])
{
    if ($entity->isNew()) {
        Cache::clearGroup('article', 'site_home');
    }
}

static Cake\Cache\Cache::groupConfigs($group = null)

groupConfigs() можно использовать для получения сопоставления между группой и её конфигурацией:

// src/Model/Table/ArticlesTable.php

// Вариант предыдущего примера, который очищает все конфигурации кэша из той же группы
public function afterSave($event, $entity, $options = [])
{
    if ($entity->isNew()) {
        $configs = Cache::groupConfigs('article');
        foreach ($configs['article'] as $config) {
            Cache::clearGroup('article', $config);
        }
    }
}

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

Глобальное включение или отключение кеша

static Cake\Cache\Cache::disable

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

// Отключаем все операции чтения кеша и записи в кэш
Cache::disable();

После отключения, все попытки чтения и записи в кеш будут возвращать null.

static Cake\Cache\Cache::enable

После отключения вы можете использовать enable() для повторного включения кеширования:

// Снова включаем все операции чтения кеша и записи в кэш
Cache::enable();

static Cake\Cache\Cache::enabled

Чтобы проверить состояние кеширования (включено оно или нет), вы можете использовать enabled().

Создание механизма хранения для кеша

Вы можете предоставить настраиваемые Cache адаптеры в App\Cache\Engine как плагины, с использованием $plugin\Cache\Engine. Механизмы кэширования src/plugin могут также переопределять основные двигатели. Кэширующие адаптеры должны находится в каталоге кеша. Если у вас есть механизм кэширования с именем MyCustomCacheEngine он должен быть помещен либо в src/Cache/Engine/MyCustomCacheEngine.php как app/libs, либо в plugins/MyPlugin/src/Cache/Engine/MyCustomCacheEngine.php - как частный плагин.

Конфигурации кэша из плагинов должны использовать синтаксис плагина с точкой:

Cache::config('custom', [
    'className' => 'MyPlugin.MyCustomCache',
    // ...
]);

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

Необходимым API для CacheEngine является:

class Cake\Cache\CacheEngine

Базовый класс движка, использующего кеш.

Cake\Cache\CacheEngine::write($key, $value, $config = 'default')

Результат

boolean for success.

Записывает значение для ключа в кеш. Необязательный строковый параметр $config указывает имя конфигурации для записи.

Cake\Cache\CacheEngine::read($key)

Результат

The cached value or false for failure.

Читает из кеша. Вернёт false если запись в кеше устарела или не существует.

Cake\Cache\CacheEngine::delete($key)

Результат

Boolean true on success.

Удаляет ключ из кеша. Вернёт false если запись в кеше устарела или не может быть удалена.

Cake\Cache\CacheEngine::clear($check)

Результат

Boolean true on success.

Удаляет все ключи из кеша. Если $check установлено в true, то вы должны

подтвердите, что каждое значение действительно истекло.

Cake\Cache\CacheEngine::clearGroup($group)

Результат

Boolean true on success.

Удаляет все ключи из кеша, относящиеся к одной группе.

Cake\Cache\CacheEngine::decrement($key, $offset = 1)

Результат

Boolean true on success.

Уменьшает числовое значение ключа и возвращает уменьшенное значение.

Cake\Cache\CacheEngine::increment($key, $offset = 1)

Результат

Boolean true on success.

Увеличивает числовое значение ключа и возвращает увеличинное значение.

Cake\Cache\CacheEngine::gc()

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

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Shells, Tasks & Console Tools

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

More Topics

Shell Helpers

Interactive Console (REPL)

Running Shells as Cron Jobs

I18N Shell

Completion Shell

Plugin Shell

Routes Shell

Upgrade Shell

Server Shell

Cache Shell

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Shell Helpers

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Interactive Console (REPL)

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Running Shells as Cron Jobs

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

I18N Shell

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Completion Shell

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Plugin Shell

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Routes Shell

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Upgrade Shell

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Server Shell

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Cache Shell

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Добавлено в версии 3.3.0: The cache shell was added in 3.3.0

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Debugging

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Deployment

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Email

Предупреждение

До версии 3.1 классы Email и Transport находились в пространстве имён Cake\Network\Email вместо пространства Cake\Mailer.

class Cake\Mailer\Email(mixed $profile = null)

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

Основное использование

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

use Cake\Mailer\Email;

После того, как вы загрузили класс Email, вы можете сразу отправить электронное письмо следующим образом:

$email = new Email('default');
$email->from(['me@example.com' => 'My Site'])
    ->to('you@example.com')
    ->subject('About')
    ->send('My message');

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

Email имеет несколько методов для определения получателей - to(), cc(), bcc(), addTo(), addCc() и addBcc(). Основное отличие состоит в том, что первые три будут перезаписывать то, что уже было установлено, а последние три просто добавят больше получателей в соответствующее поле:

$email = new Email();
$email->to('to@example.com', 'To Example');
$email->addTo('to2@example.com', 'To2 Example');
// Получатели электронной почты: to@example.com and to2@example.com
$email->to('test@example.com', 'ToTest Example');
// Получатель электронной почты: test@example.com

Не рекомендуется, начиная с версии 3.4.0: Вместо этого используйте setFrom(), setTo(), setCc() , setBcc() и setSubject() соответственно.

Выбор отправителя

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

$email = new Email();
$email->sender('app@example.com', 'MyApp emailer');

Примечание

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

Не рекомендуется, начиная с версии 3.4.0: Используйте setSender().

Конфигурация

Конфигурация для Email по умолчанию создаётся с помощью config() и configTransport(). Вы должны поместить свои настройки электронной почты в файл config/app.php. Файл config/app.default.php является примером такого файла. Определять конфигурацию электронной почты в config/app.php не обязательно. Email можно использовать без config() и использовать соответствующие методы для установки всех конфигураций отдельно или загрузки массива конфигураций.

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

Чтобы загрузить предопределённую конфигурацию, вы можете использовать метод profile() или передать его конструктору Email:

$email = new Email();
$email->profile('default');

// Или в конструкторе
$email = new Email('default');

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

$email = new Email();
$email->profile(['from' => 'me@example.org', 'transport' => 'my_custom']);

// Или в конструкторе
$email = new Email(['from' => 'me@example.org', 'transport' => 'my_custom']);

Изменено в версии 3.1: Профиль электронной почты default автоматически устанавливается при создании экземпляра Email.

Не рекомендуется, начиная с версии 3.4.0: Используйте setProfile() вместо profile().

Настройка транспорта

static Cake\Mailer\Email::configTransport($key, $config)

Сообщения электронной почты доставляются транспортом. Различные транспорты позволяют отправлять сообщения через PHP функцию mail(), SMTP-серверы или вообще не использовать, что полезно для отладки. Настройка транспортов позволяет сохранять данные конфигурации из кода приложения и упрощает развёртывание, поскольку вы можете просто изменить данные конфигурации. Пример транспортной конфигурации выглядит так:

use Cake\Mailer\Email;

// Пример настройки почты
Email::configTransport('default', [
    'className' => 'Mail'
]);

// Пример настройки SMTP
Email::configTransport('gmail', [
    'host' => 'ssl://smtp.gmail.com',
    'port' => 465,
    'username' => 'my@gmail.com',
    'password' => 'secret',
    'className' => 'Smtp'
]);

Вы можете настроить SSL SMTP-серверы, например Gmail. Для этого поставьте префикс ssl:// в хост и соответствующим образом настройте значение порта. Вы также можете включить SMTP TLS с помощью опции tls:

use Cake\Mailer\Email;

Email::configTransport('gmail', [
    'host' => 'smtp.gmail.com',
    'port' => 587,
    'username' => 'my@gmail.com',
    'password' => 'secret',
    'className' => 'Smtp',
    'tls' => true
]);

Вышеприведённая конфигурация позволит использовать TLS для сообщений электронной почты.

Предупреждение

Для этого вам потребуется разрешить доступ для менее безопасных приложений, к вашему аккаунту Google.: Как разрешить ненадежным приложениям доступ к аккаунту [https://support.google.com/accounts/answer/6010255].

Примечание

SMTP настройки для Gmail [https://support.google.com/a/answer/176600?hl=ru].

Примечание

Чтобы использовать SSL + SMTP, вам необходимо настроить SSL в вашей PHP-установке.

Параметры конфигурации также могут предоставляться как строка DSN. Это полезно при работе с переменными окружения или провайдерами PaaS:

Email::configTransport('default', [
    'url' => 'smtp://my@gmail.com:secret@smtp.gmail.com:587?tls=true',
]);

При использовании строки DSN, вы можете определить любые дополнительные параметры/опции в качестве аргументов строки запроса.

Не рекомендуется, начиная с версии 3.4.0: Используйте setConfigTransport() вместо configTransport().

static Cake\Mailer\Email::dropTransport($key)

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

Конфигурационные профили

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

'from': Электронная почта или массив отправителей. Смотрите Email::from().

'sender': Электронная почта или массив реальных отправителей. Смотрите Email::sender().

'to': Электронная почта или массив назначений. Смотрите Email::to().

'cc': Электронная почта или массив копий. Смотрите Email::cc().

'bcc': Электронная почта или массив копированных копии. Смотрите Email::bcc().

'replyTo': Электронная почта или массив, для ответов на e-mail. Смотрите Email::replyTo().

'readReceipt': Адрес электронной почты или массив адресов получателей. Смотрите Email::readReceipt().

'returnPath': Адрес электронной почты или массив адресов для возврата, если возникнет ошибка. Смотрите Email::returnPath().

'messageId': ID идентификатор сообщения электронной почты. Смотрите Email::messageId().

'subject': Тема сообщения. Смотрите Email::subject().

'message': Содержание сообщения. Не устанавливайте это поле, если вы используете отображаемый контент.

'priority': Приоритет электронной почты как числового значения (обычно от 1 до 5, причем 1 является наивысшим).

'headers': Вкладываемые заголовки. Смотрите Email::headers().

'viewRender': Если вы используете отображаемый контент, задайте имя класса Вида. Смотрите Email::viewRender().

'template': Если вы используете отображаемый контент, задайте имя шаблона. Смотрите Email::template().

'theme': Тема, используемая при создании шаблона. Смотрите Email::theme().

'layout': Если вы используете отображаемый контент, установите макет для рендеринга. Если вы хотите отобразить шаблон без макета, установите для этого поля значение null. Смотрите Email::template().

'viewVars': Если вы используете отображаемый контент, задайте массив с переменными, которые будут использоваться в представлении. Смотрите Email::viewVars().

'attachments': List of files to attach. Смотрите Email::attachments().

'emailFormat': Format of email (html, text or both). Смотрите Email::emailFormat().

'transport': Название конфигурации транспорта. Смотрите Mailer\Email::configTransport().

'log': Уровень журнала для регистрации заголовков и сообщений электронной почты. true если используете LOG_DEBUG. Смотрите так же Using Levels.

'helpers': Массив помощников, используемых в шаблоне электронной почты. Email::helpers().

Все эти конфигурации являются необязательными, кроме 'from'.

Примечание

Значения вышеуказанных ключей с использованием электронной почты или массива, например from, to, cc и т. д., будут переданы в качестве первого параметра соответствующих методов. Эквивалент для: Email::from('my@example.com', 'My Site') в вашем конфиге будет определяться как 'from' => ['my@example.com' => 'My Site']

Настройка заголовков

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

Смотрите Email::headers() и Email::addHeaders()

Не рекомендуется, начиная с версии 3.4.0: Используйте setHeaders() вместо headers().

Отправка шаблонных писем

Электронная почта часто намного больше, чем просто текстовое сообщение. Чтобы облегчить это, CakePHP предоставляет способ отправки писем с использованием view layer.

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

$email = new Email();
$email
    ->template('welcome', 'fancy')
    ->emailFormat('html')
    ->to('bob@example.com')
    ->from('app@domain.com')
    ->send();

Вышеизложенное будет использовать src/Template/Email/html/welcome.ctp для представления и src/Template/Layout/Email/html/fancy.ctp для макета(лейаута). Вы также можете отправить многостраничные шаблонные сообщения электронной почты:

$email = new Email();
$email
    ->template('welcome', 'fancy')
    ->emailFormat('both')
    ->to('bob@example.com')
    ->from('app@domain.com')
    ->send();

Это будет использовать следующие файлы шаблонов:

src/Template/Email/text/welcome.ctp

src/Template/Layout/Email/text/fancy.ctp

src/Template/Email/html/welcome.ctp

src/Template/Layout/Email/html/fancy.ctp

При отправке шаблонных писем у вас есть возможность отправить либо text, html или both.

Вы можете установить переменные вида с помощью Email::viewVars():

$email = new Email('templated');
$email->viewVars(['value' => 12345]);

В шаблонах электронной почты вы можете использовать:

<p>Here is your value: <b><?= $value ?></b></p>

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

$email->helpers(['Html', 'Custom', 'Text']);

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

Если вы хотите отправить письмо с помощью шаблонов в плагине, вы можете использовать знакомый plugin syntax делая это так:

$email = new Email();
$email->template('Blog.new_comment', 'Blog.auto_message');

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

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

$email = new Email();
$email->template('Blog.new_comment', 'Blog.auto_message');
$email->theme('TestTheme');

Это позволяет вам переопределить шаблон new_comment в своей теме, не изменяя плагин Blog. Файл шаблона должен быть создан по следующему пути: src/Template/Plugin/TestTheme/Plugin/Blog/Email/text/new_comment.ctp.

Не рекомендуется, начиная с версии 3.4.0: Используйте setTemplate() вместо template(). Используйте setLayout() вместо аргумента макета template(). Используйте setTheme() вместо theme().

Отправка вложений

Cake\Mailer\Email::attachments($attachments)

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

Строка: $email->attachments('/full/file/path/file.png') будет прикреплять файл с именем file.png

Массив: $email->attachments(['/full/file/path/file.png']) будет иметь такое же поведение, как использование строки

Массив с ключом: $email->attachments(['photo.png' => '/full/some_hash.png']) будет прикреплён файл some_hash.png с именем photo.png. Получатель увидит photo.png, а не some_hash.png

Вложенные массивы:

$email->attachments([
    'photo.png' => [
        'file' => '/full/some_hash.png',
        'mimetype' => 'image/png',
        'contentId' => 'my-unique-id'
    ]
]);

Вышеприведённый файл будет прикреплён к файлу с другим типом mimetype и с пользовательским идентификатором содержимого (при установке ID идентификатора содержимого, которое будет приложением преобразовано во встроенный). Mimetype и contentId не являются обязательными в этой форме.

4.1. Когда вы используете contentId, вы можете использовать этот файл в теле HTML, например <img src="cid:my-content-id">.

4.2. Вы можете использовать опцию contentDisposition, чтобы отключить заголовок Content-Disposition для вложения. Это полезно при отправке ваших приглашений клиентам с использованием Outlook.

4.3 Вместо параметра file вы можете предоставить содержимое файла в виде строки, используя опцию data. Это позволит вам прикреплять файлы, не требуя для них пути к файлам.

Не рекомендуется, начиная с версии 3.4.0: Используйте setAttachments() вместо attachments().

Использование транспорта

Транспорты - это классы, предназначенные для отправки электронной почты по некоторому протоколу или методу. CakePHP поддерживает сообщения Mail (по умолчанию), Debug и SMTP.

Чтобы настроить свой метод, вы должны использовать метод Cake\Mailer\Email::transport() или иметь транспорт в вашей конфигурации:

$email = new Email();

// Использовать именованный транспорт, уже настроенный с
// использованием электронной почты::configTransport()
$email->transport('gmail');

// Использовать построенный объект
$transport = new DebugTransport();
$email->transport($transport);

Не рекомендуется, начиная с версии 3.4.0: Используйте setTransport() вместо transport().

Создание пользовательских транспортов

Вы можете создавать свои пользовательские транспорты для интеграции с другими системами электронной почты (например, SwiftMailer). Чтобы создать транспорт, сначала создайте файл src/Mailer/Transport/ExampleTransport.php (где Example - это название вашего транспорта). Для начала ваш файл должен выглядеть так:

namespace App\Mailer\Transport;

use Cake\Mailer\AbstractTransport;
use Cake\Mailer\Email;

class ExampleTransport extends AbstractTransport
{
    public function send(Email $email)
    {
        // Тут что нибудь делаем
    }
}

Вы должны реализовать метод send(Email $email) с вашей пользовательской логикой. При желании вы можете реализовать метод config($config). config() вызывается перед send() и позволяет принимать пользовательские конфигурации. По умолчанию этот метод помещает конфигурацию в защищённый атрибут $ _config.

Если вам нужно вызвать дополнительные методы транспорта перед отправкой, вы можете использовать Cake\Mailer\Email::getTransport() для получения экземпляра транспортного объекта. Например:

$yourInstance = $email->getTransport()->transportClass();
$yourInstance->myCustomMethod();
$email->send();

Правила валидации e-mail адресов

Cake\Mailer\Email::emailPattern($pattern)

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

$email = new Email('default');

// Сменить шаблон электронной почты, чтобы вы могли отправлять письма
    // на соответствующие адреса.
$email->emailPattern($newPattern);

Не рекомендуется, начиная с версии 3.4.0: Используйте setEmailPattern() вместо emailPattern().

Быстрая отправка сообщений

Иногда вам нужен быстрый способ отправить электронную почту, и вам не обязательно заранее устанавливать конфигурацию. Cake\Mailer\Email::deliver() предназначен для этой цели.

Вы можете создать свою конфигурацию, используя Cake\Mailer\Email::config(), или используйте массив со всеми необходимыми параметрами и используйте статический метод Email::deliver(). Например:

Email::deliver('you@example.com', 'Subject', 'Message', ['from' => 'me@example.com']);

Этот метод отправит электронное письмо на адрес «you@example.com», от «me@example.com» с пометкой «Subject» и контентом «Message».

Возврат метода deliver() является экземпляром Cake\Mailer\Email со всеми установленными конфигурациями. Если вы не хотите отправлять электронную почту сразу и хотите настроить несколько вещей перед отправкой, вы можете передать 5-й параметр как false.

3ий параметр - это содержимое сообщения или массив с переменными (при использовании визуализированного содержимого).

4ым параметром может быть массив с конфигурациями или строка с именем конфигурации в Configure.

Если вы хотите, вы можете передать объект, тему и сообщение как null и выполнить все конфигурации в 4-м параметре (в виде массива или с помощью Configure). Проверьте список configurations, чтобы увидеть все доступные конфиги.

Отправка писем из CLI (командной строки)

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

$email->domain('www.example.org');
// Результаты в идентификаторах сообщений, таких как ``<UUID@www.example.org>`` (валидный)
// Вместо `<UUID@>`` (не валидного)

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

Не рекомендуется, начиная с версии 3.4.0: Используйте setDomain() вместо domain().

Создание многоразовых писем

Добавлено в версии 3.1.0.

Mailers позволяют создавать повторно используемые письма во всём приложении. Они также могут использоваться для хранения нескольких конфигураций электронной почты в одном месте. Это помогает сохранить код DRY(чистым) и устраняет помехи конфигурации электронной почты из других областей вашего приложения.

В этом примере мы создадим Mailer, который содержит связанные с пользователем электронные письма. Чтобы создать наш UserMailer, создайте файл src/Mailer/UserMailer.php. Содержимое файла должно выглядеть следующим образом:

namespace App\Mailer;

use Cake\Mailer\Mailer;

class UserMailer extends Mailer
{
    public function welcome($user)
    {
        $this
            ->to($user->email)
            ->subject(sprintf('Welcome %s', $user->name))
            ->template('welcome_mail', 'custom'); // По умолчанию используется шаблон с тем же именем, что и имя метода.
    }

    public function resetPassword($user)
    {
        $this
            ->to($user->email)
            ->subject('Reset password')
            ->set(['token' => $user->token]);
    }
}

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

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

namespace App\Controller;

use Cake\Mailer\MailerAwareTrait;

class UsersController extends AppController
{
    use MailerAwareTrait;

    public function register()
    {
        $user = $this->Users->newEntity();
        if ($this->request->is('post')) {
            $user = $this->Users->patchEntity($user, $this->request->getData())
            if ($this->Users->save($user)) {
                $this->getMailer('User')->send('welcome', [$user]);
            }
        }
        $this->set('user', $user);
    }
}

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

public function implementedEvents()
{
    return [
        'Model.afterSave' => 'onRegistration'
    ];
}

public function onRegistration(Event $event, EntityInterface $entity, ArrayObject $options)
{
    if ($entity->isNew()) {
        $this->send('welcome', [$entity]);
    }
}

Теперь объект почтовой программы можно зарегистрировать как прослушиватель событий, а метод onRegistration() будет вызываться каждый раз, когда будет запущено событие Model.afterSave. Информацию о том, как регистрировать объекты прослушивателя событий, см. в документации registering-event-listeners.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Error & Exception Handling

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Error & Exception Configuration

Built in Exceptions for CakePHP

Exception Renderer

Creating your own Application Exceptions

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Events System

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Tracking Events

Establishing Priorities

Stopping Events

Additional Reading

Behaviors

Компоненты

Хелперы

Testing Events

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Internationalization & Localization

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Logging

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Logging Configuration

FileLog engine takes the following options:

Logging to Syslog

Using Levels

Logging Scopes

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Формы без Модели

class Cake\Form\Form

В большинстве случаев у вас будут формы, опирающиеся на сущности ORM (entities) и таблицы ORM или другие постоянные хранилища(peristent stores), но бывают случаи когда вам необходимо проверить действие пользователя, а затем выполнить действие, если данные действительны. Наиболее распространенным примером этого является контактная форма.

Создание формы

Как правило, при использовании класса Form вы хотите использовать подкласс для определения вашей формы. Это упрощает тестирование и позволяет повторно использовать вашу форму. Формы помещаются в src/Form и обычно имеют Form в качестве суффикса класса. Например, простая контактная форма будет выглядеть так:

// в src/Form/ContactForm.php
namespace App\Form;

use Cake\Form\Form;
use Cake\Form\Schema;
use Cake\Validation\Validator;

class ContactForm extends Form
{

    protected function _buildSchema(Schema $schema)
    {
        return $schema->addField('name', 'string')
            ->addField('email', ['type' => 'string'])
            ->addField('body', ['type' => 'text']);
    }

    protected function _buildValidator(Validator $validator)
    {
        return $validator->add('name', 'length', [
                'rule' => ['minLength', 10],
                'message' => 'A name is required'
            ])->add('email', 'format', [
                'rule' => 'email',
                'message' => 'A valid email address is required',
            ]);
    }

    protected function _execute(array $data)
    {
        // Отправить письмо.
        return true;
    }
}

В приведенном выше примере мы видим, 3 защищённых(protected) метода:

_buildSchema - используется для определения данных схемы, которые используются FormHelper для создания HTML-формы. Вы можете определить тип поля, длину и точность.

_buildValidator - получает экземпляр Cake\Validation\Validator, к которому вы можете присоединить валидаторы.

_execute - позволяет определить поведение, которое вы хотите выполнить, когда вызывается execute() и данные являются валидными.

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

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

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

// В контроллере
namespace App\Controller;

use App\Controller\AppController;
use App\Form\ContactForm;

class ContactController extends AppController
{
    public function index()
    {
        $contact = new ContactForm();
        if ($this->request->is('post')) {
            if ($contact->execute($this->request->getData())) {
                $this->Flash->success('Мы скоро свяжемся с вами.');
            } else {
                $this->Flash->error('Возникла проблема с отправкой вашей формы.');
            }
        }
        $this->set('contact', $contact);
    }
}

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

$isValid = $form->validate($this->request->getData());

Установка значений формы

Чтобы установить значения для полей формы без моделей форм, можно определить значения с помощью $this->request->data(), как и во всех других формах, созданных FormHelper:

// В контроллере
namespace App\Controller;

use App\Controller\AppController;
use App\Form\ContactForm;

class ContactController extends AppController
{
    public function index()
    {
        $contact = new ContactForm();
        if ($this->request->is('post')) {
            if ($contact->execute($this->request->getData())) {
                $this->Flash->success('Мы скоро свяжемся с вами.');
            } else {
                $this->Flash->error('Возникла проблема с отправкой вашей формы.');
            }
        }

        if ($this->request->is('get')) {
            // Значения, например, из пользовательской модели.
            $this->request->data('name', 'John Doe');
            $this->request->data('email','john.doe@example.com');
        }

        $this->set('contact', $contact);
    }
}

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

Получение ошибок в форме

Как только форма была проверена, вы можете получить от нее ошибки:

$errors = $form->errors();
/* $errors contains
[
    'email' => ['Требуется валидный адрес электронной почты']
]
*/

Невалидность отдельных полей формы из контроллера

Можно исключить отдельные поля из контроллера без использования класса Validator. Наиболее распространенным вариантом использования этого является проверка правильности работы на удалённом сервере. В таком случае вы должны вручную аннулировать поля, соответствующие обратной связи с удаленного сервера:

// в src/Form/ContactForm.php
public function setErrors($errors)
{
    $this->_errors = $errors;
}

Согласно тому, как класс validator вернул бы ошибки, $errors должен быть в таком формате:

["fieldName" => ["validatorName" => "Отображаемое сообщение об ошибке"]]

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

// В контроллере
$contact = new ContactForm();
$contact->setErrors(["email" => ["_required" => "Ваш адрес электронной почты"]]);

Перейдите к созданию HTML с помощью FormHelper, чтобы увидеть результаты.

Создание HTML с помощью FormHelper

Создав класс Form, вы, скорее всего, захотите создать для него HTML-форму. FormHelper понимает объекты формы, подобные объектам ORM:

echo $this->Form->create($contact);
echo $this->Form->control('name');
echo $this->Form->control('email');
echo $this->Form->control('body');
echo $this->Form->button('Submit');
echo $this->Form->end();

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

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Плагины

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

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

В CakePHP 3.0 каждый плагин определяет собственное пространство имён верхнего уровня. Например: DebugKit. По соглашению, плагины используют своё имя пакета в качестве своего пространства имен. Если вы хотите использовать другое пространство имён, вы можете настроить плагину пространство имен, когда загружаются плагины.

Установка плагина с Composer

Многие плагины доступны на Packagist [http://packagist.org] и могут быть установлены с помощью Composer. Чтобы установить DebugKit, вам нужно сделать следующее:

php composer.phar require cakephp/debug_kit

Это установит последнюю версию DebugKit и обновит ваш composer.json, файл composer.lock, обновит vendor/cakephp-plugins.php, и обновит ваш автозагрузчик.

Если плагин, который вы хотите установить, недоступен на packagist.org, вы можете клонировать или копировать код плагина в каталог plugins. Предполагая, что вы хотите установить плагин с именем „ContactManager“, у вас должна быть папка в plugins с именем „ContactManager“. В этом каталоге будут находится src, тесты и любые другие каталоги данного плагина.

Файл карты плагина

При установке плагинов через Composer вы можете заметить, что создаётся vendor/cakephp-plugins.php. Этот файл конфигурации содержит карту имён плагинов и их путей в файловой системе. Это позволяет устанавливать плагины в стандартный каталог поставщиков, который находится за пределами обычных путей поиска. Класс Plugin будет использовать этот файл для поиска плагинов, когда они загружаются с помощью load() или loadAll(). Вам обычно не нужно будет редактировать этот файл вручную, поскольку Composer и пакет plugin-installer будут управлять ими за вас.

Загрузка плагина

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

// В config/bootstrap.php
// Или в in Application::bootstrap()

// Загружает один плагин
Plugin::load('ContactManager');

// Загружает плагин с пространством имён поставщиков на верхнем уровне.
Plugin::load('AcmeCorp/ContactManager');

// Loads all plugins at once
Plugin::loadAll();

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

Примечание

Plugin::loadAll() не будет загружать плагины с расширением имён поставщиков, которые не определены в vendor/cakephp-plugins.php.

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

bin/cake plugin load ContactManager

Это приведёт к загрузке Plugin::load('ContactManager'); snippet в bootstrap для вас.

Автозагрузка классов плагина

При использовании bake для создания плагина или при установке плагина с использованием Composer, вам обычно не нужно вносить какие-либо изменения в ваше приложение, чтобы CakePHP распознавал классы, которые живут внутри него.

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

"psr-4": {
    (...)
    "MyPlugin\\": "plugins/MyPlugin/src/",
    "MyPlugin\\Test\\": "plugins/MyPlugin/tests/"
}

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

"psr-4": {
    (...)
    "AcmeCorp\\Users\\": "plugins/AcmeCorp/Users/src/",
    "AcmeCorp\\Users\\Test\\": "plugins/AcmeCorp/Users/tests/"
}

Кроме того, вам нужно будет сообщить Composer об обновлении кеша автозагрузки:

$ php composer.phar dumpautoload

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

Plugin::load('ContactManager', ['autoload' => true]);

Конфигурация плагина

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

// В config/bootstrap.php,
// или в Application::bootstrap()

// Используем loadAll()
Plugin::loadAll([
    'Blog' => ['routes' => true],
    'ContactManager' => ['bootstrap' => true],
    'WebmasterTools' => ['bootstrap' => true, 'routes' => true],
]);

Или вы можете загружать плагины индивидуально:

// Загрузка только блога и включение маршрутов
Plugin::load('Blog', ['routes' => true]);

// Включить файл конфигурации начальной загрузки / инициализации.
Plugin::load('ContactManager', ['bootstrap' => true]);

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

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

Следующий пример загрузит загрузочный файл из всех плагинов, а также маршруты из плагина Blog:

Plugin::loadAll([
    ['bootstrap' => true],
    'Blog' => ['routes' => true]
]);

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

Plugin::loadAll([
    ['ignoreMissing' => true, 'bootstrap' => true],
    'Blog' => ['routes' => true]
]);

При загрузке плагинов, используемое имя плагина должно соответствовать пространству имён. Например, если у вас есть плагин с пространством имён верхнего уровня Users, вы должны загрузить его с помощью:

Plugin::load('User');

Если вы предпочитаете иметь имя своего поставщика как верхний уровень и иметь пространство имён, например AcmeCorp/Users, то вы должны загрузить плагин так:

Plugin::load('AcmeCorp/Users');

Это обеспечивает правильное разрешение имён классов при использовании plugin syntax.

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

Использование плагинов

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

Например, предположим, что вы хотите использовать ContactInfoHelper плагина ContactManager для вывода некоторой контактной информации в одном из ваших представлений. В вашем контроллере ваш массив $helpers может выглядеть так:

public $helpers = ['ContactManager.ContactInfo'];

Примечание

Это разделённое точкой имя класса описывается в plugin syntax.

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

echo $this->ContactInfo->address($contact);

Создание собственных плагинов

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

/src
/plugins
    /ContactManager
        /config
        /src
            /Controller
                /Component
            /Model
                /Table
                /Entity
                /Behavior
            /View
                /Helper
            /Template
                /Layout
        /tests
            /TestCase
            /Fixture
        /webroot

Обратите внимание на имя папки плагина, „ContactManager“. Важно, чтобы эта папка имела то же имя, что и плагин.

Внутри папки плагина вы заметите, что он очень похож на приложение CakePHP, и это в основном то, что есть. Вам не нужно включать какие-либо папки, которые вы не используете. Некоторые плагины могут определять только компонент и поведение, и в этом случае они могут полностью опустить каталог „Template“.

Плагин также может содержать любые другие каталоги, которые могут использовать ваше приложение, такие как Config, Console, webroot и т. Д.

Создание плагина с использованием Bake

Процесс создания плагинов может быть значительно упрощён с использованием оболочки bake.

Чтобы испечь(создать) плагин, используйте следующую команду:

bin/cake bake plugin ContactManager

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

bin/cake bake controller --plugin ContactManager Contacts

Пожалуйста, обратитесь к главе Генерация кода с помощью Bake, если у вас возникли проблемы с использованием командной строки. Обязательно заново создайте автозагрузчик после создания плагина:

$ php composer.phar dumpautoload

Маршрутизация плагина

Плагины могут предоставлять файлы маршрутов, содержащие их маршруты Каждый плагин может содержать файл config/routes.php. Этот файл маршрутов может быть загружен при добавлении плагина или загружен в файл маршрутов приложения. Чтобы создать маршруты плагинов ContactManager, введите следующее в plugins/ContactManager/config/routes.php:

<?php
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\Router;

Router::plugin(
    'ContactManager',
    ['path' => '/contact-manager'],
    function ($routes) {
        $routes->get('/contacts', ['controller' => 'Contacts']);
        $routes->get('/contacts/:id', ['controller' => 'Contacts', 'action' => 'view']);
        $routes->put('/contacts/:id', ['controller' => 'Contacts', 'action' => 'update']);
    }
);

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

Прежде чем вы сможете получить доступ к своим контроллерам, вам необходимо убедиться, что плагин загружен и загружены маршруты плагина. В вашем config/bootstrap.php добавьте следующее:

Plugin::load('ContactManager', ['routes' => true]);

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

Router::scope('/', function ($routes) {
    // Подключаем другие маршруты.
    $routes->scope('/backend', function ($routes) {
        $routes->loadPlugin('ContactManager');
    });
});

Вышеизложенное приведёт к URL-адресам, таким как /backend/contact_manager/contacts.

Добавлено в версии 3.5.0: RouteBuilder::loadPlugin() был добавлен в 3.5.0

Контроллеры плагина

Контроллеры для нашего плагина ContactManager будут храниться в plugins/ContactManager/src/Controller/. Поскольку главное, что мы будем делать, это управление контактами, нам понадобится ContactController для этого плагина.

Итак, мы размещаем наш новый ContactController в plugins/ContactManager/src/Controller и это выглядит так:

// plugins/ContactManager/src/Controller/ContactsController.php
namespace ContactManager\Controller;

use ContactManager\Controller\AppController;

class ContactsController extends AppController
{

    public function index()
    {
        //...
    }
}

Также создайте AppController, если у вас его еще нет:

// plugins/ContactManager/src/Controller/AppController.php
namespace ContactManager\Controller;

use App\Controller\AppController as BaseController;

class AppController extends BaseController
{
}

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

Если вы хотите получить доступ к тому, что у нас уже есть, посетите /contact-manager/contacts. Вы должны получить ошибку «Отсутствующая модель», потому что у нас ещё нет модели контактов.

Если ваше приложение включает в себя маршрутизацию по умолчанию CakePHP, вы сможете получить доступ к своим контроллерам плагинов, используя URL-адреса, такие как:

// Получаем доступ к указательному маршруту контроллера плагина.
/contact-manager/contacts

// Любые действия над контроллером плагина.
/contact-manager/contacts/view/1

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

/:prefix/:plugin/:controller
/:prefix/:plugin/:controller/:action

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

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

Модели плагина

Модели для плагина хранятся в plugins/ContactManager/src/Model. Мы уже определили ContactController для этого плагина, поэтому давайте создадим таблицу и объект для этого контроллера:

// plugins/ContactManager/src/Model/Entity/Contact.php:
namespace ContactManager\Model\Entity;

use Cake\ORM\Entity;

class Contact extends Entity
{
}

// plugins/ContactManager/src/Model/Table/ContactsTable.php:
namespace ContactManager\Model\Table;

use Cake\ORM\Table;

class ContactsTable extends Table
{
}

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

// plugins/ContactManager/src/Model/Table/ContactsTable.php:
namespace ContactManager\Model\Table;

use Cake\ORM\Table;

class ContactsTable extends Table
{
    public function initialize(array $config)
    {
        $this->hasMany('ContactManager.AltName');
    }
}

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

// plugins/ContactManager/src/Model/Table/ContactsTable.php:
namespace ContactManager\Model\Table;

use Cake\ORM\Table;

class ContactsTable extends Table
{
    public function initialize(array $config)
    {
        $this->hasMany('AltName', [
            'className' => 'ContactManager.AltName',
        ]);
    }
}

Вы можете использовать TableRegistry для загрузки таблиц ваших плагиновых, используя знакомый plugin syntax:

use Cake\ORM\TableRegistry;

// Prior to 3.6 use TableRegistry::get('ContactManager.Contacts')
$contacts = TableRegistry::getTableLocator()->get('ContactManager.Contacts');

Кроме того, из контекста контроллера вы можете использовать:

$this->loadModel('ContactsMangager.Contacts');

Представления плагина

Представления ведут себя точно так же, как в обычных приложениях. Просто поместите их в нужную папку внутри папки plugins/[PluginName]/src/Template/. Для нашего плагина ContactManager нам понадобится представление для нашего действия ContactsController::index(), поэтому давайте это сделаем:

// plugins/ContactManager/src/Template/Contacts/index.ctp:
<h1>Контакты</h1>
<p>Ниже приведён отсортированный список ваших контактов</p>
<!-- Здесь будет отсортированный список контактов....-->

Плагины могут предоставлять свои собственные макеты. Чтобы добавить плагины, разместите файлы шаблонов внутри plugins/[PluginName]/src/Template/Layout. Чтобы использовать плагин в вашем контроллере, вы можете сделать следующее:

public $layout = 'ContactManager.admin';

Если префикс плагина опущен, файл layout/view (макета/представления) будет нормально расположен.

Примечание

Для получения информации о том, как использовать элементы из плагина, посмотрите Элементы

Переопределение шаблонов плагинов изнутри вашего приложения

Вы можете переопределить любые виды плагинов внутри вашего приложения, используя специальные пути. Если у вас есть плагин с именем „ContactManager“, вы можете переопределить файлы шаблонов плагина с помощью конкретной логики представления, создав файлы, использующие следующий шаблон src/Template/Plugin/[Plugin]/[Controller]/[view].ctp. Для контроллера контактов вы можете сделать следующий файл:

src/Template/Plugin/ContactManager/Contacts/index.ctp

Создание этого файла позволит вам переопределить plugins/ContactManager/src/Template/Contacts/index.ctp.

Если ваш плагин находится в зависимости от composer (т.е. „TheVendor/ThePlugin“), путь к представлению „index“ пользовательского контроллера будет следующим:

src/Template/Plugin/TheVendor/ThePlugin/Custom/index.ctp

Создание этого файла позволит вам переопределить vendor/thevendor/theplugin/src/Template/Custom/index.ctp.

Если плагин реализует префикс маршрутизации, вы должны включить префикс маршрутизации в свои переопределения шаблона приложения. Например, если плагин „ContactManager“ реализовал префикс „admin“, путь должен быть:

src/Template/Plugin/ContactManager/Admin/ContactManager/index.ctp

Ресурсы плагина

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

/plugins/ContactManager/webroot/
                               css/
                               js/
                               img/
                               flash/
                               pdf/

Вы можете поместить любой тип файла в любой каталог, как и обычно в webroot.

Предупреждение

Обработка статических активов (таких как изображения, файлы JavaScript и CSS) через Диспетчер очень неэффективна. См. symlink-assets для получения дополнительной информации.

Связывание с активами в плагине

Вы можете использовать plugin syntax при связывании с активами плагина с помощью View\Helper\HtmlHelper скриптов, изображений или css-методов:

// Создаёт URL-адрес /contact_manager/css/styles.css
echo $this->Html->css('ContactManager.styles');

// Создаёт URL-адрес /contact_manager/js/widget.js
echo $this->Html->script('ContactManager.widget');

// Создаёт URL-адрес /contact_manager/img/logo.jpg
echo $this->Html->image('ContactManager.logo');

Активы плагина передаются с использованием фильтра диспетчера AssetFilter по умолчанию. Это рекомендуется только для разработки. В производстве вы должны использовать symlink plugin assets для повышения производительности.

Если вы не используете помощников(helpers), вы можете добавить /plugin_name/ в начало URL-адреса для актива в этом плагине для его обслуживания. Связывание с „/contact_manager/js/some_file.js“ будет служить активу plugins/ContactManager/webroot/js/some_file.js.

Компоненты, Помощники и Поведение

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

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

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

// Компонент, определённый в плагине 'ContactManager'
namespace ContactManager\Controller\Component;

use Cake\Controller\Component;

class ExampleComponent extends Component
{
}

// Внутри ваших контроллеров
public function initialize()
{
    parent::initialize();
    $this->loadComponent('ContactManager.Example');
}

Тот же метод применяется к Помощникам и Поведению.

Расширьте свой плагин

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

Идите дальше - включите некоторые сторонние библиотеки в „vendor“, добавьте новые оболочки в консоль cake и не забудьте создать тестовые примеры, чтобы ваши пользователи плагина могли автоматически протестировать функциональность вашего плагина!

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

Опубликуйте свой плагин

Убедитесь, что вы добавили плагин в plugins.cakephp.org <https://plugins.cakephp.org> _. Таким образом, другие люди могут использовать его как зависимость в composer. Вы также можете предложить свой плагин в список awesome-cakephp <https://github.com/FriendsOfCake/awesome-cakephp> _.

Выберите семантически значимое имя для пакета. В идеале это должен быть префикс зависимости „cakephp“ в качестве основы. Имя поставщика обычно будет вашим именем пользователя GitHub. Не используйте пространство имён CakePHP (cakephp), поскольку это зарезервировано для CakePHP, принадлежащих плагинам. Соглашение заключается в использовании строчных букв и тире в качестве разделителя.

Поэтому, если вы создали плагин „Logging“ с вашей учетной записью GitHub „FooBar“, хорошим именем будет foo-bar/cakephp-logging. И CakePHP, принадлежащий плагину „Localized“, можно найти под cakephp/localized соответственно.

Управление плагинами с помощью Микшера

Другой способ обнаружения и управления плагинами в вашем приложении CakePHP - Mixer [https://github.com/CakeDC/mixer]. Это плагин CakePHP, который помогает вам устанавливать плагины из Packagist. Это также поможет вам управлять существующими плагинами.

Примечание

IMPORTANT: Не используйте это в продакшене.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

REST

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Безопасность

CakePHP предоставляет инструменты для обеспечения безопасности вашего приложения. Следующие разделы охватывают эти инструменты:

Security

Межсайтовая подделка запроса

Security Component

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Security

class Cake\Utility\Security

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Cессии

CakePHP предоставляет собственную оболочку и набор полезных функций поверх session расширения PHP. Cессии позволяют идентифицировать уникальных пользователей по запросам и хранить постоянные данные для определённых пользователей. В отличие от Cookies, данные сессии недоступны на стороне клиента. Использование $_SESSION вообще исключается в CakePHP, и вместо этого использование классов сеанса является предпочтительным.

Настройка сессии

Конфигурация сессии обычно определяется в /config/app.php. Доступны следующие варианты:

Session.timeout - Число минут до того, как истечёт время CakePHP обработчика сессии.

Session.defaults - Позволяет использовать встроенные конфигурации сессий по умолчанию в качестве базы для настройки сессии. Ниже приведены встроенные значения по умолчанию.

Session.handler - Позволяет определить пользовательский обработчик сессии. Это используют сore database и обработчики кеш-сессии. См. ниже дополнительную информацию о обработчиках сессий.

Session.ini - Позволяет вам установить дополнительные параметры сессии для вашей ini-конфигурации. Это в сочетании с Session.handler заменяет пользовательские функции обработки сеанса предыдущих версий.

Session.cookie - Имя используемого файла cookie. По умолчанию используется значение „CAKEPHP“.

Session.cookiePath - URL-адрес, для которого установлен сессионный файл cookie. Карты в конфигурацию session.cookie_path php.ini. По умолчанию используется базовый путь к приложению.

session.cookie_secure CakePHP по умолчанию установлено в true, когда ваше приложение находится по протоколу SSL. Если ваше приложение работает как с протоколами SSL, так и с протоколом, отличным от SSL, у вас могут возникнуть проблемы с потерями сеансов. Если вам нужен доступ к сеансу как для SSL, так и для доменов, не относящихся к SSL, вам необходимо отключить этот так:

Configure::write('Session', [
    'defaults' => 'php',
    'ini' => [
        'session.cookie_secure' => false
    ]
]);

В пути к cookie сессии по умолчанию используется базовый путь приложения. Чтобы изменить это, вы можете использовать ini-значение session.cookie_path. Например, если вы хотите, чтобы ваша сессия сохранялась во всех поддоменах, вы можете сделать так:

Configure::write('Session', [
    'defaults' => 'php',
    'ini' => [
        'session.cookie_path' => '/',
        'session.cookie_domain' => '.yourdomain.com'
    ]
]);

По умолчанию PHP устанавливает, что cookie сессии истекает, как только браузер закрывается, независимо от настроенного значения Session.timeout. Тайм-аут cookie контролируется значением session.cookie_lifetime и может быть сконфигурирован с его использованием:

Configure::write('Session', [
    'defaults' => 'php',
    'ini' => [
        // Выполните инвалидацию cookie файла через 30 минут,
        // если за это время не посещена ни одна страница на сайте.
        'session.cookie_lifetime' => 1800
    ]
]);

Разница между Session.timeout и значением session.cookie_lifetime заключается в том, что последний полагается на то, что клиент говорит правду о cookie. Если вам требуется более строгая проверка тайм-аута, не полагайтесь на то, что сообщает клиент, а используйте Session.timeout.

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

Встроенные обработчики сессий и конфигурации

CakePHP поставляется с несколькими встроенными конфигурациями сессий. Вы можете либо использовать их в качестве основы для своей конфигурации сессии, либо создать полностью настраиваемое решение. Чтобы использовать значения по умолчанию, просто установите ключ „defaults“ на имя используемого по умолчанию. Затем вы можете переопределить любой дополнительный параметр, объявив его в конфигурации вашей сессии:

Configure::write('Session', [
    'defaults' => 'php'
]);

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

Configure::write('Session', [
    'defaults' => 'php',
    'cookie' => 'my_app',
    'timeout' => 4320 // 3 дня
]);

Вышеупомянутое переопределяет тайм-аут и имя файла cookie для конфигурации „php“ сессии. Встроенные конфигурации:

php - Сохраняет сессии со стандартными настройками в файле php.ini.

cake - Сохраняет сессии как файлы внутри tmp/sessions. Это хороший вариант, когда на хостах не разрешено писать за пределами собственного домашнего каталога.

database - Используйте встроенные сеансы базы данных. Смотрите ниже для получения дополнительной информации.

cache - Используйте встроенный кэш сессий. Смотрите ниже для получения дополнительной информации.

Обработчики сессий

Обработчики сессий также могут быть определены в массиве конфигурации сессии. Определив ключ конфигурации „handler.engine“, вы можете назвать имя класса или предоставить экземпляр обработчика. Класс/объект должен реализовывать собственный PHP SessionHandlerInterface. Внедрение этого интерфейса позволит Session автоматически отображать методы для обработчика. Оба основных обработчика кэша сессии и базы данных используют этот метод для сохранения сессий. Дополнительные настройки для обработчика должны быть помещены внутри массива обработчиков. Затем вы можете прочитать эти значения изнутри вашего обработчика:

'Session' => [
    'handler' => [
        'engine' => 'DatabaseSession',
        'model' => 'CustomSessions'
    ]
]

Вышеописанное показывает, как можно настроить обработчик сессии базы данных с помощью модели приложения. При использовании имён классов в качестве обработчика, CakePHP ожидает, что ваш класс будет найден в пространстве имён Network\Session. Например, если у вас есть класс AppSessionHandler, файл должен быть src/Network/Session/AppSessionHandler.php, а имя класса должно быть App\Network\Session\AppSessionHandler. Вы также можете использовать обработчики сессий изнутри плагинов, установив двигатель в MyPlugin.PluginSessionHandler.

База данных сессий

Если вам нужно использовать базу данных для хранения данных сессии, это можно настроить следующим образом:

'Session' => [
    'defaults' => 'database'
]

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

CREATE TABLE `sessions` (
  `id` char(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  `created` datetime DEFAULT CURRENT_TIMESTAMP, -- Optional
  `modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- Optional
  `data` blob DEFAULT NULL, -- for PostgreSQL use bytea instead of blob
  `expires` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Копию схемы для таблицы сессий можно найти в скелете приложения [https://github.com/cakephp/app] в config/schema/sessions.sql.

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

'Session' => [
    'defaults' => 'database',
    'handler' => [
        'engine' => 'DatabaseSession',
        'model' => 'CustomSessions'
    ]
]

Вышеприведённое будет означать, что Session использует встроенные значения „database“ по умолчанию и указывает, что таблица, называемая CustomSessions, будет делегатом для сохранения информации о сессии в базе данных.

Кеширование сессий

Класс Cache также может использоваться для хранения сессий. Это позволяет хранить сессии в кэше, таком как APC, Memcached или XCache. Есть некоторые оговорки, связанные с использованием кэша сессий, поскольку если вы исчерпаете пространство кэша, сессии начнут истекать по мере удаления записей из кэша.

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

Configure::write('Session', [
    'defaults' => 'cache',
    'handler' => [
        'config' => 'session'
    ]
]);

Это позволит настроить Session для использования класса CacheSession в качестве делегата для сохранения сессий. Вы можете использовать ключ конфигурации, в котором используется конфигурация кэша. Конфигурация кэша по умолчанию - 'default'.

Настройка ini директив

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

Configure::write('Session', [
    'defaults' => 'php',
    'ini' => [
        'session.cookie_name' => 'MyCookie',
        'session.cookie_lifetime' => 1800, // Валидно 30 минут
        'session.gc_divisor' => 1000,
        'session.cookie_httponly' => true
    ]
]);

Создание пользовательского обработчика сессии

Создание пользовательского обработчика сессии прямо в CakePHP. В этом примере мы создадим обработчик сессии, который хранит сессии как в кэше (APC), так и в базе данных. Это даёт нам лучшее из быстрого ввода-вывода APC, не беспокоясь о том, что сессии испаряются, когда кэш заполняется.

Сначала нам нужно создать наш собственный класс и поместить его в src/Network/Session/ComboSession.php. Класс должен выглядеть примерно так:

namespace App\Network\Session;

use Cake\Cache\Cache;
use Cake\Core\Configure;
use Cake\Network\Session\DatabaseSession;

class ComboSession extends DatabaseSession
{
    public $cacheKey;

    public function __construct()
    {
        $this->cacheKey = Configure::read('Session.handler.cache');
        parent::__construct();
    }

    // Чтение данных сессии.
    public function read($id)
    {
        $result = Cache::read($id, $this->cacheKey);
        if ($result) {
            return $result;
        }
        return parent::read($id);
    }

    // Запись данных в сессию.
    public function write($id, $data)
    {
        Cache::write($id, $data, $this->cacheKey);
        return parent::write($id, $data);
    }

    // Уничтожение сессии.
    public function destroy($id)
    {
        Cache::delete($id, $this->cacheKey);
        return parent::destroy($id);
    }

    // Удаление истёкших сессий.
    public function gc($expires = null)
    {
        return Cache::gc($this->cacheKey) && parent::gc($expires);
    }
}

Наш класс расширяет встроенную DatabaseSession, поэтому нам не нужно дублировать всю её логику и поведение. Мы завершаем каждую операцию с помощью Cake\Cache\Cache. Это позволяет нам получать сессии из быстрого кэша и не беспокоиться о том, что происходит, когда мы заполняем кэш. Использование этого обработчика сессии также легко. В вашем config/app.php сделайте блок сессии следующим:

'Session' => [
    'defaults' => 'database',
    'handler' => [
        'engine' => 'ComboSession',
        'model' => 'Session',
        'cache' => 'apc'
    ]
],
// Не забудьте добавить конфигурацию кэша Apc
'Cache' => [
    'apc' => ['engine' => 'Apc']
]

Теперь наше приложение начнёт использовать наш специальный обработчик сессии для чтения и записи данных сессии.

class Session

Доступ к объекту сессии

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

Controllers

Views

Helpers

Cells

Components

В дополнение к основному объекту сессии вы также можете использовать Cake\View\Helper\SessionHelper для взаимодействия с сессией в ваших представлениях. Основным примером использования сессии будет:

$name = $this->request->session()->read('User.name');

// Если вы получаете доступ к сессии несколько раз, вам,
// вероятно, понадобится локальная переменная.
$session = $this->request->session();
$name = $session->read('User.name');

Чтение и запись данных сессии

Session::read($key)

Вы можете читать значения из сессии, используя Hash::extract() совместимый синтаксис:

$session->read('Config.language');

Session::write($key, $value)

$key должна быть разделенна точками пути, который вы хотите записать в $value:

$session->write('Config.language', 'en');

Вы также можете указать один или несколько хэшей, например:

$session->write([
  'Config.theme' => 'blue',
  'Config.language' => 'en',
]);

Session::delete($key)

Когда вам нужно удалить данные из сессии, вы можете использовать delete():

$session->delete('Some.value');

static Session::consume($key)

Когда вам нужно читать и удалять данные из сессии, вы можете использовать consume():

$session->consume('Some.value');

Session::check($key)

Если вы хотите увидеть, существуют ли определённые данные в сессии, вы можете использовать check():

if ($session->check('Config.language')) {
    // Config.language существует и не является нулевым.
}

Уничтожение сессии

Session::destroy()

Уничтожение сессии полезно, когда пользователи выходят из системы. Чтобы уничтожить сессию, используйте метод destroy():

$session->destroy();

Уничтожение сессии удалит все серверные данные из сессии, но не удалит куки-файл сессии.

Возобновление идентификаторов сессии

Session::renew()

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

$session->renew();

Flash-сообщения

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

Чтобы установить и отобразить флэш-сообщения, вы должны использовать Флэш и Мгновенные сообщения

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Testing

Fixtures

Controller Integration Testing

Testing Actions That Require Authentication

Testing Events

Running Tests

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Validation

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Creating Validators

Creating Reusable Validators

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

App Class

class Cake\Core\App

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Collections

class Cake\Collection\Collection

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Folder & File

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Hash

class Cake\Utility\Hash

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Hash Path Syntax

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Http Client

class Cake\Http\Client(mixed $config = [])

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Request Method Options

Creating Scoped Clients

Response Objects

class Cake\Http\Client\Response

Reading JSON and XML Response Bodies

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Inflector

class Cake\Utility\Inflector

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Summary of Inflector Methods and Their Output

Inflection Configuration

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Number

class Cake\I18n\Number

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Formatting Currency Values

Configure formatters

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Registry Objects

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Text

class Cake\Utility\Text

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Highlighting Substrings

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Date & Time

class Cake\I18n\Time

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

Creating Time Instances

Dates

Immutable Dates and Times

class Cake\I18n\FrozenTime

class Cake\I18n\FrozenDate

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Xml

class Cake\Utility\Xml

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Константы и Функции

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

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

Глобальные функции

Здесь показаны функции CakePHP, которые доступны глобально. Большинство из них просто удобные обёртки для других функций CakePHP, таких как отладка и перевод контента.

__(string $string_id[, $formatArgs])

Эта функция обрабатывает локализацию в приложениях CakePHP. $string_id идентифицирует ID идентификатор перевода. Вы можете указать дополнительные аргументы для замены определений в строке:

__('You have {0} unread messages', $number);

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

__('You have {unread} unread messages', ['unread' => $number]);

Примечание

Ознакомьтесь с разделом Internationalization & Localization для получения дополнительной информации.

__d(string $domain, string $msg, mixed $args = null)

Позволяет переопределить текущий домен для поиска одного сообщения.

Полезно при интернационализации плагина: echo __d('PluginName', 'This is my plugin');

__dn(string $domain, string $singular, string $plural, integer $count, mixed $args = null)

Позволяет переопределить текущий домен для поиска одного множественного сообщения. Возвращает правильную множественную форму сообщения, идентифицированную $singular и $plural для подсчёта суммы $count из домена $domain.

__dx(string $domain, string $context, string $msg, mixed $args = null)

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

Контекст - это уникальный идентификатор строки перевода, который делает её уникальной в пределах одного домена.

__dxn(string $domain, string $context, string $singular, string $plural, integer $count, mixed $args = null)

Позволяет переопределить текущий домен для поиска нескольких множественных сообщений. Он также позволяет указать контекст. Возвращает правильную множественную форму сообщения, идентифицированную $singular и $plural для подсчёта суммы $count из домена $domain.

Контекст - это уникальный идентификатор строки перевода, который делает её уникальной в пределах одного домена.

__n(string $singular, string $plural, integer $count, mixed $args = null)

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

__x(string $context, string $msg, mixed $args = null)

Контекст - это уникальный идентификатор строки перевода, который делает её уникальной в пределах одного домена.

__xn(string $context, string $singular, string $plural, integer $count, mixed $args = null)

Возвращает правильную множественную форму сообщения, идентифицированную $singular и $plural для подсчета суммы $count из домена $domain. Он также позволяет указать контекст. Некоторые языки имеют более одной формы для множественных сообщений, зависящих от числа.

Контекст - это уникальный идентификатор строки перевода, который делает её уникальной в пределах одного домена.

collection(mixed $items)

Удобная оболочка для создания нового объекта Cake\Collection\Collection, обёртка для переданного аргументв. Параметр $items принимает либо объект Traversable, либо массив.

debug(mixed $var, boolean $showHtml = null, $showFrom = true)

Изменено в версии 3.3.0: Вызов этого метода вернёт переданный $var, так что вы можете, например, поместить его в оператор return.

Если основная переменная $debug является true, $var будет распечатан. Если $showHTML является true или оставлено как null, данные будут отображены в браузере. Если $showFrom не установлен в false, вывод отладки начинается со строки, из которой он был вызван. Также см Debugging

dd(mixed $var, boolean $showHtml = null)

Он ведёт себя как debug(), но выполнение также останавливается. Если основная переменная $debug является true, печатается $var. Если $showHTML является true или оставлено как null, данные будут отображены в браузере.

pr(mixed $var)

Изменено в версии 3.3.0: Вызов этого метода вернет переданный $var, так что вы можете, например, поместить его в оператор return.

Удобная обёртка для print_r(), с добавлением тегов <pre> вокруг вывода.

pj(mixed $var)

Изменено в версии 3.3.0: Вызов этого метода вернёт переданный $var, так что вы можете, например, поместить его в оператор return.

Функция удобной печати JSON с добавлением тегов <pre> вокруг вывода.

Он предназначен для отладки JSON-представления объектов и массивов.

env(string $key, string $default = null)

Изменено в версии 3.1.1: Добавлен параметр $default.

Получает переменную окружения из доступных источников. Используется в качестве резервной копии, если $_SERVER или $_ENV отключены.

Эта функция также эмулирует PHP_SELF и DOCUMENT_ROOT на неподдерживаемых серверах. На самом деле, это хорошая идея всегда использовать env() вместо $_SERVER или getenv() (особенно если вы планируете распространять код), так как это полная эмуляция обёртки.

h(string $text, boolean $double = true, string $charset = null)

Удобная обёртка для htmlspecialchars().

pluginSplit(string $name, boolean $dotAppend = false, string $plugin = null)

Разделяет имя плагина синтаксиса в своём плагине и имени класса. Если $name не имеет точки, то индекс 0 будет null.

Обычно используется как list($plugin, $name) = pluginSplit('Users.User');

namespaceSplit(string $class)

Разделяет пространство имён от имени класса.

Обычно используется как list($namespace, $className) = namespaceSplit('Cake\Core\App');

Основные определения констант

Большинство из следующих констант относятся к путям в вашем приложении.

constant APP

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

constant APP_DIR

Равно app или имени вашего каталога приложений.

constant CACHE

Путь к каталогу файлов кеша. Он может совместно использоваться хостами в многосерверной настройке.

constant CAKE

Путь к каталогу cake.

constant CAKE_CORE_INCLUDE_PATH

Путь к корневому каталогу lib.

constant CONFIG

Путь к каталогу конфигурации.

constant CORE_PATH

Путь к корневому каталогу, заканчивается слешем.

constant DS

Сокращение DIRECTORY_SEPARATOR, являющийся / для Linux и```` для Windows.

constant LOGS

Путь к каталогу журналов(логов).

constant ROOT

Путь к корневому каталогу..

constant TESTS

Путь к каталогу тестов.

constant TMP

Путь к папке временных файлов.

constant WWW_ROOT

Полный путь к webroot.

Сроки определения времени

constant TIME_START

Временная метка Unix в микросекундах как плавающее значение с момента запуска приложения.

constant SECOND

Равно 1

constant MINUTE

Равно 60

constant HOUR

Равно 3600

constant DAY

Равно 86400

constant WEEK

Равно 604800

constant MONTH

Равно 2592000

constant YEAR

Равно 31536000

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Debug Kit

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Миграции

эта страница переехала [https://book.cakephp.org/migrations/2.x/ru/].

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Приложение

Приложение содержит информацию касающуюся нововведений в каждой версии CakePHP и инструкции по миграции на новые версии.

Руководство по миграции 3.x

Руководство по миграции 3.x

Основная информация

CakePHP Development Process

Глоссарий

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Руководство по миграции 3.x

Руководства по миграции содержат информацию, касающуюся новых возможностей каждой из версий и путь перехода от версий 2.x к 3.x. Если вы используете версии 1.x, вы должны сначала обновиться до версии 2.x. Смотрите документацию версии 2.x, чтобы узнать об этом больше.

Руководство по миграции 3.5

3.5 Migration Guide

Руководство по миграции 3.4

3.4 Migration Guide

Руководство по миграции 3.3

3.3 Migration Guide

Руководство по миграции 3.2

3.2 Migration Guide

Руководство по миграции 3.1

3.1 Migration Guide

Руководство по миграции 3.0

Руководство по миграции 3.0

Руководство по миграции на новую ORM

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

3.5 Migration Guide

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

3.4 Migration Guide

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

3.3 Migration Guide

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

3.2 Migration Guide

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

3.1 Migration Guide

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Руководство по миграции на новую ORM

Одним из основных нововведений CakePHP 3.0 является полностью переписанное объектно-реляционное отображение (ORM). ORM используемая в версиях 1.x и 2.x и служившая нам длительное время имела некоторые проблемы, которые мы хотели исправить.

Например:

Frankenstein - это запись в таблице или таблица? И то и другое.

Несовместимый API - Model::read().

Не было объекта запроса - запросы всегда определялись как массивы, это имело некоторые ограничения и недостатки. Например это делало объединения и подзапросы намного сложнее.

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

Не было объекта записи - это делало добавление методов форматирования сложным либо невозможным.

Содержимость - должна быть частью ORM, а не безумным классом поведения.

Рекурсивность - должна лучше контроллироваться определением ассоциаций, которые должны быть включены, а не уровнем вложенности рекурсии.

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

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

В CakePHP 3.0 новая ORM решает эти и многие другие проблемы. Новая ORM фокусируется на реляционных данных. В будущем с помощью плагинов мы добавим в нее нереляционные хранилища, такие как ElasticSearch и т.д.

Устройство новой ORM

Новая ORM решает некоторые проблемы наличием более специализированных и сфокусированных классов. В прошлом вы использовали Model и Datasource для проведения всех операций. Теперь ORM разделена на несколько слоев:

Cake\Database\Connection - предоставляет независимую платформу для создания и использования соединений. Этот класс обеспечивает использование транзакций, выполнение запросов и предоставляет доступ к данным схемы.

Cake\Database\Dialect - классы этого пространства имен преобразуют запросы для работы с конкретной SQL платформой, учитывая их специфичиские ограничения.

Cake\Database\Type - это класс шлюза CakePHP системы преобразования типов данных в Базе Данных. Это подключаемый каркас, добавляющий абстрактные типы столбцов и обеспечивает соответствие между базой данных, PHP представлением и PDO bindings для каждого типа данных. Например столбцы типа datetime в вашем коде будут представлены в виде объектов DateTime.

Cake\ORM\Table - основная точка вхождения в новую ORM. Обеспечивает доступ к конкретной таблице. Управляет настройками отношений с другими таблицами, использует классы поведения, создает сущности и объекты запроса.

Cake\ORM\Behavior - базовый класс поведения, который работает похожим образом, как в предыдущих версиях CakePHP.

Cake\ORM\Query - изменчивый объект основанный на построителе запросов, заменяющий глубоко вложенные массивы, которые использовались в предыдущих версиях CakePHP.

Cake\ORM\ResultSet - коллекция результатов, которая предоставляет мощные инструменты для манипуляции совокупностью данных.

Cake\ORM\Entity - представляет собой одну запись в таблице. Предоставляет доступ к данным и сериализацию в различных форматах.

Вы познакомились с классами, с которыми вы будете взаимодействовать наиболее часто в новой ORM. Теперь взгляните на три наиболее самых важных класса: Table, Query и Entity - они решают большиноство задач в новой ORM и каждый имеет свое назначение.

Объект Table

Объекты класса Table предоставляют доступ к вашим данным. Они обрабатывают большинство задач, которые класс Model обрабатыватывал ранее. Класс Table обрабатывает такие задачи, как:

Создание запросов.

Предоставление finder’ов.

Валидация и сохрание сущностей.

Удаление сущностей.

Установка связей и доступ к связанным данным.

Страбатываение функций обратного вызова по событиям.

Взаимодействие с behaviors.

Глава Табличные объекты вы можете найти более подробную информацию по использованию объектов класса Table. При перемещении существующего кода класса Model, большинство кода оказалось а классе Table. Объекты класса Table не содержат SQL зависящего от конкретной платформы. Вместо этого они взаимодействуют с сущностями и построителем запросов. Объекты класса Table также взаимодействуют с behaviors и другими частями приложения через публичные события.

Объект Query

Хотя эти классы не являются классами которые вы будете строить самостроятельно, код вашего приложения будет широко использовать Query Builder, являющийся основным в новой ORM. Построитель запросов позволяет легко создавать простые и сложные запросы, включая те, которые ранее были очень сложными в ранних версиях CakePHP, например HAVING, UNION и подзапросы.

Различные вызовы метода find() в вашем приложении необходимо обновить для того, чтобы использовать новый построитель запросов. Объект Query несет ответственность за содержащиеся данные переданные в запрос, чтобы создать запрос без выполнения самого запроса. Он сотрудничает с connection/dialect для генерации специфического зависящего от платформы SQL кода, который выполняетсясоздавая на выходе ResultSet.

Объект Entity

В предыдущих версиях CakePHP класс Model возвращал массив данных, который не мог содержать какой либо логики или поведения. До тех пор, пока сообщество не сделало этот недостаток менее болезненным создав проект CakeEntity, массив результатов часто являлся причиной многих проблем разработчиков. В CakePHP 3.0 ORM всегда возвращает объект результирующего наборв, если вы явно не выключите эту функцию. Глава Entities охватывает различные задачи, которые вы можете выполнять с сущностями.

Сущности создаются одним из двух способов. Либо путем загрузки данных из Базы Данных, либо конвертацией данных запроса в сущности. Создав сущность вы можете манипулировать ее данными и сохранять сущность в таблицу взаимодействуя с объектом Table.

Ключевые отличия

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

Правила склонений обновлены

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

Классы Table представляют собой коллекцию данных а не одиночные строки.

Связи соединяют таблицы вместе, описывая отношения между многими вещами.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

CakePHP Development Process

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Глоссарий

routing array

Массив атрибутов, передаваемых в Router::url(). Обычно выглядит так:

['controller' => 'Posts', 'action' => 'view', 5]

HTML attributes

Массив key => values которые состоят из атрибутов HTML. Например:

// Дано
['class' => 'my-class', 'target' => '_blank']

// Будет cгенерировано
class="my-class" target="_blank"

Если параметр можно минимизировать или принять его имя как значение, тогда можно использовать true:

// Дано
['checked' => true]

// Будет cгенерировано
checked="checked"

plugin syntax

Синтаксис плагина относится к классу, разделённому точкой, с указанием классов, которые являются частью данного плагина:

// Плагин "DebugKit", и его класс "Toolbar".
'DebugKit.Toolbar'

// Плагин "AcmeCorp/Tools", и его класс "Toolbar".
'AcmeCorp/Tools.Toolbar'

dot notation

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

Cache.default.engine

Укажет следующее значение:

[
    'Cache' => [
        'default' => [
            'engine' => 'File'
        ]
    ]
]

CSRF

Cross Site Request Forgery. Предотвращает повторные атаки, дублированную отправку и подделку запроса с другого домена.

CDN

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

routes.php

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

DRY

Don’t repeat yourself (Не повторяйся). Является принципом разработки программного обеспечения который сокращает повторение информации всех видов. В CakePHP используется DRY для того, чтобы вы могли корректно кодировать вещи и повторно использовать их в своих приложениях.

PaaS

Platform as a Service. Платформа как поставщик услуг, обеспечивает облачный хостинг, базы данных и ресурсы кэширования. Некоторые популярные поставщики включают Heroku, EngineYard и PagodaBox

DSN

Data Source Name. Формат строки соединения, который формируется как URI. CakePHP поддерживает соединения DSN для кэшей, базы данных, журнала и электронной почты.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

PHP Namespace Index

c

| c |
- | Cake |
|     Cake\Cache |
|     Cake\Collection |
|     Cake\Console |
|     Cake\Controller |
|     Cake\Controller\Component |
|     Cake\Core |
|     Cake\Database |
|     Cake\Database\Schema |
|     Cake\Datasource |
|     Cake\Filesystem |
|     Cake\Form |
|     Cake\Http |
|     Cake\Http\Client |
|     Cake\Http\Cookie |
|     Cake\I18n |
|     Cake\Mailer |
|     Cake\ORM |
|     Cake\ORM\Behavior |
|     Cake\Routing |
|     Cake\Utility |
|     Cake\Validation |
|     Cake\View |
|     Cake\View\Helper |

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Не найдено

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

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Chronos

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Содержание

Введение

Первое знакомство с CakePHP

Соглашения превыше конфигурации

Модель

Вид

Контроллер

Цикл обработки запросов CakePHP

Только начало

Рекомендуется к прочтению

Где получить помощь

Соглашения CakePHP

Структура папок CakePHP

Инструкция по началу работы

Пример Менеджер Закладок

Получение CakePHP

Проверка нашей установки

Создание Базы данных

Конфигурация Базы данных

Генерирование шаблонного кода

Хеширование паролей

Получение закладок с определенным тегом

Пример Менеджер Закладок Часть 2

Вход пользователя

Выход пользователя

Добавление регистрации пользователей

Ограничение доступа к закладкам

Доработка форм и Вида списка закладок

Улучшение пользовательского опыта в тегах

Заключение

Руководство по миграции 3.x

Руководство по миграции 3.5

3.5 Migration Guide

Руководство по миграции 3.4

3.4 Migration Guide

Руководство по миграции 3.3

3.3 Migration Guide

Руководство по миграции 3.2

3.2 Migration Guide

Руководство по миграции 3.1

3.1 Migration Guide

Руководство по миграции 3.0

Руководство по миграции 3.0

Руководство по миграции на новую ORM

Руководства и примеры

Пример Менеджер Закладок

Получение CakePHP

Проверка нашей установки

Создание Базы данных

Конфигурация Базы данных

Генерирование шаблонного кода

Хеширование паролей

Получение закладок с определенным тегом

Пример Менеджер Закладок Часть 2

Вход пользователя

Выход пользователя

Добавление регистрации пользователей

Ограничение доступа к закладкам

Доработка форм и Вида списка закладок

Улучшение пользовательского опыта в тегах

Заключение

Пример создания блога

Получение CakePHP

Разрешения для папок tmp и logs

Создание Базы данных блога

Конфигурация Базы данных

Дополнительные настройки

Замечание о mod_rewrite

Пример создания блога - Часть 2

Создание Модели статей

Создание Контроллера статей

Создание Вида статей

Добавление статьи

Валидация данных

Редактирование статей

Удаление статей

Маршруты

Заключение

Пример создания блога - Часть 3

Создание дерева категорий

Плагин Миграций

Доработка таблиц

Генерирование шаблонного кода для категорий

Привязка TreeBehavior к CategoriesTable

Изменение порядка категорий с TreeBehavior

Изменение контроллера ArticlesController

Доработка шаблонов статей

Пример создания блога - Часть 4 - Аутентификация и авторизация

Создание всего кода касающегося пользователей

Аутентификация (Вход и выход из системы)

Авторизация (кому и к чему разрешен доступ)

Сотрудничество

Документация

Переводы

Руководство по форматированию документации

Билеты

Отчеты об ошибках

Отчеты по безопасности

Код

Начальная настройка

Работа над патчем

Отправка Pull Request

Стандарты кодирования

Добавление новых функций

Установка IDE

Отступ

Длина строки

Управляющие структуры

Сравнение

Вызовы функций

Определение метода

Цепочки методов

Комментарии в коде

Подключаемые файлы

PHP теги

Соглашение об именовании

Осторожность при использовании empty()/isset()

Руководство по совместимости

Руководства по миграции

Использование CakePHP

Работающим над CakePHP

Устаревшее

Начало работы

Установка

Требования к установке

Установка CakePHP

Установка Composer

Создание проекта CakePHP

Обновление CakePHP до актуальной версии

Установка с помощью Oven

Права доступа

Встроенный веб-сервер PHP

Полноценная установка

Запускайте

Переопределение URL

Apache

nginx

IIS7 (Windows хостинги)

Я не могу использовать переопределение URL

Конфигурация

Настройка вашего приложения

Загрузка дополнительных файлов конфигурации

Общая конфигурация

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

Конфигурация кэширования

Конфигурация ошибок и исключений

Конфигурация логирования

Настройка электронной почты

Настройка сеанса(сессии)

Конфигурация маршрутизации

Дополнительные пути классов

Плагин, Шаблон просмотра и локальные пути

Настройка интонации

Переменные среды (окружения)

Настроить класс

Запись данных конфигурации

Чтение данных конфигурации

Проверка наличия определенных данных конфигурации

Удаление данных конфигурации

Чтение и удаление данных конфигурации

Чтение и запись файлов конфигурации

Загрузка файлов конфигурации

Создание или изменение файлов конфигурации

Сохранение конфигурации времени выполнения

Восстановление конфигурации времени выполнения

Конфигурационные двигатели

Загрузочный CakePHP

Application::bootstrap()

Отключение общих таблиц

Маршрутизация

Быстрый тур

Подключение маршрутов

Элементы маршрута

Настройка параметров маршрута

Передача параметров в действие(экшен)

Использование именованных маршрутов

Префиксная маршрутизация

Маршрутизация плагинов

Создание ссылок на плагины

Маршрутизация, ориентированная на SEO

Соответствие определенным HTTP-методам

Соответствие определённым именам хостов

Расширения файлов маршрутизации

Соединение с областью Middleware (промежуточного ПО)

Группировка промежуточного ПО

Создание RESTful маршрутов

Создание маршрутов вложенных ресурсов (под-ресурсов)

Ограничение созданных маршрутов

Изменение используемых экшенов контроллера

Сопоставление дополнительных маршрутов ресурсов

Пользовательские классы маршрутов для маршрутов ресурсов

URL-инфлексия для ресурсных маршрутов

Изменение пути элемента

Переданные аргументы

Создание URL-адресов

Перенаправление маршрутизации

Пользовательские классы маршрутов

Класс маршрута по умолчанию

Метод откатов

Создание постоянных параметров URL

Обработка именованных параметров в URL-адресах

Dispatcher Filters

Объекты запроса и ответа

Запрос

Параметры запроса

Параметры строки запроса

Данные в теле запроса

PUT, PATCH или DELETE данные

Переменные среды (из $_SERVER и $_ENV)

XML или JSON данные

Информация о пути

Проверка условий запроса

Данные сеанса(сессии)

Хост и доменное имя

Чтение HTTP-метода

Ограничение того, какой HTTP метод принимает экшен

Чтение HTTP заголовков

Доверенные прокси-заголовки

Проверка принятия заголовков

Cookies

Ответ

Работа с типами контента

Отправка файлов

Отправка строки в виде файла

Настройка заголовков

Настройка body

Установка кодировки символов

Взаимодействие с кэшированием браузера

Точная настройка HTTP-кэша

Настройка файлов cookie

Настройка Cross Origin Request Headers (CORS)

Общие ошибки с неизменяемыми ответами

Коллекции файлов cookie

Создание файлов cookie

Чтение файлов cookie

Промежуточное ПО

Промежуточное ПО в CakePHP

Использование промежуточного ПО

Добавление промежуточного ПО из плагинов

Запросы и ответы PSR-7

Взаимодействие с запросами

Взаимодействие с ответами

Создание промежуточного ПО

Добавление заголовков безопасности

Промежуточное ПО для шифрования файлов cookie

Cross Site Request Forgery (CSRF) Middleware

Интеграция с FormHelper

Защита CSRF и AJAX запросы

Добавление нового HTTP-стека в существующее приложение

Контроллеры

Контроллер уровня приложения (AppController)

Поток запросов

Экшены контроллера

Взаимодействие с видами

Назначение переменных вида

Установка параметров вида

Обработка вида

Перенаправление на другие страницы

Перенаправление на другой экшен внутри того же контроллера

Загрузка дополнительных моделей

Пагинация модели

Настройка загружаемых компонентов

Настройка загружаемых хелперов

Коллбеки жизненного цикла запроса

Список событий

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

Подробнее о контроллерах

Контроллер страниц

Компоненты

Представления

Представление уровня приложения

Шаблоны Представления

Варианты вывода значений переменных

Альтернативные управляющие конструкции

Переменные Представления

Назначение переменных Представления

Расширение Представлений

Использование блоков Представления

Отображение блоков

Использование блоков для скриптов и CSS-файлов

Макеты

Использование макетов из плагинов

Элементы

Передача переменных в элемент

Кэширование элементов

Запрос элементов из плагина

Префикс маршрута и элементы

Кэширование секций вашего представления

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

Список событий

Создание ваших собственных классов представления

Подробнее о Представлениях

Ячейки Представления

Темы

Представления JSON и XML

Хелперы

Доступ к Базе Данных и ORM

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

Больше инфорации

Основы конфигурации базы данных

Query Builder

Табличные объекты

Entities

Retrieving Data & Results Sets

Validating Data

Saving Data

Deleting Data

Associations - Linking Tables Together

Behaviors

Schema System

ORM Cache Shell

Использование CakePHP

Аутентификация

Рекомендуется к прочтению

Аутентификация

Выбор типа аутентификации

Настройка обработчиков аутентификации

Кастомизация поискового запроса

Идентификация и вход пользователей

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

Создание системы аутентификации без сохранения состояния

Использование базовой аутентификации

Использование Дайджест-аутентификации

Создание кастомных объектов аутентификации

Использование кастомных объектов аутентификации

Обработка неаутентифицированных запросов

Вывод флэш-сообщений компонента Auth

Хеширование паролей

Создание пользовательских классов хеширования паролей

Изменение алгоритмов хэширования

Вход пользователей вручную

Получение доступа к вошедшим пользователям

Выход пользователей

Когда выполнять аутентификацию

Авторизация

Настройка обработчиков авторизации

Создание пользовательских объектов авторизации

Отказ от использования авторизации

Создание общедоступных экшенов

Создание экшенов требующих авторизации

Использование ControllerAuthorize

Параметры конфигурации

Тестирование экшенов защищенных AuthComponent

Кэширование

Встроенные механизмы кеширования:

Настройка класса кэша

Настройка откатов кэша

Удаление сконфигурированных кеш-двигателей

Запись в кэш

Написание нескольких ключей одновременно

Чтение через кеширование

Чтение из кеша

Одновременное чтение нескольких ключей

Удаление из кэша

Одновременное удаление нескольких ключей

Очистка закэшированных данных

Использование кеша для хранения счетчиков

Использование кеша для хранения общих результатов запроса

Использование групп

Глобальное включение или отключение кеша

Создание механизма хранения для кеша

Shells, Tasks & Console Tools

More Topics

Shell Helpers

Interactive Console (REPL)

Running Shells as Cron Jobs

I18N Shell

Completion Shell

Plugin Shell

Routes Shell

Upgrade Shell

Server Shell

Cache Shell

Debugging

Deployment

Email

Основное использование

Выбор отправителя

Конфигурация

Настройка транспорта

Конфигурационные профили

Настройка заголовков

Отправка шаблонных писем

Отправка вложений

Использование транспорта

Создание пользовательских транспортов

Правила валидации e-mail адресов

Быстрая отправка сообщений

Отправка писем из CLI (командной строки)

Создание многоразовых писем

Error & Exception Handling

Error & Exception Configuration

Built in Exceptions for CakePHP

Exception Renderer

Creating your own Application Exceptions

Events System

Tracking Events

Establishing Priorities

Stopping Events

Additional Reading

Internationalization & Localization

Logging

Logging Configuration

Logging to Syslog

Using Levels

Logging Scopes

Формы без Модели

Создание формы

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

Установка значений формы

Получение ошибок в форме

Невалидность отдельных полей формы из контроллера

Создание HTML с помощью FormHelper

Пагинация

Использование Controller::paginate()

Непосредственное использование пагинатора

Пагинация по нескольким запросам

Управление полями используемыми для сортировки

Ограничиние максимального количества строк на страницу

Присоединение дополнительных ассоциаций

Запросы на заданную страницу

Разбиение страницы в Виде

Плагины

Установка плагина с Composer

Файл карты плагина

Загрузка плагина

Автозагрузка классов плагина

Конфигурация плагина

Использование плагинов

Создание собственных плагинов

Создание плагина с использованием Bake

Маршрутизация плагина

Контроллеры плагина

Модели плагина

Представления плагина

Переопределение шаблонов плагинов изнутри вашего приложения

Ресурсы плагина

Связывание с активами в плагине

Компоненты, Помощники и Поведение

Расширьте свой плагин

Опубликуйте свой плагин

Управление плагинами с помощью Микшера

REST

Безопасность

Security

Межсайтовая подделка запроса

Использование CsrfComponent

Интеграция с FormHelper

CSRF-защита и AJAX-запросы

Отключение компонента CSRF для определенных экшенов

Security Component

Обработка Blackhole-коллбэков

Доступ к экшенам через SSL

Ограничение межконтроллерных взаимодействий

Предотвращение подделки форм

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

CSRF Protection

Отключение компонента Security для определенных экшенов

Cессии

Настройка сессии

Встроенные обработчики сессий и конфигурации

Обработчики сессий

База данных сессий

Кеширование сессий

Настройка ini директив

Создание пользовательского обработчика сессии

Доступ к объекту сессии

Чтение и запись данных сессии

Уничтожение сессии

Возобновление идентификаторов сессии

Flash-сообщения

Testing

Fixtures

Controller Integration Testing

Testing Actions That Require Authentication

Testing Events

Running Tests

Validation

Creating Validators

Creating Reusable Validators

Вспомогательные классы

App Class

Collections

Folder & File

Hash

Hash Path Syntax

Http Client

Request Method Options

Creating Scoped Clients

Response Objects

Reading JSON and XML Response Bodies

Inflector

Summary of Inflector Methods and Their Output

Inflection Configuration

Number

Formatting Currency Values

Configure formatters

Registry Objects

Text

Highlighting Substrings

Date & Time

Creating Time Instances

Dates

Immutable Dates and Times

Xml

Плагины

Bake [https://book.cakephp.org/bake/1/ru/]

Chronos [https://book.cakephp.org/chronos/1/]

Debug kit [https://book.cakephp.org/debugkit/1]

Migrations [https://book.cakephp.org/migrations/2]

ElasticSearch

Upgrade Tool

Прочее

Константы и Функции

Глобальные функции

Основные определения констант

Сроки определения времени

Приложение

Руководство по миграции 3.x

Руководство по миграции 3.x

Основная информация

CakePHP Development Process

Глоссарий

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

ElasticSearch

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Добро пожаловать

CakePHP 3 это фреймворк для веб-разработки работающий на PHP 7.4 (мин. PHP 5.6). Прочтите Первое знакомство с CakePHP чтобы получить представление об основах CakePHP 3.

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

Читайте руководство где угодно

Читайте кулинарную книгу CakePHP почти везде. Она также доступна формате PDF и EPUB, и вы можете читать ее на других устройствах офлайн.

PDF

EPUB

Оригинальный источник GitHub [http://github.com/cakephp/docs]

Получение помощи

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

Первые шаги

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

После ознакомления с руководством, вы можете освежить в памяти ключевые моменты по созданию приложений CakePHP:

Цикл обработки запросов CakePHP

Соглашения которые использует CakePHP

Контроллеры обрабатывают запросы и координируют ваши Модели и ответы, создаваемые вашим приложением.

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

Модели Это ключевой ингредиент каждого приложения. Они осуществляют валидацию данных и являются основой логики вашего приложения.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Contents

Первое знакомство с CakePHP

Соглашения превыше конфигурации

Модель

Вид

Контроллер

Цикл обработки запросов CakePHP

Только начало

Рекомендуется к прочтению

Инструкция по началу работы

Пример Менеджер Закладок

Пример Менеджер Закладок Часть 2

Руководство по миграции 3.0

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

Инструментарий обновления

Структура папок приложения

CakePHP должен устанавливаться с помощью Composer

Пространства имен

Удаленные константы

Конфигурация

Новая ORM

Основы

Отладка

Настройки/конфигурация объекта

Кеш

Ядро

Консоль

Shell / Task

Событие

Логи (журналирование)

Роутинг (маршрутизация)

Руководства и примеры

Пример Менеджер Закладок

Пример Менеджер Закладок Часть 2

Пример создания блога

Пример создания блога - Часть 2

Пример создания блога - Часть 3

Пример создания блога - Часть 4 - Аутентификация и авторизация

Сотрудничество

Документация

Билеты

Код

Стандарты кодирования

Руководство по совместимости

Установка

Требования к установке

Установка CakePHP

Права доступа

Встроенный веб-сервер PHP

Полноценная установка

Запускайте

Переопределение URL

Конфигурация

Настройка вашего приложения

Дополнительные пути классов

Настройка интонации

Переменные среды (окружения)

Настроить класс

Чтение и запись файлов конфигурации

Загрузочный CakePHP

Отключение общих таблиц

Маршрутизация

Быстрый тур

Подключение маршрутов

Создание RESTful маршрутов

Переданные аргументы

Создание URL-адресов

Перенаправление маршрутизации

Пользовательские классы маршрутов

Создание постоянных параметров URL

Обработка именованных параметров в URL-адресах

Объекты запроса и ответа

Запрос

Ответ

Настройка файлов cookie

Настройка Cross Origin Request Headers (CORS)

Общие ошибки с неизменяемыми ответами

Коллекции файлов cookie

Контроллеры

Контроллер уровня приложения (AppController)

Поток запросов

Экшены контроллера

Взаимодействие с видами

Перенаправление на другие страницы

Загрузка дополнительных моделей

Пагинация модели

Настройка загружаемых компонентов

Настройка загружаемых хелперов

Коллбеки жизненного цикла запроса

Подробнее о контроллерах

Представления

Представление уровня приложения

Шаблоны Представления

Использование блоков Представления

Макеты

Элементы

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

Создание ваших собственных классов представления

Подробнее о Представлениях

Доступ к Базе Данных и ORM

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

Больше инфорации

Аутентификация

Рекомендуется к прочтению

Аутентификация

Авторизация

Параметры конфигурации

Тестирование экшенов защищенных AuthComponent

Консоль Bake

Кэширование

Встроенные механизмы кеширования:

Настройка класса кэша

Запись в кэш

Чтение из кеша

Удаление из кэша

Очистка закэшированных данных

Использование кеша для хранения счетчиков

Использование кеша для хранения общих результатов запроса

Использование групп

Глобальное включение или отключение кеша

Создание механизма хранения для кеша

Shells, Tasks & Console Tools

More Topics

Debugging

Deployment

Email

Основное использование

Конфигурация

Настройка заголовков

Отправка шаблонных писем

Отправка вложений

Использование транспорта

Быстрая отправка сообщений

Отправка писем из CLI (командной строки)

Создание многоразовых писем

Error & Exception Handling

Error & Exception Configuration

Built in Exceptions for CakePHP

Exception Renderer

Creating your own Application Exceptions

Events System

Tracking Events

Establishing Priorities

Stopping Events

Internationalization & Localization

Logging

Logging Configuration

Logging to Syslog

Формы без Модели

Создание формы

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

Установка значений формы

Получение ошибок в форме

Невалидность отдельных полей формы из контроллера

Создание HTML с помощью FormHelper

Пагинация

Использование Controller::paginate()

Непосредственное использование пагинатора

Пагинация по нескольким запросам

Управление полями используемыми для сортировки

Ограничиние максимального количества строк на страницу

Присоединение дополнительных ассоциаций

Запросы на заданную страницу

Разбиение страницы в Виде

Плагины

Установка плагина с Composer

Загрузка плагина

Конфигурация плагина

Использование плагинов

Создание собственных плагинов

Маршрутизация плагина

Контроллеры плагина

Модели плагина

Представления плагина

Ресурсы плагина

Компоненты, Помощники и Поведение

Расширьте свой плагин

Опубликуйте свой плагин

Управление плагинами с помощью Микшера

REST

Безопасность

Security

Межсайтовая подделка запроса

Security Component

Cессии

Настройка сессии

Встроенные обработчики сессий и конфигурации

Настройка ini директив

Создание пользовательского обработчика сессии

Доступ к объекту сессии

Чтение и запись данных сессии

Уничтожение сессии

Возобновление идентификаторов сессии

Flash-сообщения

Testing

Fixtures

Controller Integration Testing

Testing Events

Running Tests

Validation

Creating Validators

App Class

Collections

Folder & File

Hash

Hash Path Syntax

Http Client

Request Method Options

Creating Scoped Clients

Response Objects

Inflector

Summary of Inflector Methods and Their Output

Inflection Configuration

Number

Formatting Currency Values

Configure formatters

Registry Objects

Text

Highlighting Substrings

Date & Time

Creating Time Instances

Dates

Immutable Dates and Times

Xml

Константы и Функции

Глобальные функции

Основные определения констант

Сроки определения времени

Debug Kit

Миграции

Приложение

Руководство по миграции 3.x

Основная информация

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Using CakePHP

Introduction to all the key parts of CakePHP:

Установка

Конфигурация

Маршрутизация

Объекты запроса и ответа

Контроллеры

Компоненты

Контроллер страниц

Представления

Ячейки Представления

Хелперы

Представления JSON и XML

Доступ к Базе Данных и ORM

Табличные объекты

Query Builder

Entities

Error & Exception Handling

Кэширование

Logging

Формы без Модели

Cессии

REST

Аутентификация

Пагинация

Межсайтовая подделка запроса

Email

Form

Html

Validation

Testing

Deployment

Shells, Tasks & Console Tools

Сотрудничество

Руководства и примеры

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Upgrade Tool

Примечание

The documentation is not currently supported in Russian language for this page.

Please feel free to send us a pull request on Github [https://github.com/cakephp/docs] or use the Improve This Doc button to directly propose your changes.

You can refer to the english version in the select top menu to have information about this page’s topic.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Расширение возможностей Bake

эта страница переехала [https://book.cakephp.org/bake/1.x/ru/development.html].

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Генерация кода с помощью Bake

эта страница переехала [https://book.cakephp.org/bake/1.x/ru/usage.html].

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

Промежуточное ПО

Объекты промежуточного ПО дают вам возможность „обернуть“ ваше приложение в повторно используемые, составные уровни обработки запроса или логику построения ответа. Визуально ваше приложение заканчивается в центре, а промежуточное ПО обёрнуто в приложение подобно луковице. Здесь мы можем увидеть приложение, завёрнутое в Routes, Assets, Exception Handling и в промежуточное ПО заголовка - CORS.

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

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

Промежуточное ПО (Middleware) является частью нового HTTP-стека в CakePHP, который использует интерфейсы запросов и ответов PSR-7. Поскольку CakePHP использует стандарт PSR-7, вы можете использовать любое промежуточное ПО, совместимое с PSR-7, доступное на The Packagist [https://packagist.org].

Промежуточное ПО в CakePHP

CakePHP предоставляет несколько промежуточных программ для обработки общих задач в веб-приложениях:

Cake\Error\Middleware\ErrorHandlerMiddleware ловит исключения из обёрнутого промежуточного ПО и отображает страницу с ошибкой с помощью обработчика исключений Error & Exception Handling.

Cake\Routing\AssetMiddleware проверяет, обращается ли запрос к теме или файлу активов плагина, например, к CSS, JavaScript или файлу изображения, хранящимся в папке webroot плагина или в папке с соответствующей темой.

Cake\Routing\Middleware\RoutingMiddleware использует Router для анализа входящего URL-адреса и назначения параметров маршрутизации для запроса.

Cake\I18n\Middleware\LocaleSelectorMiddleware позволяет автоматически переключать язык в заголовке Accept-Language, отправленного браузером.

Cake\Http\Middleware\SecurityHeadersMiddleware упрощает добавление заголовков, связанных с безопасностью, таких как X-Frame-Options для ответов.

Cake\Http\Middleware\EncryptedCookieMiddleware даёт вам возможность манипулировать зашифрованными файлами cookie, если вам нужно манипулировать файлом cookie с обфускацией данных.

Cake\Http\Middleware\CsrfProtectionMiddleware добавляет CSRF-защиту в ваше приложение.

Использование промежуточного ПО

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

Чтобы применить промежуточное ПО ко всем запросам, используйте метод middleware вашего класса App\Application. Если у вас нет класса``AppApplication``, обратитесь к разделу Добавление нового HTTP-стека в существующее приложение для получения дополнительной информации. В начале процесса запроса вызывается hook-метод middleware вашего приложения, вы можете использовать объект MiddlewareQueue для подключения промежуточного ПО:

namespace App;

use Cake\Http\BaseApplication;
use Cake\Error\Middleware\ErrorHandlerMiddleware;

class Application extends BaseApplication
{
    public function middleware($middlewareQueue)
    {
        // Привяжите обработчик ошибок к очереди промежуточного ПО.
        $middlewareQueue->add(new ErrorHandlerMiddleware());
        return $middlewareQueue;
    }
}

Помимо добавления в конец MiddlewareQueue, вы можете выполнять различные операции:

$layer = new \App\Middleware\CustomMiddleware;

// Добавленное промежуточное ПО будет последним в очереди.
$middlewareQueue->add($layer);

// Промежуточное ПО будет первым в очереди.
$middlewareQueue->prepend($layer);

// Вставьте в определенный слот. Если слот выходит за пределы,
// он будет добавлен в конец.
$middlewareQueue->insertAt(2, $layer);

// Вставить перед другим промежуточным ПО.
// Если названный класс не может быть найден,
// будет создано исключение.
$middlewareQueue->insertBefore(
    'Cake\Error\Middleware\ErrorHandlerMiddleware',
    $layer
);

// Вставить после другого промежуточного ПО.
// Если названный класс не может быть найден, промежуточное
// ПО будет добавлено в конец.
$middlewareQueue->insertAfter(
    'Cake\Error\Middleware\ErrorHandlerMiddleware',
    $layer
);

Помимо применения промежуточного ПО для всего приложения, вы можете применять его и к определённым наборам маршрутов, используя Соединение с областью Middleware (промежуточного ПО).

Добавление промежуточного ПО из плагинов

После того как очередь промежуточного ПО была подготовлена приложением, запускается событие Server.buildMiddleware. Это событие может быть полезно для добавления промежуточного ПО из плагинов. Плагины могут регистрировать слушателей в своих сценариях начальной загрузки, которые добавляют промежуточное ПО:

// В плагине ContactManager bootstrap.php
use Cake\Event\EventManager;

EventManager::instance()->on(
    'Server.buildMiddleware',
    function ($event, $middlewareQueue) {
        $middlewareQueue->add(new ContactPluginMiddleware());
    });

Запросы и ответы PSR-7

Промежуточное ПО и новый HTTP-стек построены поверх интерфейсов запроса и ответа PSR-7 [http://www.php-fig.org/psr/psr-7/]. В то время как всё промежуточное ПО будет подвержено этим интерфейсам, ваши контроллеры, компоненты и представления не будут зависеть от этих интерфейсов.

Взаимодействие с запросами

RequestInterface предоставляет методы взаимодействия с заголовками, методом, URI и телом запроса. Чтобы взаимодействовать с заголовками, вы можете:

// Чтение заголовка в виде текста
$value = $request->getHeaderLine('Content-Type');

// Чтение заголовка в виде массива
$value = $request->getHeader('Content-Type');

// Прочитайте все заголовки как ассоциативный массив.
$headers = $request->getHeaders();

Запросы также предоставляют доступ к файлам cookie и загружаемым файлам, которые они содержат:

// Получите массив значений cookie.
$cookies = $request->getCookieParams();

// Получить список объектов UploadedFiles (загруженных файлов).
$files = $request->getUploadedFiles();

// Прочитайте данные файла.
$files[0]->getStream();
$files[0]->getSize();
$files[0]->getClientFileName();

// Переместите файл.
$files[0]->moveTo($targetPath);

Запросы содержат объект URI, который содержит методы для взаимодействия с запрошенным URI:

// Получить URI
$uri = $request->getUri();

// Чтение данных из URI.
$path = $uri->getPath();
$query = $uri->getQuery();
$host = $uri->getHost();

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

base содержит базовый каталог для вашего приложения, если он есть.

webroot содержит каталог webroot для вашего приложения.

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

session содержит экземпляр объекта CakePHP - Session. См. Доступ к объекту сессии для получения дополнительной информации о том, как использовать объект сеанса (сессии).

Взаимодействие с ответами

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

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

// Это *не* модифицирует $response.
// Новый объект не был присвоен переменной.
$response->withHeader('Content-Type', 'application/json');

// Это работает!
$newResponse = $response->withHeader('Content-Type', 'application/json');

Чаще всего вы будете устанавливать заголовки и тела ответов по запросам:

// Назначить заголовки и код состояния
$response = $response->withHeader('Content-Type', 'application/json')
    ->withHeader('Pragma', 'no-cache')
    ->withStatus(422);

// Записать в тело
$body = $response->getBody();
$body->write(json_encode(['errno' => $errorCode]));

Создание промежуточного ПО

Промежуточное ПО может быть реализовано как анонимные функции(Closures) или как invokable классы. В то время как Closures подходят для небольших задач, они же и усложняют тестирование и могут создавать сложный класс Application. Классы промежуточного ПО в CakePHP имеют несколько соглашений:

Файлы классов промежуточного ПО должны быть помещены в src/Middleware. Например: src/Middleware/CorsMiddleware.php

Классы промежуточного ПО должны быть помечены как Middleware. Например: LinkMiddleware.

Предполагается, что промежуточное ПО реализует протокол промежуточного ПО.

Пока что не формальный интерфейс, Middleware действительно имеет soft-интерфейс или „протокол“. Протокол выглядит следующим образом:

Промежуточное ПО должно реализовывать(implement) __invoke($request, $response, $next).

Промежуточное ПО должно возвращать объект, реализующий PSR-7 ResponseInterface.

Промежуточное ПО может возвращать ответ либо путём вызова $next, либо путём создания собственного ответа. Мы можем видеть(использовать) оба варианта в нашем простом промежуточном ПО:

// В src/Middleware/TrackingCookieMiddleware.php
namespace App\Middleware;

class TrackingCookieMiddleware
{
    public function __invoke($request, $response, $next)
    {
        // Вызов делегатов $next() для следующего промежуточного ПО
        // в очереди приложения.
        $response = $next($request, $response);

        // При изменении ответа, вы должны сделать это после
        // *следующего* вызова.
        if (!$request->getCookie('landing_page')) {
            $response->cookie([
                'name' => 'landing_page',
                'value' => $request->here(),
                'expire' => '+ 1 year',
            ]);
        }
        return $response;
    }
}

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

// В src/Application.php
namespace App;

use App\Middleware\TrackingCookieMiddleware;

class Application
{
    public function middleware($middlewareQueue)
    {
        // Добавьте своё промежуточное ПО в очередь
        $middlewareQueue->add(new TrackingCookieMiddleware());

        // Можете добавить и другое промежуточное ПО в очередь

        return $middlewareQueue;
    }
}

Добавление заголовков безопасности

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

X-Content-Type-Options

X-Download-Options

X-Frame-Options

X-Permitted-Cross-Domain-Policies

Referrer-Policy

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

use Cake\Http\Middleware\SecurityHeadersMiddleware;

$headers = new SecurityHeadersMiddleware();
$headers
    ->setCrossDomainPolicy()
    ->setReferrerPolicy()
    ->setXFrameOptions()
    ->setXssProtection()
    ->noOpen()
    ->noSniff();

$middlewareQueue->add($headers);

Добавлено в версии 3.5.0: SecurityHeadersMiddleware добавлено в 3.5.0

Промежуточное ПО для шифрования файлов cookie

Если в вашем приложении есть файлы cookie, содержащие данные, которые вы хотите обфусцировать, чтобы защитить от вмешательства пользователя, вы можете использовать шифрующее промежуточное ПО CakePHP для шифрования и дешифрования файлов cookie. Данные cookie шифруются через OpenSSL с использованием AES:

use Cake\Http\Middleware\EncryptedCookieMiddleware;

$cookies = new EncryptedCookieMiddleware(
    // Имена файлов cookie, которые нужно защитить
    ['secrets', 'protected'],
    Configure::read('Security.cookieKey')
);

$middlewareQueue->add($cookies);

Примечание

Рекомендуется, чтобы ключ шифрования, который вы используете для данных cookie, использовался исключительно для данных cookie.

Алгоритмы шифрования и стиль заполнения, используемые промежуточным ПО для cookie, обратно совместимы с CookieComponent из более ранних версий CakePHP.

Добавлено в версии 3.5.0: EncryptedCookieMiddleware было добавлено 3.5.0

Cross Site Request Forgery (CSRF) Middleware

Защита CSRF может применяться ко всему вашему приложению или к конкретным областям. Применяя CsrfProtectionMiddleware к вашему промежуточному стеку, защита CSRF может быть применена ко всему вашему приложению или к конкретным областям:

use Cake\Http\Middleware\CsrfProtectionMiddleware;

$options = [
    // ...
];
$csrf = new CsrfProtectionMiddleware($options);

$middlewareQueue->add($csrf);

Параметры могут быть переданы в конструктор промежуточного ПО. Доступные параметры конфигурации:

cookieName Имя файла cookie для отправки. По умолчанию используется csrfToken.

expiry Как долго должен продолжаться токен CSRF. По умолчанию используется сеанс браузера.

secure Будет ли cookie установлен с флагом Secure. То есть, cookie будет установлен только на HTTPS-соединение, и любая попытка соединения по HTTP не удастся. По умолчанию используется false.

httpOnly Будет ли cookie установлен с флагом HttpOnly. По умолчанию используется false.

field Поле формы для проверки. По умолчанию используется _csrfToken. Для изменения этого также потребуется настроить FormHelper.

Когда включено, вы можете получить доступ к текущему токену CSRF в объекте запроса:

$token = $this->request->getParam('_csrfToken');

Добавлено в версии 3.5.0: CsrfProtectionMiddleware добавлено в 3.5.0

Интеграция с FormHelper

CsrfProtectionMiddleware легко интегрируется с FormHelper. Каждый раз, когда вы создаёте форму с помощью FormHelper, она вставляет скрытое поле, содержащее токен CSRF.

Примечание

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

Защита CSRF и AJAX запросы

Помимо параметров данных запроса, токены CSRF могут быть отправлены через специальный заголовок X-CSRF-Token. Использование заголовка часто упрощает интеграцию токена CSRF с тяжелыми приложениями JavaScript или конечными точками API на основе XML/JSON.

CSRF Token можно получить через Cookie csrfToken.

Добавление нового HTTP-стека в существующее приложение

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

Сначала обновите свой webroot/index.php. Скопируйте содержимое файла из скелета скелета приложения [https://github.com/cakephp/app/tree/master/webroot/index.php].

Создайте класс Application. См. выше раздел Использование промежуточного ПО, чтобы это сделать. Или скопируйте пример из скелета приложения [https://github.com/cakephp/app/tree/master/src/Application.php].

Создайте config/requirements.php, если он не существует, и добавьте содержимое из скелета приложения [https://github.com/cakephp/app/blob/master/config/requirements.php].

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

Если вы запускаете тесты, вам также нужно будет обновить tests/bootstrap.php , скопировав содержимое файла из скелета приложения [https://github.com/cakephp/app/tree/master/tests/bootstrap.php].

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.

© Copyright 2021, Cake Software Foundation, Inc. Обновлено: апр. 09, 2021. Создано с помощью Sphinx 2.4.4.