可以给视图加复合主键_灵活视图处理的模式,第1部分–使用复合材料

可以给视图加复合主键

I would dare say that views, in the many forms they can take, are underrated entities in search of a true identity. From the first moment MVC started making inroads in the PHP community, the acronym’s “V” has been unfairly reduced to the level of a plain template whose responsibility is limited to rendering a few HTML pages.

我敢说,以多种形式采取的观点都是为了寻求真实身份而被低估的实体。 从MVC开始进入PHP社区的第一刻起,首字母缩略词的“ V”就被不公平地减少到一个纯模板的级别,该模板的职责仅限于呈现几个HTML页面。

While no one will disagree that the role of views is intimately bound to outputting data, treating them as just mere templates with little or no logic behind them is rather daft. Views not only can be full-fledged objects carrying state, but also observers of a model, flexible enough to accommodate and react accordingly to fluctuations whenever the model’s state changes. And while those changes surely will ripple down the fancy user interface, this is merely a consequence of their responsive nature rather than the primary reason for their existence.

尽管没有人会不同意视图的角色与输出数据紧密相关,但将它们视为仅模板,而背后却很少或没有逻辑是相当愚蠢的。 视图不仅可以是带有状态的完整对象,而且可以是模型的观察者,它们具有足够的灵活性以适应模型状态变化时的波动并对波动做出相应的React。 尽管这些更改肯定会削弱精美的用户界面,但这仅是其响应能力的结果,而不是其存在的主要原因。

On the flip side, this kind of late revindication raises an interesting point as well. If views are such nifty components, exposing to the outside world all sort of rich and wonderful features, we might believe that setting up a mechanism capable of massaging them in a pragmatic manner would be harder to implement than just rendering a few naive HTML templates.

另一方面,这种后期的辩护也提出了一个有趣的观点。 如果视图是这么漂亮的组件,并且向外界展示了各种丰富而美妙的功能,我们可能会相信,建立一种能够以务实的方式对它们进行处理的机制比仅呈现一些简单HTML模板更难实现。

In a sense, it could be. As usual, seemingly-complex issues can be addressed by appealing to simpler approaches. Moreover, since in many cases views are conceptually modeled as skeletal PHP objects sharing some sort of common rendering interface (thus honoring Polymorphism), it’s relatively simple to treat them either individually or in conjunction through the reins of a unified API.

从某种意义上说可能是这样。 像往常一样,可以通过采用更简单的方法来解决看似复杂的问题。 此外,由于在许多情况下,视图在概念上被建模为共享某种公共呈现接口的骨架PHP对象(因此尊重多态性),因此将它们单独或通过统一API进行联合处理相对简单。

If you reread the previous sentence, you’ll see a resemblance to the functionality that the Composite pattern brings to the table. That’s pretty nice, sure. Though, it’d be even nicer to inject recursively views into other views without spoiling the contract with client code, something that would let us implement some neat decorators further down the road capable of assembling pipelines of views at runtime which could then be rendered all in one go.

如果您重新阅读上一句话,您会发现类似于Composite模式带到表中的功能。 当然,那很好。 虽然,将递归视图插入其他视图而又不破坏与客户端代码的契约会更好,这将使我们能够进一步实现一些整洁的装饰器,从而能够在运行时组装视图管道,然后可以呈现所有视图一气呵成。

Sounds pretty much like nerd baiting? Well, it’s not. To overcome your skepticism, in this two-part tutorial I’ll show you how to implement from scratch a couple of customizable view handling modules by sinking our teeth into the goodies of the Composite and Decorator patterns.

听起来很像书呆子诱饵吗? 好吧,不是。 为了克服您的怀疑,在这个由两部分组成的教程中,我将向您展示如何通过将我们的牙齿沉浸在Composite和Decorator模式的优点中从头实现几个可定制的视图处理模块。

迈出第一步–实施基本的View模块 (Making the First Move – Implementing a basic View Module)

Included in the classic GoF’s repertoire, the Composite pattern is one of the most glaring examples of the old “Favor Composition over Inheritance” mantra in action. Simply put, the pattern’s forces allow you to manipulate single and multiple instances of a given component by using the same API. This ability is especially appealing for traversing tree structures composed of leaf and branch objects (yep, objects fed with other objects) without having to clutter your code with conditionals, hence deploying an elegant solution that rests on the pillars of Polymorphism.

复合模式包含在经典的GoF曲目中,它是旧的“从继承中的偏爱组成”口头禅中最明显的例子之一。 简而言之,模式的作用力使您可以使用相同的API来操作给定组件的单个和多个实例。 此功能特别适用于遍历由叶和分支对象(是,由其他对象提供的对象)组成的树结构,而不必使用条件限制使代码混乱,因此可以部署基于多态性的优雅解决方案。

While in theory everything looks sweet and charming when it comes to consuming Composites, it’s rather complex to figure out how to put them to work in the real world to build up a flexible view module from scratch. There’s no need to feel anguish, as the implementation of the module in question is actually a fairly straightforward process, reduced to first creating a class capable of modeling the data and behavior of single views (the so-called leaf objects), and second defining an appropriate counterpart, responsible for manipulating recursively composite views.

从理论上讲,在使用Composites时,一切看起来都很甜美和迷人,但是弄清楚如何使它们在现实世界中工作以从头开始构建灵活的视图模块是相当复杂的。 不必感到痛苦,因为所讨论的模块的实现实际上是一个相当简单的过程,简化为首先创建一个能够对单个视图的数据和行为建模的类(所谓的叶子对象),然后进行定义一个合适的对应对象,负责处理递归组合视图。

Since in this case I want to keep the whole implementation concise and easy to understand from front to back, the first element I plan to add to the module will be a class charged with engendering individual views. Here’s how this one looks:

因为在这种情况下,我想使整个实现简洁明了,并且易于前后理解,所以我计划添加到模块中的第一个元素将是负责产生各个视图的类。 这是这个样子:

<?php
namespace LibraryView;

interface ViewInterface
{
    public function setTemplate($template);
    public function getTemplate();
    public function __set($field, $value);
    public function __get($field);
    public function __isset($field);
    public function __unset($field);
    public function render();
}
<?php
namespace LibraryView;

class View implements ViewInterface 
{
    const DEFAULT_TEMPLATE = "default.php";
    
    protected $template = self::DEFAULT_TEMPLATE;
    protected $fields = array();
    
    public function __construct($template = null, array $fields = array()) {
        if ($template !== null) {
            $this->setTemplate($template);
        }
        if (!empty($fields)) {
            foreach ($fields as $name => $value) {
                $this->$name = $value;
            }
        } 
    }
    
    public function setTemplate($template) {
        $template = $template . ".php";
        if (!is_file($template) || !is_readable($template)) {
            throw new InvalidArgumentException(
                "The template '$template' is invalid.");   
        }
        $this->template = $template;
        return $this;
    }
      
    public function getTemplate() {
        return $this->template;
    }
    
    public function __set($name, $value) {
        $this->fields[$name] = $value;
        return $this;
    }
    
    public function __get($name) {
        if (!isset($this->fields[$name])) {
            throw new InvalidArgumentException(
                "Unable to get the field '$field'.");
        }
        $field = $this->fields[$name];
        return $field instanceof Closure ? $field($this) : $field;
    }
    
    public function __isset($name) {
        return isset($this->fields[$name]);
    }
    
    public function __unset($name) {
        if (!isset($this->fields[$name])) {
            throw new InvalidArgumentException(
                "Unable to unset the field '$field'.");
        }
        unset($this->fields[$name]);
        return $this;
    }
    
    public function render() {
        extract($this->fields);
        ob_start();
        include $this->template;
        return ob_get_clean();
    }
}

As denoted before, views are appealing candidates who pray hard for being modeled as POPOs, and certainly this View class honors this concept. Its functionality boils down to just using some PHP magic behind the scenes in order to assign and strip fields out of the view which are in turn parsed by its render() method.

如前所述,视图是吸引人的候选人,他们为将其建模为POPO而努力祈祷,当然,该View类也尊重这一概念。 它的功能归结为仅在幕后使用一些PHP魔术,以便将字段分配和剥离视图,然后由其render()方法对其进行解析。

Considering the malleable nature of the class, it’s easy to get it up and running without much fuss. To do so, first we should make sure to define its default template, which at a very basic level might look like this:

考虑到类的可塑性,很容易在没有大惊小怪的情况下启动和运行它。 为此,首先我们应该确保定义其默认模板,该模板在一个非常基本的级别上可能看起来像这样:

<!doctype html>
<head>
  <meta charset="utf-8">
  <title>The Default Template</title>
</head>
 <body>
  <header>
    <h1>You are viewing the default page!</h1>
<?php echo $this->header;?>
  </header>
  <section>
<?php echo $this->body;?>
  </section>
  <footer>
<?php echo $this->footer;?>
  </footer>
 </body>
</html>

With this template in place, it’s time to see how to hook it up to the View class so that it can be populated and dumped to the browser. The following snippet does just that:

使用此模板后,就该看看如何将其连接到View类,以便可以将其填充并转储到浏览器。 以下代码片段就是这样做的:

<?php
use LibraryLoaderAutoloader,
    LibraryViewView;
    
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();

$view = new View();
$view->header = "This is my fancy header section";
$view->body = "This is my fancy body section";
$view->footer = "This is my fancy footer section";
echo $view->render();

Not bad at all, right? And even though I have to admit the view implements a little more magic than what I’m usually comfortable with, it performs fairly decent when it comes to creating renderable view objects. What’s more, considering that the class is capable of parsing closures dropped into the template, we could be a little bolder and use it for generating two-step views. To accomplish this, we should create at least one minimalist partial file, called partial.php or something like that, similar to this one:

一点都不差吧? 尽管我不得不承认视图实现的功能比我平时所能接受的要多,但在创建可渲染视图对象时,它的表现还是相当不错的。 此外,考虑到该类能够解析放入模板中的闭包,我们可以大胆一些,并使用它来生成两步视图。 为此,我们应该至少创建一个最小的部分文件,称为partial.php或类似的文件,类似于此文件:

<p><?php echo $this->content;?></p>

Swapping out each template property with the partial would then be a trivial process, reduced to defining a few closures:

将每个模板属性与Partial交换掉将是一个简单的过程,简化为定义一些闭包:

<?php
$view = new View();

$view->header = function () {
    $header = new View("partial");
    $header->content = "This is my fancy header section";
    return $header->render();
};

$view->body = function () {
    $body = new View("partial");
    $body->content = "This is my fancy body section";
    return $body->render();
};

$view->footer = function () {
    $footer = new View("partial");
    $footer->content = "This is my fancy footer section";
    return $footer->render();
};

echo $view->render();

We’ve managed to implement a pretty flexible view module which can be used for rendering isolated objects and even dummy partials. Regardless of all these virtues, however, the module still falls short when it comes to rendering trees of views. This could be done by nesting closures, which at least to my taste, would be a pretty sloppy solution. How about appealing to the Composite pattern instead? After all, it provides this functionality right out of the box, while retaining the essence of OOP.

我们设法实现了一个非常灵活的视图模块,该模块可用于呈现孤立的对象,甚至是虚拟局部。 但是,不管所有这些优点如何,在渲染视图树时,模块仍然不足。 这可以通过嵌套闭包来完成,至少按我的口味,这将是一个相当草率的解决方案。 如何吸引复合模式呢? 毕竟,它立即提供了此功能,同时保留了OOP的本质。

建立复合视图–树叶和树枝相等 (Building a Composite View – Leaves and Branches as Equals)

In fact, it’s pretty simple to amend the structure of the previous module to give it the ability to handle composite views through a unified API. The first change we should introduce is to make the View class an implementer of a few segregated interfaces rather than the single one it currently consumes. The set of contracts defined by these finer-grained interfaces would look as following:

实际上,修改前一个模块的结构非常简单,以使其能够通过统一的API处理复合视图。 我们应该引入的第一个更改是使View类成为几个单独接口的实现者,而不是它当前使用的单个接口。 这些更细粒度的接口定义的合同集如下所示:

<?php
namespace LibraryView;

interface TemplateInterface
{
    public function setTemplate($template);
    public function getTemplate();
}
<?php
namespace LibraryView;

interface ContainerInterface
{
    public function __set($field, $value);
    public function __get($field);
    public function __isset($field);
    public function __unset($field);
}
<?php
namespace LibraryView;

interface ViewInterface
{
    public function render();
}

Though not explicit, the most engaging facet of defining several granular contracts is that now the render() method can be equally implemented at the same time by a class handling standalone views and by others manipulating composite ones. With this feature, we should now refactor the signature of the View class so it can adhere to these changes:

尽管不是很明确,但是定义多个粒度协定的最吸引人的方面是,现在, render()方法可以由处理独立视图的类和其他操纵组合视图的类同时实现。 使用此功能,我们现在应该重构View类的签名,以便它可以坚持以下更改:

<?php
class View implements TemplateInterface, ContainerInterface, ViewInterface 
{
    // the same implementation goes here
}

View is still a fully-renderable entity, even tough its functionality responds to the contracts imposed by disparate interfaces. At this point you might be wondering where and how the nuts and bolts of the Composite pattern might fit into this schema. Leave your anxiety behind and look at the following class, which… yep, has the neat ability for parsing recursively multiple views in one go:

View仍然是可完全渲染的实体,即使其功能强大,也要响应不同接口所施加的契约。 在这一点上,您可能想知道Composite模式的细节在哪里以及如何适合此模式。 不用担心,继续看下面的课程,是的,…具有很好的能力可以一次性解析多个视图:

<?php
namespace LibraryView;

class CompositeView implements ViewInterface
{
    protected $views = array();
    
    public function attachView(ViewInterface $view) {
        if (!in_array($view, $this->views, true)) {
            $this->views[] = $view;
        }
        return $this;
    }
    
    public function detachView(ViewInterface $view) {
        $this->views = array_filter($this->views, function ($value) use ($view) {
            return $value !== $view;
        });
        return $this;
    }
    
    public function render() {
        $output = "";
        foreach ($this->views as $view) {
            $output .= $view->render();
        }
        return $output;
    }
}

The CompositeView class is nothing but a plain view container disguised behind a fancy name, though its attachView()/detachView() tandem allows us to add and remove views at will, including other composite views. This nifty recursive ability, often present in typical implementations of the Composite pattern, brings a lot of flexibility for free. For instance, consider a common scenario where an HTML page is made up of the classic header, body, and footer. To make the example even easier, let’s say the sections have the following header.php, body.php, and footer.php templates tied up to them:

CompositeView类不过是在奇特的名称后面伪装的普通视图容器,尽管它的attachView() / detachView()串联允许我们detachView()添加和删​​除视图,包括其他组合视图。 这种精美的递归功能通常出现在Composite模式的典型实现中,带来了许多免费的灵活性。 例如,考虑一个常见的场景,其中HTML页面由经典的页眉,正文和页脚组成。 为了使示例更加简单,我们假设各节将以下header.phpbody.phpfooter.php模板绑定在一起:

<!doctype html>
<head>
  <meta charset="utf-8">
  <title>A Sample Template</title>
</head>
<body>
  <header>
    <h1>Welcome to this page, which you're accessing from <?php echo $this->ip;?></h1>
    <p><?php echo $this->content;?></p>
  </header>
<section>
  <p><?php echo $this->content;?></p>
</section>
<footer>
   <p><?php echo $this->content;?></p>
  </footer>
 </body>
</html>

In a traditional approach, each section could be easily assembled as an insulated view and rendered linearly, which is all fine and well. Nevertheless, we could use the CompositeView class and just render the whole page via a single method call, as following:

在传统方法中,每个部分都可以轻松地组装为绝缘视图并线性渲染,这一切都很好。 不过,我们可以使用CompositeView类,并仅通过单个方法调用来呈现整个页面,如下所示:

<?php
use LibraryLoaderAutoloader,
    LibraryViewView,
    LibraryViewCompositeView;
    
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();

$header = new View("header");
$header->content = "This is my fancy header section";
$header->ip = function () {
    return $_SERVER["REMOTE_ADDR"];
};

$body = new View("body");
$body->content = "This is my fancy body section";

$footer = new View("footer");
$footer->content = "This is my fancy footer section";

$compositeView = new CompositeView;

echo $compositeView->attachView($header)
                   ->attachView($body)
                   ->attachView($footer)
                   ->render();

The charm of this approach rests on the intrinsic flexibility the Composite pattern hides behind its hood. Considering that both plain and composite views are implementers of the same interface, it’s simple to inject them recursively and create complex layouts without having to amend a single chunk of client code.

这种方法的魅力在于Composite模式隐藏在其引擎盖后面的固有灵活性。 考虑到纯视图和复合视图都是同一接口的实现者,因此很容易以递归方式注入它们并创建复杂的布局,而无需修改单个客户代码。

The above example is in general pretty conservative since the recursive injection of composites actually never takes place. However, putting a more liberal plan into action, where single and composite views are just passed around onto other views, should be an easy process. If you’re feeling bold and want to give the view module a different twist, you have green light to do so.

上面的示例通常非常保守,因为复合材料的递归注入实际上从未发生过。 但是,将更宽松的计划付诸行动,将单一视图和组合视图仅传递给其他视图,应该是一个容易的过程。 如果您感觉胆大,并且希望给视图模块一个不同的角度,则可以这样做。

结束语 (Closing Remarks)

Commonly placed under a shallow and fuzzy “flexible programming” category, the Composite pattern is pretty much a paradoxical example where the feelings of joy generated by its elegance quickly get swept under the carpet of a tangled implementation. When properly tamed, though, it proves to be a pretty a solid solution that allows us to manipulate easily view objects, either as isolated entities or in the form of recursive sets.

复合模式通常放在浅而模糊的“灵活编程”类别下,几乎是一个自相矛盾的例子,其优雅所产生的喜悦很快就被纠结的实现所掩盖。 但是,如果适当地加以驯服,它被证明是一个非常可靠的解决方案,它使我们能够轻松地以独立实体或递归集的形式操纵视图对象。

This doesn’t mean that one has to hold on tight to the pattern’s virtues whenever it’s necessary to deploy somewhere a view handling mechanism, as being that dogmatic is pretty pointless. What’s more, if Composites just don’t fit your taste, it’s feasible to tread an alternate path and implement a flexible view module by appealing to the functionality brought by Decorators. In the follow up I’ll be covering the inner workings of precisely this approach, so don’t miss it!

这并不意味着每当有必要将视图处理机制部署到某个地方时,就必须紧紧抓住模式的优点,因为教条主义是毫无意义的。 而且,如果Composites根本不适合您的口味,则可以通过使用Decorators带来的功能,走一条替代路径并实现一个灵活的视图模块,这是可行的。 在后续文章中,我将介绍这种方法的内部原理,所以请不要错过!

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/flexible-view-manipulation-1/

可以给视图加复合主键

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值