webino/webino-draw

Zend Framework 2 的 XHTML 布局渲染器

1.0.0-beta1 2012-11-01 09:21 UTC

This package is auto-updated.

Last update: 2024-08-29 03:26:13 UTC


README

Build Status Coverage Status Scrutinizer Code Quality Dependency Status
Latest Stable Version Latest Unstable Version Total Downloads License

提供配置布局渲染的能力。 仍在开发中,仅供娱乐使用。

演示 | API


WebinoDraw principle

特性

  • 自动转义
  • Ajax 支持
  • 可配置布局
  • 逻辑与模板解耦
  • 支持纯 XHTML5(无需额外标记)
  • 触发事件
  • 绘制表单(集合或映射到 HTML)
  • 绝对化 URL
  • 缓存
  • 使用 PHP 函数、ZF2 视图变量、助手和过滤器
  • 您仍然可以使用 phtml,但为什么!

为什么?

  • 无逻辑标记的模板
  • 启用缓存后非常快
  • 覆盖配置设置以更改模块化项目的模板逻辑行为

设置

以下步骤是使此模块正常工作所必需的,考虑到 zf2-skeleton 或非常类似的应用程序

  1. "minimum-stability": "dev" 添加到您的 composer.json 中,因为此模块正在开发中

  2. 运行 php composer.phar require webino/webino-draw:dev-develop

  3. WebinoDraw 添加到启用的模块列表中

要求

  • XHTML(5) 声明

快速入门

例如,将以下代码添加到您的模块配置中某个位置

'webino_draw' => [
    'instructions' => [
        // Add draw instructions here
        'draw-node-example' => [
            'locator' => 'body',
            'value'   => 'Hello Webino!',
        ],
    ],
],

...重新加载您的浏览器,您应该会看到“Hello Webino!”作为正文内容。

渲染基于映射到 DOM 节点的指令

'draw-node-example' => [              // custom name
    'locator' => 'body',              // node locator
    'helper'  => 'WebinoDrawElement', // draw helper
    'value'   => 'Hello Webino!',     // helper options
],

您可以使用 CSS 选择器XPath,甚至将它们组合起来将 DOM 节点映射到绘制指令。

'draw-node-example' => [
    'locator' => [
        'body a',
        '.customclass',
        'xpath=//title',
        'xpath=//footer',
    ],
    'value' => 'Hello Webino!',
],

注意:可以设置许多 CSS 或 / 和 XPath 定位器

使用 stackIndex 选项指定每个指令的优先级

'draw-node-example' => [
    'stackIndex' => 9,
    'locator'    => 'body',
    'value'      => 'Hello Webino!',
],

在指令层次结构中,您可以使用相对定位器

'quick-contact' => [
    'locator' => '.quick-contact',         // the same node
    'instructions' => [
        'widget' => [
            'locator' => '.',              // the same node
            'locator' => 'xpath=.',        // the same node
            // ...
        ],
    ],
],

注意:每个子定位器 CSS 选择器都将解析为相对的。如果您想通过绝对 CSS 选择器进行匹配,请以双斜杠开始,例如 //.quick-contact

使用 节点变量

'draw-node-example' => [
    'locator' => 'a',
    'value'   => 'customprefix {$_nodeName} {$_nodeValue} {$_nodePath} customsuffix',
    'html'    => '<custom>{$_innerHtml}</custom>',
    'replace' => '{$_outerHtml}<custom/>',
    'attribs' => [
        'title' => '{$_nodeValue} {$_href}',
        'href'  => '{$_href}#customfragment',
    ],
],

注意:节点变量以下划线为前缀,以避免冲突。

使用 视图变量

假设控制器操作返回具有多维数组的视图模型。

'draw-node-example' => [
    'locator' => 'body',
    'value'   => '{$viewvar}',
],

设置和覆盖

'draw-node-example' => [
    'locator' => 'body',
    'value'   => '{$viewvar}',
    'var' => [
        'set' => [
            'viewvar' => 'customval',
        ],
     ],
],

获取变量

'draw-node-example' => [
    'locator' => 'body',
    'value'   => '{$depthvar}',
    'var' => [
        'fetch' => [
            'depthvar' => 'value.in.the.depth',
        ],
    ],
],

设置默认变量

'draw-node-example' => [
    'locator' => 'body',
    'value'   => '{$viewvar}',
    'var' => [
        'default' => [
            'viewvar' => 'defaultval',
        ],
     ],
],

推送变量

'draw-node-example' => [
    'locator' => 'body',
    'var' => [
        'push' => [
            'myvalue.in.the.depth' => 'mydepthvar',
        ],
    ],
],

使用 函数视图助手过滤器

'draw-node-example' => [
    'locator' => 'body',
    'value'   => '{$customvar}',
    'var' => [
        'helper' => [
            'customvar' => [
                'customhelper' => [
                    '__invoke' => [[]],
                ],
                'customfunction' => [[]],
            ],
        ],
        'filter' => [
            'pre' => [
                'customvar' => [
                    'customfilter'   => [],
                    'customfunction' => [],
                ],
            ],
            'post' => [],
        ],
    ],
],

注意:在函数/方法参数中修改变量值、助手/过滤器定义接受 {$var}

通过视图数组循环

'draw-node-example' => [
    'locator' => 'ul li',
    'value'   => '{$_key} {$_index} {$property}',
    'loop' => [
        'base'    => 'array.in.the.depth',
        'index'   => '0',
        'onEmpty' => [
            'locator' => 'ul',
            'replace' => '<p>You have no items.</p>',
        ],
    ],
],

注意:额外变量以前缀下划线开头,以避免冲突。

触发 事件

'event-example' => [
    'locator' => 'body',
    'trigger' => [
        'event-example.test',
    ],
],

然后附加监听器

$this->getEventManager()->getSharedManager()->attach(
    'WebinoDraw',
    'event-example.test',
    function(DrawEvent $event) {

        // set custom variables
        $event->getHelper()->setVars([]);

        // do something with the nodes
        $event->getNodes()->setValue("my node value");

        // change instruction node
        $event->setSpec([
            // draw instructions
            'value' => '{$_nodeValue} VALUE',
            'attribs' => [
                'title' => 'Hello from Controller!',
            ],
        ]);
    }
);

从控制器设置指令

$this->getServiceLocator()->get('WebinoDraw')->setInstructions([
    'custom' => [
        'locator' => '.customclass',
        'value'   => 'Custom value',
    ],
]);

设置指令始终合并,因此在某些情况下,清除它们很有用

$this->getServiceLocator()->get('WebinoDraw')->clearInstructions();

缓存

'cache-example' => [
    'locator'   => 'body',
    'cache'     => 'exampleCacheTag',
    'cache_key' => ['{$var}'],
    'cache_key_trigger' => [
        'draw.cache.byPage',
    ],
],

附加缓存键监听器

$this->getEventManager()->getSharedManager()->attach(
    'WebinoDraw',
    'draw.cache.byPage',
    function(Event $event) use ($navigation) {

        $page = $navigation->getActivePage();
        return $page->getHref();
    }
);

清除缓存

$this->getServiceLocator()->get('WebinoDrawCache')->clearByTags(['exampleCacheTag']);

注意:当加载缓存数据时,将跳过 draw 助手操作,也不会触发 AjaxEvent。

指令集

指令集允许您在自定义名称下配置一组绘制指令

'webino_draw' => [
    'instructionset' => [
        'customname' => [
            // Add draw instructions here
        ],
    ],
],

稍后您可以获取这些指令并将它们设置到 WebinoDraw 服务中

$draw = $this->getServiceLocator()->get('WebinoDraw');
$draw->setInstructions(
    $draw->instructionsFromSet('customname')
);

Ajax

WebinoDraw 支持Ajax,这意味着您可以从 Web 服务器请求布局体的任何片段,并通过网页 DOM 中的 JavaScript 更新元素。

假设Ajax是在网页背景中发送/接收数据的进程。为此使用JSON格式。

  1. 设置Ajax处理程序。使用以下jQuery脚本

     jQuery(document).ready(function($){
         $(document).on("click", ".ajax-link", function(event) {
             event.preventDefault();
             $.get($(this).attr("href"), function(data) {
    
                 // replace element HTML with each received fragment
                 $.each(data.fragment, function(selector, html) {
                     $(selector).replaceWith(html);
                 });
    
                 // custom data whatever
                 if (data.extraExample) {
                     $(".my-ajax-data").html(data.extraExample);
                 }
             }, "json");
         });
     });
    

    注意:上述脚本使所有具有类名"ajax-link"的元素都具有Ajax功能。该元素必须有"href"属性。

    注意:JSON data.fragment 包含 选择器 => XHTML 对。

    注意:我们可以接收自定义参数并对它们做任何事情。

  2. 将id和类名"ajax-fragment"添加到您想通过Ajax更改的每个元素上。

    假设以下元素在布局的body标签中的某个地方

     <div id="my-ajax-area" class="ajax-fragment">Ajax-able content</div>
    
  3. 现在,当您点击Ajax功能元素时,每个Ajax功能片段都将更新。

Ajax事件

Ajax请求触发Ajax事件,然后您可以添加自定义JSON数据并更改要渲染的片段的XPath。

$this->getEventManager()->getSharedManager()->attach(
    'WebinoDraw',
    AjaxEvent::EVENT_AJAX,
    function(AjaxEvent $event) {

        // add custom JSON data
        $event->setJson(['extraExample' => 'my extra ajax']);

        // change XPath of fragments to render
        $event->setFragmentXpath('//*[contains(@class, "my-ajax-fragment"])');
    }
);

注意:如果您使用JsonModel作为MvcEvent响应,则不会触发Ajax事件。

Ajax设置

有默认设置来配置Ajax支持

'webino_draw' => [
    // container is the area to render (in the layout)
    'ajax_container_xpath' => '//body',

    // fragment is the part of the container to receive
    'ajax_fragment_xpath' => '//*[contains(@class, "ajax-fragment") and @id]',
],

注意:只有与容器XPath匹配的元素才会渲染。

如果您想针对布局更具体地设置这些设置,可以覆盖它们。

布局可能包含许多Ajax容器和许多片段。您可以根据Ajax设置通过XPath匹配它们。例如,您可能只想渲染导航和内容,例如 'ajax_container_xpath' => '//nav|//content',然后也将它们作为片段接收 'ajax_fragment_xpath' => '//nav|//content'。因此,只会渲染和接收所需的部件,从而通过跳过布局体中Ajax容器周围的头、尾和其他无用部分来节省资源。

辅助工具

通过消耗DOM节点、选项和数据来执行DOM节点操作,提供自定义类的模块化。

WebinoDrawElement

使用它来修改网页元素。许多选项,非常强大。

'draw-element-example' => [
    'locator' => '.customclass',
    'helper'  => 'WebinoDrawElement',                // default (not required to set)

    // Helper options:
    'value' => 'Draw element example value',         // set node value
    'render' => [
        'script' => 'script/path'                    // render view script to variable
    ],
    'fragments' => [                                 // HTML fragments of the template to variables
        'frag' => '.frag-class'                      // pairs of customName => locator, gives us fragOuterHtml and fragInnerHtml variables
    ],
    'html' => '<span>HTML value</span>{$script}',    // set node XHTML
    'attribs' => [                                   // set attributes
        'title' => 'Attribute example'
    ],
    'remove'  => '.',                                // locator|array, removes target node
    'replace' => '<strong/>',                        // XHTML, replaces node
    'onEmpty' => [                                   // custom options if node is empty
        'value' => 'Empty node example',             // use same options as normal
    ],
    'var' => [
        'set' => [                                   // set variables values
            'myvar' => 'myval',                      // key => value pairs
        ],
        'fetch' => [                                 // fetch variables from multidimensional array
            'myvar' => 'base.path',                  // local variable name => variable base path pairs
        ],
        'default' => [                               // set default variables values
            'myvar' => 'mydefaultval',               // key => default value pairs
        ],
        'push' => [                                  // push variables values to the global context
            'my.base.path' => 'myval',               // custom variable base path => value pairs
        ],
        'helper' => [                                // use helpers on variables
            'customvar' => [
                '_join_result' => false,             // bool, disable the string result joining, default true
                'customhelper' => [                  // zend helper
                    '__invoke' => [[]],              // zend helper methods with params
                ],
                'customfunction' => [[]],            // use php function with params
            ],
        ],
        'filter' => [                                // filter variables
            'pre' => [                               // filter called before helpers
                'customvar' => [
                    'customfilter'   => [],          // use zend filter with params
                    'customfunction' => [],          // use php function with params
                ],
            ],
            'post' => [
                                                     // filter called after helper, same as for pre
            ],
        ],
    ],
    'onVar' => [                                     // variables logic
        'customIndex' => [                           // options per variable
            'var'        => '{$customvar}',          // test variable value
            'equalTo'    => '',                      // condition method (or)
            'notEqualTo' => '',                      // condition method
            'instructionset' => [                    // sub-instructionset to expand instructions

            ],
            'instructions' => [                      // sub-instructions processed when condition is true

            ],
        ],
    ],
    'instructionset' => [                            // instructionset to expand instructions

    ],
    'instructions' => [                              // sub-instructions to draw over nodes
                                                     // add different helper instructions
    ],
    'trigger' => [
        'event-example.test',                        // event name per item, identificator = WebinoDraw
    ],
    'loop' => [                                      // loop node by view array items
        'base'    => 'depth.items',                  // path to view array
        'index'   => '0',                            // index start point (optional)
        'offset'  => '0',                            // items offset (optional)
        'length'  => '0',                            // items length (optional)
        'shuffle' => false,                          // shuffle items
        'helper' => function(                        // LoopHelper|callable, called on each item (optional)
            $loopArgument, array $options
        ){},
        'onEmpty'  => [                              // custom options if items array is empty
                                                     // use same options as normal
        ],
        'instructionset' => [                        // instructionset to expand instructions

        ],
        'instructions' => [                          // instructions to draw looped element nodes
                                                     // add same instructions as normal
        ],
    ],
    'cache' => '',                                   // string|array cache tags
],

WebinoDrawForm

使用它来渲染表单。如果 <form/> 模板为空,则使用默认渲染,否则尝试通过名称属性匹配表单元素。

'draw-form-example' => [
    'localtor' => 'form.form-example',
    'helper'   => 'WebinoDrawForm',

    // Helper options:
    'form'        => 'exampleForm',             // form available via ServiceManager
    'route'       => 'example_route',           // available route
    'populate'    => '{$values}',               // populate form values
    'text_domain' => __NAMESPACE__,             // form translator text domain
    'instructionset' => [                       // instructionset to expand instructions

    ],
    'instructions' => [                         // sub-instructions to decorate the form
                                                // add different helper instructions
    ],
    'trigger' => [                              // trigger event passes form to the event parameters
        'form-example.event',                   // event name per item, identificator = WebinoDraw
    ],
    'cache' => '',                              // string|array cache tags
],

假设表单模板

<form class="form-example">
    <input name="example_text_element"/>        <!-- form elements are mapped by name attribute -->
    <input name="send"/>
</form>

如果您没有表单,可以轻松创建一个

'forms' => [
    'exampleForm' => [
        'hydrator' => \Zend\Stdlib\Hydrator\ArraySerializable::class,
        'attributes' => [
            'method' => 'post',
            'class'  => 'example-form',
        ],
        'elements' => [
            'example_text_element' => [
                'spec' => [
                    'name' => 'example_text_element',
                    'options' => [
                        'label' => 'Label example',
                    ],
                    'attributes' => [
                        'type'        => 'text',
                        'placeholder' => 'Type something ...',
                    ],
                ],
            ],
        ],
        'input_filter' => [
            'example_text_element' => [
                'name'     => 'example_text_element',
                'required' => true,
                'validators' => [

                ],
            ],
        ],
    ],
],

WebinoDrawAbsolutize

绝对化相对URL(默认属性:src、href、action)。

'absolutize' => [
    'stackIndex' => 9999998,
    'helper'     => 'WebinoDrawAbsolutize',
    'locator'    => (new \WebinoDraw\Draw\Helper\Absolutize\AbsolutizeLocator)->getLocator(),
],

通过 my-attr 属性扩展定位器

'absolutize' => [
    'locator' => [
        'my-attr' => 'xpath=//@my-attr' . (new \WebinoDraw\Draw\Helper\Absolutize\AbsolutizeLocator)->getCondition(),
    ],
],

注意:现在您不需要在URL前加 $this->view->basePath()

陷阱

  • 在出现问题时,使用 <![CDATA[ ]]> 与实体,如 &amp;<![CDATA[&amp;]]>

  • 您可能会遇到垃圾(编码)文本的结果,将布局中的meta charset替换为以下内容

    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    
  • 要绘制表单集合,使用WebinoDraw FormElement和FormRow视图辅助工具。它解决了当使用Zend FormElement视图辅助工具时与翻译器的一些问题,因为它不允许将翻译器文本域传递给其元素。

  • 要指定渲染表单元素的视图辅助工具,设置其 view_helper 选项。

  • 如果您不想绝对化元素属性,请将其添加到其属性中的 data-webino-draw-absolutize="no"

  • 当使用具有视图辅助工具的辅助工具时,如果多个方法在同一变量上调用,则自动连接字符串结果。使用 _join_result = false 选项禁用此行为。

示例

更多示例请参阅:examples/config/module.config.php

手动设置

  1. 安装ZendSkeletonApplication

  2. 设置WebinoDraw模块

  3. 设置模块测试配置

    • 复制:vendor/webino/webino-draw/test/resources/application.config.php
    • 粘贴到应用程序:config/application.config.php(替换)
  4. 检查您的应用程序欢迎页面是否有变化

待办事项

  • DrawHelper如何
  • 添加DrawResized以调整图像大小
  • 编写针对draw表单和新的功能(事件 + 子指令 + Ajax + 缓存)的测试
  • 编写针对DrawPagination的测试 + 手册
  • 编写 DrawTranslate 的测试和手册
  • 编写 DrawElement {$_outerHtml} 的测试和示例
  • 编写 DrawAjaxHtmlStrategy 的测试
  • 编写关于变量 fetch _first 和 _last 魔术键的文档
  • 编写对 onVar 支持的测试
  • 编写扩展指令集的规格指令测试
  • 禁用变量辅助工具的多方法调用,自动合并结果,向下不兼容
  • 重新设计变量辅助工具和过滤器 API,因为辅助工具/过滤器名称作为键在通过配置合并时会导致问题,向下不兼容
  • 编写循环辅助工具的手册和测试
  • 升级 Zend MVC
  • 升级 Zend Form

附录

请,如果您对这个 Zend 框架模块感兴趣,请报告任何问题,并不要犹豫贡献。我们非常欢迎对模块开发的任何贡献。

问题 | Fork | 开发