Zend Framework 2 數(shù)據(jù)庫(kù)和模型

2018-09-28 20:15 更新

數(shù)據(jù)和模型

數(shù)據(jù)庫(kù)

通過(guò)控制器動(dòng)作方法和視圖腳本,建立了 Album 模塊,是時(shí)候注意到程序中的模型部分了。記住,模型是解決程序核心目的的一部分(也將模型稱(chēng)之為業(yè)務(wù)規(guī)則),在本例中,也是數(shù)據(jù)處理的一部分。使用 Zend Framework 中的 Zend\Db\TableGateway\TableGateway 類(lèi),這個(gè)類(lèi)是用來(lái)查找,插入,更新和刪除數(shù)據(jù)庫(kù)中表記錄的。

通過(guò) PHP 的 PDO 驅(qū)動(dòng),使用 MySQL,創(chuàng)建名為 zf2tutorial 數(shù)據(jù)庫(kù),然后運(yùn)行 SQL 語(yǔ)句創(chuàng)建一個(gè) album 表,并插入一些數(shù)據(jù)。

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

(所選擇的測(cè)試數(shù)據(jù)是英國(guó)亞馬遜暢銷(xiāo)書(shū)的相關(guān)信息!)

現(xiàn)在數(shù)據(jù)庫(kù)中有了一些數(shù)據(jù),可以為其編寫(xiě)一個(gè)簡(jiǎn)單的模型。

模型文件

Zend Framework 沒(méi)有提供 Zend\Model 控件,因?yàn)槟P褪悄愕臉I(yè)務(wù)邏輯,取決于你想讓它如何工作。根據(jù)你的需求,這里仍然有很多控件可以供你使用。一種方法是,在你的程序中模型類(lèi)對(duì)應(yīng)一種實(shí)體,然后使用映射器對(duì)象來(lái)加載和存儲(chǔ)實(shí)體到數(shù)據(jù)庫(kù)。另一種是使用對(duì)象關(guān)系映射計(jì)算,例如 Doctrine 或者 Propel。

在這個(gè)教程中,每一個(gè)專(zhuān)輯就是一個(gè) Album 對(duì)象(或者說(shuō)是實(shí)體),通過(guò)使用 Zend\Db\TableGateway\TableGateway 構(gòu)建一個(gè) AlbumTable 類(lèi),然后使用 AlbumTable 構(gòu)建一個(gè)簡(jiǎn)單的模型。在數(shù)據(jù)庫(kù)的表中,Table Data Gateway 的設(shè)計(jì)模式可以實(shí)現(xiàn)數(shù)據(jù)接口。請(qǐng)注意,雖然 Table Data Gateway 模式在大系統(tǒng)中可能會(huì)被限制。但這個(gè)也是個(gè)誘惑,如果你把數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)代碼放進(jìn)控制器的動(dòng)作方法,Zend\Db\TableGateway\AbstractTableGateway 這個(gè)類(lèi)就會(huì)暴露這些代碼。請(qǐng)不要這么做!

module/Album/src/Album/Model 路徑下新建一個(gè) Album.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 實(shí)體對(duì)象是一個(gè)簡(jiǎn)單的 PHP 類(lèi)。為了與 Zend\DbTableGateway 類(lèi)一起工作。需要實(shí)現(xiàn) exchangeArray() 方法。這個(gè)方法簡(jiǎn)單地將 data 數(shù)組中的數(shù)據(jù)拷貝到對(duì)應(yīng)實(shí)體屬性。

下一步,在 module/Album/src/Album/Model 目錄下新建 AlbumTable.php,內(nèi)容如下:

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

這里做了許多事情。首先,我們?cè)跇?gòu)造函數(shù)中設(shè)置了保護(hù)屬性 $tableGatewayTableGateway 的實(shí)例。我們將使用這個(gè)來(lái)屬性操作數(shù)據(jù)庫(kù)的 album 表。

繼續(xù)創(chuàng)建一些幫助方法,程序會(huì)使用 table gateWay 和這個(gè)實(shí)例。fetchAll() 檢索數(shù)據(jù)庫(kù)中 albums 表所有的記錄,然后將結(jié)果返回 ResultSet,getAlbum() 通過(guò) id 檢索一個(gè) Album 對(duì)象,saveAlbum()要么創(chuàng)建一個(gè)新的記錄,或更新已經(jīng)存在記錄,deleteAlbum()移除一條記錄。這些代碼不需要解釋的。

使用 ServiceManager 來(lái)配置 table gateway 然后注入到 AlbumTable

為了總是使用同一個(gè)的 AlbumTable 實(shí)例,我們將使用 ServiceManager 來(lái)定義如何創(chuàng)建。最容易的是,在模塊類(lèi)中,我們創(chuàng)建一個(gè)名為 getServiceConfig() 的方法,它可以被 ModuleManager 自動(dòng)調(diào)用,適用于 ServiceManager。然后,當(dāng)我們需要它的時(shí)候,就可以取回它。

為了配置 ServiceManager,在 ServiceManager 需要的時(shí)候,我們提供一個(gè)類(lèi)的名字或者一個(gè)工廠(chǎng)(閉包或者回調(diào)),來(lái)產(chǎn)生實(shí)例化的對(duì)象。我們通過(guò)實(shí)現(xiàn) getServiceConfig() 來(lái)提供一個(gè)工廠(chǎng),這個(gè)工廠(chǎng)用來(lái)創(chuàng)建一個(gè) AlbumTable。添加這個(gè)方法到 module/Album 目錄下的 Module.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);
                },
            ),
        );
    }
}

這個(gè)方法返回 factories 的數(shù)組,在傳遞給 ServiceManager 之前,通過(guò) ModuleManager 進(jìn)行合并。Album\Model\AlbumTable 的工廠(chǎng)使用 ServiceManager 來(lái)創(chuàng)建一個(gè) AlbumTableGateway 對(duì)象,以便傳遞到 AlbumTable 對(duì)象。一般會(huì)告知 ServiceManager 對(duì)象,AlbumTableGateway 對(duì)象的創(chuàng)建是通過(guò)得到一個(gè) Zend\Db\Adapter\Adapter對(duì)象(也是從 ServiceManager 獲取)來(lái)完成的,然后使用 AlbumTableGateway 對(duì)象來(lái)創(chuàng)建一個(gè) TableGateway 對(duì)象。TableGateway 對(duì)象每當(dāng)他創(chuàng)建一條新記錄時(shí),都會(huì)告知一個(gè) Album 對(duì)象。TableGateway 類(lèi)使用原型模式創(chuàng)建結(jié)果集和實(shí)體。這意味著在請(qǐng)求的時(shí)候不是實(shí)例化,而是系統(tǒng)克隆先前實(shí)例化的對(duì)象。參考 PHP Constructor Best Practices and the Prototype Pattern。

最終,我們需要配置 ServiceManager,讓其知道如何獲取 Zend\Db\Adapter\Adapter。這是通過(guò)使用一個(gè)工廠(chǎng)類(lèi) Zend\Db\Adapter\AdapterServiceFactory,我們能在合并配置系統(tǒng)中配置它。Zend Framework 2 的 ModuleManager 合并每一個(gè)模塊的 module.config.php 文件中所有的配置,然后合并導(dǎo)出到 config/autoload 下的文件(*.global.php*.local.php 文件)。我們將添加我們的數(shù)據(jù)庫(kù)配置信息到 global.php 這個(gè)文件,這個(gè)文件應(yīng)該提交到你的版本控制系統(tǒng)。如果你想的話(huà),現(xiàn)在可以使用 local.php (版本控制系統(tǒng)之外的)來(lái)存儲(chǔ)數(shù)據(jù)庫(kù)的證書(shū)。修改 config/autoload/global.php (在 Zend 骨架的 root 中,并不在 Album 模塊中),代碼如下:

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

你應(yīng)該將你的數(shù)據(jù)庫(kù)證書(shū)放到 config/autoload/local.php,所以他們不是在 Git 存儲(chǔ)庫(kù)(就像 local.php 會(huì)被忽略):

return array(
     'db' => array(
         'username' => 'YOUR USERNAME HERE',
         'password' => 'YOUR PASSWORD HERE',
     ),
 );

回到控制器

現(xiàn)在 ServiceManager 可以創(chuàng)建一個(gè) AlbumTable 實(shí)例了,我們可以添加一個(gè)方法到控制器去見(jiàn)檢索它。添加 getAlbumTable()AlbumController 類(lèi)中:

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

你也應(yīng)該添加下面這句到類(lèi)首部:

protected $albumTable;

每當(dāng)我們需要與模型進(jìn)行交互的時(shí)候,就可以在控制器中調(diào)用 getAlbumTable()。

如果服務(wù)定位器在 Module.php 中正確配置,那么在調(diào)用 getAlbumTable() 時(shí)將會(huì)拿到一個(gè) Album\Model\AlbumTable 實(shí)例。

列舉專(zhuān)輯

為了列舉專(zhuān)輯,我們需要從模型中檢索他們?nèi)缓髠鬟f到視圖。為此,我們?cè)?AlbumController 填寫(xiě) indexAction()。更新 AlbumControllerindexAction() 方式如下:

// module/Album/src/Album/Controller/AlbumController.php:
 // ...
     public function indexAction()
     {
         return new ViewModel(array(
             'albums' => $this->getAlbumTable()->fetchAll(),
         ));
     }
 // ...

在 Zend Framework 2 中,為了在視圖中設(shè)置變量,我們返回一個(gè) ViewModel 實(shí)例,構(gòu)造函數(shù)的第一個(gè)參數(shù)是一個(gè)來(lái)自動(dòng)作的數(shù)組,包含我們需要的數(shù)據(jù)。然后會(huì)自動(dòng)傳遞到視圖腳本。ViewModel 對(duì)象還允許我們改變視圖腳本,但是默認(rèn)是使用{controller name}/{action name}。我們現(xiàn)在可以填寫(xiě)視圖腳本 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>

第一件事是我們要為頁(yè)面設(shè)置標(biāo)題,標(biāo)題可以在瀏覽器的標(biāo)題欄顯示,可以在 <head> 部分使用視圖幫助方法 headTitle() 來(lái)進(jìn)行設(shè)置標(biāo)題。然后我們添加一個(gè)創(chuàng)建新 album 的鏈接。

Zend Framework 2 提供了視圖幫助方法 url(),用來(lái)創(chuàng)建 URL。url() 的第一個(gè)參數(shù)是 route,用來(lái)創(chuàng)建 URL,第二個(gè)參數(shù)是所有變量的數(shù)組,用來(lái)填充字段的。在本例中,我們使用 ‘a(chǎn)lbum’ route 以及設(shè)置兩個(gè)字段變量 actionid。

我們遍歷了 $albums 中從控制器分配的動(dòng)作。Zend Framework 2 視圖系統(tǒng)自動(dòng)確認(rèn)了填充到視圖腳本域的變量,所以我們不用擔(dān)心還要像舊框架 Zend Framework 1 中那樣使用前綴 $this-> 來(lái)區(qū)別變量,當(dāng)然你仍然可以在這么做。

然后創(chuàng)建一個(gè)表格來(lái)展示每一個(gè) album 的標(biāo)題和藝術(shù)家,提供一些鏈接,用以修改和刪除記錄。一個(gè)標(biāo)準(zhǔn)的變量是:使用迭代器循環(huán)遍歷 albums 的列表,我們交替使用冒號(hào)和 endforeach 的形式;相對(duì)于嘗試匹配括號(hào),這就可以十分簡(jiǎn)單訪(fǎng)問(wèn)元素了。再一次使用視圖幫助方法 url() 來(lái)創(chuàng)建修改和刪除鏈接。

注意

我們可以使用視圖幫助方法 escapeHtml() 來(lái)輔助保護(hù)我們自己的跨站腳本的缺陷。(詳情查看 http://en.wikipedia.org/wiki/Cross-site_scripting

如果你打開(kāi) http://zf2-tutorial.localhost/album,就能看到如下效果:

image

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)