zend framework2使用教程【一】-安装
zend framework2使用教程【一】-安装
一、前往:http://www.zendframework.com/downloads/latest,下载最新版zf,当前教程使用版本为2.1.0
二、在你的web根目录新建文件夹zf,将压缩包中的内容解压缩到zf文件夹中
三、前往:https://nodeload.github.com/zendframework/ZendSkeletonApplication/zip/master,下载zf2的教程目录。将压缩包的文件解压到zf文件夹
以上三部完成后,你的zf文件夹中的目录结构应该如下:
四、打开你的apache的httpd.conf文件夹,在文件的最后添加zf的环境变量,如:
SetEnv ZF2_PATH "D:\Program Files (x86)\Web\www\zf\library"
请根据你自己的目录来设置,设置完成后保存文件,重启apache。
五、新建一个.htaccess文件,包含如下内容:
<IfModule mod_rewrite.c>RewriteEngine onRewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]</IfModule>
将该文件夹放置于zf根目录,确保你的apache开启了rewrite模块。
访问http://localhost/zf/public,如果出现了欢迎界面,说明安装成功。
zend framework2使用教程【二】–读取数据
这篇教程篇幅可能会很长,大家要有耐心看,文章最后有源码下载,站长建议大家下载了源码后对照教程看,会更顺畅。
zf2是用模块系统在每个模块中来组织管理你的主应用程序代码,在上一教程中,Application模块被用来向整个应用程序提供bootstrapping,错误处理和路由控制。它通常为每个应用的首页提供应用层的控制器,但是在这个教程中我们将不使用这个默认的模块,因为我们想要相册列表作为我们的主页,这将由我们自己创建的模块来完成。
我们将把所有的代码放置于Album模块中,其中包含控制器、模型、表单和视图,当然是经过配置的。我们会根据需要来对Application模块进行调整。
我们首先来创建模块目录。
在module目录中创建一个名为Album的目录,结构如下:
zf/
/module
/Album
/config
/src
/Album
/Controller
/Form
/Model
/view
/album
/album
正如你所见,Album模块为我们将创建的不同类型的文件分配了独立的目录。在Album名称空间内包含有类的PHP文件位于src/Album目录,所以当我们需要时,在我们的模块中我们可以拥有多个名称空间。view目录也有一个名为album的子目录,该目录中存放我们的视图脚本。
zf2提供了一个ModuleManager【模块管理器】来载入和配置模块。它将在模块根目录【这里是module/album】中查找module.php,并且希望在文件中找到一个名为module的类,这就是说已知模块中的类将拥有模块名字的名称空间,即模块目录的名称。
在Album模块中创建Module.php:在zf/Module/Album目录中创建Module.php:
<?php
namespace Album;
class Module
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
}
模块管理器将为我们自动调用 getAutoloaderConfig()和getConfig()方法。
getAutoloaderConfig()方法将返回一个数组,该数组与zf2的AutoloaderFactory兼容。为了对它进行配置,所以我们要向ClassmapAutoloader添加一个类映射文件,并且将该模块的名称空间添加到StandardAutoloader【标准自动载入器】。StandardAutoloader需要一个名称空间和路径来查找文件。它遵循PSR-0标准,所以类名到文件名的映射规则跟PSR-0 rules中的每个规则一样。
由于我们是在开发中,所以我们不需要使用类映射来载入文件。所以我们为类映射自动载入器提供了一个空数组,在Module/Album目录中创建一个名为autoload_classmap.php的文件,代码如下:
<?php
return array();
由于这是一个空数组,所以当不论何时自动载入器在Album名称空间中查找一个类时,它都将为我们转到StandardAutoloader【标准自动载入器】。
在注册了自动载入器之后,让我们再来看一看getConfig()方法。这个方法仅仅载入config/module.config.php文件。
在module/Album/config目录中创建一个名为module.config.php的文件:
<?php
return array(
'controllers' => array(
'invokables' => array(
'Album\Controller\Album' => 'Album\Controller\AlbumController',
),
),
'view_manager' => array(
'template_path_stack' => array(
'album' => __DIR__ . '/../view',
),
),
);
配置信息将会由ServiceManager【服务管理器】传递给相关组件,我们需要2个初始部分:controllers【控制器】和view_manager【视图管理器】。控制器部分呈现了由模块提供的所有控制器列表;我们将需要一个控制器:AlbumController。通过键名"Album\Controller\Album"来引用,控制器名称必须在整个模块中唯一,所以我们为它加了一个前缀--即我们的模块名称。
在view_manager部分,我们将添加我们的视图目录到TemplatePathStack选项,这将允许它在我们的view目录中找到Album模块的视图脚本。
现在我们需要告诉ModuleManager【模块管理器】,这个新的模块存在。我们可以在config/application.config.php文件中完成这项工作,更新这个文件,那么它的的modules部分将包含Album模块,所以该文件现在的内容应该如下:
<?php
return array(
'modules' => array(
'Application',
'Album', // <-- 这是我们需要添加的行
),
'module_listener_options' => array(
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'./module',
'./vendor',
),
),
);
正如你所见,我们将Album模块添加到了Application模块之后。
我们现在可以准备为我们的模块添加自定义的代码了。
我们将建立一个非常简单的存货清单系统来展现我们的相册集合。在主页中,我们将列出相册集合并且允许我们添加、修改和删除。我们将需要以下页面:
PageDescription
主页现实相册列表并提供修改、删除的链接。同样的还有一个添加新相册的链接。
添加新相册这个页面将允许我们添加新的相册。
编辑相册这个页面将提供一个表单来编辑相册
删除相册这个页面将确认我们想删除一个相册并最终删除它。
在我们建立我们的文件之前,理解框架如何组织页面非常重要。应用程序中的每个页面都被视作为一个action【动作】,而所有的actions都位于模块的控制器中。因此,一般来说你需要将相关的action组织到对应的控制器中,例如,一个全新的控制器可能含有actions:current, archived和view。
由于我们的相册系统含有4个页面,我们将把它们组织到Album模块的 AlbumController控制器中,作为4个actions。如下:
PageControllerAction
主页AlbumControllerindex
添加新相册AlbumControlleradd
编辑相册AlbumControlleredit
删除相册AlbumControllerdelete
从URL到一个action的映射,使用路由来完成。可以在模块的module.config.php文件中定义路由。我们将为actions定义路由,以下是配置过的
module.config.php文件,高亮部分是新加的:
<?php
return array(
'controllers' => array(
'invokables' => array(
'Album\Controller\Album' => 'Album\Controller\AlbumController',
),
),
// 下面的部分是新加的,你应该将它们添加到你自己的文件中
'router' => array(
'routes' => array(
'album' => array(
'type' => 'segment',
'options' => array(
'route' => '/album[/:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Album\Controller\Album',
'action' => 'index',
),
),
),
),
),
'view_manager' => array(
'template_path_stack' => array(
'album' => __DIR__ . '/../view',
),
),
);
路由的名称叫album,类型【type】为segment,segment【分割】路由将允许我们识别url中的占位符,并且将这些占位符映射到匹配出的路由中的参数。在当前情况下是:/album[/:action][/:id],它将匹配任何以/album开头的url,下一个segment将会是一个可选的action的名称,再下一个将被映射到一个可选的id,方括号表示一个segment是可选的。constraints部分,将限制segment的字符是我们所预期的,在这里我们限制action以一个字母开头,接下来的字符只能是字母、下划线或星号。我们将id限制为数字。
这项路由将允许我们拥有下面的url:
URLPageAction
/album主页index
/album/add添加新相册add
/album/edit/2使用id2编辑相册edit
/album/delete/4使用id4删除相册delete
下面我们准备建立控制器:在zf2中,控制器是一个被称为{Controllername}Controller的类。{Controllername}必须以大写字母开头,这个类位于一个名叫 {Controllername}Controller.php的文件中,该文件位于模块的Controller目录。在这里,即:module/Album/src/Album/Controller。每一个行为在控制器中是一个public方法,以{action name}Action形式命名,这里的{action name}应该以小写字母开头。
让我们在zf/module/Album/src/Album/Controller目录中继续创建我们的控制类:AlbumController.php,代码如下:
<?php
namespace Album\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class AlbumController extends AbstractActionController
{
public function indexAction()
{
}
public function addAction()
{
}
public function editAction()
{
}
public function deleteAction()
{
}
}
我们现在已经建立了4个我们想使用的actions,在我们创建视图之前它们不会其作用。每个url对应的action如下:
URLMethod called
http://localhost/zf/public/albumAlbum\Controller\AlbumController::indexAction
http://localhost/zf/public/album/addAlbum\Controller\AlbumController::addAction
http://localhost/zf/public/album/editAlbum\Controller\AlbumController::editAction
http://localhost/zf/public/album/deleteAlbum\Controller\AlbumController::deleteAction
我们现在拥有了一个可用的路由器,并且已经为应用程序的每个页面建立了action。
现在是时候建立视图和模型层了。
为了将视图整合进我们的应用,我们需要做的就是创建视图脚本文件。这些文件将由DefaultViewStrategy【默认视图策略】来执行,并且可以传递给由控制器action方法返回的任何变量或视图模型。这些视图脚本存储在我们模块的视图目录中,目录名称跟控制器目录名称一样。创建如下4个空视图文件:
module/Album/view/album/album/index.phtml
module/Album/view/album/album/add.phtml
module/Album/view/album/album/edit.phtml
module/Album/view/album/album/delete.phtml
现在我们可以向其中填充任何内容,接下来开始使用数据库和模型。
我们创建一个名为zf的数据库,使用下面的语句建立数据表并插入测试数据:
CREATE TABLE album (
id int(11) NOT NULL auto_increment,
artist varchar(100) NOT NULL,
title varchar(100) NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO album (artist, title)
VALUES ('The Military Wives', 'In My Dreams');
INSERT INTO album (artist, title)
VALUES ('Adele', '21');
INSERT INTO album (artist, title)
VALUES ('Bruce Springsteen', 'Wrecking Ball (Deluxe)');
INSERT INTO album (artist, title)
VALUES ('Lana Del Rey', 'Born To Die');
INSERT INTO album (artist, title)
VALUES ('Gotye', 'Making Mirrors');
我们将使用PHP的PDO驱动,现在我们在数据库中有了一些测试数据,我们可以为它写一个非常简单的模型。
zf2并不提供一个Zend\Model组件,因为model是你自己的业务逻辑,由你自己来决定它如何工作。根据你的需求,这里有许多其他的组件供你选择。其中一个方法是让你的模型类在应用程序中呈现每个实体,然后使用映射对象来载入和保存实体内容到数据库。另一种方法是使用ORM,就像Doctrine或Propel。
在本教程中,我们将使用Zend\Db\TableGateway\TableGateway类来创建一个非常简单的模型--AlbumTable类,在TableGateway中,每一个album对象都是一个Album对象【即实体】,这是一种”表数据网关设计模式“的部署方式,它允许我们访问数据库中的一张表里的数据。需要注意,尽管如此,表数据网关设计模式在较大的系统中可能成为瓶颈。可能你想要把数据库访问代码放置于一个控制器action方法里,因为这些都呈现在 Zend\Db\TableGateway\AbstractTableGateway中,zf2开发小组强烈建议你不要这么做!
现在我们在module/Album/src/Album/Model:目录中来创建一个名为Album.php的文件。代码如下:
<?php
namespace Album\Model;
class Album
{
public $id;
public $artist;
public $title;
public function exchangeArray($data)
{
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->artist = (isset($data['artist'])) ? $data['artist'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
}
}
我们的Album实体对象是一个简单的php类。为了让它与Zend\Db’s TableGateway一起工作,我们需要实现一个exchangeArray()方法。这个方法将由数组传递过来的数据复制给实体的属性。稍后,我们将为我们的表单添加一个输入过滤器。
接下来,我们在module/Album/src/Album/Model目录中创建AlbumTable.php。代码如下:
<?php
namespace Album\Model;
use Zend\Db\TableGateway\TableGateway;
class AlbumTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
public function getAlbum($id)
{
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
public function saveAlbum(Album $album)
{
$data = array(
'artist' => $album->artist,
'title' => $album->title,
);
$id = (int)$album->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getAlbum($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Form id does not exist');
}
}
}
public function deleteAlbum($id)
{
$this->tableGateway->delete(array('id' => $id));
}
}
我们仍然有许多事情要做,第一,我们要为TableGateway实体设置一个访问规则为protected的属性$tableGateway,它被传递给构造函数。我们将使用这个类来为我们的相册系统操作数据库。
接下来我们还要创建一些应用程序要用来与表数据网关交互的方法, fetchAll()方法从数据库获取所有的相册记录,类型为ResultSet【结果集】。getAlbum()方法将获取单独的一行记录作为一个Album对象,saveAlbum()方法可以在数据库创建一条新的记录或者更新一条已存在的记录。deleteAlbum()将完全删除一条记录。这些方法的代码意义当然是不言自明。
为了总是能够使用AlbumTable对象的相同实例,我们将使用ServiceManager【服务管理器】来定义如何创建一个实例。在模型类中这是最容易完成的,在类里我们创建一个getServiceConfig()方法,它将被ModuleManager自动调用,并且应用到ServiceManager。当我们需要它的时候,我们就能够重新获得。
要配置ServiceManager,我们可以在ServiceManager需要的时候,提供一个将被实例化的类的名称或者实例化对象的工厂(闭包或回调函数)。我们首先来实现getServiceConfig()方法,该方法提供一个创建AlbumTable的工厂。将这个方法添加到 Module.php的底部,位于module/Album。
<?php
namespace Album;
// Add these import statements:
use Album\Model\Album;
use Album\Model\AlbumTable;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;
class Module
{
// getAutoloaderConfig() and getConfig() methods here
// Add this method:
public function getServiceConfig()
{
return array(
'factories' => array(
'Album\Model\AlbumTable' => function($sm) {
$tableGateway = $sm->get('AlbumTableGateway');
$table = new AlbumTable($tableGateway);
return $table;
},
'AlbumTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Album());
return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
}
这个方法返回一个工厂数组,在被传递给ServiceManager之前,这些工厂都被ModuleManager整合在一起。Album\Model\AlbumTable的工厂使用ServiceManager来创建一个传递给AlbumTable的AlbumTableGateway。我们同时也告诉了ServiceManager一个AlbumTableGateway通过获取Zend\Db\Adapter\Adapter【这也是从ServiceManager获取的】被创建,我们将使用AlbumTableGateway来创建一个TableGateway对象。 一旦 TableGateway创建一个新的结果行,它将被告知使用一个Album对象。TableGateway类在创建结果集和实体时,使用原型模式。这意味着,当需要时,代之于即时实例化,系统将克隆一个预先实例化好了的对象。查看PHP Constructor Best Practices and the Prototype Pattern以获取更多细节。
最后我们需要配置ServiceManager,这样它就知道该如何来获取一个 Zend\Db\Adapter\Adapter。我们将使用一个名叫Zend\Db\Adapter\AdapterServiceFactory的工厂来完成这一配置,我们可以在内置的配置系统中来配置这个工厂。zf2的ModuleManager从每个模块的 module.config.php文件中整合了所有的配置,然后将它们整合到 位于config/autoload(*.global.php and then *.local.php files)目录的文件中。我们将向global.php添加数据库配置信息,这些信息你应该提交给你的版本控制系统。在CVS之外,你可以使用local.php来为你的数据库存储凭据【即用户名和密码】,如果你想这么做的话。修改zf/config/autoload/global.php,如下:
<?php
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=zf2tutorial;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter'
=> 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
);
你应该将你的数据库凭据存储在config/autoload/local.php文件中,这样它们就不会位于git仓库中【因为local.php被忽略了。】
<?php
return array(
'db' => array(
'username' => 'YOUR USERNAME HERE',
'password' => 'YOUR PASSWORD HERE',
),
);
现在ServiceManager已经为我们创建了一个AlbumTable实例,我们可以向控制器添加一个方法来获取它。将getAlbumTable()方法添加到AlbumController控制器类:
// module/Album/src/Album/Controller/AlbumController.php:
public function getAlbumTable()
{
if (!$this->albumTable) {
$sm = $this->getServiceLocator();
$this->albumTable = $sm->get('Album\Model\AlbumTable');
}
return $this->albumTable;
}
在类的顶部,你还应该添加:protected $albumTable;
现在,当我们需要和模型交互时,我们可以从控制器中调用getAlbumTable()方法。
为了现实相册列表,我们需要从模型获取它们并传递给视图。为了达到这一目的,我们需要实现AlbumController控制器中的indexAction()方法。更新 AlbumController的indexAction()方法如下:
// module/Album/src/Album/Controller/AlbumController.php:
// ...
public function indexAction()
{
return new ViewModel(array(
'albums' => $this->getAlbumTable()->fetchAll(),
));
}
// ...
使用zf2,要在视图中设置变量,我们返回一个ViewModel模型实例,这个模型实例构造函数的第一个参数是一个来自于action的数组,其中包含了我们需要的数据,这些数据接下来将被自动传递给视图脚本。ViewModel对象也允许我们来改变已经被使用的视图脚本,但是默认情况下是用的是{controller name}/{action name},现在我们可以完成index.phtml视图脚本。
<?php// module/Album/view/album/album/index.phtml:
$title = ‘My albums’;$this->headTitle($title);?><h1><?php echo $this->escapeHtml($title); ?></h1><p><a href=”<?php echo $this->url(‘album’, array(‘action’=>’add’));?>”>Add new album</a></p>
<table><tr><th>Title</th><th>Artist</th><th> </th></tr><?php foreach ($albums as $album) : ?><tr><td><?php echo $this->escapeHtml($album->title);?></td><td><?php echo $this->escapeHtml($album->artist);?></td><td><a href=”<?php echo $this->url(‘album’,array(‘action’=>’edit’, ‘id’ => $album->id));?>”>Edit</a><a href=”<?php echo $this->url(‘album’,array(‘action’=>’delete’, ‘id’ => $album->id));?>”>Delete</a></td></tr><?php endforeach; ?></table>
我们要做的第一件事,是使用headTitle()视图帮助函数来设置页面的标题和head部分的标题,该函数将现实浏览器的标题栏。接下来我们创建一个添加新相册的链接。
url()视图帮助函数由zf2提供,被用来创建我们需要的链接。它的第一个参数是我们想要用来构造url的路由名称,第二个参数是一个数组,包含了所有要填充进占位符的变量【译者注:其实就是action的名称】。在这里,我们使用album路由,它被设置为接受2个占位符:action和id。
我们变量从控制的action分配来的变量$albums,zf2视图系统将自动确保这些变量被释放到视图脚本的范围内。所以我们不需要担心要像zf1中那样使用$this->前缀来获取它们的值,但是,如果你喜欢,你仍然可以这样做。
接下来我们创建一个表格来现实相册的标题和作者,并且提供允许编辑和删除记录的链接。一个标准的foreach循环被用来遍历相册列表,我们使用替代语法冒号和 endforeach;因为这样比使用花括号更简单。再一次使用url()视图帮助函数来创建编辑和删除的链接。
注意:我们总是使用escapeHtml()视图帮助函数来避免XSS【跨站脚本攻击】。
zend framework2使用教程【三】–模块目录结构和文件
我们上一节,学习如何使用zf2框架链接数据库,并把数据显示在视图中。由于教程较长,所以可能很多人还没有完全理解zf2中模块化开发的意思,以及它的目录结构和文件的意义,因此再用一个教程来对这些进行较详细的讲解。
我们上一节的教程中,在除了默认的application模块外,添加了一个自定义的模块album,如果你分别访问这两个模块,你会发现它们现实的是不同的内容,它们之间是互相不干扰的。我们再来着重看一下album模块的目录结构:
config文件夹存放的是含有模块配置信息的php文件,包括控制器和url路由的配置等。
src文件夹存放模块的模型和控制器,在src文件夹中含有一个与模块同名的album文件夹,在album文件夹中又含有存放模型和控制器的文件夹。
view文件夹是存放模块试图脚本的文件夹,注意在这个文件夹中含有一个与模块名称同名的子文件夹中album,在album又含有一个同样的文件夹album,在第二个album文件夹中才存放了视图脚本。
在这个示例中,其实也告诉了我们zf2模块化开发的一般过程。如果你要建立一个新的模块,只需要将album复制一份,然后将对应的文件夹改名,并且将配置文件中的模块、控制器的名称更换即可。下面我再来看一下,zf2的MVC执行流程。
我们访问的首页是http://localhost/zf/public/album/index
album是album模块的album控制器,index是控制器中的indexaction方法,我们看到了,在url中只出现了index,而不是indexaction。这个方法的函数体如下:
public function indexAction(){return new ViewModel(array('albums' => $this->getAlbumTable()->fetchAll(),));}
$this->getAlbumTable()获取album模块的表数据网关对象实例,然后再调用该实例的fetchAll()方法,获取相关数据。
getAlbumTable()的函数体如下:
public function getAlbumTable(){if (!$this->albumTable) {$sm = $this->getServiceLocator();$this->albumTable = $sm->get('Album\Model\AlbumTable');}return $this->albumTable;}
在这个方法中,将首先获取一个服务管理器的实例-$sm,然后调用服务管理器的get方法来获取album的表数据网关对象的实例。
而’albums’实际上就是被传递给视图脚本的变量名称,它是一个对象数组,更多关于zf2操作数据库并返回值的信息需要查看zf2的官方API文档。
Album\Model\AlbumTable,这里实际上就是调用Album\Model\AlbumTable.php,在AlbumTable.php中有一个名为AlbumTable的类,这就是album模块对应的表数据网关,我们里理解为get方法返回该类的一个实例,并将其赋值给了控制器的属性。
这里再说一下一般MVC框架中的模型的概念。在一般的MVC框架中,是没有表数据网关这个概念的。我们可以简单的将zf2中的表数据网关对应于一般MVC框架的模型,因为在网关中写的就是实际处理数据操作的代码。
而album模型里还有一个文件Album.php,这个文件表示的是Album对象的一个实体。它起的是数据交换【中转】的作用,即把通过数据网关获取的数据赋值给实体。
正因为多了一个模型实体,才显得zf2与其他mvc框架与众不同,大家只要慢慢习惯了这一点,也就好了。
那么站长再列一下zf2的MVC流程:
可以简单地这么认为:
zf2->AlbumController.php->indexaction()->AlbumTable->数据库数据【省去返回箭头,因为返回的是数据】
我们的首页显示,基于的就是这样一个访问流程。
大家如果看完上一节教程,对zf2的执行流程还不够明了的,再看一看这个教程,应该就差不多理解了,其实跟一般地MVC框架差不多,无非就是zf2中的模型有模型实体和表数据网关2部分,其它的是一样的。
zend framework2使用教程【四】–翻译和样式
我们从 git上下载的示例非常好,但是我们需要修改标题并移除版权信息。
默认的应用使用 Zend\I18n’s的翻译机制来翻译所有文本。它使用位于Application/language目录的.po文件,你需要下载 poedit软件来修改文本的内容。打开poedit并打开application/language/en_US.po,点击列表中的“Skeleton Application”,并在空白栏里填写“我的zf应用”来对它进行翻译,你可以使用任何你喜欢的语句。翻译完之后,点击工具栏上的保存,poedit会为我们创建一个en_us.mo的文件。如果你发现没有生成.mo文件,一次点击文件-首选项-编辑器,查看“行为”一栏下的“保存时自动编译.mo文件”前的勾勾是否勾上。
要移除版权信息,我们需要编辑Application模块下的layout.phtml视图脚本:
// module/Application/view/layout/layout.phtml:
// Remove this line:
<p>© 2005 - 2012 by Zend Technologies Ltd. <?php echo $this->translate('All
rights reserved.') ?></p>
现在刷新我们之前建立的首页,看看翻译效果吧。
zend framework2使用教程【五】–表单动作一
现在我们可以编写添加新相册的方法了,这部分包含2方面的内容:1、显示一个输入相册信息的表单2、处理表单提交并叫数据保存到数据库
和之前一样,放出源码下载【不含数据库】:http://pan.baidu.com/share/link?shareid=276110&uk=2585386604
我们使用Zend\Form来完成这项功能, Zend\Form组件处理表单以及数据验证。我们为Album实体添加一个Zend\InputFilter【输入过滤器】,我们首先需要创建一个类Album\Form\AlbumForm,它继承Zend\Form\Form,来定义我们的表单。在 module/Album/src/Album/Form目录中创建一个名为AlbumForm.php的文件,代码如下:
setAttribute(‘method’, ‘post’);$this->add(array(‘name’ => ‘id’,‘attributes’ => array(‘type’ => ‘hidden’,),));$this->add(array(‘name’ => ‘artist’,‘attributes’ => array(‘type’ => ‘text’,),‘options’ => array(‘label’ => ‘Artist’,),));$this->add(array(‘name’ => ‘title’,‘attributes’ => array(‘type’ => ‘text’,),‘options’ => array(‘label’ => ‘Title’,),));$this->add(array(‘name’ => ‘submit’,‘attributes’ => array(‘type’ => ‘submit’,‘value’ => ‘Go’,‘id’ => ‘submitbutton’,),));}}
在 AlbumForm的构造函数中,当我们调用父类的构造函数时,我们设置了一个name参数,接下来设置了方法来创建4个表单元素:id,artist,title和submit按钮。对每一个表单项目我们设置了input的属性和选项,包括显示在页面上的标签。
同时我们还需为表单设置校验器,在zf2中,我们使用输入过滤器来完成,它既可以单独使用,也可以在任何实现了 InputFilterAwareInterface接口的类中使用,比如模型实体。我们将要为module/Album/src/Album/Model中的Album.php文件添加输入过滤器,代码如下:
namespace Album\Model;
use Zend\InputFilter\Factory as InputFactory; // use Zend\InputFilter\InputFilter; // use Zend\InputFilter\InputFilterAwareInterface; // use Zend\InputFilter\InputFilterInterface; //
class Album implements InputFilterAwareInterface{public $id;public $artist;public $title;protected $inputFilter; // id = (isset($data['id'])) ? $data['id'] : null;$this->artist = (isset($data['artist'])) ? $data['artist'] : null;$this->title = (isset($data['title'])) ? $data['title'] : null;}
// Add content to this method:public function setInputFilter(InputFilterInterface $inputFilter){throw new \Exception(“Not used”);}
public function getInputFilter(){if (!$this->inputFilter) {$inputFilter = new InputFilter();$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(‘name’ => ‘id’,‘required’ => true,‘filters’ => array(array(‘name’ => ‘Int’),),)));
$inputFilter->add($factory->createInput(array(‘name’ => ‘artist’,‘required’ => true,‘filters’ => array(array(‘name’ => ‘StripTags’),array(‘name’ => ‘StringTrim’),),‘validators’ => array(array(‘name’ => ‘StringLength’,‘options’ => array(‘encoding’ => ‘UTF-8′,‘min’ => 1,‘max’ => 100,),),),)));
$inputFilter->add($factory->createInput(array(‘name’ => ‘title’,‘required’ => true,‘filters’ => array(array(‘name’ => ‘StripTags’),array(‘name’ => ‘StringTrim’),),‘validators’ => array(array(‘name’ => ‘StringLength’,‘options’ => array(‘encoding’ => ‘UTF-8′,‘min’ => 1,‘max’ => 100,),),),)));
$this->inputFilter = $inputFilter;}
return $this->inputFilter;}}
InputFilterAwareInterface定义了2个方法:setInputFilter()和getInputFilter()。我们只需要实现getInputFilter(),所以在setInputFilter()中,我们仅仅抛出一个异常。
在getInputFilter()方法内,我们实例化一个InputFilter并且添加我们需要校验的input域。我们为我们希望过滤或校验的属性来添加一个input域。对于id,我们添加了一个Int过滤器,因为我们只需要整型数据;对于text类型的元素,我们添加了2个过滤器:StripTags和StringTrim来移除不想要的HTML标签和不需要的空格,同样地它们被设置required并且添加一个StringLength校验器来保证用户输入的字符长度不会超过数据库的设置。
加下来我们要显示表单并处理它的提交。我们在AlbumController控制器的addAction()方法中来实现://…use Zend\Mvc\Controller\AbstractActionController;use Zend\View\Model\ViewModel;use Album\Model\Album; // use Album\Form\AlbumForm; // get(‘submit’)->setValue(‘Add’);
$request = $this->getRequest();if ($request->isPost()) {$album = new Album();$form->setInputFilter($album->getInputFilter());$form->setData($request->getPost());
if ($form->isValid()) {$album->exchangeArray($form->getData());$this->getAlbumTable()->saveAlbum($album);
// Redirect to list of albumsreturn $this->redirect()->toRoute(‘album’);}}return array(‘form’ => $form);}//…
在添加了AlbumForm之后,我们实现了addAction()方法,现在让我们看看addAction()方法的细节:$form = new AlbumForm();$form->get(‘submit’)->setValue(‘Add’);上面的代码实例化AlbumForm对象,并将submit按钮的value设置为Add。之所以要这样做,是因为当我们在编辑一个相册时可以重复使用表单并使用一个不同的标签。
$request = $this->getRequest();if ($request->isPost()) {$album = new Album();$form->setInputFilter($album->getInputFilter());$form->setData($request->getPost());if ($form->isValid()) {上面的代码表示,如果Request对象的isPost方法返回值为真,表单就被提交,所以我们从album实体设置表单的输入过滤器。,接下来我们设置从表单获取的数据并使用isValid方法查看数据是否有效。
$album->exchangeArray($form->getData());$this->getAlbumTable()->saveAlbum($album);
如果表单提交有效,我们就从表单获取数据并且使用saveAlbum()将数据保存到模型。
// Redirect to list of albumsreturn $this->redirect()->toRoute(‘album’);在保存完新相册之后,我们使用Redirect控制器插件重定向到相册列表。
return array(‘form’ => $form);最后,我们返回想要传递给视图的变量。在这里,仅仅是表单对象。注意zf2也允许你仅仅返回一个将被复制给视图脚本的变量数组。它将在后台为你创建一个ViewModel,这可以让我们少打几个字。
现在我们可以在add.phtml视图脚本中渲染表单了:headTitle($title);?>
form;$form->setAttribute(‘action’, $this->url(‘album’, array(‘action’ => ‘add’)));$form->prepare();
echo $this->form()->openTag($form);echo $this->formHidden($form->get(‘id’));echo $this->formRow($form->get(‘title’));echo $this->formRow($form->get(‘artist’));echo $this->formSubmit($form->get(‘submit’));echo $this->form()->closeTag();
我们再一次的显示了标题,并且接着显示表单。zf提供了一些视图帮助函数来让这个过程变得更容易,form()视图助手拥有openTag()方法closeTag(),我们可以用它们来打开或关闭表单。接着,对每个有标签的元素,我们使用 formRow()方法,但是对于另外2个独立的元素,我们使用formHidden() 和formSubmit()方法。
作为一种可替代方案,我们也可以使用内置的formCollection表单视图助手来处理表单的显示过程。例如,在上面的视图脚本中,使用下面的语句来替换所有的用于显示表单的echo语句:echo $this->formCollection($form);上面的语句将循环表单结构,为每个表单元素调用恰当的标签,元素和错误视图助手,但是你仍然可以使用打开或关闭标签来包装formCollection($form) 。在你使用默认的HTML标签来显示表单时,这可以降低你视图脚本的复杂性。
现在点击首页的Add new album链接,你可以看见一个含有输入表单的页面,并且可以添加一个新的相册了。
zend framework2使用教程【六】–表单动作二
上一节我们介绍了通过表单提交向数据库增加数据的操作,这一节我们将介绍编辑和删除。
编辑相册和增加相册差不多,所以代码非常相似。这次我们将使用控制器的editAction()方法:
// 填充方法的内容如下
public function editAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('album', array(
'action' => 'add'
));
}
$album = $this->getAlbumTable()->getAlbum($id);
$form = new AlbumForm();
$form->bind($album);
$form->get('submit')->setAttribute('value', 'Edit');
$request = $this->getRequest();
if ($request->isPost()) {
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$this->getAlbumTable()->saveAlbum($form->getData());
// Redirect to list of albums
return $this->redirect()->toRoute('album');
}
}
return array(
'id' => $id,
'form' => $form,
);
代码看上去应该很熟悉,让我们来看一看与增加相册代码的不同。首先,我们来看一看匹配路由中的id并且使用它来载入相册内容并编辑:
$id = (int) $this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('album', array(
'action' => 'add'
));
}
$album = $this->getAlbumTable()->getAlbum($id);params是一个控制器插件,它提供了一种从匹配的路由中获取参数的便捷方式。我们使用它来从url路由中获取id的值,这个url路由是我们在module.config.php中创建的。如果id为0,我们就跳转到add方法,否则我们就从数据库获取相册实体。
$form = new AlbumForm();
$form->bind($album);
$form->get('submit')->setAttribute('value', 'Edit');
表单的bind()方法向表单添加模型,这将用于以下2种情形:1、当显示表单时,从模型释放每个元素的初始值2、在isValid()方法中校验成功后,来自表单的数据被传递回模型。
这些操作由一个hydrator【译者注:待译】对象来完成,zf中有许多hydrator,默认使用的是Zend\Stdlib\Hydrator\ArraySerializable,它将在模型中查找2个方法:getArrayCopy()和exchangeArray()。我们已经在Album实体中写了exchangeArray()方法,所以现在只需要写getArrayCopy()方法:
// module/Album/src/Album/Model/Album.php:
// ...
public function exchangeArray($data)
{
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->artist = (isset($data['artist'])) ? $data['artist'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
}
// Add the following method:
public function getArrayCopy()
{
return get_object_vars($this);
}
// ...
使用bind()方法的结果就是,我们不需要将表单数据传回给$album,因为在bind方法中,这已经完成了。所以,我们调用映射器的saveAlbum()方法来向数据库保存修改后的数据。
视图脚本edit.phtml跟添加相册的代码很相似:
<?php
// module/Album/view/album/album/edit.phtml:
$title = 'Edit album';
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<?php
$form = $this->form;
$form->setAttribute('action', $this->url(
'album',
array(
'action' => 'edit',
'id' => $this->id,
)
));
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formHidden($form->get('id'));
echo $this->formRow($form->get('title'));
echo $this->formRow($form->get('artist'));
echo $this->formSubmit($form->get('submit'));
echo $this->form()->closeTag();
唯一的区别,就是页面标题和表单提交地址不同。
最后我们来看删除相册。
为了圆满结束我们的这个应用,我们需要实现删除相册功能。在首页的相册列表中,在每个相册的后面,我们都有一个删除链接,当点击它的时候,将执行删除。如果你这样想,那么你就错了。还记得我们的HTTP说明吗,不要使用GET方法来执行一个不可逆的动作,而应该使用POST方法。
当点击删除链接时,我们应该使用一个确认表单,如果点击yes,我们就执行删除;由于这个表单很简单,我们将它直接写在视图脚本中【毕竟,Zend\Form是一个可选的功能。译者注:译者就不太喜欢使用表单组件】。
让我们来完成控制器的delete方法:
// Add content to the following method:
public function deleteAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('album');
}
$request = $this->getRequest();
if ($request->isPost()) {
$del = $request->getPost('del', 'No');
if ($del == 'Yes') {
$id = (int) $request->getPost('id');
$this->getAlbumTable()->deleteAlbum($id);
}
// Redirect to list of albums
return $this->redirect()->toRoute('album');
}
return array(
'id' => $id,
'album' => $this->getAlbumTable()->getAlbum($id)
);
}
跟之前一样,我们从匹配的url路由中获取id,并且检测request对象的isPost()方法来决定是否显示一个确认页面或删除相册。我们使用模型的deleteAlbum()方法来删除记录,接着返回相册列表。如果请求不是POST类型,我们就从数据库获取正确的数据并传递给视图,当然是根据对应的id来获取。
视图脚本是一个简单的表单:
<?php
// module/Album/view/album/album/delete.phtml:
$title = 'Delete album';
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<p>Are you sure that you want to delete
'<?php echo $this->escapeHtml($album->title); ?>' by
'<?php echo $this->escapeHtml($album->artist); ?>'?
</p>
<?php
$url = $this->url('album', array(
'action' => 'delete',
'id' => $this->id,
));
?>
<form action="<?php echo $url; ?>" method="post">
<div>
<input type="hidden" name="id" value="<?php echo (int) $album->id; ?>" />
<input type="submit" name="del" value="Yes" />
<input type="submit" name="del" value="No" />
</div>
</form>
在这个脚本中,我们向用户显示一条确认信息,和一个含有yes和no按钮的表单。在方法中,我们检查提交的值是否是yes,然后执行删除操作。
最后,目前来说,zf应用默认显示的页面并不是我们的相册主页。这归因于Application模块的module.config.php文件,我们打开module/Application/config/module.config.php,修改默认的控制器。找到数组中的home键,将controller从Application\Controller\Index修改为Album\Controller\Album:
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'Album\Controller\Album', // <-- change here
'action' => 'index',
),
),
),
好了,现在我们已经拥有了一个能够完全正常运行的应用了。
这一节就不给源码了,因为跟之前的差不多,只要简单复制一下就可以了。
zend framework2使用教程【七】–API文档查询
大家好,这是zend framework2教程的最后一节。前6节是根据官方教程翻译过来的,很遗憾,zf官方并未提供更详细的教程,因此接下来就需要大家根据前面的教程,自己练习。
本教程中提供的api非常有限,实际上,zf2拥有一个强大健壮的api库,官方提供了查询页面,地址如下:
http://www.zendframework.com/apidoc/2.1/
腾讯微博API调用示例
下载了腾讯微博API的SDK,发现里面给出的示例少得可怜,怎么办?
不要紧,官方文档提供了所有API的调用方法,不过不细心的人恐怕就看不见了。
1、前往http://wiki.open.t.qq.com/index.php/API%E6%96%87%E6%A1%A3#access
2、点击friends/add
3、翻页到页面的最后,就会发现该接口的调用示例了:
Tencent::api("https://open.t.qq.com/api/friends/add",array( "format" => "xml", "name" => "wbapihelper", "fopenids" => ""),"post",false);
其它接口的调用示例也是一样查询的。
数组中各个参数的意思,请查看页面中的“参数说明”