菜菜鳥Zend Framework 2 不徹底學習塗鴉(六)-- 數據庫和模式

這幾天一直在看這部分的教程,很是糾結的一篇教程,徹底不能保證你們都能看懂,因此僅供參考。 php

數據庫(Database)和模型(Models)

1、數據庫(Database)

如今咱們已經創建了 Album 模塊中的控制器(Controller)和 action 方法以及視圖(view)代碼,是時候看看咱們應用程序中的模式(model)部分了。模式(model)是涉及應用程序核心目的的一部分(也稱做:商業規則),在咱們的例子中涉及到數據庫。咱們使用 ZF2 框架的 Zend\Db\TableGateway\TableGateway 類來對數據表進行查找,插入,更新和刪除操做。 html

咱們經過 PHP 的 PDO 驅動來使用 MySQL,創建一個名爲 zf2tutorial 的數據庫,運行如下 SQL 聲明來創建唱片數據表而且插入一些數據。 mysql

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');


咱們如今已經在數據庫中有了些數據,能夠爲它寫一個很是簡單的模式了。 git

2、模式(Model)文件

ZF2 並不提供 Zend\Model 組件,應爲模式(model)是你項目的業務邏輯(business logic),須要你來肯定要它如何工做。你能夠根據你的須要使用有不少組件。一種方法是在你的項目中爲每一個實體構建模式類別(model classes),而且使用對象映射(mapper objects)來調用和保存實體到數據庫中。另外一種方法是使用Object-relational mapping (ORM)技術,就像 Doctrine 或者 Propel。 sql

在這個教程中,咱們將創建一個很是簡單的模式。每一個 album object 是 Album object(把它看作實體)中,使用 Zend\Db\TableGateway\TableGateway 類建立一個 AlbumTable 類。這是一種數據表數據通道(Table Data Getway)的實現,這種設計模式顧及到了在數據庫中數據的接口鏈接。咱們意識到雖然數據表數據通道模式可能在大型系統中成爲一個瓶頸,可是依然吸引咱們將數據庫訪問代碼放到控制器裏的 action 方法中。這樣暴露了 Zend\Db\TableGateway\AbstractTableGateway 別這麼作! 數據庫

在 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;
    }
}


咱們的 Album 實體對象是一個簡單的 PHP 類。爲了能和 Zend\Db 的 TableGateway 一塊兒工做,咱們須要實現 exchangeArray() 方法。這個方法簡單的複製來自實體屬性並經過數組傳入的數據。咱們將在從此添加一個針對表單的輸入過濾器。 數組

接下來咱們在 module/Album/src/Album/Model 目錄下創建一個 PHP 文件 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,在構造函數中把 TableGateway 類型的對象傳遞給它。咱們將爲咱們的唱片在數據庫上使用它。

咱們也建立了一些輔助函數(helper methods),咱們的應用程序將使用這些輔助函數來與數據表通道接口進行交互。fetchAll() 方法從數據庫中檢索全部的唱片數據行,返回結果集(ResultSet),getAlbum() 方法檢索單個數據行並做爲唱片對象(Album object)返回,saveAlbum() 方法建立一個新的唱片數據行或者更新一個已經存在的數據行,deleteAlbum() 方法徹底移除一行數據行。每一個方法的代碼都是簡單的不用說明的。 閉包

3、使用服務管理器(ServiceManager)配置數據表通道(Table Getway)並添加到唱片數據表

爲了始終爲咱們的 AblumTable 使用相同的接口,咱們要使用服務管理器(ServiceManager)來定義如何建立這些接口。這是很是容易實現的,在 Module 類中建立一個 getServiceConfig() 的方法,這個方法會被模塊管理器(ModuleManager)自動調用而且應用於服務管理器(ServiceManager)。咱們如今能夠在咱們須要的時候在控制器中檢索了。

爲了配置服務管理器(ServiceManager),在服務管理器(ServiceManager)須要的時候,咱們能夠提供類的名稱來實例化或者使用工廠模式(閉包或者回調)實例化一個對象。咱們從實現 getServiceConfig() 開始,getServiceConfig() 提供了一個工廠模式來建立一個 AlbumTable。在 module/Album 目錄下的 Module.php 的底部添加這個方法,代碼以下:

<?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);
                },
            ),
        );
    }
}

這個方法返回一個名爲 factories 的數組,在傳遞給服務管理器(ServiceManager)以前被模塊管理器(ModuleManager)組合在了一塊兒。Album\Model\AlbumTable 的工廠模式使用 ServiceManager 建立了一個AlbumTableGateway 並傳遞給 AlbumTable。咱們也能夠告訴 ServiceManager,獲取 Zend\Db\Adapter\Adapter 後建立了 AlbumTableGateway(一樣來自於ServiceManager)並且使用它建立了 TableGateway 對象。不管什麼時候建立一個新的結果行 TableGateway 都須要使用一個 Album 對象。數據表通道(TableGetway)類使用模板模式建立結果集和實體。也就是說當須要時代替實例,系統克隆一個預先實例化的對象。參考《PHP 構造函數最佳實踐和模板模式》或者更多信息。


最後咱們須要配置 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 模塊中)添加如下代碼:

<?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',
    ),
);

4、回到控制器(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 的實例。

5、唱片列表

爲了展現唱片列表,咱們須要經過模式(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 中輸入如下代碼:

<?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>

首先咱們作的是設定頁面的標題(在佈局(Layout)中使用)而且使用 headTitle() 這個視圖輔助函數(view helper)爲 <head> 段落設定了標題,這將在瀏覽器的標題欄中顯示頁面標題。而後咱們爲新建唱片添加了一個鏈接。

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),具體請看(http://en.wikipedia.org/wiki/Cross-site_scripting

若是你如今打開 http://zf2-tutorial.localhost/album,你將會看到


未完待續......謝謝

相關文章
相關標籤/搜索