krak/presenter

展示者设计模式的小型简单实现

v2.0.0 2014-11-05 08:37 UTC

This package is auto-updated.

Last update: 2024-09-18 17:33:21 UTC


README

简单而强大的展示者模式实现。

安装

使用composer安装。

{
    "repositories": [
        {
            "type": "vcs",
            "url": "http://gitlab.bighead.net/bighead/krak-presenter.git"
        }
    ],
    "require": {
        "krak/presenter": "~2.0",

        /* only if you use the CachePresenter */
        "doctrine/cache": "~1.0",

        /* include if you use the EventListener\ViewListener */
        "symfony/event-dispatcher": "~2.0",
        "symfony/http-kernel": "~2.0",
        "symfony/http-foundation": "~2.0",

        /* include if you use the Provider\ViewPresenterServiceProvider */
        "pimple/pimple": "~3.0"
    }
}

设计

krak Presenters分为三个主要组件:展示者、装饰者和视图模型。

展示者和装饰者都实现了展示者接口

interface Presenter
{
    /**
     * Present the view and return the content associated with it
     * @param mixed $view
     * @return string
     */
    public function present($view);

    /**
     * Whether or not the presenter can actually present the data/view
     * @param mixed $data
     * @return bool
     */
    public function canPresent($view);
}

展示者/装饰者将接受一个视图模型进行展示,然后返回其内容。

因此调用

$content = $presenter->present($view);

将始终返回与视图关联的内容。

展示者

展示者将接受视图模型并“展示”它们,即将其渲染为字符串响应。此库附带两个不同的展示者:视图和模拟。

视图展示者将从视图模型中获取适当的视图文件内容。

模拟展示者主要用于测试,但它只是SplObjectStorage的一个简单包装。

ViewPresenter

<?php

use Krak\Presenter\ViewPresenter;
use Krak\Presenter\View\View;
use Krak\Presenter\View\ViewTrait;
use Symfony\Component\Config\FileLocator;

class MyView implements View
{
    use ViewTrait;

    private $view_file = 'my-view';

    // or just define the getViewFile function

    public function getViewFile()
    {
        return 'my-view';
    }

    public function getHeader()
    {
        return '<h1>Header</h1>';
    }
}

$presenter = new ViewPresenter(FileLocator(__DIR__ . '/views'), 'php', 'v');
echo $presenter->present(new MyView());
// __DIR__ . /views/my-view.php
<html>
    <?=$v->getData()?>
</html>

最后的echo语句将输出

<html>
    <h1>Header</h1>
</html>

ViewPresenter构造函数接受一个文件定位器、一个扩展(默认为空字符串)以及视图模型的别名(默认为'view')。

扩展

$presenter->setExtension('php');
echo $presenter->getExtension();
// outputs: php

如果有设置扩展,则将在每个视图文件名末尾追加.{ext}

别名

$presenter->setViewAlias('v_alias');
echo $presenter->getViewAlias();
// outputs: v_alias

// then in some-view file
<html>
    <?=$v_alias->getData()?>
</html>

MockPresenter

use Krak\Presenter\MockPresenter;

$presenter = new MockPresenter();
$view = new stdClass();
$presenter->mock($view, 'some-data');

echo $presenter->present($view);

输出将是

some-data

装饰者

展示者系统设计在展示者接口周围,这使得装饰器的使用非常容易。

此库附带两个装饰器:缓存和树。

缓存

缓存展示者只是一个装饰器,它将尝试在渲染视图之前从缓存中获取展示者数据。

use Krak\Presenter\CachePresenter;
use Krak\Presenter\View\CacheableView;
use Doctrine\Common\Cache\ArrayCache;

class MyView implements CacheableView
{
    public function getCacheTuple()
    {
        return array('my-view-cache-key', 3600);
    }
}

/* $presenter is another instanceof Presenter defined beforehand */
$cache = new ArrayCache();
$cache_presenter = new CachePresenter($presenter, $cache);

$data = $cache_presetner->present(new MyView());

var_dump($data === $cache->fetch('my-view-cache-key');

输出将是true,因为缓存展示者将数据添加到了缓存中,下一个对同一视图模型的展示调用将直接从缓存返回数据,而不是将其委托给其内部的展示者。

bool(true)

现在,CachePresenter将只缓存实现CacheableView接口的视图模型。

树展示者是另一个装饰器,允许展示视图的层次结构/树。树展示者仅遍历实现TreeView接口的视图。关于树视图的一个重要注意事项是如何一次性处理多个项目。

调用$tree_presenter->presenter($view)将返回树对象的输出,它是树的根。然后它将遍历树,并使用其内部展示者获取每个子视图的所有数据。然后它将展示的内容注入到子视图中,以便父视图可以使用。

use Krak\Presenter\TreePresenter;
use Krak\Presenter\View\View;
use Krak\Presenter\View\TreeView;
use Krak\Presenter\View\TreeViewTrait;
use Krak\Presenter\ViewPresenter;

class TreeViewChild implements View, TreeView
{
    use ViewTrait;
    use TreeViewTrait;

    private $view_file = 'child-view.php';

    public function getData()
    {
        return 'some-data';
    }
}

class TreeViewParent implements View, TreeView
{
    use TreeViewTrait;

    private $child1;
    private $view_file = 'parent-view.php';

    public function __construct()
    {
        $this->child1 = new TreeViewChild();
    }

    public function getChildren()
    {
        return array($this->child1);
    }

    public function getChild1()
    {
        return $this->child1;
    }
}

$view_presenter = new ViewPresenter($locator);
$tree_presenter = new TreePresenter($view_presenter);

echo $tree_presenter->present(new TreeViewParent());
// child-view.php
<span><?=$view->getData()?></span>
// parent-view.php
<div>
    <?=$view->getChild1()->getContent()?>
</div>

最后的echo语句将显示

<div>
    <span>some-data</span>
</div>

视图模型

视图模型是负责渲染视图的实际对象。使用Krak Presenter库,有四种类型的视图:视图、树视图、缓存视图和匿名视图。

视图

interface View
{
    /**
     * @return string
     */
    public function getViewFile();
}

非常简单的界面,用于检索要加载的视图文件。该界面由ViewPresenter使用。您可以使用ViewTrait为您定义这些方法。

TreeView

interface TreeView
{
    /**
     * @return TreeView[]
     */
    public function getChildren();

    /**
     * Set the rendered content for this view
     * @var string
     */
    public function setContent($content);

    /**
     * get the rendered content for this view
     * @return string
     */
    public function getContent();
}

该接口设计用于与TreePresenter一起使用,如您所见,它允许遍历TreeView模型构成的树,并允许每个视图保存其内容。您可以使用TreeViewTrait为您定义这些方法。

CacheableView

interface CacheableView
{
    /**
     * Returns a tuple of the cache key and ttl
     * @return array
     */
    public function getCacheTuple();
}

该接口设计用于与CachePresenter一起使用。缓存元组如下所示

array('key', 3600);

其中元组有一个键为'key'和一个TTL为3600。

匿名视图

有时您不需要构建整个类,只是为了渲染带有一些数据的文件,为此,我们有了匿名视图模型。

匿名视图实现了View接口。您可以这样创建匿名视图

use Krak\Presenter\View\AnonymousView;

$v = new AnonymousView('some-view-file', array('key'=>'val'));
// or
$v = AnonymousView::create('some-view-file'); // data is defaulted to an empty array

然后在some-view-file

<div>
    <?=$view->key?>
</div>

此视图将渲染为

<div>
    val
</div>

缓冲区

缓冲区是一个在处理TreeView时非常有用的简单实用工具。缓冲区本质上只是保存字符串内容,但您可以将同一个缓冲区与多个视图共享,它们将向同一个缓冲区追加内容。

例如,假设您想允许每个视图定义一些JavaScript。然后每个视图定义的JavaScript应该输出到页面底部。您可以使用缓冲区轻松完成。

首先,我们假设每个视图都有一个具有相同实例的缓冲区作为公共变量,名为js_buf

// root view file
<html>
    <body>
        <?=$view->getInnerView()->getContent()?>
    </body>
    <?=$view->js_buf->getContents()?>
</html>
// inner view file
<?php $view->js_buf->start()?>
<script type="text/javascript">
    // some javascript
</script>
<?php $view->js_buf->end()?>
<div>
    <!-- content -->
</div>

视图监听器

如果您使用基于Symfony HttpKernel的项目,您可以注册视图监听器,以便您的控制器可以返回视图并将它们转换为响应。

use Krak\Presenter\EventListener\ViewListener,
    Krak\Presenter\ViewPresenter;

$view_presenter = ...
$listener = new ViewListener($view_presenter);

/* dispatcher instanceof Symfony\Component\EventDispatcher\EventDispatcher */
$dispatcher->addSubscriber($listener);

然后在您的控制器中,您只需返回一个视图模型,如下所示,它将被转换为响应

    public function showAction()
    {
        // ...

        /* instanceof Krak\Presenter\View\View */
        return new ViewModel();
    }

服务提供者

如果您在项目中使用Pimple,则可以使用ViewPresenterServiceProvider将视图呈现器注册为服务。

$container->register(new ViewPresenterServiceProvider(), [
    'presenter.ext' => 'php',
    'presenter.view_alias' => 'view',
    'presenter.paths' => [__DIR__],
    // instead of paths, you can also just specify the file locator
    'presenter.file_locator' => function() {
        return new Symfony\Component\Config\FileLocator([__DIR__]);
    }
]);

$locator = $container['presenter.file_locator'];
$presenter = $container['presenter'];

如果您想使用装饰器,则可以使用Pimple::extend方法,如下所示

use Krak\Presenter\TreePresenter;

$container->extend('presenter', function($presenter, $c)
{
    return new TreePresenter($presenter);
});