Все еще торт, часть 3.0.0

http://www.sanisoft.com/blog/2015/03/24/cakephp-3-0-0-released/
  • Перевод
  • Tutorial
Наконец-то вышел в релиз CakePHP 3.0.0.

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




Что нового появилось в CakePHP 3.0.0:
  • новая ORM
  • более быстрая и гибкая маршрутизация
  • улучшенная миграция
  • улучшенная локализация
  • улучшенная панель инструментов для отладки
  • наличие менеджера зависимостей
  • автономные библиотеки
  • View Cells


Я уже достаточно наигрался с версией 3.0, поэтому давайте немного разберемся с возможностями этого фреймворка.

Меню навигации по тутору:

Миграции в CakePHP 3 – Быстрый запуск
View Cells в CakePHP 3.0 – Быстрый запуск
Events в CakePHP 3 – инструкция в 4 шага
Формы Twitter bootstrap в CakePHP 3
Изменение разметки для нумерации страниц в CakePHP 3
Темы в CakePHP 3 – Пошаговая инструкция


Миграции в CakePHP 3 – Быстрый запуск



В CakePHP 3 миграции – это отдельный плагин и обертка для библиотек Phinx.

Для начала немного истории. Каждая команда, в которой я работал, использовала определенные системы контроля версий для работы с кодом, среди которых «любимчиком» была система Git. Однако если дело доходило до изменений синхронизации структуры базы данных, многие были озадачены. Использовать миграции казалось каким-то чудом! :)

Шаг 1 – Установка плагина миграции

Установите CakePHP и затем отредактируйте файл composer.json, добавив следующее:
"require": {
    "cakephp/migrations": "dev-master"
}

Запустите обновление менеджера зависимостей и затем загрузите плагин в ваше приложение, отредактировав файл bootstrap.php и добавив туда следующую строку:
Plugin::load('Migrations');

Вот и все! Теперь вы готовы к созданию первой миграции.

Шаг 2 – Создание первой миграции

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

./bin/cake migrations create CreatePostsTable

и вы увидите

using migration path /home/tariquesani/WWW/migration/config/Migrations
created ./config/Migrations/20141015052852_create_posts_table.php

Как вы видите, для вас был создан скелет для миграции. Откройте его в своем любимом редакторе кода, где он будет выглядеть в виде
<?php

use Phinx\Migration\AbstractMigration;

class CreatePostsTable extends AbstractMigration
{
    /**
     * Change Method.
     *
     * More information on this method is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-change-method
     *
     * Uncomment this method if you would like to use it.
     *
    public function change()
    {
    }
    */
    
    /**
     * Migrate Up.
     */
    public function up()
    {
    
    }

    /**
     * Migrate Down.
     */
    public function down()
    {

    }
}

Давайте напишем кое-что в function up() для создания таблицы Posts. «function up» нужна для всего, что вы хотите добавить или изменить, развивая проект, а «function down()» нужна для реверса или отката этих изменений. Вы также можете использовать новую функцию Phinx change(), которая является реверсивной, однако сейчас мы будем работать только с «up» и «down». Отредактируйте эти две функции до вида
   /**
     * Migrate Up.
     */
    public function up()
    {
        $posts = $this->table('posts');

        $posts->create();


    }

    /**
     * Migrate Down.
     */
    public function down()
    {

        $this->dropTable('posts');

    }


Шаг 3 – Запуск созданной миграции

Для запуска миграции введите

./bin/cake migrations migrate

Вы увидите

Welcome to CakePHP v3.0.0-beta2 Console
— App: src
Path: /home/tariquesani/WWW/migration/src/
— using migration path /home/tariquesani/WWW/migration/config/Migrations
using environment default
using adapter mysql
using database caketest

== 20141015052852 CreatePostsTable: migrating
== 20141015052852 CreatePostsTable: migrated 0.2205s

Если вы посмотрите в базу данных, то там определенно будет таблица Posts с автоинкрементным полем ID. Мы могли бы добавить необходимые столбцы в нашей первой миграции, однако так как мы этого не сделали, давайте изменим эту таблицу, создав еще одну миграцию.

Шаг 3 – Создание другой таблицы миграции

Выполните

./bin/cake migrations create AlterPostsTable

Отредактируйте функцию Change новой миграции, внеся туда следующий код. Обратите внимание: мы ничего не пишем в down().
    public function change()
    {
    
        $posts = $this->table('posts');

        $posts->addColumn('title', 'string')
              ->addColumn('body', 'text')
              ->addColumn('created', 'datetime')
              ->addColumn('modified', 'datetime')
              ->save();

    }

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

Шаг 4 – Попытка отката

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

./bin/cake migrations rollback

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

./bin/cake migrations status

то вы увидите

Status Migration ID Migration Name
— up 20141015052852 CreatePostsTable
down 20141015060152 AlterPostsTable

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

Также вы можете выполнять запросы SQL непосредственно в файлах миграции (как это сделать описано в документации Phinx). Также есть разговоры о том, что будет реализована возможность первоначального импорта структуры из существующей базы данных, что будет очень хорошо для выполнения проектов с использованием Миграций. Так как миграции – это текстовые файлы, их можно сохранить в формате VCS и синхронизировать со всеми разработчиками.


View Cells в CakePHP 3.0 – Быстрый запуск



Концепт View Cells в CakePHP 3 разрабатывался довольно давно. Книжное определение View Cells: мини-контроллеры с помощью которых можно просмотреть логику класса и отобразить шаблоны. В 2007 году о них писал Энди Доусон. Если бы мне пришлось рассказать, где используются View Cells, я бы сказал: «Think Widgets» — если вы работаете с WordPress, вы понимаете, о чем я. В CakePHP всегда была возможность сделать requestAction(), но это вызывало перегрузку процессора. Со временем пробовали применять различные варианты, включая инстанцирование контроллера, создание помощников, но пользоваться ими всегда было сложно. View Cells имеет два неоспоримых преимущества: небольшой размер и модульность.

Давайте попробуем использовать View Cell в новом приложении. Я предполагаю, что вы можете установить CakePHP 3 и создать приложение для Публикаций. В таблице базы данных должно быть поле Title, кроме обязательного столбца ID, остальные же поля вы устанавливаете по своему усмотрению.

Сейчас я вам покажу, как создать виджет Последние публикации с использованием View Cells.

Шаг 1

После того, как вы создали и проверили работу приложения Публикации, снова откройте терминал и создайте View Cell, используя следующую команду

cake bake cell Posts

Эта команда создаст файлы src/View/Cell/PostsCell.php и src/Template/Cell/Posts/display.ctp, а также файл для написания тестов. Файл PostsCell.php – это класс, эквивалентный мини-контроллеру, а файл display.ctp – файл шаблона для отображения метода класса PostsCell. Об этом чуть позже.

Шаг 2

Откройте файл PostsCell.php в своем любимом редакторе. Он будет выглядеть примерно так
<?php
namespace App\View\Cell;

use Cake\View\Cell;

/**
 * Posts cell
 */
class PostsCell extends Cell {

/**
 * List of valid options that can be passed into this
 * cell's constructor.
 *
 * @var array
 */
	protected $_validCellOptions = [];

/**
 * Default display method.
 *
 * @return void
 */
	public function display() {
            #add code 
	}

}

Вы увидите, что классы модуля подчиняются определенным правилам.
• модули находятся в App\View\Cell
• названия классов заканчиваются на Cell
• классы наследуют от Cake\View\Cell.

При этом создается пустой метод display(). Это метод по умолчанию, который вызывается при отображении модуля. Давайте введем в метод display код. Мы собираемся создать виджет Последние публикации, для этих целей подойдет следующий код
	public function display() {
		$this->loadModel('Posts');

		$total_posts = $this->Posts->find()->count();

		$recent_posts = $this->Posts->find()
									->select('title')
									->order(['created' => 'DESC'])
									->limit(3)
									->toArray();

		$this->set(['total_posts' => $total_posts, 'recent_posts' => $recent_posts]);
	}

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

Шаг 3

Откройте файл src/Template/Cell/Posts/display.ctp и скопируйте туда код
<div class="actions">
	<h3><?= __('Recent Posts') ?></h3>
	<ul>
		<li><strong>You have <?= $total_posts ?> posts total</strong></li>
		<?php 
		foreach ($recent_posts as $post) {
			echo "<li>".$post['title']."</li>";
		}
		?>
	</ul>
</div>

Код довольно простой, поэтому я не буду утомлять вас объяснениями. Мы почти закончили… Еще один шаг!

Шаг 4

Последний шаг – отобразить модуль так, как нам необходимо. Я хочу отображать этот виджет на каждой странице, поэтому я воспользуюсь решением по умолчанию. Откройте файл src/Template/Layout/default.ctp и скопируйте туда две строки кода под строкой $this->Flash->render().
<?php  $cell = $this->cell('Posts'); ?>
<?= $cell ?>

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

Вывод

View Cells – гибкий и мощный инструмент, который сейчас встроен непосредственно в CakePHP. Конечно, с помощью него можно выполнять гораздо более интересные задачи, чем простое создание виджетов. Я настоятельно рекомендую прочесть book.cakephp.org/3.0/en/views/cells.html для того, чтобы полностью осознать пользу этого инструмента.


Events в CakePHP 3 – инструкция в 4 шага



В CakePHP система Events появилась с версии 2.1, а в версии 3.0 это изменит принципы разметки, созданные почти год назад. Система Events в CakePHP во многом похожа с шаблоном «Наблюдатель». Я думаю, что вы уже знакомы с системой событий в CakePHP в том виде, в котором она была представлена в версии 2.х, но если нет – ознакомьтесь с этой замечательной статьей Мартина Бина.

Давайте разберемся на примере очень простого сценария, где вы будете руководить событием при сохранении Публикации. Класс-слушатель реагирует на это событие и записывает о нем данные в файл журнала. Конечно, это не бог весть какое серьезное действие, но это всего лишь пример для начала работы с системой Events в CakePHP 3. На самом деле, классы-слушатели выполняют гораздо больше работы. Если вы объедините Events с чем-то вроде Gearman, вы сможете получить действительно мощное и масштабируемое приложение.

Шаг 1

Установите CakePHP 3 и создайте шаблон MVC для Публикаций. Вид вашей таблицы для Публикаций абсолютно не важен, так как мы будем работать с полем ID и классом-слушателем.

Шаг 2

Откройте файл PostsTable.php, который расположен в src/Model/Table и скопируйте следующий код в метод afterSave
    public function afterSave($created, $options = array()) {
        if ($created) {

            $event = new Event('Model.Post.created', $this, $options);

            $this->eventManager()->dispatch($event);
        }
    }

Этот код написал на простом английском языке. В нем сказано, что при создании публикации создается новый Event под названием «Model.Post.created» и отправляется при помощи eventManager. Вы можете назвать его как угодно, но лучше следовать принципам наименования «Слой.Объект.глагол». (было «Вы можете вызвать его в любой момент», оригинал «You can call your event whatever you want»)

Шаг 3

Создадим класс-слушатель. Класс-слушатель – это реализация интерфейса Cake\Event\EventListener. Классы-слушатели, которые реализуют этот интерфейс, должны работать с соблюдением метода implementedEvents(). Этот метод возвращает ассоциативный массив с названиями всех событий, которые обрабатываются классом. Я хочу сохранить своего слушателя в папке Event на том же уровне, как и Модель, Контроллер и пр. Я задал область имен App\Event, таким образом, он может автоматически загружаться через PSR-4. Создайте файл под названием PostListener.php в папке Event со следующим кодом
namespace App\Event;

use Cake\Log\Log;
use Cake\Event\EventListener;

class PostListener implements EventListener {

    public function implementedEvents() {
        return array(
            'Model.Post.created' => 'updatePostLog',
        );
    }

    public function updatePostLog($event,  $entity, $options) {
         Log::write(
        'info',
        'A new post was published with id: ' . $event->data['id']);
    }
}

ImplementedEvents сообщает системе, какой метод необходимо вызвать при возникновении определенного события. В нашем случае при событии Model.Post.created.будет вызван метод updatePostLog. Метод updatePostLog просто записывает сообщение в файл logs/debug.log.

Шаг 4

Наконец, мы должны зарегистрировать этот класс-слушатель. Для этого мы воспользуемся доступным EventManager. Вставьте следующий код в конце файла config/bootstrap.php
use App\Event\PostListener;
$PostListener = new PostListener();

use Cake\Event\EventManager;
EventManager::instance()->attach($PostListener);

Вот и все! Теперь попробуйте создать публикацию или несколько публикаций и в файле debug.log появится

2014-08-27 04:48:57 Info: A new post was published with id: 14
2014-08-27 05:34:27 Info: A new post was published with id: 15
2014-08-28 04:44:41 Info: A new post was published with id: 16

Теперь у вас есть базовая система событий. Вот здесь вы можете посмотреть руководство пользователя и узнать, что еще вы можете сделать с этой системой – book.cakephp.org/3.0/en/core-libraries/events.html.


Формы Twitter bootstrap в CakePHP 3



Много вопросов было о создании форм Twitter bootstrap в CakePHP 3. Дискуссии велись вокруг разных подходов.

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

Мы создадим текстовое поле для ввода данных в виде

И при возникновении ошибки оно будет выглядеть вот так

HTML-код такого поля выглядит следующим образом
<div class="form-group">
  <label for="title">Title</label>
  <div class="input-group">
    <span class="input-group-addon">@</span>
    <input type="text" name="title" class="form-control" required="required" maxlength="50" id="title">
  </div>
</div>

Синтаксис формы помощника, который мы будем использовать
echo $this->Form->input('title', ['addon'=>'@', "class"=>"form-control"]);

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

Шаг 1

Создайте файл под названием form-templates.php в src/Config/ folder.

Шаг 2

В файл form-templates.php поместите следующий код (не забудьте добавить открывающий тэг php)
$config = [
    'addoninput' => '<div class="input-group"><span class="input-group-addon">{{addon}}</span><input type="{{type}}" name="{{name}}"{{attrs}}></div>',
    'inputContainer' => '<div class="form-group">{{content}}</div>',
    'inputContainerError' => '<div class="form-group has-error has-feedback {{type}}{{required}}">{{content}}{{error}}</div>',
    'error' => '<div class="alert alert-danger" >{{content}}</div>',
];

Здесь я определил дополнительный шаблон под названием addoninput с дополнительными символами для использования. Я также переопределил inputContainer, inputContainerError и шаблоны ошибок для лучших возможностей bootstrap.

Шаг 3

Двух шагов было бы достаточно, если бы я не хотел получить расширение, но я хочу его получить, как как оно здорово выглядит и визуально полезно для пользователей. Для нормальной работы расширения мне нужно переопределить Основной виджет. Скопируйте Basic.php из cakephp/src/View/Widget в app/src/View/Widget. Единственное отличие заключается в методе визуализации, который теперь выглядит
    public function render(array $data, ContextInterface $context) {
        $data += [
            'name' => '',
            'val' => null,
            'type' => null,
            'escape' => true,
        ];

        $data['value'] = $data['val'];
        $addon = '';
        $template = 'input';

        if(isset($data['addon'])) {
            $addon = $data['addon'];
            $template = 'addoninput';
        }

        unset($data['val'], $data['addon']);

        return $this->_templates->format( $template, [
            'name' => $data['name'],
            'addon'=> $addon,
            'attrs' => $this->_templates->formatAttributes(
                $data,
                ['name', 'type']
            ),
        ]);
    }

Так как это был эксперимент, мне просто нужно было сделать несколько проверок в $data и изменить шаблон в addoninput.

Шаг 4

Наконец, позволим приложению узнать о виджетах и шаблонах форм, добавив следующий код в контроллер приложения
public $helpers = [
     'Form' => [
       'widgets'  => [
         '_default'  => ['App\View\Widget\Basic'],
       ],
       'templates' => 'form-templates.php',
    ]
];

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

Основные правила для использования пользовательских шаблонов, виджетов и форм помощников:
  • изменения разметки – изменения только шаблонов
  • условная разметка, объединение полей ввода (думаю, ввода даты и времени) – создание виджетов
  • дополнительная обработка данных, изменение значений по умолчанию, например, отсутствия маркировки, обязательных проверок HTML 5 – нарушение пользовательской формы помощника.



Изменение разметки для нумерации страниц в CakePHP 3



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

echo $this->Paginator->numbers(array('before' => ''));

Это работало, но было запутано и часто приводило к появлению ошибок.

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



В такой



Шаг 1

Создайте файл под названием paginator-templates.php в папке src/Config/.

Шаг 2

Поместите следующий код в paginator-templates.php (не забудьте про открывающий тэг php)
$config = [
    'number' => '<option>{{text}}</option>',
    'current' => '<option selected >{{text}}</option>',
];


Шаг 3

Откройте шаблон с нумерацией, например: Posts/index.ctp из предыдущей записи в блоге и замените следующие строки
echo $this->Paginator->prev('< ' . __('previous'));
echo $this->Paginator->numbers();
echo $this->Paginator->next(__('next') . ' >');

на
<form method="get">
    <label for="pageselect">Page number</label>
    <select name='page' id='pageselect'>
          <?= $this->Paginator->numbers(); ?>
    </select>
    <button type="submit">Go</button>
</form>

Важная строка в приведенном выше фрагменте кода — $this->Paginator->numbers(); все остальное – это простая форма, которая использует метод GET. Я мог бы использовать помощник для форм, чтобы создать начало и конец формы, но здесь это не имеет особого смысла

Шаг 4

Добавьте следующий код в контроллер
    public $helpers = [
         'Paginator' => ['templates' => 'paginator-templates']
         ];

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

Дополнительно

Здесь мы изменили разметку нумерации страниц для всего приложения. Мы можем сделать это и для одной темы путем загрузки шаблонов из плагинов
public $helpers = [
         'Paginator' => ['templates' => 'Twit.paginator-templates']
         ];

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

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

Ниже приведены доступные шаблоны по умолчанию, таким образом, вы можете менять из в зависимости от своих нужд и предпочтений
$config = [
    'nextActive' => '<li class="next"><a rel="next" href="{{url}}">{{text}}</a></li>',
    'nextDisabled' => '<li class="next disabled"><span>{{text}}</span></li>',
    'prevActive' => '<li class="prev"><a rel="prev" href="{{url}}">{{text}}</a></li>',
    'prevDisabled' => '<li class="prev disabled"><span>{{text}}</span></li>',
    'counterRange' => '{{start}} - {{end}} of {{count}}',
    'counterPages' => '{{page}} of {{pages}}',
    'first' => '<li class="first"><a href="{{url}}">{{text}}</a></li>',
    'last' => '<li class="last"><a href="{{url}}">{{text}}</a></li>',
    'number' => '<li><a href="{{url}}">{{text}}</a></li>',
    'current' => '<li class="active"><span>{{text}}</span></li>',
    'ellipsis' => '<li class="ellipsis">...</li>',
    'sort' => '<a href="{{url}}">{{text}}</a>',
    'sortAsc' => '<a class="asc" href="{{url}}">{{text}}</a>',
    'sortDesc' => '<a class="desc" href="{{url}}">{{text}}</a>',
    'sortAscLocked' => '<a class="asc locked" href="{{url}}">{{text}}</a>',
    'sortDescLocked' => '<a class="desc locked" href="{{url}}">{{text}}</a>',
];




Темы в CakePHP 3 – Пошаговая инструкция



Почти каждое приложение, которое я разрабатывал, имело темы, которые мог изменять как конечный пользователь, так и администратор, а также эти темы можно было менять «на лету». В CakePHP 2.x выполнить такое было просто. Еще после первого альфа-релиза CakePHP 3.x я захотел попробовать, как в версии 3 была реализована работа с темами.
Я хочу показать, как превратить это


Тема по умолчанию

в это


Тема Twitter bootstrap, которую мы создадим

Я думаю, вы знаете, как установить CakePHP 3 и умеете создавать Модель, Контроллер и ассоциации.

Шаг 0

До начала работы создайте таблицу публикаций с использованием SQL вида
--
-- Table structure for table `posts`
--

CREATE TABLE IF NOT EXISTS `posts` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(50),
  `body` text,
  `created` datetime,
  `modified` datetime,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

--
-- Dumping data for table `posts`
--

INSERT INTO `posts` (`id`, `title`, `body`, `created`, `modified`) VALUES
(1, 'The title', 'This is the post body.', '2014-02-28 14:15:57', '2014-07-19 11:02:12'),
(2, 'A title once again', 'And the post body follows.', '2014-02-28 14:15:57', '2014-07-19 11:02:19'),
(3, 'Title strikes back', 'This is really exciting! Not.', '2014-02-28 14:15:57', '2014-07-19 11:02:24'),
(5, 'This is a new post', 'Content of new post', '2014-07-19 04:41:18', '2014-07-19 04:41:18');


Шаг 1

Создайте Модель Публикации, Контроллер Публикаций и Отображения. На этой стадии если вы перейдете в yourapp/posts, вы сможете увидеть экран, похожий на первый скриншот

Шаг 2

Темы в CakePHP 3 являются полноценными плагинами, поэтому создайте плагин под названием «Twit» путем ввода следующей команды в командную строку

Console/cake bake plugin Twit

Шаг 3

Мы собираемся создать тему под названием Twitter Bootstrap. Для этого загрузите дистрибутив Bootstrap, распакуйте его и скопируйте CSS, шрифты и папку JS в plugins/Twit/webroot/. Обратите внимание: это может быть оптимизировано, потому что обслуживание статистических ресурсов с помощью диспетчера неоправданно дорого, но об этом как-нибудь потом.

Шаг 4

Несколько слов о том, как в CakePHP 3 работают темы. Файлы вашей темы находятся в plugins/Twit/src/Template, структура файлов в папке plugins/Twit/src/Template идентична структуре в src/Template/ в основном приложении, таким образом, файл разметки новой темы находится в plugins/Twit/src/Template/Layout. Создайте файл под названием default.ctp и скопируйте туда следующий код. Не пугайтесь, по-настоящему необходимы только строки кода 10-23 и 54-58, остальное – это структура документа Twitter Bootstrap.
<?php
$cakeDescription = 'CakePHP: the rapid development php framework';
?>
<!DOCTYPE html>
<html lang="en">
  <head>
    <?= $this->Html->charset(); ?>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>
        <?= $cakeDescription; ?>:
        <?= $this->fetch('title'); ?>
    </title>
    <?php
        echo $this->Html->meta('icon');

        echo $this->Html->css('bootstrap.min.css');
        echo $this->Html->css('starter-template.css');

        echo $this->fetch('meta');
        echo $this->fetch('css');
        echo $this->fetch('script');
    ?>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>

    <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="#">Home</a></li>
            <li><a href="#about">About</a></li>
            <li><a href="#contact">Contact</a></li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </div>
    <div class="container">
        <h1><?= $this->Html->link($cakeDescription, 'http://cakephp.org'); ?></h1>

        <?= $this->Flash->render(); ?>

        <?= $this->fetch('content'); ?>
    </div>
  </body>
</html>


Шаг 5

Аналогично вставьте следующий код в файл index.ctp и поместите его в папку plugins/Twit/src/Template/Posts
<div class="row">
    <div class="col-md-8">
        <h2><?= __('Posts'); ?></h2>
        <table class="table table-bordered">
        <tr>
            <th><?= $this->Paginator->sort('id'); ?></th>
            <th><?= $this->Paginator->sort('title'); ?></th>
            <th><?= $this->Paginator->sort('body'); ?></th>
            <th><?= $this->Paginator->sort('created'); ?></th>
            <th class="actions"><?= __('Actions'); ?></th>
        </tr>
        <?php foreach ($posts as $post): ?>
        <tr>
            <td><?= h($post->id); ?> </td>
            <td><?= h($post->title); ?> </td>
            <td><?= h($post->body); ?> </td>
            <td><?= h($post->created); ?> </td>
            <td class="actions">
                <?= $this->Html->link(__('View'), ['action' => 'view', $post->id], ['class' => 'btn btn-sm btn-default']); ?>
                <?= $this->Html->link(__('Edit'), ['action' => 'edit', $post->id], ['class' => 'btn btn-sm btn-info']); ?>
                <?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $post->id], ['confirm' => __('Are you sure you want to delete # %s?', $post->id), 'class' => 'btn btn-sm btn-danger']); ?>
            </td>
        </tr>
        <?php endforeach; ?>
        </table>
        <p><?= $this->Paginator->counter(); ?></p>
        <ul class="pagination">
        <?php
            echo $this->Paginator->prev('< ' . __('previous'));
            echo $this->Paginator->numbers();
            echo $this->Paginator->next(__('next') . ' >');
        ?>
        </ul>
    </div>
    <div class="col-md-4">
            <h3><?= __('Actions'); ?></h3>

            <?= $this->Html->link(__('New Post'), ['action' => 'add'], ['class' => 'btn btn-default']); ?>
    </div>
</div>


Шаг 6

Откройте файл AppController.php основного приложения и добавьте следующую строку для активации новой темы

public $theme = 'Twit';

Шаг 7

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

Выводы



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

Меню навигации по тутору:
Миграции в CakePHP 3 – Быстрый запуск
View Cells в CakePHP 3.0 – Быстрый запуск
Events в CakePHP 3 – инструкция в 4 шага
Формы Twitter bootstrap в CakePHP 3
Изменение разметки для нумерации страниц в CakePHP 3
Темы в CakePHP 3 – Пошаговая инструкция

P.S. Перевод был подготовлен командой проекта Vircities компании IlkFinKom. Подписывайтесь на наш блог и читайте еще больше интересных материалов как про наш проект, так и про инструменты которыми мы пользуемся в его реализации.
  • +6
  • 14,8k
  • 3

IlkFinKom

37,78

Наш игровой мир — ваши амбиции!

Поделиться публикацией
Комментарии 3
    +1
    Спасибо за статью, то что мне было нужно! Работать с ним пока не собираюсь, ZF2 полностью устраивает, а вот для того чтобы поиграться и посмотреть фреймворк для общего развития — то что надо. И примеры есть и простор для дальнейшего творчества
      0
      Хм… Есть пара интересных инструментов. Нужно будет посмотреть что еще они сделали, особенно с их ORM.
      Я уже довольно долго использую CakePHP 2.x, но ограничил его применение небольшими проектами из-за излишней магии, которую нельзя отключить не поменяв код фреймворка, что в итоге и пришлось сделать =( С того момента я поставил на CakePHP крест. До сих пор его использую только из-за того, что у меня на нем уже куча наработок для несложных сайтов и все-равно периодически матерю его магию (мыши плакали, кололись, но продолжали жрать кактус).
      Пока я склоняюсь к Symfony — мне понравилось как он устроен. Достаточно ненавязчиво, хотя и посложнее чем CakePHP.
        0
        Я уже пожалуй не смогу выбрать столь монолитный фреймворк, как CakePHP или Yii, после знакомства с Symfony2 и Dependency Injection.

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

        Самое читаемое