hoffmann/silex-view

Silex 的通用视图

dev-master 2014-01-05 12:08 UTC

This package is not auto-updated.

Last update: 2024-09-28 13:56:26 UTC


README

Silex-View 类似于 Django 类视图Flask 可插入视图,是用于 PHP 微框架 Silex 的基于类的视图的实现。

在 Silex 中,您可以将闭包附加到路由上。以下是一个简单的示例。

$app->get('/blog/show/{id}', function (Application $app, Request $request, $id) {
    ...
});

Silex 根据类型提示注入 $app$request 变量。当路由匹配时,调用闭包,并将 $app$request 绑定到您的 Silex 应用程序和当前请求。可以在函数定义中添加类似 $id 参数的路由变量。

这是一种构建小型应用的简单快捷方式。但在我看来,将控制器逻辑放在闭包中会导致代码紧密耦合,难以测试。

Silex 文档 展示了如何将控制器放在类中

$app->get('/', 'Igorw\Foo::bar');

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

namespace Igorw
{
    class Foo
    {
        public function bar(Request $request, Application $app)
        {
            ...
        }
    }
}

这种方法更好。现在您可以测试控制器类,使用模拟的 $request$application 对象。作为奖励,您的路由定义小巧且干净,您的控制器与路由代码分离,可重用。

我不喜欢两点

  • 您将控制器类作为字符串传递给路由函数。这可能是一种 PHP 方式,但这对我来说并不合适。而且,这让我烦恼的是,我选择的 IDE phpstrom 无法识别它,因此您不能点击它或使用转到定义快捷键。

  • 您无法向控制器构造函数传递参数。这对我的影响更大。

Silex-View 有一个简单的 BaseView 类,您可以从中继承

use SilexView\BaseView

class MyView extends BaseView
{
    private $greeting;
    function __construct($greeting){
        $this->greeting = $greeting;
    } 

    function get($request, $app){
        return $this->greeting.' '.$request->get('name');
    }  
}

并在路由定义中使用它

$app->get('/hello/{name}', MyView::asView('hello'));

BaseView::asView() 是一个静态方法,它返回一个闭包,当路由匹配时将被调用

class BaseView
{
    public static function asView()
    {
        $classname= get_called_class();
        $args = func_get_args();
        return function(\Symfony\Component\HttpFoundation\Request $request, 
                        \Silex\Application $app) use ($classname, $args){
            $cls = new \ReflectionClass($classname);
            $instance = $cls->newInstanceArgs($args);
            return $instance->dispatch($request, $app);
        };
...

传递给 asView 函数的所有参数都将转发到继承的控制器类的构造函数。受 Django 类视图 的启发,BaseView 类根据请求的 HTTP 方法分发请求。因此,GET 请求将传递到控制器的 get(..) 方法,而 POST 请求将传递到 post(...) 方法。使用这种约定,构建 REST 控制器非常简单且干净。

class BaseView
{
    ...
    protected $http_method_names = array('get', 'post', 'put', 'delete', 'head', 'options', 'trace');
    public function dispatch($request, $app)
    {
        $method = strtolower($request->getMethod());
        //if no head method is defined use get
        if ("head" === $method && ! method_exists($this, "head"))
            $method = "get";
        if (! (in_array($method, $this->http_method_names) && 
               method_exists($this, $method)))
            return $this->httpMethodNotAllowed($method);
        return $this->$method($request, $app);
    }

TemplateView 类是用于渲染模板的 GET 请求的快捷方式。您只需要创建一个子类并实现 getContextData() 函数,该函数应返回一个数组,其中包含在您的 Twig 模板中需要的参数。

class MyTemplate extends TemplateView
{
    function getContextData($request, $app)
    {
        return array('name' => "Joe");
    }
} 

实现如下

class TemplateView extends BaseView
{

    /*
     * Get the Template Name for the view
     * default implementation is to use the class name without namespace
     */
    function getTemplateName(){
        $cls = explode('\\', get_class($this));
        return end($cls).'.twig';

    }

    function get($request, $app)
    {
        return $app["twig"]->render($this->getTemplateName(), 
                                    $this->getContextData($request, $app));
    }

    function getContextData($request, $app)
    {
    }
}