Zend Framework 2 建立自己的 Module (4)

Database and models

在這裡,我們使用了:Zend\Db\TableGateway\TableGateway 做為對資料庫中的資料表進行新增,查詢,修改以及刪除。

我們使用 MySQL 並透過 PHP 的 PDO 層作為存取資料庫,我們先建立一個名為: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');

The model files

Zend Framework 並沒有提供 Model 之類的 component 原因是因為這部分是我們自己的商業邏輯,我們需要自己寫的

我們建立一個名為 Album.php 並放在 /path/to/project-name/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 = (!empty($data['id'])) ? $data['id'] : null;
$this->artist = (!empty($data['artist'])) ? $data['artist'] : null;
$this->title = (!empty($data['title'])) ? $data['title'] : null;
}
}
?>
view raw Album.php hosted with ❤ by GitHub

在 Album.php 中,就只有一個簡單的 Album class 我們為了要與 Zend\Db 中的 TableGateway 運作正常,因此我們需要實做 exchangeArray() 方法,這個方法可以簡易的從另一個項目設定中去複製一個陣列中的資料,並把資料可以印出在 view 上 (或表單)

我們建立了一個 AlbumTable.php 檔在: /path/to/project-name/module/Album/src/Album/Model 目錄下

AlbumTable.php

其中裡面的方法功能介紹如下:fetchAll 就是得到一個查詢所有 Album 的集合,getAlbum 則是得到某一行的且傳回物件,saveAlbum 則是儲存一行 Album 相關的資料,最後的 deleteAlbum 則是刪除某一行指定的 Album 記錄。

<?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('Album id does not exist');
}
}
}
public function deleteAlbum($id) {
$this -> tableGateway->delete(array('id' => (int) $id));
}
}
?>

Using ServiceManager to configure the table gateway and inject into the AlbumTable

修改前面的 Module.php,位置在:/path/to/project-name/module/Album/Module.php

<?php
namespace Album;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Album\Model\Album;
use Album\Model\AlbumTable; //後來新增
use Zend\Db\ResultSet\ResultSet; //後來新增
use Zend\Db\TableGateway\TableGateway; //後來新增
class Module implements AutoloaderProviderInterface, ConfigProviderInterface {
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';
}
//getServiceConfig 為後來新增
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);
},
),
);
}
}
?>

接下來要加入有關資料庫登入的驗證資訊,所以我們要分別編輯:/path/to/project-name/config/autoload/global.php 與 /path/to/project-name/config/autoload/local.php

global.php

就是加入 db 資料庫相關的,像這裡就是定義 PDO 的,下面則是加入 servive_manager 與 Zend\Db\Adapter\Adapter 內建操作資料庫有關的。

<?php
/**
* Global Configuration Override
*
* You can use this file for overriding configuration values from modules, etc.
* You would place values in here that are agnostic to the environment and not
* sensitive to security.
*
* @NOTE: In practice, this file will typically be INCLUDED in your source
* control, so do not include passwords or other sensitive information in this
* file.
*/
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',
),
)
);

local.php

則是填入登入 MySQL 的驗證資訊,如帳號與密碼。

<?php
/**
* Local Configuration Override
*
* This configuration override file is for overriding environment-specific and
* security-sensitive configuration information. Copy this file without the
* .dist extension at the end and populate values as needed.
*
* @NOTE: This file is ignored from Git by default with the .gitignore included
* in ZendSkeletonApplication. This is a good practice, as it prevents sensitive
* credentials from accidentally being committed into version control.
*/
return array(
'db' => array(
'username' => 'your user-name',
'password' => 'your password',
)
);
?>
view raw local.php hosted with ❤ by GitHub

 

Back to the controller

Listing albums

 

AlbumController.php 前面已經有使用過了,在這裡可以看到一些註解,可以知道加了哪幾行。

<?php
namespace Album\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class AlbumController extends AbstractActionController {
//加入這一行
protected $albumTable;
public function indexAction() {
return new ViewModel(array(
'albums' => $this->getAlbumTable()->fetchAll(),
));
}
public function addAction() {
}
public function editAction() {
}
public function deleteAction() {
}
//加入這一段方法
public function getAlbumTable() {
if (!$this->albumTable) {
$sm = $this->getServiceLocator();
$this->albumTable = $sm->get('Album\Model\AlbumTable');
}
return $this->albumTable;
}
}
?>

index.phtml

下面是有關於視圖 (view) index.phtml 的 code

由 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 class="table">
<tr>
<th>Title</th>
<th>Artist</th>
<th>&nbsp;</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>

We always use the escapeHtml() view helper to help protect ourselves from Cross Site Scripting (XSS) vulnerabilities (see http://en.wikipedia.org/wiki/Cross-site_scripting).

我們常會使用 escapeHtml() 來防止跨站腳本的漏洞。

 

最後,輸入網址:http://localhost/project-name/public/album 就會看到如下的畫面

user-guide.database-and-models.album-list

 

到這裡就完成了一個簡單的表格列出專輯清單,下一篇我們會介紹:Styling and Translations 樣式與翻譯。