月度归档:2014年01月

PHP框架Phalcon 之 Assets Manager

Phalcon Assets Manager

基本思路是,Phalcon\Assets\Manager管理多个集合(默认添加两个名为css和js的集合),集合中存放同类型的资源,对集合来说,可以把集合中的资源串起来(如果串起来则还要设置访问URI,设置存放路径),然后应用过滤器,还可以把集合内容输出,设置集合前缀(集合资源统一使用这个前缀);对资源来说有本地资源和远程资源之分;过滤器是应用到集合的,它对集合中的资源逐一应用过滤器,多个过滤器可应用到同一个集合。实际上,过滤器只要实现filter方法。

看以上的类图视乎很复杂,实际很简单,Phalcon MVC的DI中默认有注册Phalcon\Assets\Manager,所有在视图或控制器中只要使用$this->assets即可获取一个实例:

$this->assets
    ->collection('footer')
    ->addJs('js/jquery.js')
    ->addJs('js/bootstrap.min.js');

调用Assets管理器调用collection方法返回一个集合实例(同时注册到管理器中),然后调用这个集合的addJs方法往这个集合中添加资源(字符串指定的资源会转换成一个资源实例),Assets管理器中默认建立了css和js集合,如果要添加资源到这两个集合,则可以直接:

$this->assets
    ->addJs('js/jquery.js')
    ->addJs('js/bootstrap.min.js');

但是这里的addJs返回的是资源管理器本身。如果要对集合进行操作,则要调用get(“js”)或collection(“js”)获取集合实例。或者干脆这样:

$this->assets
    ->collection(‘js’)
    ->addJs('js/jquery.js')
    ->addJs('js/bootstrap.min.js');

对集合的操作:

$scripts = $this->assets->collection('header');
if ($config->environment == 'development') {
    $scripts->setPrefix('/');
} else {
    $scripts->setPrefix('http:://cdn.example.com/');
}
$scripts->setLocal(flase)
->addJs('code.jquery.com/jquery-1.10.0.min.js', true, false)	    
->addJs('common-functions.js')
    ->addJs('page-functions.js')
->join(true)
->setTargetPath('public/production/final.js')
->setTargetUri('production/final.js')
->addFilter(new Phalcon\Assets\Filters\Jsmin())
->addFilter(new MyApp\Assets\Filters\LicenseStamper());

//输出默认集合css的资源
$this->assets->outputCss();
//输出默认集合js的资源
$this->assets->outputJs();
//输出集合header的资源(Js类型)
$this->assets->outputJs(‘header’);

当一个集合调用addCss()方法时,这个集合就是Phalcon\Assets\Resource\Css类型,调用addJs()方法时这个集合就是Phalcon\Assets\Resource\Js类型。

注意,集合的输出,还是依靠Assets资源管理器。集合本身没有输出方法,不过如果你喜欢,你可以用foreach循环输出。

集合的setLocal(boolean $local)方法是Sets if the collection uses local resources by default(设置默认是否是本地资源,true表示是,false表示否,true是默认值),所谓本地资源就是资源相对于本地。

集合的addJs()方法第二参数也是指示是否是本地资源,第三参数表示是否应用过滤。

(经验:如果一个资源是非本地资源时,只要在addXxx时指定第二参数为false即可)。

第一种情况:不对集合进行join操作
如果资源是本地资源则添加本地路径前缀,如果是非本地资源则原样输出,不过,如果资源字符串是一个http开头的字符串,那么它总是被认为是非本地资源的。组件不会去验证本地资源或非本地资源是否存在。

$this->assets->collection("header")
        ->setLocal(false)
        ->addJs('js/jquery.js')
        ->addJs('js/common-functions.js',true)
        ->addJs('js/page-functions.js')
        ->addJs('http://code.jquery.com/jquery-1.10.0.min.js',true);
//输出
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="/tutorial/js/common-functions.js"></script>
<script type="text/javascript" src="js/page-functions.js"></script>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.0.min.js"></script>

使用setLoacal设置集合默认都为非本地资源,addJs默认就是非本地资源,第二个addJs明确指定了是本地资源,所以输出添加了本地前缀,最后一个addJs也设置为本地资源,但是由于是http开头,所以它的设置无效。

另外还要注意,可以对集合资源统一添加前缀(setPrefix方法),在不进行join操作情况下,如果添加的前缀是一个链接(就是以http开头),在输出体现上,这个前缀应用到集合中的全部资源(不管是不是本地资源),但是如果不是一个链接,在输出体现上前缀只会添加到本地资源上。


第二种情况:对集合进行join操作

如果把集合进行join操作,那么就需要把文件读取进来,所以对应文件必须存在。本地存在的文件,设置成非本地,无效。总是先判断本地是否存在,如果不存在再判断资源是不是非本地资源,如果是非本地则再去远程抓取(注意如果本地不存在而又没有设置为非本地资源,则报错)。

$this->assets->collection("header")
        ->addJs('js/jquery.js')
        ->addJs('js/common-functions.js',false)
        ->addJs('js/page-functions.js')
        ->addJs('http://code.jquery.com/jquery-1.10.0.min.js',true)
        ->join(true)->setTargetPath('js/jsmin/final.js')->setTargetUri('js/jsmin/final.js')
        ->addFilter(new Phalcon\Assets\Filters\Jsmin())
        ;

第一个addJs是本地资源,如果把js/jquery.js删除,则报PhalconException: Resource ‘js/jquery.js’ does not have a valid source path异常。

第二个addJs明确指定为非本地资源,由于本地存在,文件成功读取,直接无视非本地资源设置。

最后一个AddJs设置为本地资源,也报PhalconException: Resource ‘http://code.jquery.com/jquery-1.10.0.min.js’ does not have a valid source path异常,说明它不会把http开头的资源当做非本地资源。

另外还要注意,可以对集合资源统一添加前缀(setPrefix方法),在进行join操作情况下,前缀会添加到setTargetUri设置的url前面。

最后总结一下,最后不要应用非本地资源,如果要使用最后放入到一个统一集合中,并且不要应用join操作。输出集合资源时,可以充分利用setPrefix方法:

$scripts = $this->assets->collection('header');
if ($config->environment == 'development') {
    $scripts->setPrefix('/');
} else {
    $scripts->setPrefix('http:://cdn.example.com/');
}

这样开发环境下直接输出对应本地资源,而部署时可以添加外部前缀(CDN)

当使用join或者使用了join和应用过滤器,就会引发一个新问题,join会把所有文件串在一起,如果应用了过滤器还会过滤之后串在一起,然后产生一个文件,难道每次都重复这个过程?答案是每次都会重复。

于是我写了个小扩展,可以把每次生成的文件写入缓存,避免每次都重复这个过程:
https://github.com/vfeelit/Phalcon_JsCssMin
————————————————————————-
以下为官方文档

Assets Management

Phalcon\Assets is a component that allows the developer to manage static resources such as css stylesheets or javascript libraries in a web application.
Phalcon\Assets是一个允许开发者在Web应用中管理比如CSS样式或者javascript静态资源的组件。

Phalcon\Assets\Manager is available in the services container, so you can add resources from any part of the application where the container is available.
可以从服务容器中获取Phalcon\Assets\Manager,只要容器可用的地方你可以从应用的任何部分添加资源。

Adding Resources
Assets supports two built-in resources: css and javascripts. You can create other resources if you need. The assets manager internally stores two default collections of resources one for javascript and another for css.
Assets支持两个内置资源:CSS和javascripts。如果需要你也可以创建其它资源。Assets管理器内部存储两个默认资源集合,一个是javascript,另一个是CSS。

You can easily add resources to these collections like follows:
向下面这样你可以很容易添加资源到这些集合:

<?php

class IndexController extends Phalcon\Mvc\Controller
{
    public function index()
    {

        //Add some local CSS resources
        $this->assets
            ->addCss('css/style.css')
            ->addCss('css/index.css');

        //and some local javascript resources
        $this->assets
            ->addJs('js/jquery.js')
            ->addJs('js/bootstrap.min.js');

    }
}

Then in the views added resources can be printed:
在视图中添加的资源可以输出:

<html>
    <head>
        <title>Some amazing website</title>
        <?php $this->assets->outputCss() ?>
    </head>
    <body>

        <!-- ... -->

        <?php $this->assets->outputJs() ?>
    </body>
<html>

//Volt syntax:
<html>
    <head>
        <title>Some amazing website</title>
          {{ assets.outputCss() }}
    </head>
    <body>

        <!-- ... -->

          {{ assets.outputJs() }}
    </body>
<html>

–Local/Remote resources 本地/远程资源
Local resources are those who’re provided by the same application and they’re located in the document root of the application. URLs in local resources are generated by the ‘url’ service, usually Phalcon\Mvc\Url.
本地资源是那些放在应用根目录下和相同应用一起提供资源。本地资源的URL由‘url’服务产生,通常是指Phalcon\Mvc\Url。

Remote resources are those such as common library like jquery, bootstrap, etc. that are provided by a CDN.
远程资源是那些由CDN提供的公共库,比如jquery,bootstrap等。

<?php

public function indexAction()
{

    //Add some local CSS resources
    $this->assets
        ->addCss('//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css', false)
        ->addCss('css/style.css', true);
}

–Collections 集合
Collections groups resources of the same type, the assets manager implicitly creates two collections: css and js. You can create additional collections to group specific resources for ease of placing those resources in the views:
集合把相同类型的资源进行分组,assets管理器隐式创建两个集合:CSS和JS。你可以创建其它的集合用来把特定资源进行分组,以方便在视图中放置这些资源:

<?php

//Javascripts in the header
$this->assets
    ->collection('header')
    ->addJs('js/jquery.js')
    ->addJs('js/bootstrap.min.js');

//Javascripts in the footer
$this->assets
    ->collection('footer')
    ->addJs('js/jquery.js')
    ->addJs('js/bootstrap.min.js');

//Then in the views:
<html>
    <head>
        <title>Some amazing website</title>
        <?php $this->assets->outputJs('header') ?>
    </head>
    <body>

        <!-- ... -->

        <?php $this->assets->outputJs('footer') ?>
    </body>
<html>

//Volt syntax:
<html>
    <head>
        <title>Some amazing website</title>
          {{ assets.outputCss('header') }}
    </head>
    <body>

        <!-- ... -->

          {{ assets.outputJs('footer') }}
    </body>
<html>

(集合是什么,就是为了分组资源,比如以上的footer集合,里面添加了两个资源,当然集合中的资源应该是同类型的)

–Prefixes 前缀
Collections can be URL-prefixed, this allows to easily change from a server to other at any moment:
集合可以有URL前缀,这个使得在任何时候要从一个服务器改变到另外服务器时变的容易。

<?php

$scripts = $this->assets->collection('footer');

if ($config->environment == 'development') {
    $scripts->setPrefix('/');
} else {
    $scripts->setPrefix('http:://cdn.example.com/');
}

$scripts->addJs('js/jquery.js')
        ->addJs('js/bootstrap.min.js');

//A chainable syntax is available too:
//链式操作
<?php

$scripts = $assets
    ->collection('header')
    ->setPrefix('http://cdn.example.com/')
    ->setLocal(false)   //表示不使用本地?
    ->addJs('js/jquery.js')
    ->addJs('js/bootstrap.min.js');

Minification/Filtering 压缩/过滤
Phalcon\Assets provides built-in minification of Javascript and CSS resources. The developer can create a collection of resources instructing the Assets Manager which ones must be filtered and which ones must be left as they are. In addition to the above, Jsmin by Douglas Crockford is part of the core extension offering minification of javascript files for maximum performance. In the CSS land, CSSMin by Ryan Day is also available to minify CSS files:
Phalcon\Assets对Javascript and CSS提供了内置的压缩。开发者可以创建资源的集合用以指引Assets管理器那些需要过滤和那些不需要。

The following example shows how to minify a collection of resources:

$manager

    //These Javascripts are located in the page's bottom
    ->collection('jsFooter')

    //The name of the final output
    ->setTargetPath('final.js')

    //The script tag is generated with this URI
    ->setTargetUri('production/final.js')

    //This is a remote resource that does not need filtering
    ->addJs('code.jquery.com/jquery-1.10.0.min.js', true, false)

    //These are local resources that must be filtered
    ->addJs('common-functions.js')
    ->addJs('page-functions.js')

    //Join all the resources in a single file
    ->join(true)

    //Use the built-in Jsmin filter
    ->addFilter(new Phalcon\Assets\Filters\Jsmin())

    //Use a custom filter
    ->addFilter(new MyApp\Assets\Filters\LicenseStamper());

It starts getting a collection of resources from the assets manager, a collection can contain javascript or css resources but not both. Some resources may be remote, that is, they’re obtained by HTTP from a remote source for further filtering. It is recommended to convert the external resources to local eliminating the overhead of obtaining them.
从assets管理器获取资源集合开始,集合可以包含javascript或css资源。一些资源可能是远程的,就是从远程通过HTTP获取它们。推荐转换外部资源到本地以消除开销。

<?php

//These Javascripts are located in the page's bottom
$js = $manager->collection('jsFooter');

As seen above, method addJs is used to add resources to the collection, the second parameter indicates whether the resource is external or not and the third parameter indicates whether the resource should be filtered or left as is:
如上,addJs方法用来添加资源到集合,第二参数指示资源是否是外部资源,第三参数指示资源是否应用过滤:(这里要玛尼一下的是,API参考中addJs第二参数名称是$local,明显让人感觉就是设置它是不是本地资源,true是本地,false是非本地,这个解释跟上面的解释相反)

<?php

// This a remote resource that does not need filtering
$js->addJs('code.jquery.com/jquery-1.10.0.min.js', true, false);

// These are local resources that must be filtered
$js->addJs('common-functions.js');
$js->addJs('page-functions.js');

Filters are registered in the collection, multiple filters are allowed, content in resources are filtered in the same order as filters were registered:
在集合中注册过滤器,多过滤器是允许的,内容过滤时应用过滤器的顺序和注册它们时的顺序一致:

<?php

//Use the built-in Jsmin filter
$js->addFilter(new Phalcon\Assets\Filters\Jsmin());

//Use a custom filter
$js->addFilter(new MyApp\Assets\Filters\LicenseStamper());

Note that both built-in and custom filters can be transparently applied to collections. Last step is decide if all the resources in the collection must be joined in a single file or serve each of them individually. To tell the collection that all resources must be joined you can use the method ‘join’:
内置的和自定义的过滤器可以透明地应用到集合。最后一个步骤是要决定是把所有资源串起来是各自保留独立。要把所有资源串起来,你可以使用方法“join”:

<?php

// This a remote resource that does not need filtering
$js->join(true);

//The name of the final file path
$js->setTargetPath('public/production/final.js');

//The script html tag is generated with this URI
$js->setTargetUri('production/final.js');

If resources are going to be joined, we need also to define which file will be used to store the resources and which uri will be used to show it. These settings are set up with setTargetPath() and setTargetUri().
如果把资源串起来,我们还需要定义使用哪个文件来存储这个资源和用哪个uri在展示它,这个两个设置对应setTargetPath() and setTargetUri()。(这两个操作使得存储位置和访问URI脱节,难道每次都搞一次?)

Built-In Filters 内置过滤器
Phalcon provides 2 built-in filters to minify both javascript and css respectively, their C-backend provide the minimum overhead to perform this task:
Phalcon提供了两个内置的过滤器来压缩javascript and css的展示:

Filter Description
Phalcon\Assets\Filters\Jsmin Minifies Javascript removing unnecessary characters that are ignored by Javascript interpreters/compilers
Phalcon\Assets\Filters\Cssmin Minifies CSS removing unnecessary characters that are already ignored by browsers

Custom Filters 自定义过滤器
In addition to built-in filters, a developer can create his own filters. These can take advantage of existing and more advanced tools like YUI, Sass, Closure, etc.:

<?php

use Phalcon\Assets\FilterInterface;

/**
 * Filters CSS content using YUI
 *
 * @param string $contents
 * @return string
 */
class CssYUICompressor implements FilterInterface
{

    protected $_options;

    /**
     * CssYUICompressor constructor
     *
     * @param array $options
     */
    public function __construct($options)
    {
        $this->_options = $options;
    }

    /**
     * Do the filtering
     *
     * @param string $contents
     * @return string
     */
    public function filter($contents)
    {

        //Write the string contents into a temporal file
        file_put_contents('temp/my-temp-1.css', $contents);

        system(
            $this->_options['java-bin'] .
            ' -jar ' .
            $this->_options['yui'] .
            ' --type css '.
            'temp/my-temp-file-1.css ' .
            $this->_options['extra-options'] .
            ' -o temp/my-temp-file-2.css'
        );

        //Return the contents of file
        return file_get_contents("temp/my-temp-file-2.css");
    }
}

//用法
<?php

//Get some CSS collection
$css = $this->assets->get('head');

//Add/Enable the YUI compressor filter in the collection
$css->addFilter(new CssYUICompressor(array(
     'java-bin' => '/usr/local/bin/java',
     'yui' => '/some/path/yuicompressor-x.y.z.jar',
     'extra-options' => '--charset utf8'
)));

Custom Output 自定义输出
Methods outputJs and outputCss are available to generate the necessary HTML code according to each type of resources. You can override this method or print the resources manually in the following way:

<?php

foreach ($this->assets->collection('js') as $resource) {
    echo \Phalcon\Tag::javascriptInclude($resource->getPath());
}

PHP框架Phalcon 之 视图助手

View Helpers
Writing and maintaining HTML markup can quickly become a tedious乏味 task because of the naming conventions命名惯例 and numerous attributes that have to be taken into consideration. Phalcon deals with this complexity by offering Phalcon\Tag, which in turn offers view helpers to generate HTML markup.

This component can be used in a plain HTML+PHP view or in a Volt template.

This guide is not intended to be a complete documentation of available helpers and their arguments. Please visit the Phalcon\Tag page in the API for a complete reference.(非完整手册)

Document Type of Content 内容文档类型
Phalcon provides Phalcon\Tag::setDoctype() helper to set document type of the content. Document type setting may affect HTML output produced by other tag helpers. For example, if you set XHTML document type family, helpers that return or output HTML tags will produce self-closing tags to follow valid XHTML standard.

Available document type constants in Phalcon\Tag namespace are:

Constant Document type
HTML32 HTML 3.2
HTML401_STRICT HTML 4.01 Strict
HTML401_TRANSITIONAL HTML 4.01 Transitional
HTML401_FRAMESET HTML 4.01 Frameset
HTML5 HTML 5
XHTML10_STRICT XHTML 1.0 Strict
XHTML10_TRANSITIONAL XHTML 1.0 Transitional
XHTML10_FRAMESET XHTML 1.0 Frameset
XHTML11 XHTML 1.1
XHTML20 XHTML 2.0
XHTML5 XHTML 5
//Setting document type.
<?php $this->tag->setDoctype(\Phalcon\Tag::HTML401_STRICT); ?>

//Getting document type.
<?= $this->tag->getDoctype() ?>
<html>
<!-- your HTML code -->
</html>

//The following HTML will be produced.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
        "http://www.w3.org/TR/html4/strict.dtd">
<html>
<!-- your HTML code -->
</html>

//Volt syntax:
{{ get_doctype() }}
<html>
<!-- your HTML code -->
</html>

Generating Links 产生链接
A real common task in any web application or website is to produce links that allow us to navigate from one page to another. When they are internal URLs we can create them in the following manner:

<!-- for the default route -->
<?= $this->tag->linkTo("products/search", "Search") ?>

<!-- with CSS attributes -->
<?= $this->tag->linkTo(array('products/edit/10', 'Edit', 'class' => 'edit-btn')) ?>

<!-- for a named route -->
<?= $this->tag->linkTo(array(array('for' => 'show-product', 'title' => 123, 'name' => 'carrots'), 'Show')) ?>

Actually, all produced URLs are generated by the component Phalcon\Mvc\Url (or service “url” failing) 实际上产生的URL是Phalcon\Mvc\Url提供的功能。

Same links generated with Volt:

<!-- for the default route -->
{{ link_to("products/search", "Search") }}

<!-- for a named route -->
{{ link_to(['for': 'show-product', 'id': 123, 'name': 'carrots'], 'Show') }}

<!-- for a named route with class -->
{{ link_to(['for': 'show-product', 'id': 123, 'name': 'carrots'], 'Show','class'=>'edit-btn') }}

(实际上,这个搞法跟原生PHP又有何先进之处?)

Creating Forms 生成表单
Forms in web applications play an essential基本的 part in retrieving user input. The following example shows how to implement a simple search form using view helpers:

<!-- Sending the form by method POST -->
<?= $this->tag->form("products/search") ?>
    <label for="q">Search:</label>
    <?= $this->tag->textField("q") ?>
    <?= $this->tag->submitButton("Search") ?>
<?= $this->tag->endForm() ?>

<!-- Specifying another method or attributes for the FORM tag -->
<?= $this->tag->form(array("products/search", "method" => "get")); ?>
    <label for="q">Search:</label>
    <?= $this->tag->textField("q"); ?>
    <?= $this->tag->submitButton("Search"); ?>
<?= $this->tag->endForm() ?>

//
<form action="/store/products/search/" method="get">
     <label for="q">Search:</label>
     <input type="text" id="q" value="" name="q" />
     <input type="submit" value="Search" />
</form>

Phalcon also provides a form builder to create forms in an object-oriented manner.
Phalcon提供了表单构建器。

–Helpers to Generate Form Elements
Phalcon provides a series of helpers to generate form elements such as text fields, buttons and more. The first parameter of each helper is always the name of the element to be generated. When the form is submitted, the name will be passed along with the form data. In a controller you can get these values using the same name by using the getPost() and getQuery() methods on the request object ($this->request).

<?php echo $this->tag->textField("username") ?>

<?php echo $this->tag->textArea(array(
    "comment",
    "This is the content of the text-area",
    "cols" => "6",
    "rows" => 20
)) ?>

<?php echo $this->tag->passwordField(array(
    "password",
    "size" => 30
)) ?>

<?php echo $this->tag->hiddenField(array(
    "parent_id",
    "value"=> "5"
)) ?>

//Volt syntax:
{{ text_field("username") }}

{{ text_area("comment", "This is the content", "cols": "6", "rows": 20) }}

{{ password_field("password", "size": 30) }}

{{ hidden_field("parent_id", "value": "5") }}

Making Select Boxes
Generating select boxes (select box) is easy, especially if the related data is stored in PHP associative arrays. The helpers for select elements are Phalcon\Tag::select() and Phalcon\Tag::selectStatic(). Phalcon\Tag::select() has been was specifically designed to work with Phalcon\Mvc\Model, while Phalcon\Tag::selectStatic() can with PHP arrays.
Phalcon\Tag::select()跟Phalcon\Mvc\Model一起工作,Phalcon\Tag::selectStatic()跟数组一起工作:

<?php

// Using data from a resultset
echo $this->tag->select(
    array(
        "productId",
        Products::find("type = 'vegetables'"),
        "using" => array("id", "name")
    )
);

// Using data from an array
echo $this->tag->selectStatic(
    array(
        "status",
        array(
            "A" => "Active",
            "I" => "Inactive",
        )
    )
);

//You can add an “empty” option to the generated HTML:
<?php

// Creating a Select Tag with an empty option
echo $this->tag->select(
    array(
        "productId",
        Products::find("type = 'vegetables'"),
        "using" => array("id", "name"),
        "useEmpty" => true
    )
);

<select id="productId" name="productId">
    <option value="">Choose..</option>
    <option value="101">Tomato</option>
    <option value="102">Lettuce</option>
    <option value="103">Beans</option>
</select>

<?php

// Creating a Select Tag with an empty option with default text
echo $this->tag->select(
    array(
        'productId',
        Products::find("type = 'vegetables'"),
        'using' => array('id', "name"),
        'useEmpty' => true,
        'emptyText' => 'Please, choose one...',
        'emptyValue' => '@'
    )
);

<select id="productId" name="productId">
    <option value="@">Please, choose one..</option>
    <option value="101">Tomato</option>
    <option value="102">Lettuce</option>
    <option value="103">Beans</option>
</select>

//Volt syntax for above example:
{# Creating a Select Tag with an empty option with default text #}
{{ select('productId', products, 'using': ['id', 'name'],
    'useEmpty': true, 'emptyText': 'Please, choose one...', 'emptyValue': '@') }}

Assigning HTML attributes 分配HTML属性
All the helpers accept an array as their first parameter which can contain additional HTML attributes for the element generated.

<?php $this->tag->textField(
    array(
        "price",
        "size"        => 20,
        "maxlength"   => 30,
        "placeholder" => "Enter a price",
    )
) ?>

//or using Volt:
{{ text_field("price", "size": 20, "maxlength": 30, "placeholder": "Enter a price") }}

//The following HTML is generated:
<input type="text" name="price" id="price" size="20" maxlength="30"
    placeholder="Enter a price" />

Static Content Helpers
Phalcon\Tag also provide helpers to generate tags such as script, link or img. They aid in quick and easy generation of the static resources of your application

–Images

<?php

// Generate <img src="/your-app/img/hello.gif">
echo $this->tag->image("img/hello.gif");

// Generate <img alt="alternative text" src="/your-app/img/hello.gif">
echo $this->tag->image(
    array(
       "img/hello.gif",
       "alt" => "alternative text"
    )
);

//Volt syntax:
{# Generate <img src="/your-app/img/hello.gif"> #}
{{ image("img/hello.gif") }}

{# Generate <img alt="alternative text" src="/your-app/img/hello.gif"> #}
{{ image("img/hello.gif", "alt": "alternative text") }}

–Stylesheets

<?php

// Generate <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Rosario" type="text/css">
echo $this->tag->stylesheetLink("http://fonts.googleapis.com/css?family=Rosario", false);

// Generate <link rel="stylesheet" href="/your-app/css/styles.css" type="text/css">
echo $this->tag->stylesheetLink("css/styles.css");


//Volt syntax:
{# Generate <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Rosario" type="text/css"> #}
{{ stylesheet_link("http://fonts.googleapis.com/css?family=Rosario", false) }}

{# Generate <link rel="stylesheet" href="/your-app/css/styles.css" type="text/css"> #}
{{ stylesheet_link("css/styles.css") }}

–Javascript

<?php

// Generate <script src="http://localhost/javascript/jquery.min.js" type="text/javascript"></script>
echo $this->tag->javascriptInclude("http://localhost/javascript/jquery.min.js", false);

// Generate <script src="/your-app/javascript/jquery.min.js" type="text/javascript"></script>
echo $this->tag->javascriptInclude("javascript/jquery.min.js");


//Volt syntax:
{# Generate <script src="http://localhost/javascript/jquery.min.js" type="text/javascript"></script> #}
{{ javascript_include("http://localhost/javascript/jquery.min.js", false) }}

{# Generate <script src="/your-app/javascript/jquery.min.js" type="text/javascript"></script> #}
{{ javascript_include("javascript/jquery.min.js") }}

HTML5 elements – generic HTML helper
Phalcon offers a generic HTML helper that allows the generation of any kind of HTML element. It is up to the developer to produce a valid HTML element name to the helper.

<?php

// Generate
// <canvas id="canvas1" width="300" class="cnvclass">
// This is my canvas
// </canvas>
echo $this->tag->tagHtml("canvas", array("id" => "canvas1", "width" => "300", "class" => "cnvclass"), false, true, true);
echo "This is my canvas";
echo $this->tag->tagHtmlClose("canvas");

Tag Service
Phalcon\Tag is available via the ‘tag’ service, this means you can access it from any part of the application where the services container is located:

<?php echo $this->tag->linkTo('pages/about', 'About') ?>

You can easily add new helpers to a custom component replacing the service ‘tag’ in the services container:

<?php

class MyTags extends \Phalcon\Tag
{
    //...

    //Create a new helper
    static public function myAmazingHelper($parameters)
    {
        //...
    }

    //Override an existing method
    static public function textField($parameters)
    {
        //...
    }
}

Then change the definition of the service ‘tag’:

<?php

$di['tag'] = function() {
    return new MyTags();
};

Creating your own helpers
You can easily create your own helpers. First, start by creating a new folder within the same directory as your controllers and models.在模型或控制器同级下建立一个目录。 Give it a title that is relative to what you are creating. For our example here, we can call it “customhelpers”. Next we will create a new file titled MyTags.php within this new directory. At this point, we have a structure that looks similar to : /app/customhelpers/MyTags.php. In MyTags.php, we will extend the Phalcon\Tag and implement your own helper. Below is a simple example of a custom helper:

<?php

class MyTags extends \Phalcon\Tag
{

    /**
     * Generates a widget to show a HTML5 audio tag
     *
     * @param array
     * @return string
     */
    static public function audioField($parameters)
    {

        // Converting parameters to array if it is not
        if (!is_array($parameters)) {
            $parameters = array($parameters);
        }

        // Determining attributes "id" and "name"
        if (!isset($parameters[0])) {
            $parameters[0] = $parameters["id"];
        }

        $id = $parameters[0];
        if (!isset($parameters["name"])) {
            $parameters["name"] = $id;
        } else {
            if (!$parameters["name"]) {
                $parameters["name"] = $id;
            }
        }

        // Determining widget value,
        // \Phalcon\Tag::setDefault() allows to set the widget value
        if (isset($parameters["value"])) {
            $value = $parameters["value"];
            unset($parameters["value"]);
        } else {
            $value = self::getValue($id);
        }

        // Generate the tag code
        $code = '<audio id="'.$id.'" value="'.$value.'" ';
        foreach ($parameters as $key => $attributeValue) {
            if (!is_integer($key)) {
                $code.= $key.'="'.$attributeValue.'" ';
            }
        }
        $code.=" />";

        return $code;
    }

}

After creating our custom helper, we will autoload the new directory that contains our helper class from our “index.php” located in the public directory.
让其能自动加载:

<?php

try {

    $loader = new \Phalcon\Loader();
    $loader->registerDirs(array(
        '../app/controllers',
        '../app/models',
        '../app/customhelpers' // Add the new helpers folder
    ))->register();

    $di = new Phalcon\DI\FactoryDefault();

    // Assign our new tag a definition so we can call it
    $di->set('MyTags',  function()
    {
        return new MyTags();
    });

    $application = new \Phalcon\Mvc\Application($di);
    echo $application->handle()->getContent();

    } catch(\Phalcon\Exception $e) {
         echo "PhalconException: ", $e->getMessage();
    }

}

Now you are ready to use your new helper within your views:

<body>

    <?php
    echo MyTags::audioField(array(
        'name' => 'test',
        'id' => 'audio_test',
        'src' => '/path/to/audio.mp3'
        ));
    ?>

</body>

In next chapter, we’ll talk about Volt a faster template engine for PHP, where you can use a more friendly syntax for using helpers provided by Phalcon\Tag.

PHP框架Phalcon 之 使用视图

Phalcon视图组件图:
Phalcon视图组件
Phalcon\Mvc\View在渲染时实际会把模板引擎勾进来,调用它的render()方法,所以实际所谓的模板引擎,主要是实现render()方法,它把它的模板引擎的特有语法进行转换,根本不见得比原始PHP高效,而且语法怪异。Phalcon默认的模板引擎就是Phalcon\Mvc\View\Engine\Php,如果不使用模板引擎,就不需要关注这个了。我们需要彻底搞明白Phalcon\Mvc\View。

Phalcon\Mvc\View实现层叠视图:

Action View			跟Action同名,最先被渲染
Controller Layout		跟控制器同名,访问views/layouts目录中
Main Layout			名称为index

当前渲染顺序,取出Action View放入Controller Layout,再把Controller Layout放入Main Layout,如果没有或设置不使用Main Layout,那Controller Layout就直接输出,如果没有或设置不使用Controller Layout,那Action View直接输出,如果设置不使用Action View($this->view->disable()方法),则不会输入任何视图文件。

在Controller Layout之前和之后,分别可以设置模板层,比如:

$this->view->setTemplateBefore();
$this->view-> setTemplateAfter()

这样就对应了这个类中定义的LEVEN:

integer LEVEL_MAIN_LAYOUT		//index
integer LEVEL_AFTER_TEMPLATE		//自定义模板
integer LEVEL_LAYOUT			//layouts中控制器同名文件
integer LEVEL_BEFORE_TEMPLATE		//自定义模板
integer LEVEL_ACTION_VIEW		//Actoin同名
integer LEVEL_NO_RENDER			//不使用视图

这样,渲染顺序如下,先取出Action View,然后把Action View注入到TemplateBefore模板,然后把前面的整体注入Controller Layout,再然后把之前的整体 注入TemplateAfter模板,然后再把之前的整体注入Main Layout。

结构如下:

Main Layout[
  TemplateAfter[
    Controller Layout[
      TemplateBefore[Action View]
    ]
  ]
]

如果TemplateBefore没有,Action View就注入Controller Layout,TemplateAfter没有,Controller Layout就注入Main Layout。同理,如果Controller Layout也没有,Action View直接注入Main Layout,如果Main Layout也没有,那么Action View就直接输出了。

以上的6个层次,可以通过setRenderLevel()方法设置当前要工作到哪个层次,比如设置为LEVEL_NO_RENDER将不使用视图,如果设置为LEVEL_ACTION_VIEW,则取出Action View后就返回,如果设置为LEVEL_BEFORE_TEMPLATE,那么Action View注入TemplateBefore后,就返回,依次类推。

前面提到的注入实际就是把内容插入的意思,在View中是通过方法getContent()来实现,在一个视图中调用getContent(),意思就是把前面的视图注入到当前位置,如果几个模板都有设置,那么它们能够串起来的魔法就是getContent()方法。如果某个模板没有给出getContent()方法,那么视图层次就无法串起来,最总只会输出找到的当前模板和它之后的内容,举例,如果layout中没有getContent(),那么action view就无法注入到layout,那么只能输出layout本身(没有action view)和layout之后层次的内容。

到这里,Phalcon视图的核心内容已经描述清楚了。接下来就是具体问题:
1 如何设置视图文件目录?
调用setViewsDir(),实际上在index.php中就应该设置:

    $di->set('view', function(){
        $view = new \Phalcon\Mvc\View();
        $view->setViewsDir('../app/views/');
        return $view;
    });

如果要获取它就是调用getViewsDir()了。

2 如何设置布局目录(控制器布局)?
默认,布局目录是视图目录下的layouts目录,可以通过setLayoutsDir()来改变,但是它必须处于视图目录下。

3 如何设置Main View?
以上是针对目录设置,这里是针对文件,默认Main View是视图目录下的index.phtml文件,可以通过setMainView(string $viewPath)来改变。

4 如何修改布局文件?
默认布局文件是和控制器同名的文件,并且放入layouts中(可以通过setLayoutsDir()改变),可以通过setLayout()来改变名称。这个方法比较又用的地方在于,如果每个控制器使用相同布局,那么就可以让每个控制器都使用它,而不用分别建立和控制器同名的布局。

5 如果使用跟Action名称不一样的视图文件?
可以通过pick()来替代默认的Action视图。

6 如何使用局部视图?
在一个视图文件中,可以通过:

$this->partial("shared/header");

这样的就可以局部添加视图到当前视图中,这个为页面组织提供了手段。默认局部视图文件存放在view目录下,可以通过setPartialsDir指定到view目录下的某个目录或路径。

7 如何使用视图缓存?
可以通过getCache ()获取缓存对象实例(注意这个是获取实例),视图实现了Phalcon\DI\Injectable接口,DI的资源通过__get()的作用映射到属性,所以实际这个getCache()方法就是调用$this->viewCache。另外视图的isCaching ()方法(没有参数)看起来是判断视图是否在缓存中(实际上真不知道判断的依据是什么)。这也是不是在说明视图的cache()只能设置针对整个View的缓存呢?

<?php
$this->view->cache(array('key' => 'my-key', 'lifetime' => 86400));

这样的操作的确缓存整个视图。在没有指定key时缓存的key就是一串散列字符串,大概isCaching方法就是使用这个字符串吧,实际cache方法除了可以设置缓存,当缓存存在并且没有过期,它就直接取出来用了,所以cache方法是实际缓存对象的二次封装而已,而它操作的目标是整个视图。它为缓存整个页面提供快捷方法(否则需要到最后那个渲染层次设置缓存)。

<?php
 
class PostsController extends \Phalcon\Mvc\Controller
{
 
    public function showAction()
    {
        //Cache the view using the default settings
        $this->view->cache(true);
    }
 
    public function showArticleAction()
    {
        // Cache this view for 1 hour
        $this->view->cache(array(
            "lifetime" => 3600
        ));
    }
 
    public function resumeAction()
    {
        //Cache this view for 1 day with the key "resume-cache"
        $this->view->cache(
            array(
                "lifetime" => 86400,
                "key"      => "resume-cache",
            )
        );
    }
 
    public function downloadAction()
    {
        //Passing a custom service
        $this->view->cache(
            array(
                "service"  => "myCache",
                "lifetime" => 86400,
                "key"      => "resume-cache",
            )
        );
    }
 
}

以上的cache调用,如果没有缓存则进行缓存,只要缓存保存了,那么它就是调出缓存,lifetime就是判断是否过期的依据。如果lifetime没有指定就使用viewCache设置的默认值。

整个文档都没有说明如何缓存视图片段:

//public/index.php 添加
$di->set('viewCache', function() {

    //Cache data for one day by default
    $frontCache = new \Phalcon\Cache\Frontend\Output(array(
        "lifetime" => 86400
    ));

    //Memcached connection settings
    $cache = new \Phalcon\Cache\Backend\File($frontCache, array(
        "cacheDir" => "../app/cache/view/",
    ));

    return $cache;
});

//在具体的视图文件中
<?php
$content = $this->viewCache->start("post.last",3600);
if($content === null){
?>
<!-- 具体的视图内容 -->
<?php
$this->viewCache->save();
}else{
        echo $content;
}

任何的片段都可以采用这个模式进行缓存。start()方法内部启用了输出缓存,所有任何输出在save()方法调用之前都被它收集。

注意,根据视图的层次渲染,如果布局视图缓存了,那么Action View就不会被读进来,但是在渲染布局视图时,还是会先去读取Action View(也可能来自缓存),尽管到达布局视图时并没有使用它而是直接返回了布局视图的缓存。

以上内容为本人总结,内容准确性无法百分百保证….
———————————————————————-
##以下为官方文档

Using Views 使用视图

Views represent代表 the user interface of your application. Views are often HTML files with embedded PHP code that perform tasks related solely仅仅 to the presentation of the data. Views handle the job of providing data to the web browser or other tool that is used to make requests from your application.

The Phalcon\Mvc\View and Phalcon\Mvc\View\Simple are responsible for the managing the view layer of your MVC application.

Integrating Views with Controllers 在控制器中集成视图
Phalcon automatically passes the execution to the view component as soon as a particular controller has completed its cycle. The view component will look in the views folder for a folder named as the same name of the last controller executed and then for a file named as the last action executed.进入视图目录寻找和控制器名称一样的目录然后在这个目录中寻找和Action名称一样的视图文件。 For instance, if a request is made to the URL http://127.0.0.1/blog/posts/show/301, Phalcon will parse the URL as follows:

Server Address 127.0.0.1
Phalcon Directory blog
Controller posts
Action show
Parameter 301

The dispatcher will look for a “PostsController” and its action “showAction”. A simple controller file for this example:
分发器将寻找PostsController和showAction:

<?php

class PostsController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    public function showAction($postId)
    {
        // Pass the $postId parameter to the view
        $this->view->setVar("postId", $postId);
    }

}

The setVar allows us to create view variables on demand so that they can be used in the view template. The example above demonstrates how to pass the $postId parameter to the respective view template.

Hierarchical Rendering 渲染层次
Phalcon\Mvc\View supports a hierarchy of files and is the default component for view rendering in Phalcon. This hierarchy allows for common layout points (commonly used views), as well as controller named folders defining respective view templates.
Phalcon\Mvc\View支持分层文件并且是视图渲染默认组件。分层允许公共布局点(视图公共使用),用控制器名称命名的目录代表视图模板。

This component uses by default PHP itself as the template engine, therefore views should have the .phtml extension. If the views directory is app/views then view component will find automatically for these 3 view files.
视图组件默认使用PHP作为它的模板引擎,视图文件必须以.phtml结尾。如果视图目录是app/views,视图组件将自动寻找3个视图文件。

Name File Description
Action View app/views/posts/show.phtml This is the view related to the action. It only will be shown when the “show” action was executed.
Controller Layout app/views/layouts/posts.phtml This is the view related to the controller. It only will be shown for every action executed within the controller “posts”. All the code implemented in the layout will be reused for all the actions in this controller.
Main Layout app/views/index.phtml This is main action it will be shown for every controller or action executed within the application.
app/views/index.phtml		全局布局
app/views/layouts/posts.phtml	控制器布局
app/views/posts/show.phtml	控制器Action模板

You are not required to implement all of the files mentioned above. Phalcon\Mvc\View will simply move to the next view level in the hierarchy of files. If all three view files are implemented, they will be processed as follows:
不需要全部都实现以上的层次。Phalcon\Mvc\View将会移动到一层次:

<!-- app/views/posts/show.phtml -->

<h3>This is show view!</h3>

<p>I have received the parameter <?php echo $postId ?></p>

<!-- app/views/layouts/posts.phtml -->

<h2>This is the "posts" controller layout!</h2>

<?php echo $this->getContent() ?>

<!-- app/views/index.phtml -->
<html>
    <head>
        <title>Example</title>
    </head>
    <body>

        <h1>This is main layout!</h1>

        <?php echo $this->getContent() ?>

    </body>
</html>

Note the lines where the method $this->getContent() was called. This method instructs Phalcon\Mvc\View on where to inject the contents of the previous view executed in the hierarchy. For the example above, the output will be:
$this->getContent()表示把下一层次能入注入。

<!-- app/views/index.phtml -->
<html>
    <head>
        <title>Example</title>
    </head>
    <body>

        <h1>This is main layout!</h1>

        <!-- app/views/layouts/posts.phtml -->

        <h2>This is the "posts" controller layout!</h2>

        <!-- app/views/posts/show.phtml -->

        <h3>This is show view!</h3>

        <p>I have received the parameter 101</p>

    </body>
</html>

–Using Templates使用模板
Templates are views that can be used to share common view code. They act as controller layouts, so you need to place them in the layouts directory.
模板是用来共享视图代码的视图。它们扮演了控制器布局,所以你需要把它们放入到layouts目录。

<?php

class PostsController extends \Phalcon\Mvc\Controller
{
    public function initialize()
    {
        $this->view->setTemplateAfter('common');
    }

    public function lastAction()
    {
        $this->flash->notice("These are the latest posts");
    }
}

<!-- app/views/index.phtml -->
<!DOCTYPE html>
<html>
    <head>
        <title>Blog's title</title>
    </head>
    <body>
        <?php echo $this->getContent() ?>
    </body>
</html>

<!-- app/views/layouts/common.phtml -->

<ul class="menu">
    <li><a href="/">Home</a></li>
    <li><a href="/articles">Articles</a></li>
    <li><a href="/contact">Contact us</a></li>
</ul>

<div class="content"><?php echo $this->getContent() ?></div>

<!-- app/views/layouts/posts.phtml -->

<h1>Blog Title</h1>

<?php echo $this->getContent() ?>

<!-- app/views/posts/last.phtml -->

<article>
    <h2>This is a title</h2>
    <p>This is the post content</p>
</article>

<article>
    <h2>This is another title</h2>
    <p>This is another post content</p>
</article>

输出:

<!-- app/views/index.phtml -->
<!DOCTYPE html>
<html>
    <head>
        <title>Blog's title</title>
    </head>
    <body>
        <!-- app/views/layouts/common.phtml -->

<ul class="menu">
    <li><a href="/">Home</a></li>
    <li><a href="/articles">Articles</a></li>
    <li><a href="/contact">Contact us</a></li>
</ul>

<div class="content"><!-- app/views/layouts/posts.phtml -->
<h1>Blog Title</h1>

<!-- app/views/posts/last.phtml -->

<article>
    <h2>This is a title</h2>
    <p>This is the post content</p>
</article>

<article>
    <h2>This is another title</h2>
    <p>This is another post content</p>
</article>

</div>
    </body>
</html>

(可见setTemplateAfter(‘common’)的意思是在common之后应用控制器布局。common是一个全局公用模板,让入layouts目录中。实际common.phtml也是可以放入layouts目录的子目录中,只要在调用setTemplateAfter时指定即可。)

–Control Rendering Levels 控制渲染层次
As seen above, Phalcon\Mvc\View supports a view hierarchy. You might need to control the level of rendering produced by the view component. The method PhalconMvc\View::setRenderLevel() offers this functionality.
PhalconMvc\View::setRenderLevel()设置渲染层次。

This method can be invoked from the controller or from a superior view layer to interfere with the rendering process.
这个方法可以在控制器或视图的上一层调用。

<?php

use Phalcon\Mvc\Controller,
    Phalcon\Mvc\View;

class PostsController extends Controller
{

    public function indexAction()
    {

    }

    public function findAction()
    {

        // This is an Ajax response so it doesn't generate any kind of view
        $this->view->setRenderLevel(View::LEVEL_NO_RENDER);

        //...
    }

    public function showAction($postId)
    {
        // Shows only the view related to the action
        $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
    }

}
Class Constant Description Order
LEVEL_NO_RENDER Indicates to avoid generating any kind of presentation.  
LEVEL_ACTION_VIEW Generates the presentation to the view associated to the action. 1
LEVEL_BEFORE_TEMPLATE Generates presentation templates prior to the controller layout. 2
LEVEL_LAYOUT Generates the presentation to the controller layout. 3
LEVEL_AFTER_TEMPLATE Generates the presentation to the templates after the controller layout. 4
LEVEL_MAIN_LAYOUT Generates the presentation to the main layout. File views/index.phtml 5

–Disabling render levels
You can permanently or temporarily disable render levels. A level could be permanently disabled if it isn’t used at all in the whole application:
(玛尼的,LEVEL_BEFORE_TEMPLATE和LEVEL_AFTER_TEMPLATE简直难以理喻)

<?php

use Phalcon\Mvc\View;

$di->set('view', function(){

    $view = new View();

    //Disable several levels
    $view->disableLevel(array(
        View::LEVEL_LAYOUT => true,
        View::LEVEL_MAIN_LAYOUT => true
    ));

    return $view;

}, true);

Or disable temporarily in some part of the application:

<?php

use Phalcon\Mvc\View,
    Phalcon\Mvc\Controller;

class PostsController extends Controller
{

    public function indexAction()
    {

    }

    public function findAction()
    {
        $this->view->disableLevel(View::LEVEL_MAIN_LAYOUT);
    }

}

–Picking Views
As mentioned above, when Phalcon\Mvc\View is managed by Phalcon\Mvc\Application the view rendered is the one related with the last controller and action executed. You could override this by using the Phalcon\Mvc\View::pick() method:
视图渲染跟最后一个控制器和Action执行相关的。不过可以使用pick方法重写:

<?php

class ProductsController extends \Phalcon\Mvc\Controller
{

    public function listAction()
    {
        // Pick "views-dir/products/search" as view to render
        $this->view->pick("products/search");

        // Pick "views-dir/products/list" as view to render
        $this->view->pick(array('products'));

        // Pick "views-dir/products/list" as view to render
        $this->view->pick(array(1 => 'search'));
    }

}

(玛尼,array写法报错)

–Disabling the view
If your controller doesn’t produce any output in the view (or not even have one) you may disable the view component avoiding unnecessary processing:

<?php

class UsersController extends \Phalcon\Mvc\Controller
{

    public function closeSessionAction()
    {
        //Close session
        //...

        //An HTTP Redirect
        $this->response->redirect('index/index');

        //Disable the view to avoid rendering
        $this->view->disable();
    }

}

You can return a ‘response’ object to avoid disable the view manually:

<?php

class UsersController extends \Phalcon\Mvc\Controller
{

    public function closeSessionAction()
    {
        //Close session
        //...

        //An HTTP Redirect
        return $this->response->redirect('index/index');
    }

}

Simple Rendering 简单渲染器
Phalcon\Mvc\View\Simple is an alternative component to Phalcon\Mvc\View. It keeps most of the philosophy of Phalcon\Mvc\View but lacks of a hierarchy of files which is, in fact, the main feature of its counterpart.
Phalcon\Mvc\View\Simple没有层次,主要功能是counterpart

This component allows the developer to have control of when a view is rendered and its location. In addition, this component can leverage of view inheritance available in template engines such as Volt and others.
这个组件允许开发控制一个视图什么时候渲染和位置。

The default component must be replaced in the service container:

<?php

$di->set('view', function() {

    $view = new Phalcon\Mvc\View\Simple();

    $view->setViewsDir('../app/views/');

    return $view;

}, true);

Automatic rendering must be disabled in Phalcon\Mvc\Application (if needed):
自动渲染必须禁用

<?php

try {

    $application = new Phalcon\Mvc\Application($di);

    $application->useImplicitView(false);

    echo $application->handle()->getContent();

} catch (\Exception $e) {
    echo $e->getMessage();
}

To render a view it’s necessary to call the render method explicitly indicating the relative path to the view you want to display:
显式调用render方法

<?php

class PostsController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {
        //Render 'views-dir/index.phtml'
        echo $this->view->render('index');

        //Render 'views-dir/posts/show.phtml'
        echo $this->view->render('posts/show');

        //Render 'views-dir/index.phtml' passing variables
        echo $this->view->render('index', array('posts' => Posts::find()));

        //Render 'views-dir/posts/show.phtml' passing variables
        echo $this->view->render('posts/show', array('posts' => Posts::find()));
    }

}

Using Partials
Partial templates are another way of breaking the rendering process into simpler more manageable chunks可管理的块 that can be reused by different parts of the application. With a partial, you can move the code for rendering a particular piece of a response to its own file.

One way to use partials is to treat them as the equivalent等同 of subroutines: as a way to move details out of a view so that your code can be more easily understood. For example, you might have a view that looks like this:

<div class="top"><?php $this->partial("shared/ad_banner") ?></div>

<div class="content">
    <h1>Robots</h1>

    <p>Check out our specials for robots:</p>
    ...
</div>

<div class="footer"><?php $this->partial("shared/footer") ?></div>

Method partial() does accept a second parameter as an array of variables/parameters that only will exists in the scope of the partial:

<?php $this->partial("shared/ad_banner", array('id' => $site->id, 'size' => 'big')) ?>

Transfer values from the controller to views
Phalcon\Mvc\View is available in each controller using the view variable ($this->view). You can use that object to set variables directly to the view from a controller action by using the setVar() method.

<?php

class PostsController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    public function showAction()
    {
        //Pass all the posts to the views
        $this->view->setVar("posts", Posts::find());

        //Using the magic setter
        $this->view->posts = Posts::find();

        //Passing more than one variable at the same time
        $this->view->setVars(array(
            'title' => $post->title,
            'content' => $post->content
        ));
    }

}

A variable with the name of the first parameter of setVar() will be created in the view, ready to be used. The variable can be of any type, from a simple string, integer etc. variable to a more complex structure such as array, collection etc.

<div class="post">
<?php

  foreach ($posts as $post) {
    echo "<h1>", $post->title, "</h1>";
  }

?>
</div>

Using models in the view layer
Application models are always available at the view layer. The Phalcon\Loader will instantiate them at runtime automatically:

<div class="categories">
<?php

    foreach (Categories::find("status = 1") as $category) {
       echo "<span class='category'>", $category->name, "</span>";
    }

?>
</div>

Although you may perform model manipulation operations such as insert() or update() in the view layer, it is not recommended since it is not possible to forward the execution flow to another controller in the case of an error or an exception.

Caching View Fragments
Sometimes when you develop dynamic websites and some areas of them are not updated very often, the output is exactly the same between requests. Phalcon\Mvc\View offers caching a part or the whole rendered output to increase performance.
Phalcon\Mvc\View提供部分或整个渲染输出缓存。

Phalcon\Mvc\View integrates with Phalcon\Cache to provide an easier way to cache output fragments. You could manually set the cache handler or set a global handler:

<?php

class PostsController extends \Phalcon\Mvc\Controller
{

    public function showAction()
    {
        //Cache the view using the default settings
        $this->view->cache(true);
    }

    public function showArticleAction()
    {
        // Cache this view for 1 hour
        $this->view->cache(array(
            "lifetime" => 3600
        ));
    }

    public function resumeAction()
    {
        //Cache this view for 1 day with the key "resume-cache"
        $this->view->cache(
            array(
                "lifetime" => 86400,
                "key"      => "resume-cache",
            )
        );
    }

    public function downloadAction()
    {
        //Passing a custom service
        $this->view->cache(
            array(
                "service"  => "myCache",
                "lifetime" => 86400,
                "key"      => "resume-cache",
            )
        );
    }

}

When we do not define a key to the cache, the component automatically creates one using a md5 hash of the name of the view currently being rendered. It is a good practice to define a key for each action so you can easily identify the cache associated with each view.

When the View component needs to cache something it will request a cache service from the services container. The service name convention for this service is “viewCache”:

<?php

use Phalcon\Cache\Frontend\Output as OutputFrontend,
    Phalcon\Cache\Backend\Memcache as MemcacheBackend;

//Set the views cache service
$di->set('viewCache', function() {

    //Cache data for one day by default
    $frontCache = new OutputFrontend(array(
        "lifetime" => 86400
    ));

    //Memcached connection settings
    $cache = new MemcacheBackend($frontCache, array(
        "host" => "localhost",
        "port" => "11211"
    ));

    return $cache;
});

The frontend must always be Phalcon\Cache\Frontend\Output and the service ‘viewCache’ must be registered as always open (not shared) in the services container (DI)

When using views, caching can be used to prevent controllers from needing to generate view data on each request.

To achieve this we must identify uniquely each cache with a key. First we verify that the cache does not exist or has expired to make the calculations/queries to display data in the view:

<?php

class DownloadController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

        //Check whether the cache with key "downloads" exists or has expired
        if ($this->view->getCache()->exists('downloads')) {

            //Query the latest downloads
            $latest = Downloads::find(array(
                'order' => 'created_at DESC'
            ));

            $this->view->latest = $latest;
        }

        //Enable the cache with the same key "downloads"
        $this->view->cache(array(
            'key' => 'downloads'
        ));
    }

}

The PHP alternative site is an example of implementing the caching of fragments.

Template Engines 模板引擎
Template Engines help designers to create views without the use of a complicated syntax. Phalcon includes a powerful and fast templating engine called Volt.
Phalcon提供了模板引擎Volt。

Additionally, Phalcon\Mvc\View allows you to use other template engines instead of plain PHP or Volt.
Phalcon\Mvc\View允许使用模板引擎替换PHP或Volit。

Using a different template engine, usually requires complex text parsing using external PHP libraries in order to generate the final output for the user. This usually increases the number of resources that your application will use.
需要使用PHP第三类库,将增加应用使用的资源。

If an external template engine is used, Phalcon\Mvc\View provides exactly the same view hierarchy and it’s still possible to access the API inside these templates with a little more effort.

This component uses adapters, these help Phalcon to speak with those external template engines in a unified way, let’s see how to do that integration.

–Creating your own Template Engine Adapter
–Changing the Template Engine
……

Injecting services in View
Every view executed is included inside a Phalcon\DI\Injectable instance, providing easy access to the application’s service container.

The following example shows how to write a jQuery ajax request using a url with the framework conventions. The service “url” (usually Phalcon\Mvc\Url) is injected in the view by accessing a property with the same name:

<script type="text/javascript">

$.ajax({
    url: "<?php echo $this->url->get("cities/get") ?>"
})
.done(function() {
    alert("Done!");
});

</script>

Stand-Alone Component
All the components in Phalcon can be used as glue components individually because they are loosely coupled to each other:

–Hierarchical Rendering
Using Phalcon\Mvc\View in a stand-alone mode can be demonstrated below

<?php

$view = new \Phalcon\Mvc\View();

//A trailing directory separator is required
$view->setViewsDir("../app/views/");

// Passing variables to the views, these will be created as local variables
$view->setVar("someProducts", $products);
$view->setVar("someFeatureEnabled", true);

//Start the output buffering
$view->start();

//Render all the view hierarchy related to the view products/list.phtml
$view->render("products", "list");

//Finish the output buffering
$view->finish();

echo $view->getContent();

A short syntax is also available:

<?php

$view = new \Phalcon\Mvc\View();

echo $view->getRender('products', 'list',
    array(
        "someProducts" => $products,
        "someFeatureEnabled" => true
    ),
    function($view) {
        //Set any extra options here
        $view->setViewsDir("../app/views/");
        $view->setRenderLevel(Phalcon\Mvc\View::LEVEL_LAYOUT);
    }
);

–Simple Rendering
Using Phalcon\Mvc\View\Simple in a stand-alone mode can be demonstrated below:

<?php

$view = new \Phalcon\Mvc\View\Simple();

//A trailing directory separator is required
$view->setViewsDir("../app/views/");

// Render a view and return its contents as a string
echo $view->render("templates/welcomeMail");

// Render a view passing parameters
echo $view->render("templates/welcomeMail", array(
    'email' => $email,
    'content' => $content
));

View Events
Phalcon\Mvc\View and Phalcon\Mvc\View\Simple are able to send events to an EventsManager if it is present. Events are triggered using the type “view”. Some events when returning boolean false could stop the active operation. The following events are supported:

Event Name Triggered Can stop operation?
beforeRender Triggered before starting the render process Yes
beforeRenderView Triggered before rendering an existing view Yes
afterRenderView Triggered after rendering an existing view No
afterRender Triggered after completing the render process No
notFoundView Triggered when a view was not found No

The following example demonstrates how to attach listeners to this component:

<?php

$di->set('view', function() {

    //Create an events manager
    $eventsManager = new Phalcon\Events\Manager();

    //Attach a listener for type "view"
    $eventsManager->attach("view", function($event, $view) {
        echo $event->getType(), ' - ', $view->getActiveRenderPath(), PHP_EOL;
    });

    $view = new \Phalcon\Mvc\View();
    $view->setViewsDir("../app/views/");

    //Bind the eventsManager to the view component
    $view->setEventsManager($eventsManager);

    return $view;

}, true);

The following example shows how to create a plugin that clean/repair the HTML produced by the render process using Tidy:

<?php

class TidyPlugin
{

    public function afterRender($event, $view)
    {

        $tidyConfig = array(
            'clean' => true,
            'output-xhtml' => true,
            'show-body-only' => true,
            'wrap' => 0,
        );

        $tidy = tidy_parse_string($view->getContent(), $tidyConfig, 'UTF8');
        $tidy->cleanRepair();

        $view->setContent((string) $tidy);
    }

}

//Attach the plugin as a listener
$eventsManager->attach("view:afterRender", new TidyPlugin());

PHP框架Phalcon 之 在ORM中缓存

Caching in the ORM 在ORM中缓存
Every application is different, we could have models whose data change frequently and others that rarely change. Accessing database systems is often one of the most common bottlenecks in terms of performance. This is due to the complex connection/communication processes that PHP must do in each request to obtain data from the database. Therefore, if we want to achieve good performance we need to add some layers of caching where the application requires it.
不经常改变的数据要缓存起来…

This chapter explains the possible points where it is possible to implement caching to improve performance. The framework gives you the tools to implement the cache where you demand of it according to the architecture of your application.
说它提供了缓存。

Caching Resultsets 缓存结果集
A well established technique to avoid the continuous access to the database is to cache resultsets that don’t change frequently using a system with faster access (usually memory).
把不经常改变的结果集缓存起来。

When Phalcon\Mvc\Model requires a service to cache resultsets, it will request it to the Dependency Injector Container with the convention name “modelsCache”.
当Phalcon\Mvc\Model要去缓存结果集,它会通过DI容器获取“modelsCache”(这个是缓存对象,复杂读取写入缓存)。

As Phalcon provides a component to cache any kind of data, we’ll explain how to integrate整合 it with Models. First, you must register it as a service in the services container:
首先要在容器中注册:

<?php

//Set the models cache service
$di->set('modelsCache', function() {

    //Cache data for one day by default
    $frontCache = new \Phalcon\Cache\Frontend\Data(array(
        "lifetime" => 86400
    ));

    //Memcached connection settings
    $cache = new \Phalcon\Cache\Backend\Memcache($frontCache, array(
        "host" => "localhost",
        "port" => "11211"
    ));

    return $cache;
});

You have complete control in creating and customizing the cache before being used by registering the service as an anonymous function. Once the cache setup is properly defined you could cache resultsets as follows:
可以通过匿名函数完全控制缓存对象的创建和定制。如下缓存结果集:

<?php

// Get products without caching
$products = Products::find();

// Just cache the resultset. The cache will expire in 1 hour (3600 seconds)
$products = Products::find(array(
    "cache" => array("key" => "my-cache")
));

// Cache the resultset for only for 5 minutes
$products = Products::find(array(
    "cache" => array("key" => "my-cache", "lifetime" => 300)
));

// Using a custom cache(取回缓存的方法)
$products = Products::find(array("cache" => $myCache));

Caching could be also applied to resultsets generated using relationships:
缓存也可以应用到由关系产生的结果集

<?php

// Query some post
$post = Post::findFirst();

// Get comments related to a post, also cache it
$comments = $post->getComments(array(
    "cache" => array("key" => "my-key")
));

// Get comments related to a post, setting lifetime
$comments = $post->getComments(array(
    "cache" => array("key" => "my-key", "lifetime" => 3600)
));

When a cached resultset needs to be invalidated, you can simply delete it from the cache using the previously specified key.
缓存无效,可以通过指定的key删除。

Note that not all resultsets must be cached. Results that change very frequently should not be cached since they are invalidated very quickly and caching in that case impacts performance. Additionally, large datasets that do not change frequently could be cached, but that is a decision that the developer has to make based on the available caching mechanism and whether the performance impact to simply retrieve that data in the first place is acceptable.(一段废话)

Overriding find/findFirst 重写find/findFirst
As seen above, these methods are available in models that inherit Phalcon\Mvc\Model:

<?php

class Robots extends Phalcon\Mvc\Model
{

    public static function find($parameters=null)
    {
        return parent::find($parameters);
    }

    public static function findFirst($parameters=null)
    {
        return parent::findFirst($parameters);
    }

}

By doing this, you’re intercepting all the calls to these methods, this way, you can add a cache layer or run the query if there is no cache. For example, a very basic cache implementation, uses a static property to avoid that a record would be queried several times in a same request:
重写这两个方法,添加缓存层:

<?php

class Robots extends Phalcon\Mvc\Model
{

    protected static $_cache = array();

    /**
     * Implement a method that returns a string key based
     * on the query parameters
     */
    protected static function _createKey($parameters)
    {
        $uniqueKey = array();
        foreach ($parameters as $key => $value) {
            if (is_scalar($value)) {
                $uniqueKey[] = $key . ':' . $value;
            } else {
                if (is_array($value)) {
                    $uniqueKey[] = $key . ':[' . self::_createKey($value) .']';
                }
            }
        }
        return join(',', $uniqueKey);
    }

    public static function find($parameters=null)
    {

        //Create an unique key based on the parameters
        $key = self::_createKey($parameters);

        if (!isset(self::$_cache[$key])) {
            //Store the result in the memory cache
            self::$_cache[$key] = parent::find($parameters);
        }

        //Return the result in the cache
        return self::$_cache[$key];
    }

    public static function findFirst($parameters=null)
    {
        // ...
    }

}

Access the database is several times slower than calculate a cache key, you’re free in implement the key generation strategy you find better for your needs. Note that a good key avoids collisions as much as possible, this means that different keys returns unrelated records to the find parameters.

In the above example, we used a cache in memory, it is useful as a first level cache. Once we have the memory cache, we can implement a second level cache layer like APC/XCache or a NoSQL database:
实现第二层次缓存:

<?php

public static function find($parameters=null)
{

    //Create an unique key based on the parameters
    $key = self::_createKey($parameters);

    if (!isset(self::$_cache[$key])) {

        //We're using APC as second cache
        if (apc_exists($key)) {

            $data = apc_fetch($key);

            //Store the result in the memory cache
            self::$_cache[$key] = $data;

            return $data;
        }

        //There are no memory or apc cache
        $data = parent::find($parameters);

        //Store the result in the memory cache
        self::$_cache[$key] = $data;

        //Store the result in APC
        apc_store($key, $data);

        return $data;
    }

    //Return the result in the cache
    return self::$_cache[$key];
}

This gives you full control on how the the caches must be implemented for each model, if this strategy is common to several models you can create a base class for all of them:
建立一个公共模型,然后让其它模型继承它,那么就可以使用自定义的缓存策略。

<?php

class CacheableModel extends Phalcon\Mvc\Model
{

    protected static function _createKey($parameters)
    {
        // .. create a cache key based on the parameters
    }

    public static function find($parameters=null)
    {
        //.. custom caching strategy
    }

    public static function findFirst($parameters=null)
    {
        //.. custom caching strategy
    }
}

Then use this class as base class for each ‘Cacheable’ model:

<?php

class Robots extends CacheableModel
{

}

Forcing Cache
这个内容跟以上内容重复。

Caching PHQL Queries PHQL查询缓存
All queries in the ORM, no matter how high level syntax we used to create them are handled internally using PHQL. This language gives you much more freedom to create all kinds of queries. Of course these queries can be cached:
内部都使用PHQL,这种查询可以被缓存:(PHQL先装换成IR,这里就是缓存这个IR)

<?php

$phql = "SELECT * FROM Cars WHERE name = :name:";

$query = $this->modelsManager->createQuery($phql);

$query->cache(array(
    "key" => "cars-by-name",
    "lifetime" => 300
));

$cars = $query->execute(array(
    'name' => 'Audi'
));

(注意,缓存对象务必要在DI中先注册)

If you don’t want to use the implicit cache just save the resulset into your favorite cache backend:

<?php

$phql = "SELECT * FROM Cars WHERE name = :name:";

$cars = $this->modelsManager->executeQuery($phql, array(
    'name' => 'Audi'
));

apc_store('my-cars', $cars);

Reusable Related Records
Caching Related Records
Caching Related Records Recursively
Caching based on Conditions
Caching of PHQL planning

jQuery基础备忘

ancestor descendant		祖先和后代关系,区别父子关系
parent > child			父子关系
prev + next			匹配prev之后的第一个兄弟元素。注意,由于prev可能有多个匹配,所以最终可能返回一个集合
prev ~ siblings			匹配prev之后的所有兄弟元素

+ 可用next()方法代替,~ 可用nextAll()代替。jQuery中还有siblings()方法,它返回当前元素的所有兄弟元素,而不是当前元素之后的兄弟元素。

过滤选择器根据某累过滤规则进行元素的匹配,书写时都以冒号开头。
1 简单过滤选择器语法

first() :first		第一个
last()  :last		最后一个
:not(selector)		获取给定选择器外的所有元素
:even			索引为偶数的元素,索引从0开始
:odd			索引为奇数的元素,所以从0开始
:eq(index)		等于给定索引的元素,所以从0开始
:gt(index)		大于给定索引的元素,所以从0开始
:lt(index)		小于给定索引的元素,所以从0开始	
:header			获取所有标题类型的元素,如h1、h2...
:animated		获取正在执行动画效果的元素

2 内容过滤选择器

:contains(text)		获取包含给定文本的元素
:empty			获取所有不包含子元素或者文本的空元素
:has(selector)		获取含有选择器所匹配的元素
:parent			获取含有子元素或者文本的元素(不为空)

3 可见性过滤选择器

:hidden			获取所有不可见元素,或者type为hidden的元素
:visible		获取所有的可见元素

4 属性过滤选择器
属性过滤选择器根据元素的某个属性获取元素,如ID号或匹配属性值的内容,并以“[”开始,以“]”号结尾。

[attribute]		获取包含给定属性的元素
[attribute=value]	获取等于给定的属性是某个特定值的元素
[attribute!=value]	获取不等于给定的属性是某个特定值的元素
[attribute^=value]	获取给定的属性是以某些值开始的元素
[attribute$=value]	获取给定的属性是以某些值结束的元素
[attribute*=value]	获取给定的属性是以包含某些值的元素
[selector1][selector2][selectorN]	获取满足多个条件的符合属性的元素(例子:$("div[id='dcon'][title*='B']").show())

5 子元素过滤选择器

:nth-child(eq|even|odd|index)
:first-child
:last-child
:only-child

6 表单对象属性过滤选择器

:enabled				获取表单中所有属性为可用的元素
:disabled				获取表单中所有属性为不可用的元素
:checked				获取表单中所有属性为被选中的元素
:selected				获取表单中所有选中option的元素

7 表单选择器

:input
:text
:password
:radio
:checkbox
:submit
:reset
:button
:file

PHP框架Phalcon 之 模型 模型元数据

Models Meta-Data 模型元数据
To speed up development Phalcon\Mvc\Model helps you to query fields and constraints from tables related to models. To achieve this, Phalcon\Mvc\Model\MetaData is available to manage and cache table meta-data.
为了加快开发Phalcon\Mvc\Model帮助你从表格关联到模型时查询字段和约束。为了达到这个目标,可用Phalcon\Mvc\Model\MetaData管理和缓存表元数据。

Sometimes it is necessary to get those attributes when working with models. You can get a meta-data instance as follows:
有时当使用模型时有必要去获取那些属性:

<?php

$robot = new Robots();

// Get Phalcon\Mvc\Model\Metadata instance
$metaData = $robot->getModelsMetaData();

// Get robots fields names
$attributes = $metaData->getAttributes($robot);
print_r($attributes);

// Get robots fields data types
$dataTypes = $metaData->getDataTypes($robot);
print_r($dataTypes);

–Caching Meta-Data 缓存元数据
Once the application is in a production stage, it is not necessary to query the meta-data of the table from the database system each time you use the table. This could be done caching the meta-data using any of the following adapters:
缓存元数据到如下介质:

Adapter Description API
Memory This adapter is the default. The meta-data is cached only during the request. When the request is completed, the meta-data are released as part of the normal memory of the request. This adapter is perfect when the application is in development so as to refresh the meta-data in each request containing the new and/or modified fields. Phalcon\Mvc\Model\MetaData\Memory
Session This adapter stores meta-data in the $_SESSION superglobal. This adapter is recommended only when the application is actually using a small number of models. The meta-data are refreshed every time a new session starts. This also requires the use of session_start() to start the session before using any models. Phalcon\Mvc\Model\MetaData\Session
Apc This adapter uses the Alternative PHP Cache (APC) to store the table meta-data. You can specify the lifetime of the meta-data with options. This is the most recommended way to store meta-data when the application is in production stage. Phalcon\Mvc\Model\MetaData\Apc
XCache This adapter uses XCache to store the table meta-data. You can specify the lifetime of the meta-data with options. This is the most recommended way to store meta-data when the application is in production stage. Phalcon\Mvc\Model\MetaData\Xcache
Files This adapter uses plain files to store meta-data. By using this adapter the disk-reading is increased but the database access is reduced Phalcon\Mvc\Model\MetaData\Files

As other ORM’s dependencies, the metadata manager is requested from the services container:

<?php

$di['modelsMetadata'] = function() {

    // Create a meta-data manager with APC
    $metaData = new \Phalcon\Mvc\Model\MetaData\Apc(array(
        "lifetime" => 86400,
        "prefix"   => "my-prefix"
    ));

    return $metaData;
};

——————————
以上内容并没有把这个问题说透彻。modelsMetadata默认不是Phalcon\DI\FactoryDefault会初始化的内容,所以要缓存元数据,就要手动添加如上代码到DI。

先举例:

$u = new Users();
$md = $u->getModelsMetaData();
print_r($md->getAttributes($u));

//输出
Array ( [0] => id [1] => name [2] => email )

//监控到产生了如下SQL
[Tue, 02 Sep 14 02:01:30 +0800][INFO] SELECT IF(COUNT(*)>0, 1 , 0) FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_NAME`='users'
[Tue, 02 Sep 14 02:01:30 +0800][INFO] DESCRIBE `users` 

可见,元数据的获取实际是发送DESCRIBE指令,它可以获取表结构,这些所有元数据,基本跟表结构等同。如果设置了缓存元数据,比如插入:

    $di->set('modelsMetadata',function(){
        $metaData = new \Phalcon\Mvc\Model\Metadata\Files(array(
                'metaDataDir' => __DIR__.'/../app/cache/metadata/'
        ));
        return $metaData;
    }); 

代码运行,缓存文件产生:

pwd
/******/app/cache/metadata
ls
map-users.php  meta-users-users.php

第二次运行代码,没有发现再次产生SQL。运行一个查询看看有什么不同:

$u = new Users();
$md = $u->getModelsMetaData();
$us = Users::find(1);

//SQL输出
[Tue, 02 Sep 14 02:16:42 +0800][INFO] SELECT `users`.`id`, `users`.`name`, `users`.`email` FROM `users` WHERE `users`.`id` = 1

可以看到查询前先确认表是否存在的查询 和 获取表结构的DESCRIBE查询没有产生,因为这些已经被本地缓存了。这个就说明要获取元数据是先到modelsMetadata缓存找,不存储再正常查询然后写入缓存,这个过程自动完成,只要在DI设置了modelsMetadata。
——————————
–Meta-Data Strategies 元数据策略
As mentioned above the default strategy to obtain the model’s meta-data is database introspection反思. In this strategy, the information schema is used to know the fields in a table, its primary key, nullable fields, data types, etc.

You can change the default meta-data introspection in the following way:

<?php

$di['modelsMetadata'] = function() {

    // Instantiate a meta-data adapter
    $metaData = new \Phalcon\Mvc\Model\MetaData\Apc(array(
        "lifetime" => 86400,
        "prefix"   => "my-prefix"
    ));

    //Set a custom meta-data introspection strategy
    $metaData->setStrategy(new MyInstrospectionStrategy());

    return $metaData;
};

(修改元数据策略)
–Database Introspection Strategy
This strategy doesn’t require any customization and is implicitly used by all the meta-data adapters.
默认工作在这个策略下。
–Annotations Strategy 注释策略(很新鲜)
This strategy makes use of annotations to describe the columns in a model:

<?php

class Robots extends \Phalcon\Mvc\Model
{

    /**
     * @Primary
     * @Identity
     * @Column(type="integer", nullable=false)
     */
    public $id;

    /**
     * @Column(type="string", length=70, nullable=false)
     */
    public $name;

    /**
     * @Column(type="string", length=32, nullable=false)
     */
    public $type;

    /**
     * @Column(type="integer", nullable=false)
     */
    public $year;

}

(字段的注释指明了类型 长度 是否可空,这些信息也可以从数据库获取)
Annotations must be placed in properties that are mapped to columns in the mapped source. Properties without the @Column annotation are handled as simple class attributes.
要用@Column开头,否则就是简单类属性。(看起来是利用反射来实现的)

The following annotations are supported:

Name Description
Primary Mark the field as part of the table’s primary key
Identity The field is an auto_increment/serial column
Column This marks an attribute as a mapped column

The annotation @Column supports the following parameters:

Name Description
type The column’s type (string, integer, decimal, boolean)
length The column’s length if any
nullable Set whether the column accepts null values or not

The annotations strategy could be set up this way:
只要如下设置一下,就可以修改元数据策略了

<?php

use Phalcon\Mvc\Model\MetaData\Apc as ApcMetaData,
    Phalcon\Mvc\Model\MetaData\Strategy\Annotations as StrategyAnnotations;

$di['modelsMetadata'] = function() {

    // Instantiate a meta-data adapter
    $metaData = new ApcMetaData(array(
        "lifetime" => 86400,
        "prefix"   => "my-prefix"
    ));

    //Set a custom meta-data database introspection
    $metaData->setStrategy(new StrategyAnnotations());

    return $metaData;
};

–Manual Meta-Data
Phalcon can obtain the metadata for each model automatically without the developer must set them manually using any of the introspection strategies presented above.

The developer also has the option of define the metadata manually. This strategy overrides any strategy set in the meta-data manager. New columns added/modified/removed to/from the mapped table must be added/modified/removed also for everything to work properly.

The following example shows how to define the meta-data manually:

?php

use Phalcon\Mvc\Model,
    Phalcon\Db\Column,
    Phalcon\Mvc\Model\MetaData;

class Robots extends Model
{

    public function metaData()
    {
        return array(

            //Every column in the mapped table
            MetaData::MODELS_ATTRIBUTES => array(
                'id', 'name', 'type', 'year'
            ),

            //Every column part of the primary key
            MetaData::MODELS_PRIMARY_KEY => array(
                'id'
            ),

            //Every column that isn't part of the primary key
            MetaData::MODELS_NON_PRIMARY_KEY => array(
                'name', 'type', 'year'
            ),

            //Every column that doesn't allows null values
            MetaData::MODELS_NOT_NULL => array(
                'id', 'name', 'type', 'year'
            ),

            //Every column and their data types
            MetaData::MODELS_DATA_TYPES => array(
                'id' => Column::TYPE_INTEGER,
                'name' => Column::TYPE_VARCHAR,
                'type' => Column::TYPE_VARCHAR,
                'year' => Column::TYPE_INTEGER
            ),

            //The columns that have numeric data types
            MetaData::MODELS_DATA_TYPES_NUMERIC => array(
                'id' => true,
                'year' => true,
            ),

            //The identity column, use boolean false if the model doesn't have
            //an identity column
            MetaData::MODELS_IDENTITY_COLUMN => 'id',

            //How every column must be bound/casted
            MetaData::MODELS_DATA_TYPES_BIND => array(
                'id' => Column::BIND_PARAM_INT,
                'name' => Column::BIND_PARAM_STR,
                'type' => Column::BIND_PARAM_STR,
                'year' => Column::BIND_PARAM_INT,
            ),

            //Fields that must be ignored from INSERT SQL statements
            MetaData::MODELS_AUTOMATIC_DEFAULT_INSERT => array(
                'year' => true
            ),

            //Fields that must be ignored from UPDATE SQL statements
            MetaData::MODELS_AUTOMATIC_DEFAULT_UPDATE => array(
                'year' => true
            )

        );
    }

}

一点关于中国空军歼击机的文字

中国歼击机

有官方报道J10B是4代机,那么说明中国现在对歼击机的分法是按照5代来分了。根据报道,中国上世纪90年代,从俄罗斯购买了大批的苏27,在吃透了苏27的基础上仿制了J11,并在其基础上进行优化升级,最终生产了J11B。而美国在上世纪70年代末就开始装备了3代战机,中国那时候正是动乱年代,90年代引入了苏27后,才正式有3代战机,至少相差10年,如果从掌握3代机的技术来说,中国比老美至少晚了20年以上。中国的歼击机的发展,选择了一条捷径,那就是从俄罗斯采购3代机,然后吃透3代机的技术,再然后是在掌握了三代机的技术上发展了4代机。J10的研制也是如此,据说得到了以色列方面的技术帮助,而以色列是最早采购美国F16战机的国家,中国目前发展的J10B,据说已经是4代机了(三代半),而中国周边的国家或地区,如日本、韩国、新加坡、台湾等,采购的是美国F15和F16居多,它们应该都是3代机,F15和F16的改进型的这些4代机,是美国现在的主力,估计也不会允许出口,所以中国对周边就有了压倒性的优势。

目前中国已经成功试飞了J20,估计2018年可以装备5代机,但是美国从2005年就已经开始装备了F22,所以从技术来说,中国应该至少还落后老美10年,从中国的这个发展来看,中国的歼击机技术获得了跨代发展,上世纪90年代花钱购买的3代机,非常物有所值。

现在看到很多人说中国的技术涉及抄袭,妄自菲薄。我想说的是,在落后一大截的情况下,抄袭是唯一能赶超并逆袭的方法。在完全吃透了别人的技术的同时,大批的人才以及配套设置就会建立起来,这个为后面的4代和5代机的研究打下坚实的基础,现在的J11B,除了外形类似外,基本已经脱胎换骨了(据说发动机还是短板)。

如果J20开始装备空军,那么就歼击机层面,就可以和老美有得一比了。中国需要一支强大的空军,至少能够和老美抗衡,对日本以及周边国家或地区有压倒性优势,那么日本这样的小杂种才能安分一些,因为当美国不能成为它的靠山时,它还有什么底气?

单从技术上来说,中国的J10B和J11B跟美国的F16和F15,应该差别不大了,不过真格斗起来,应该还是会吃亏,这个不是技术问题了,是实战经验问题。

关于分辨率

分辨率

2K 和 4K名称来着横向分辨率接近2K 和 4K。

电脑的分辨率1024*768 1280*720 1366*768,当前电脑显示器一般都大于等于1366*768。

当前电视机顶盒输出的标清信号一般是7xx*5xx,高清机顶盒输出的信号大于等于1280*720。

当前大多数的电视的物理分辨率大于等于1280*720, 1920*1080的所谓2K电视是当前主流。

在高清机顶盒都没有很普及的情况下,1280*720的电视已经足够,如果需要看高清电影,那么当前主流的1920*1080电视也足够了。至于4K,完全扯淡,再过10年也无法普及。

当前低端的投影仪很多物理分辨率为800*600,如果用来投射标清电视,绰绰有余。如果要投射高清电视就选用1280*720估计比较好,如果要看高清电影就需要选择1920*1080的,但是1920*1080分别率的投影仪,当前的价格虚高。如果采购了一个1920*1080的投影仪,而你又有对应的片源,那么投影到200寸(或更大)的屏幕上,跟在当前的电影院看电影基本一样。如果投影仪的分辨率比较低,投射的屏幕越大,画面自然不够清晰,原因在于投射面越大画面被拉得也越大。

当前家庭影院看起来没有必要选择1920*1080的投影仪,一方面这个分辨率的投影仪当前价格虚高,另一方面家庭影院投射面一般100寸左右,100寸左右的投射,只要能支持到1280*720就足清晰了,当然在1080P的电视上看1080P的高清电影,细腻的度当然会比投射到100寸的屏幕上好很多,但是损失一些细腻度换取了大屏幕体验,也合算吧。

需要批一下的是当前的4K电视。当前机顶盒输出的信号还没有到达1080P(就是2K),所以用2K的电视看所谓的标清或高清电视,已经极度浪费了,只有少部分人会来看个全高清电影等。在视频源都远远无法到达1080P的情况下,开始了4K的炒作,真是无比恶心,而最悲剧的是很多人压根不知道自己为何要买4K电视,只知道越大越好,就比如你有那么大的管子,都是只有一股小泉水流进来,很明显,严重的浪费但自己还不晓得。如果你要搞一部能发挥4K显示效果的电影,粗糙估计也得20G吧,把这个数字在砍半,10G吧,假如你要下载它,假如你的是10M带宽,你至少需要下载2.275个小时,很明显,10M的带宽,你无法在线看,因为一般的电影都没有那么长的时间,除非阉割一下(就是降低质量)。

不过当前很多小区可以接入100M带宽了,如果100M的带宽开始普及,那么有线电视可以光荣退休了,完全可以使用宽带代替它。总而言之,4K电视离我们还很遥远,如果你现在如果购买了一个4K电视,当到了能使用上4K的电视信号时,你的电视估计也差不多报废了。