Статьи

Масштабирование приложений Silex

По моему скромному мнению, Silex великолепен. Идеально создавать прототипы, но когда наше приложение растет, оно превращается в беспорядок. Это было то, что я думал до прошлого месяца, когда я присутствовал на большом разговоре о Silex с Хавьером Эгилузом. ХОРОШО. Масштабирование Silex это не то же самое, что с приложением Symfony, но это возможно.

Создать приложение Silex с помощью composer довольно просто:

{
    "require": {
        "silex/silex": "1.0.*"
    },
    "minimum-stability": "dev"
}

Но есть и лучший способ. Мы можем использовать скелет Фабьена Потенциера . С этим скелетом мы можем лучше организовать наш код.

Мы также можем использовать классы в качестве контроллеров вместо использования замыкания со всем кодом. У Игоря Видлера есть отличный пост об этом. Вы можете прочитать это здесь .

Сегодня я играю с Silex и хочу вам кое-что показать. Давайте начнем:

Возможно, вы знаете, что я большой поклонник Symfony Dependency Injection Container (вы можете прочитать об этом здесь и здесь ), но Silex использует Pimple. На самом деле приложение Silex расширяет класс Pimple. Моя идея заключается в следующем:

В посте Игоря мы видим, как использовать такие вещи:

$app->match('/video/{id}', 'Gonzalo123\ApiController::indexAction')->method('GET')->bind('video_info');

Моя идея — хранить эту информацию в служебном контейнере (мы будем использовать DIC Symfony) Например, здесь мы можем увидеть наши rout.yml:

routes:
  video_info:
    pattern:  /video/{id}
    controller: Gonzalo123\ApiController::initAction
    requirements:
      _method:  GET

Как мы видим, нам нужно реализовать одно расширение для псевдонима «маршруты». В этом примере мы реализуем только необходимые функции для файлов YAML.

<?php

use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class SilexRouteExtension implements ExtensionInterface
{
    /**
     * Loads a specific configuration.
     *
     * @param array            $config    An array of configuration values
     * @param ContainerBuilder $container A ContainerBuilder instance
     *
     * @throws InvalidArgumentException When provided tag is not defined in this extension
     *
     * @api
     */
    public function load(array $config, ContainerBuilder $container)
    {

    }

    /**
     * Returns the namespace to be used for this extension (XML namespace).
     *
     * @return string The XML namespace
     *
     * @api
     */
    public function getNamespace()
    {

    }

    /**
     * Returns the base path for the XSD files.
     *
     * @return string The XSD base path
     *
     * @api
     */
    public function getXsdValidationBasePath()
    {

    }

    /**
     * Returns the recommended alias to use in XML.
     *
     * This alias is also the mandatory prefix to use when using YAML.
     *
     * @return string The alias
     *
     * @api
     */
    public function getAlias()
    {
        return "routes";

    }
}

И теперь нам нужно только подготовить DIC. Согласно рекомендации Фабьена в его скелете Silex, нам нужно только изменить файл src / controllers.php

<?php

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

// Set up container
$container = new ContainerBuilder();
$container->registerExtension(new SilexRouteExtension);
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../config/'));
// load configuration
$loader->load('routes.yml');
$app['container'] = $container;

$app->mount('/api', include 'controllers/myApp.php');

$container->compile();

$app->error(function (\Exception $e, $code) use ($app) {
    if ($app['debug']) {
        return;
    }

    $page = 404 == $code ? '404.html' : '500.html';

    return new Response($app['twig']->render($page, array('code' => $code)), $code);
});

и теперь мы определяем config / rout.yml

routes:
  video_info:
    pattern:  /video/{videoId}
    controller: Gonzalo123\ApiController::initAction
    requirements:
      _method:  GET

И, наконец, магия в наших контроллерах / myApp.php:

<?php

$myApp = $app['controllers_factory'];

foreach ($container->getExtensionConfig('routes')[0] as $name => $route) {
    $controller = $myApp->match($route['pattern'], $route['controller']);
    $controller->method($route['requirements']['_method']);
    $controller->bind($name);
}
return $myApp;

Класс для этого примера: src / Gonzalo123 / ApiController.php

<?php

namespace Gonzalo123;

use Silex\Application;
use Symfony\Component\HttpFoundation\Request;

use Symfony\Component\HttpFoundation\JsonResponse;

class ApiController
{
    public function initAction(Request $request, Application $app)
    {
        return new JsonResponse(array(1, 1, $request->get('id')));
    }
}

Как вы можете видеть, идея состоит в том, чтобы использовать классы в качестве контроллеров, определить их в контейнере службы и создать необходимый код Silex, повторяющийся в конфигурации. Что вы думаете?