Нативный MVC для Silex PHP Framework

в 10:24, , рубрики: mvc, php, silex, метки: , ,

После прочтения статьи, рассказывающей как модифицировать микро-фреймворк Silex под архитектуру MVC, у меня возникло двойственное впечатление. Способ имеет право на жизнь, однако:

  1. в проекте не всегда нужна ORM, хочется иметь и простую реализацию Модели;
  2. в Silex уже есть (хотя и не совсем явные) нативные контроллеры;
  3. писать свои автозагрузчики, когда есть возможность добавить нужное в Composer — не есть хорошо.

Давайте посмотрим, что можно сделать.

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

+ project
|   + protected
|       composer.json
|       composer.phar
|       composer.lock
|       + app
|           + Controllers
|           + Models
|           + Views
|       + vendor
|       + providers
|           + Providers
|   + public
|       .htaccess
|       index.php
|       + css
|       + img
|       + js

Я настроил виртуальный хост на папку project/public, поэтому .htaccess будет один

# project/public/.htaccess
<IfModule mod_rewrite.c>
    Options -MultiViews
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

Устанавливаем Silex с помощью менеджера пакетов Composer

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

  • возможность одной командой обновить все библиотеки до актуальных или необходимых версий;
  • генерация одного автозагрузчика для всего необходимого.

Создадим файл project/protected/composer.json примерно такого содержания (в зависимости от того, что необходимо вам):

{
    "require":{
        "silex/silex":"1.0.*"
    },
    "autoload":{
        "psr-0":{
            "Providers" : "",
            "Controllers" : "app/"
        }
    }
}

Здесь мы хотим получить сам Silex и указываем, что наших Провайдеров (о них чуть дальше) будем загружать из project/protected/Providers, а Контроллеры из project/protected/app/Controllers.
Набор команд для установки:

cd /path/to/project/protected
curl -s http://getcomposer.org/installer | php
php composer.phar install

Silex Providers

Providers — замечательная возможность Silex, позволяющая внедрить стороннюю функциональность. Провайдеры бывают двух типов: ControllerProviderInterface для контроллеров и ServiceProviderInterface для всего остального (в нашем случае — для Модели).

Модель

Напишем простой Service Provider и загрузчик моделей.

<?php
// project/protected/Providers/ModelsServiceProvider.php

namespace Providers;

use SilexApplication;
use SilexServiceProviderInterface;

class ModelsServiceProvider implements ServiceProviderInterface {
    public function register(Application $app) {
        $app['models.path'] = array();
        $app['models']      = $app->share(function($app) {
            return new Models($app);
        });
    }
    public function boot(Application $app) {

    }
}
class Models {
    private $app;
    public function __construct(Application $app) {
        $this->app = $app;
    }
    public function load($modelName, $modelMethod, $data = array()) {
        require_once $this->app['models.path'] . $modelName . '.php';

        $Model = new $modelName($this->app);

        return $Model->$modelMethod($data);
    }

}

С Моделью, в общем-то, всё. Теперь мы можем загрузить любую, находящуюся в директории project/protected/app/Models с помощью конструкции $app['models']->load('Class', 'Method', $data) с возможностью передать в нее нужные данные $data. Осталось лишь зарегистрировать нашего провайдера в Silex.

Контроллер

Единственное, о чем нам непосредственно нужно позаботиться, так это об автозагрузке классов контроллеров, но это за нас уже сделал Composer. Так что теперь мы можем подключать контроллеры стандартным методом Silex mount. Посмотрим, как будет выглядеть файл index.php, простейший Контроллер и Модель.

index.php

<?php // project/public/index.php

require_once __DIR__ . '/../protected/vendor/autoload.php';

$app = new SilexApplication();

$app->register(new ProvidersModelsServiceProvider(), array(
    'models.path' => __DIR__ . '/../protected/app/models/'
));

$app->mount('/', new ControllersIndex());

$app->run();

Контроллер

<?php // project/protected/Controllers/Index.php

namespace Controllers;

use SilexApplication;
use SilexRoute;
use SilexControllerProviderInterface;
use SilexControllerCollection;

class Index implements ControllerProviderInterface {

    public function connect(Application $app) {
        $index = new ControllerCollection(new Route());

        $index->get('/', function() use ($app) {
            $label = $app['models']->load('Pages', 'index');

            return $label;
        });

        $index->get('/{name}', function($name) use ($app) {
            $name = $app['models']->load('Pages', 'hello', $name);

            return "Hello{$name}";
        });

        return $index;
    }
}

Кратко по коду. Метод connect() говорит Silex, что роуты, описанные внутри, надо обрабатывать как часть контроллера, который мы замаунтили в index.php (в данном случае базовым URL для этого контроллера является корень приложения — /). Далее создается переменная $index, она представляет собой что-то вроде частички $app и имеет только функции роутинга. Сами роуты пишутся как обычно.

Модель

<?php // project/protected/Models/Pages.php

class Pages {

    public function index() {
        return "Index";
    }

    public function hello($name) {
        return ", {$name}!";
    }

}

Что в итоге? Простейшая реализация MVC, с возможностью в любой момент начать писать в стандартном для Silex стиле. В данном контексте не рассматривалось Представление, так как уже есть множество готовых решений, из который лично я предпочитаю использовать Twig, благо интеграция с Silex у него 100%.
Весь проект доступен на github.

Автор: geakstr

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js