Spec-Zone.ru › Phalcon 3

Микроприложения

С помощью Phalcon вы можете создавать приложения, похожие на «микро-фреймворки». Для этого вам нужно написать минимальное количество кода для создания приложения PHP. Микроприложения подходят для реализации небольших приложений, API и прототипов практичным способом.

use Phalcon\Mvc\Micro;

$app = new Micro();

$app->get(
    "/say/welcome/{name}",
    function ($name) {
        echo "<h1>Welcome $name!</h1>";
    }
);

$app->handle();

Создание микроприложения

Phalcon\Mvc\Micro — это класс, ответственный за реализацию микроприложения.

use Phalcon\Mvc\Micro;

$app = new Micro();

Определение маршрутов

После создания объекта вам необходимо добавить несколько маршрутов. Phalcon\Mvc\Router управляет маршрутизацией внутри. Маршруты всегда должны начинаться с /. Ограничение метода HTTP необязательно при определении маршрутов, чтобы указать маршрутизатору соответствовать только в том случае, если запрос также соответствует методам HTTP. Следующий пример показывает, как определить маршрут для метода GET:

$app->get(
    "/say/hello/{name}",
    function ($name) {
        echo "<h1>Hello! $name</h1>";
    }
);

Метод «get» указывает, что связанный метод HTTP — GET. Маршрут /say/hello/{name} также имеет параметр {$name}, который передаётся напрямую обработчику маршрута (анонимной функции). Обработчики выполняются при совпадении маршрута. Обработчик может быть любым вызываемым элементом в пользовательской среде PHP. Следующий пример показывает, как определить различные типы обработчиков:

// With a function
function say_hello($name) {
    echo "<h1>Hello! $name</h1>";
}

$app->get(
    "/say/hello/{name}",
    "say_hello"
);

// With a static method
$app->get(
    "/say/hello/{name}",
    "SomeClass::someSayMethod"
);

// With a method in an object
$myController = new MyController();
$app->get(
    "/say/hello/{name}",
    [
        $myController,
        "someAction"
    ]
);

// Anonymous function
$app->get(
    "/say/hello/{name}",
    function ($name) {
        echo "<h1>Hello! $name</h1>";
    }
);

Phalcon\Mvc\Micro предоставляет набор методов для определения метода HTTP (или методов), для которых ограничен маршрут:

// Matches if the HTTP method is GET
$app->get(
    "/api/products",
    "get_products"
);

// Matches if the HTTP method is POST
$app->post(
    "/api/products/add",
    "add_product"
);

// Matches if the HTTP method is PUT
$app->put(
    "/api/products/update/{id}",
    "update_product"
);

// Matches if the HTTP method is DELETE
$app->delete(
    "/api/products/remove/{id}",
    "delete_product"
);

// Matches if the HTTP method is OPTIONS
$app->options(
    "/api/products/info/{id}",
    "info_product"
);

// Matches if the HTTP method is PATCH
$app->patch(
    "/api/products/update/{id}",
    "info_product"
);

// Matches if the HTTP method is GET or POST
$app->map(
    "/repos/store/refs",
    "action_product"
)->via(
    [
        "GET",
        "POST",
    ]
);

Для доступа к данным метода HTTP $app необходимо передать в замыкание:

// Matches if the HTTP method is POST
$app->post(
    "/api/products/add",
    function () use ($app) {
        echo $app->request->getPost("productID");
    }
);

Маршруты с параметрами

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

// This route have two parameters and each of them have a format
$app->get(
    "/posts/{year:[0-9]+}/{title:[a-zA-Z\-]+}",
    function ($year, $title) {
        echo "<h1>Title: $title</h1>";
        echo "<h2>Year: $year</h2>";
    }
);

Маршрут начала

Обычно стартовый маршрут в приложении — это маршрут /, и он чаще всего будет доступен через метод GET. Этот сценарий кодируется следующим образом:

// This is the start route
$app->get(
    "/",
    function () {
        echo "<h1>Welcome!</h1>";
    }
);

Правила перенаправления

Следующие правила могут быть использованы вместе с Apache для перенаправления URIS:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^((?s).*)$ index.php?_url=/$1 [QSA,L]
</IfModule>

Работа с ответами

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

// Direct output
$app->get(
    "/say/hello",
    function () {
        echo "<h1>Hello! $name</h1>";
    }
);

// Requiring another file
$app->get(
    "/show/results",
    function () {
        require "views/results.php";
    }
);

// Returning JSON
$app->get(
    "/get/some-json",
    function () {
        echo json_encode(
            [
                "some",
                "important",
                "data",
            ]
        );
    }
);

Помимо этого, у вас есть доступ к сервису «ответ», с помощью которого вы можете лучше управлять ответом:

$app->get(
    "/show/data",
    function () use ($app) {
        // Set the Content-Type header
        $app->response->setContentType("text/plain");

        $app->response->sendHeaders();

        // Print a file
        readfile("data.txt");
    }
);

Или создать объект ответа и вернуть его из обработчика:

$app->get(
    "/show/data",
    function () {
        // Create a response
        $response = new Phalcon\Http\Response();

        // Set the Content-Type header
        $response->setContentType("text/plain");

        // Pass the content of a file
        $response->setContent(file_get_contents("data.txt"));

        // Return the response
        return $response;
    }
);

Выполнение перенаправлений

Перенаправления могут выполняться для перенаправления потока выполнения на другой маршрут:

// This route makes a redirection to another route
$app->post("/old/welcome",
    function () use ($app) {
        $app->response->redirect("new/welcome");

        $app->response->sendHeaders();
    }
);

$app->post("/new/welcome",
    function () use ($app) {
        echo "This is the new Welcome";
    }
);

Генерация URL для маршрутов

Phalcon\Mvc\Url может использоваться для создания URL на основе определённых маршрутов. Вам нужно задать имя для маршрута; таким образом, служба «url» может создать соответствующий URL:

// Set a route with the name "show-post"
$app->get(
    "/blog/{year}/{title}",
    function ($year, $title) use ($app) {
        // ... Show the post here
    }
)->setName("show-post");

// Produce a URL somewhere
$app->get(
    "/",
    function () use ($app) {
        echo '<a href="', $app->url->get(
            [
                "for"   => "show-post",
                "title" => "php-is-a-great-framework",
                "year"  => 2015
            ]
        ), '">Show the post</a>';
    }
);

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

В микроприложении создаётся контейнер сервисов Phalcon\Di\FactoryDefault неявно; кроме того, вы можете создать контейнер вне приложения для управления его сервисами:

use Phalcon\Mvc\Micro;
use Phalcon\Di\FactoryDefault;
use Phalcon\Config\Adapter\Ini as IniConfig;

$di = new FactoryDefault();

$di->set(
    "config",
    function () {
        return new IniConfig("config.ini");
    }
);

$app = new Micro();

$app->setDI($di);

$app->get(
    "/",
    function () use ($app) {
        // Read a setting from the config
        echo $app->config->app_name;
    }
);

$app->post(
    "/contact",
    function () use ($app) {
        $app->flash->success("Yes!, the contact was made!");
    }
);

Синтаксис массива разрешён для удобного задания/получения сервисов в внутреннем контейнере сервисов:

use Phalcon\Mvc\Micro;
use Phalcon\Db\Adapter\Pdo\Mysql as MysqlAdapter;

$app = new Micro();

// Setup the database service
$app["db"] = function () {
    return new MysqlAdapter(
        [
            "host"     => "localhost",
            "username" => "root",
            "password" => "secret",
            "dbname"   => "test_db"
        ]
    );
};

$app->get(
    "/blog",
    function () use ($app) {
        $news = $app["db"]->query("SELECT * FROM news");

        foreach ($news as $new) {
            echo $new->title;
        }
    }
);

Обработчик ненайденного ресурса

Когда пользователь пытается получить доступ к маршруту, который не определён, микроприложение попытается выполнить обработчик «Not-Found». Пример такого поведения показан ниже:

$app->notFound(
    function () use ($app) {
        $app->response->setStatusCode(404, "Not Found");

        $app->response->sendHeaders();

        echo "This is crazy, but this page was not found!";
    }
);

Модели в микроприложениях

Модели могут использоваться прозрачно в микроприложениях, требуется только автозагрузчик для загрузки моделей:

$loader = new \Phalcon\Loader();

$loader->registerDirs(
    [
        __DIR__ . "/models/"
    ]
)->register();

$app = new \Phalcon\Mvc\Micro();

$app->get(
    "/products/find",
    function () {
        $products = Products::find();

        foreach ($products as $product) {
            echo $product->name, "<br>";
        }
    }
);

$app->handle();

Внедрение экземпляров моделей

Используя класс Phalcon\Mvc\Model\Binder, вы можете внедрять экземпляры моделей в свои маршруты:

$loader = new \Phalcon\Loader();

$loader->registerDirs(
    [
        __DIR__ . "/models/"
    ]
)->register();

$app = new \Phalcon\Mvc\Micro();
$app->setModelBinder(new \Phalcon\Mvc\Model\Binder());

$app->get(
    "/products/{product:[0-9]+}",
    function (Products $product) {
        // do anything with $product object
    }
);

$app->handle();
Поскольку объект Binder использует внутренний API Reflection, который может быть ресурсоёмким, существует возможность задать кэш. Это можно сделать, используя второй аргумент в setModelBinder(), который также может принимать имя службы или просто передав экземпляр кэша конструктору Binder.
В настоящее время Binder будет использовать только первичный ключ моделей для выполнения findFirst(). Пример маршрута для вышеуказанного будет /products/1

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

Phalcon\Mvc\Micro может отправлять события в EventsManager (если он присутствует). События срабатывают с типом «микро». Поддерживаются следующие события:

Имя события Срабатывание Можно остановить операцию?
beforeHandleRoute Вызывается основной метод, на этом этапе приложение не знает, есть ли соответствующий маршрут Да
beforeExecuteRoute Маршрут был сопоставлен и содержит допустимый обработчик, на этом этапе обработчик ещё не выполнен Да
afterExecuteRoute Срабатывает после выполнения обработчика Нет
beforeNotFound Срабатывает, когда ни один из определённых маршрутов не соответствует запрошенному URI Да
afterHandleRoute Срабатывает после успешного завершения всего процесса Да
afterBinding Срабатывает после привязки моделей, но перед выполнением обработчика Да

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

use Phalcon\Mvc\Micro;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;

// Create a events manager
$eventsManager = new EventsManager();

$eventsManager->attach(
    "micro:beforeExecuteRoute",
    function (Event $event, $app) {
        if ($app->session->get("auth") === false) {
            $app->flashSession->error("The user isn't authenticated");

            $app->response->redirect("/");

            $app->response->sendHeaders();

            // Return (false) stop the operation
            return false;
        }
    }
);

$app = new Micro();

// Bind the events manager to the app
$app->setEventsManager($eventsManager);

События middleware

Помимо менеджера событий, события можно добавлять с помощью методов «before», «after» и «finish»:

$app = new Phalcon\Mvc\Micro();

// Executed before every route is executed
// Return false cancels the route execution
$app->before(
    function () use ($app) {
        if ($app["session"]->get("auth") === false) {
            $app["flashSession"]->error("The user isn't authenticated");

            $app["response"]->redirect("/error");

            // Return false stops the normal execution
            return false;
        }

        return true;
    }
);

$app->map(
    "/api/robots",
    function () {
        return [
            "status" => "OK",
        ];
    }
);

$app->after(
    function () use ($app) {
        // This is executed after the route is executed
        echo json_encode($app->getReturnedValue());
    }
);

$app->finish(
    function () use ($app) {
        // This is executed when the request has been served
    }
);

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

$app->finish(
    function () use ($app) {
        // First 'finish' middleware
    }
);

$app->finish(
    function () use ($app) {
        // Second 'finish' middleware
    }
);

Код для middleware можно повторно использовать с помощью отдельных классов:

use Phalcon\Mvc\Micro\MiddlewareInterface;

/**
 * CacheMiddleware
 *
 * Caches pages to reduce processing
 */
class CacheMiddleware implements MiddlewareInterface
{
    public function call($application)
    {
        $cache  = $application["cache"];
        $router = $application["router"];

        $key = preg_replace("/^[a-zA-Z0-9]/", "", $router->getRewriteUri());

        // Check if the request is cached
        if ($cache->exists($key)) {
            echo $cache->get($key);

            return false;
        }

        return true;
    }
}

Затем добавьте экземпляр в приложение:

$app->before(
    new CacheMiddleware()
);

Доступны следующие события middleware:

Имя события Срабатывание Можно остановить операцию?
before До выполнения обработчика. Может использоваться для управления доступом к приложению Да
after Выполняется после выполнения обработчика. Может использоваться для подготовки ответа Нет
finish Выполняется после отправки ответа. Может использоваться для выполнения очистки Нет
finish | Выполняется после отправки ответа. Может использоваться для выполнения очистки | Нет |

Использование контроллеров в качестве обработчиков

Средние приложения, использующие подход Mvc\Micro, могут потребовать упорядочить обработчики в контроллерах. Вы можете использовать Phalcon\Mvc\Micro\Collection для группировки обработчиков, относящихся к контроллерам:

use Phalcon\Mvc\Micro\Collection as MicroCollection;

$posts = new MicroCollection();

// Set the main handler. ie. a controller instance
$posts->setHandler(
    new PostsController()
);

// Set a common prefix for all routes
$posts->setPrefix("/posts");

// Use the method 'index' in PostsController
$posts->get("/", "index");

// Use the method 'show' in PostsController
$posts->get("/show/{slug}", "show");

$app->mount($posts);

Контроллер «PostsController» может выглядеть так:

use Phalcon\Mvc\Controller;

class PostsController extends Controller
{
    public function index()
    {
        // ...
    }

    public function show($slug)
    {
        // ...
    }
}

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

$posts->setHandler("PostsController", true);
$posts->setHandler("Blog\Controllers\PostsController", true);

Возвращение ответов

Обработчики могут возвращать сырые ответы с помощью Phalcon\Http\Response или компонента, реализующего соответствующий интерфейс. Когда ответы возвращаются обработчиками, они автоматически отправляются приложением.

use Phalcon\Mvc\Micro;
use Phalcon\Http\Response;

$app = new Micro();

// Return a response
$app->get(
    "/welcome/index",
    function () {
        $response = new Response();

        $response->setStatusCode(401, "Unauthorized");

        $response->setContent("Access is not authorized");

        return $response;
    }
);

Отображение представлений

Phalcon\Mvc\View\Simple может использоваться для отображения представлений. Следующий пример демонстрирует, как это сделать:

$app = new Phalcon\Mvc\Micro();

$app["view"] = function () {
    $view = new \Phalcon\Mvc\View\Simple();

    $view->setViewsDir("app/views/");

    return $view;
};

// Return a rendered view
$app->get(
    "/products/show",
    function () use ($app) {
        // Render app/views/products/show.phtml passing some variables
        echo $app["view"]->render(
            "products/show",
            [
                "id"   => 100,
                "name" => "Artichoke"
            ]
        );
    }
);

Обратите внимание, что этот код использует Phalcon\Mvc\View\Simple, который использует относительные пути вместо контроллеров и действий. Если вы хотите использовать Phalcon\Mvc\View\Simple вместо этого, вам необходимо изменить параметры метода render():

$app = new Phalcon\Mvc\Micro();

$app["view"] = function () {
    $view = new \Phalcon\Mvc\View();

    $view->setViewsDir("app/views/");

    return $view;
};

// Return a rendered view
$app->get(
    "/products/show",
    function () use ($app) {
        // Render app/views/products/show.phtml passing some variables
        echo $app["view"]->render(
            "products",
            "show",
            [
                "id"   => 100,
                "name" => "Artichoke"
            ]
        );
    }
);

Обработка ошибок

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

$app = new Phalcon\Mvc\Micro();

$app->get(
    "/",
    function () {
        throw new \Exception("An error");
    }
);

$app->error(
    function ($exception) {
        echo "An error has occurred";
    }
);

Если обработчик возвращает «false», исключение останавливается.

Связанные источники

  • Создание простого REST API — учебник, объясняющий, как создать микроприложение для реализации RESTful веб-службы.
  • Магазин стикеров — очень простое микроприложение, использующее подход микро-MVC [Github].

© 2011–2017 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/latest/reference/micro.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API