05.02.2019     474

В этой заметке я расскажу о PHP-фреймворке Symfony с использованием понятий из MODX. Статья будет полезна разработчикам, которые долгое время работают на MODX, но планируют использовать так же Symfony для некоторых проектов. Такие разработчики привыкли писать сниппеты, плагины, создавать шаблоны, дополнительные поля (TV параметры) и упаковывать всё это в пакет. Как всё это можно делать на Symfony?

Шаблоны

В отличии от MODX в Symfony все шаблоны хранятся в файлах в папке "templates" (можно изменить в конфигурации). Есть два варианта шаблонизатора: чистый PHP и Twig. PHP более быстр, а Twig более безопасен. Т.к. шаблоны Twig кэшируются в PHP, то скорость работы тоже довольно хорошая. Этот шаблонизатор имеет на много больше возможностей по сравнению с шаблонизатором MODX. Новички часто путают плейсхолдеры [[*pagetitle]] и [[+pagetitle]]. У Twig такой путаницы нет, значения переменных всегда выводятся одинаково - {{ pagetitle }}. Пример с фильтром: {{ pagetitle | upper }}.

Сниппеты

Сниппеты - одна из самых важных и удобных сущностей в MODX. Один сниппет содержит в себе все части MVC (Model-View-Controller). Пример использования сниппета getResources:

[[!getResources?
&parents=`[[*id]]`
&where=`{"template:=":8}`
&tpl=`myRowTpl`]]

В Symfony тоже можно создать что-то похожее на сниппет - расширение Twig (фильтр или функция). Для этого нужно в файле "src/Twig/AppExtension.php" зарегистрировать, например, функцию:

<?php

namespace App\Twig;

use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class AppExtension extends AbstractExtension
{
    public function getFunctions()
    {
        return [
            new TwigFunction('contentList', [AppContentList::class, 'contentListFunction'], [// Указываем имя класса и метод
                'is_safe' => ['html'],// Функция возвращает безопасный HTML код
                'needs_environment' => true// Нужен доступ к окружению Twig для рендеринга шаблона
            ])
        ];
    }
}

Теперь нужно создать класс "AppContentList" (название любое), который будет формировать нужный вам HTML-код.

<php

namespace App\Twig;

class AppContentList
{
    /** @var ContainerInterface */
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }
    
    /**
     * @param \Twig_Environment $environment
     * @param string $chunkName
     * @param $collectionName
     * @param array $criteria
     * @param array $orderBy
     * @param int $limit
     * @return string
     */
    public function contentListFunction(
        \Twig_Environment $environment,
        $chunkName,
        $collectionName,
        $criteria,
        $orderBy = ['_id' => 'asc'],
        $limit = 20
    )
    {
        $templateName = sprintf('catalog/%s.html.twig', $chunkName);
        ...
        return $environment->render($templateName, [
            'items' => $items,
            'total' => $total
        ]);
    }
}

Этот класс нужно зарегистрировать (или использовать существующий AppRuntime) в сервисах (config/services.yaml):

services:
    ...
    App\Twig\AppContentList:
        public: false
        arguments: ['@service_container']
        tags:
            - {name: twig.runtime}

Так эта функция будет вызываться в шаблоне:

{{ contentList(
    'content_list',
    'products',
    {"isActive": true, "parentId": currentCategory.id },
    {"_id": "desc"},
    20
) }}

Согласитесь, очень похоже на вызов сниппета в MODX. Все параметры отправляются в метод как аргументы. Так же интересная особенность, что, если используется база данных MongoDB, то JSON-строка {"isActive": true, "parentId": currentCategory.id } это уже готовый запрос в БД, который дальше уже не нужно преобразовывать во что-то иное. Т.е. в шаблоне можно делать более сложные запросы и для этого не нужно лесть в PHP-код. Этот пример взят из Shopkeeper 4. Более подробно о MongoDB расскажу в одной из следующих статей.

Плагины

В MODX плагин - это PHP-функция (или целый класс), которая прикрепляется к определенным событиям и может получать данные из функции вызова этого события. Можно создать любые свои события, которые нужно сохранить в БД. Пример вызова события в MODX:

$modx->invokeEvent('OnChunkRender',array(
   'id' => $chunk->get('id'),
));

В Symfony тоже можно создавать события и использовать уже созданные системные.

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

$event = new GenericEvent($order);
$order = $eventDispatcher->dispatch('order.before_create', $event)->getSubject();

Остается только создать подписчика на событие и положить файл в папку "src/App/EventSubscriber/".

<?php

// OrderSubscriber.php

namespace App\EventSubscriber;

use App\MainBundle\Document\Order;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\GenericEvent;

class OrderSubscriber implements EventSubscriberInterface
{

    public static function getSubscribedEvents()
    {
        return [
            'order.before_create' => 'onOrderBeforeCreate',
            'order.created' => 'onOrderCreated'
        ];
    }

    public function onOrderBeforeCreate(GenericEvent $event)
    {
        /** @var Order $order */
        $order = $event->getSubject();

        // Сделать что-то...

    }

    public function onOrderCreated(GenericEvent $event)
    {
        /** @var Order $order */
        $order = $event->getSubject();

        // Сделать что-то...

    }
}

Если в конфигурации (config/services.yaml) включено "services._defaults.autoconfigure", то все подписчики, которые находятся в папке "src/App/EventSubscriber/", будут автоматически регистрироваться. Т.е. если вы хотите использовать существующее событие, нужно только создать класс-подписчик на это событие в определенной папке.

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

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

  1. Установить локальный веб-сервер для разработки, например, WampServer. В данном случае можно просто установить PHP, но сервер вам пригодится в будущем.
  2. Установить Git.
  3. Установить Composer.
  4. Установить Symfony Client.
  5. В командной строке перейти в папку "C:\wamp64\www" (пример для Windows): cd C:\wamp64\www.
  6. Запустить команду symfony new --demo symfony_demo.
  7. Перейти в созданную папку cd symfony_demo.
  8. Запустить команду symfony serve --no-tls.
  9. Открыть в браузере адрес http://localhost:8000. Вы получили готовое приложение на Symfony с админкой и обучающими материалами.