MVC приложения

All the hard work behind orchestrating the operation of MVC in Phalcon is normally done by 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();

Ручная обработка

If you do not wish to use Phalcon\Mvc\Application, the code above can be changed as follows:

<?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();

The following replacement of Phalcon\Mvc\Application lacks of a view component making it suitable for Rest APIs:

<?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();
}

Although the above implementations are a lot more verbose than the code needed while using Phalcon\Mvc\Application, offers an alternative in bootstrapping your application. В зависимости от ваших нужд, вы можете захотеть иметь полный контроль над тем, что должно быть создано или нет, или заменить определенные компоненты своими для расширения функциональности по умолчанию.

Одномодульные и многомодульные приложения

С помощью этого компонента можно запускать разные типы MVC приложений:

Одномодульное приложение

Single MVC applications consist of one module only. Namespaces can be used but are not necessary. An application like this would have the following file structure:

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();
}

Многомодульное приложение

A multi-module application uses the same document root for more than one module. In this case the following file structure can be used:

multiple/
  apps/
    frontend/
       controllers/
       models/
       views/
       Module.php
    backend/
       controllers/
       models/
       views/
       Module.php
  public/
    css/
    img/
    js/

Each directory in apps/ have its own MVC structure. A Module.php is present to configure specific settings of each module like autoloaders or custom services:

<?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;
                }
            );
        }
    ]
);

When Phalcon\Mvc\Application have modules registered, always is necessary that every matched route returns a valid module. Каждый зарегистрированный модуль должен иметь соответствующий класс и функцию для настройки самого модуля. Each module class definition must implement two methods: registerAutoloaders() and registerServices(), they will be called by Phalcon\Mvc\Application according to the module to be executed.

События приложения

Phalcon\Mvc\Application is able to send events to the EventsManager (if it is present). События вызываются с типом 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) {
        // ...
    }
);

Дополнительная литература