002 Полное MVC приложение
Вся сложная работа по дирижированию работой MVC в Phalcon обычно выполняется с помощью Phalcon\Mvc\Application. Этот компонент инкапсулирует все сложные операции, требуемые в фоновом режиме, отвечает за создание каждого необходимого компонента и интеграцию его с проектом, позволяя паттерну MVC работать как положено.
Следующий код начальной загрузки является типичным для Phalcon приложения:
<?php
use Phalcon\Mvc\Application;
// Регистрация автозагрузчиков
// ...
// Регистрация сервисов
// ...
// Обработка запроса
$application = new Application($di);
try {
    $response = $application->handle();
    $response->send();
} catch (\Exception $e) {
    echo 'Exception: ', $e->getMessage();
}
Основная работа контроллера происходит при вызове метода handle():
<?php $response = $application->handle();
Ручная обработка
Если вы не желаете использовать Phalcon\Mvc\Application, код выше должен быть изменен следующим образом:
<?php
// Получить сервис 'router'
$router = $di['router'];
$router->handle();
$view = $di['view'];
$dispatcher = $di['dispatcher'];
// Посылаем обработанные параметры маршрута в диспетчер
$dispatcher->setControllerName(
    $router->getControllerName()
);
$dispatcher->setActionName(
    $router->getActionName()
);
$dispatcher->setParams(
    $router->getParams()
);
// Запускаем отображение вид
$view->start();
// Доставляем запрос
$dispatcher->dispatch();
// Отображаем соответствующие виды
$view->render(
    $dispatcher->getControllerName(),
    $dispatcher->getActionName(),
    $dispatcher->getParams()
);
// Заканчиваем отображение вида
$view->finish();
$response = $di['response'];
// Передаем вывод из вида в ответ
$response->setContent(
    $view->getContent()
);
// Отсылаем ответ
$response->send();
Следующая доработка компонента view в Phalcon\Mvc\Application, делает его пригодным для Rest API:
<?php
use Phalcon\Http\ResponseInterface;
// Получение сервиса 'router'
$router = $di['router'];
$router->handle();
$dispatcher = $di['dispatcher'];
// Передача обработанных параметров маршрута в диспетчер
$dispatcher->setControllerName(
    $router->getControllerName()
);
$dispatcher->setActionName(
    $router->getActionName()
);
$dispatcher->setParams(
    $router->getParams()
);
// Доставка запроса
$dispatcher->dispatch();
// Получение значения, которое вернуло последнее выполненное действие
$response = $dispatcher->getReturnedValue();
// Проверка является ли возвращенное значение объектом 'response'
if ($response instanceof ResponseInterface) {
    // Send the response
    $response->send();
}
Еще один вариант, который перехватывает исключения произведенные в диспетчере, пересылаемые в другие действия последовательно:
<?php
use Phalcon\Http\ResponseInterface;
// Получение сервиса 'router'
$router = $di['router'];
$router->handle();
$dispatcher = $di['dispatcher'];
// Передача обработанных параметров маршрута диспетчеру
$dispatcher->setControllerName(
    $router->getControllerName()
);
$dispatcher->setActionName(
    $router->getActionName()
);
$dispatcher->setParams(
    $router->getParams()
);
try {
    // Доставка запроса
    $dispatcher->dispatch();
} catch (Exception $e) {
    // Произошло исключение, доставка контроллеру/методу предназначенному для этого
    // Передача обработанных параметров маршрута диспетчеру
    $dispatcher->setControllerName('errors');
    $dispatcher->setActionName('action503');
    // доставка запроса
    $dispatcher->dispatch();
}
// Получение возвращаемого значения из последнего выполненного действия
$response = $dispatcher->getReturnedValue();
// Проверка является ли возвращенное значение объектом 'response'
if ($response instanceof ResponseInterface) {
    // Send the response
    $response->send();
}
Хотя реализация выше является более подробной, чем код необходимый при использовании Phalcon\Mvc\Application, она предлагает альтернативу начальной загрузки вашего приложения. В зависимости от ваших нужд, вы можете захотеть иметь полный контроль над тем, что должно быть создано или нет, или заменить определенные компоненты своими для расширения функциональности по умолчанию.
Одномодульные и многомодульные приложения
С помощью этого компонента можно запускать разные типы MVC приложений:
Одномодульное приложение
Отдельные приложения MVC состоят только из одного модуля. Пространства имен можно использовать, но не обязательно. Такое приложение будет иметь следующую файловую структуру:
single/
  app/
    controllers/
    models/
    views/
  public/
    css/
    img/
    js/Если не используется пространство имён, то в качестве файла загрузки MVC можно использовать следующий подход:
<?php
use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\Mvc\Application;
use Phalcon\Di\FactoryDefault;
$loader = new Loader();
$loader->registerDirs(
    [
        '../apps/controllers/',
        '../apps/models/',
    ]
);
$loader->register();
$di = new FactoryDefault();
// Регистрация view компонента
$di->set(
    'view',
    function () {
        $view = new View();
        $view->setViewsDir('../apps/views/');
        return $view;
    }
);
$application = new Application($di);
try {
    $response = $application->handle();
    $response->send();
} catch (\Exception $e) {
    echo $e->getMessage();
}
Если же используются пространства имён, то инициализация приложения может быть реализована следующим образом:
<?php
use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\Application;
use Phalcon\Di\FactoryDefault;
$loader = new Loader();
// Использование автозагрузчика с префиксами пространств имён
$loader->registerNamespaces(
    [
        'Single\Controllers' => '../apps/controllers/',
        'Single\Models'      => '../apps/models/',
    ]
);
$loader->register();
$di = new FactoryDefault();
// Регистрация пространства имён по умолчанию для контроллеров
$di->set(
    'dispatcher',
    function () {
        $dispatcher = new Dispatcher();
        $dispatcher->setDefaultNamespace('Single\Controllers');
        return $dispatcher;
    }
);
// Регистрация компонента представлений
$di->set(
    'view',
    function () {
        $view = new View();
        $view->setViewsDir('../apps/views/');
        return $view;
    }
);
$application = new Application($di);
try {
    $response = $application->handle();
    $response->send();
} catch (\Exception $e) {
    echo $e->getMessage();
}
Многомодульное приложение
Многомодульное приложение использует один и тот же корень документа для нескольких модулей. В этом случае можно использовать следующую файловую структуру:
multiple/
  apps/
    frontend/
       controllers/
       models/
       views/
       Module.php
    backend/
       controllers/
       models/
       views/
       Module.php
  public/
    css/
    img/
    js/Каждый каталог в apps/ имеет свою собственную структуру MVC.  Module.php присутствует для настройки конкретных параметров каждого модуля, таких как автозагрузчики или пользовательские службы:
<?php
namespace Multiple\Backend;
use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\DiInterface;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\ModuleDefinitionInterface;
class Module implements ModuleDefinitionInterface
{
    /**
     * Регистрация специфичного автозагрузчика для модуля
     */
    public function registerAutoloaders(DiInterface $di = null)
    {
        $loader = new Loader();
        $loader->registerNamespaces(
            [
                'Multiple\Backend\Controllers' => '../apps/backend/controllers/',
                'Multiple\Backend\Models'      => '../apps/backend/models/',
            ]
        );
        $loader->register();
    }
    /**
     * Регистрация специфичных сервисов для модуля
     */
    public function registerServices(DiInterface $di)
    {
        // Registering a dispatcher
        $di->set(
            'dispatcher',
            function () {
                $dispatcher = new Dispatcher();
                $dispatcher->setDefaultNamespace('Multiple\Backend\Controllers');
                return $dispatcher;
            }
        );
        // Регистрация компонента представлений
        $di->set(
            'view',
            function () {
                $view = new View();
                $view->setViewsDir('../apps/backend/views/');
                return $view;
            }
        );
    }
}
Для загрузки многомодульных MVC приложений можно использовать такой файл автозагрузки:
<?php
use Phalcon\Mvc\Router;
use Phalcon\Mvc\Application;
use Phalcon\Di\FactoryDefault;
$di = new FactoryDefault();
// Указываем маршруты для модулей
$di->set(
    'router',
    function () {
        $router = new Router();
        $router->setDefaultModule('frontend');
        $router->add(
            '/login',
            [
                'module'     => 'backend',
                'controller' => 'login',
                'action'     => 'index',
            ]
        );
        $router->add(
            '/admin/products/:action',
            [
                'module'     => 'backend',
                'controller' => 'products',
                'action'     => 1,
            ]
        );
        $router->add(
            '/products/:action',
            [
                'controller' => 'products',
                'action'     => 1,
            ]
        );
        return $router;
    }
);
// Создание приложения
$application = new Application($di);
// Регистрация установленных модулей
$application->registerModules(
    [
        'frontend' => [
            'className' => 'Multiple\Frontend\Module',
            'path'      => '../apps/frontend/Module.php',
        ],
        'backend'  => [
            'className' => 'Multiple\Backend\Module',
            'path'      => '../apps/backend/Module.php',
        ]
    ]
);
try {
    // Обработка запроса
    $response = $application->handle();
    $response->send();
} catch (\Exception $e) {
    echo $e->getMessage();
}
Если вы желаете хранить конфигурацию модуля в загрузочном файле, можно использовать анонимную функцию для регистрации модуля:
<?php
use Phalcon\Mvc\View;
// Создание компонента представлений
$view = new View();
// Установка опций для компонента представлений
// ...
// Регистрация установленных модулей
$application->registerModules(
    [
        'frontend' => function ($di) use ($view) {
            $di->setShared(
                'view',
                function () use ($view) {
                    $view->setViewsDir('../apps/frontend/views/');
                    return $view;
                }
            );
        },
        'backend' => function ($di) use ($view) {
            $di->setShared(
                'view',
                function () use ($view) {
                    $view->setViewsDir('../apps/backend/views/');
                    return $view;
                }
            );
        }
    ]
);
Когда Phalcon\Mvc\Application зарегистрирует модули, каждый сопоставленный маршрут должен возвращать существующий модуль. Каждый зарегистрированный модуль должен иметь соответствующий класс и функцию для настройки самого модуля. Каждое определение класса модуля обязательно должно реализовать два метода: registerAutoloaders() и registerServices(), они будут автоматически вызваны Phalcon\Mvc\Application при выполнении модуля.
События приложения
Phalcon\Mvc\Application может отправлять события в EventsManager (если он присутствует). События вызываются с типом application. Поддерживаются следующие события:
| Название события | Вызывается | 
|---|---|
| boot | Выполняется, когда приложение обрабатывает первый запрос | 
| beforeStartModule | До инициализации зарегистрированного модуля | 
| afterStartModule | После инициализации зарегистрированного модуля | 
| beforeHandleRequest | До выполнения цикла диспетчера | 
| afterHandleRequest | После выполнения цикла диспетчера | 
Следующий пример демонстрирует, как прикрепить слушателей к этому компоненту:
<?php
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
$eventsManager = new EventsManager();
$application->setEventsManager($eventsManager);
$eventsManager->attach(
    'application',
    function (Event $event, $application) {
        // ...
    }
);
