这几天一直在看这部分的教程,非常纠结的一篇教程,完全不能保证大家都能看懂,所以仅供参考。
数据库(Database)和模型(Models)
一、数据库(Database)
现在我们已经建立了 Album 模块中的控制器(Controller)和 action 方法以及视图(view)代码,是时候看看我们应用程序中的模式(model)部分了。模式(model)是涉及应用程序核心目的的一部分(也称作:商业规则),在我们的例子中涉及到数据库。我们使用 ZF2 框架的 Zend\Db\TableGateway\TableGateway 类来对数据表进行查找,插入,更新和删除操作。
我们通过 PHP 的 PDO 驱动来使用 MySQL,建立一个名为 zf2tutorial 的数据库,运行以下 SQL 声明来建立唱片数据表并且插入一些数据。
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');
我们现在已经在数据库中有了些数据,可以为它写一个非常简单的模式了。
二、模式(Model)文件
ZF2 并不提供 Zend\Model 组件,应为模式(model)是你项目的业务逻辑(business logic),需要你来确定要它如何工作。你可以根据你的需要使用有很多组件。一种方法是在你的项目中为每个实体构建模式类别(model classes),并且使用对象映射(mapper objects)来调用和保存实体到数据库中。另一种方法是使用Object-relational mapping (ORM)技术,就像 Doctrine 或者 Propel。
在这个教程中,我们将建立一个非常简单的模式。每个 album object 是 Album object(把它看做实体)中,使用 Zend\Db\TableGateway\TableGateway 类创建一个 AlbumTable 类。这是一种数据表数据通道(Table Data Getway)的实现,这种设计模式顾及到了在数据库中数据的接口连接。我们意识到虽然数据表数据通道模式可能在大型系统中成为一个瓶颈,但是依然吸引我们将数据库访问代码放到控制器里的 action 方法中。这样暴露了 Zend\Db\TableGateway\AbstractTableGateway 别这么做!
在 module/Album/src/Album/Model 目录下创建一个文件 Album.php
id = (!empty($data['id'])) ? $data['id'] : null; $this->artist = (!empty($data['artist'])) ? $data['artist'] : null; $this->title = (!empty($data['title'])) ? $data['title'] : null; }}
我们的 Album 实体对象是一个简单的 PHP 类。为了能和 Zend\Db 的 TableGateway 一起工作,我们需要实现 exchangeArray() 方法。这个方法简单的复制来自实体属性并通过数组传入的数据。我们将在今后添加一个针对表单的输入过滤器。
接下来我们在 module/Album/src/Album/Model 目录下建立一个 PHP 文件 AlbumTable.php,并输入以下代码:
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,在构造函数中把 TableGateway 类型的对象传递给它。我们将为我们的唱片在数据库上使用它。
我们也创建了一些辅助函数(helper methods),我们的应用程序将使用这些辅助函数来与数据表通道接口进行交互。fetchAll() 方法从数据库中检索所有的唱片数据行,返回结果集(ResultSet),getAlbum() 方法检索单个数据行并作为唱片对象(Album object)返回,saveAlbum() 方法创建一个新的唱片数据行或者更新一个已经存在的数据行,deleteAlbum() 方法完全移除一行数据行。每个方法的代码都是简单的不用说明的。
三、使用服务管理器(ServiceManager)配置数据表通道(Table Getway)并添加到唱片数据表
为了始终为我们的 AblumTable 使用相同的接口,我们要使用服务管理器(ServiceManager)来定义如何创建这些接口。这是非常容易实现的,在 Module 类中创建一个 getServiceConfig() 的方法,这个方法会被模块管理器(ModuleManager)自动调用并且应用于服务管理器(ServiceManager)。我们现在可以在我们需要的时候在控制器中检索了。
为了配置服务管理器(ServiceManager),在服务管理器(ServiceManager)需要的时候,我们可以提供类的名称来实例化或者使用工厂模式(闭包或者回调)实例化一个对象。我们从实现 getServiceConfig() 开始,getServiceConfig() 提供了一个工厂模式来创建一个 AlbumTable。在 module/Album 目录下的 Module.php 的底部添加这个方法,代码如下:
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); }, ), ); }}
这个方法返回一个名为 factories 的数组,在传递给服务管理器(ServiceManager)之前被模块管理器(ModuleManager)组合在了一起。Album\Model\AlbumTable 的工厂模式使用 ServiceManager 创建了一个AlbumTableGateway 并传递给 AlbumTable。我们也可以告诉 ServiceManager,获取 Zend\Db\Adapter\Adapter 后创建了 AlbumTableGateway(同样来自于ServiceManager)而且使用它创建了 TableGateway 对象。无论何时创建一个新的结果行 TableGateway 都需要使用一个 Album 对象。数据表通道(TableGetway)类使用模板模式创建结果集和实体。也就是说当需要时代替实例,系统克隆一个预先实例化的对象。参考《》或者更多信息。
最后我们需要配置 Servicemanager,让它知道如何得到 Zend\Db\Adapter\Adapter。为了实现这个功能需要使用一个叫 Zend\Db\Adapter\AdapterServiceFactory 的工厂模式,我们可以在混合配置系统中配置这个模式。ZF2 的模块管理器(ModuleManager)合并所有来自于每个模块的 module.config.php 文件的配置,合并后保存在 config/autoload 目录中的文件中(*.global.php 文件以及 *.local.php文件)。我们将我们的数据库配置信息添加到 global.php 文件中,这个文件你应该提交到你的版本控制软件中。如果你需要可以使用 local.php(除了VCS)来存储你数据库的证书。修改 config/autoload/global.php(在 Zend 骨架的根目录下,不是在 Album 模块中)添加以下代码:
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会被忽略)
array( 'username' => 'YOUR USERNAME HERE', 'password' => 'YOUR PASSWORD HERE', ),);
四、回到控制器(Controller)
现在 ServiceManager 可以为我们创建一个 AlbumTable 实例了,我们可以在控制器(Controller)中添加一个方法来取回这个 AlbumTable 实例,在 AlbumController 类中添加 getAlbumTable() 方法。添加的代码如下:
// 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;我们现在可以在我们需要和模式(Model)进行交互时在控制器(Controller)内部调用 getAlbumTable() 方法了。
如果服务探测器(service locator)在 Module.php 内得到了正确的配置,当调用 getAlbumTable() 方法时我们会得到一个 Album\Model\AlbumTable 的实例。
五、唱片列表
为了展示唱片列表,我们需要通过模式(Model)获取并传递给视图(view)。我们要在 AlbumController 中的 indexAction() 方法中输入些代码,将 AlbumController 中的 indexAction() 方法更新为以下代码:
// module/Album/src/Album/Controller/AlbumController.php:// ... public function indexAction() { return new ViewModel(array( 'albums' => $this->getAlbumTable()->fetchAll(), )); }// ...使用 ZF2,为了在视图(view)中设定变量,我们返回了一个视图模式(ViewModel)实例。视图模式结构的第一个参数是一个数组,这个数组来自于我们需要的包含数据的 action。会自动传递给视图(view)代码。视图模式(ViewModel)对象允许我们修改已经使用的视图(view)代码,但是默认使用的是 {controller name}/{action name}。我们现在可以在 index.phtml 中输入以下代码:
headTitle($title);?>escapeHtml($title); ?>
Title | Artist | |
---|---|---|
escapeHtml($album->title);?> | escapeHtml($album->artist);?> | Edit Delete |
url() 视图辅助函数(view helper)是由 ZF2 提供的被用作创建我们需要的连接。url() 的第一个参数是我们希望构建 URL 时所用到的路由名称,第二个参数是一个包含所有变量的数组,这个数组中的变量是我们需要使用的而且合适的占位符。在这个例子中,我们使用 'album' 路由,同时接受两个占位符变量:action 和 id。
我们迭代来自于我们指定的控制器(Controller)中的 action 的 $albums。ZF2 视图系统(view system)自动确定变量被提取到了视图代码的作用域之内,所以我们不必担心在变量前面的前缀 $this->,因为我们在 ZF1 中已经不得不使用到了;不管怎样你可以按照你的想法来使用。
我们然后建立了一个表格来显示每个唱片的标题和艺术家并且提供了编辑和删除记录的链接。一个标准的 foreach: 循环被用来迭代唱片列表,我们使用冒号来交替使用,并用 endforeach; 来结尾,这样的结构比使用花括号更容易检测。最后,url() 视图辅助函数(view helper)创建了编辑和删除链接。
注意:
我们始终使用 escapeHtml() 视图辅助函数(view helper)来保护我们自己的代码免受 XSS 攻击(Cross Site Scripting (XSS) vulnerabilities),具体请看()
如果你现在打开 ,你将会看到
未完待续......谢谢