鍍金池/ 教程/ PHP/ 介紹 Zend\Db\Sql 和 Zend\Stdlib\Hydrator
了解 Router
回顧博客應(yīng)用程序
應(yīng)用 Form 和 Fieldset
編輯數(shù)據(jù)和刪除數(shù)據(jù)
介紹我們第一個“博客” Module
介紹 Zend\Db\Sql 和 Zend\Stdlib\Hydrator
介紹 Service 和 ServiceManager
為不同的數(shù)據(jù)庫后臺做準(zhǔn)備

介紹 Zend\Db\Sql 和 Zend\Stdlib\Hydrator

在上一個章節(jié)中我們介紹了映射層并且創(chuàng)建了 PostMapperInterface。現(xiàn)在是時候?qū)⑦@個接口進(jìn)行實現(xiàn)了,以便讓我們能再次使用 PostService。作為一個指導(dǎo)性示例,我們會使用 Zend\Db\Sql 類。

準(zhǔn)備數(shù)據(jù)庫

在我們能開始使用數(shù)據(jù)庫之前,我們應(yīng)該先準(zhǔn)備一個數(shù)據(jù)庫。在這個示例中我們會使用一個 MySQL 數(shù)據(jù)庫,名稱為 blog,并且可以在 localhost 上被訪問。這個數(shù)據(jù)庫會擁有一個叫做 posts 的表,表擁有三個屬性 id、title、text,其中 id 是主鍵。為了演示需要,請使用這個數(shù)據(jù)庫 dump:

 CREATE TABLE posts (
   id int(11) NOT NULL auto_increment,
   title varchar(100) NOT NULL,
   text TEXT NOT NULL,
   PRIMARY KEY (id)
 );

 INSERT INTO posts (title, text)
   VALUES  ('Blog #1',  'Welcome to my first blog post');
 INSERT INTO posts (title, text)
   VALUES  ('Blog #2',  'Welcome to my second blog post');
 INSERT INTO posts (title, text)
   VALUES  ('Blog #3',  'Welcome to my third blog post');
 INSERT INTO posts (title, text)
   VALUES  ('Blog #4',  'Welcome to my fourth blog post');
 INSERT INTO posts (title, text)
   VALUES  ('Blog #5',  'Welcome to my fifth blog post');

Zend\Db\Sql 的一些小知識

要使用 Zend\Db\Sql 來查詢一個數(shù)據(jù)庫,你需要擁有一個可用的數(shù)據(jù)庫連接。這個鏈接使用過任何實現(xiàn) Zend\Db\Adapter\AdapterInterface 接口的類創(chuàng)建的。最方便的創(chuàng)建這種類的方法是通過使用(監(jiān)聽配置鍵 db 的) Zend\Db\Adapter\AdapterServiceFactory。讓我們從創(chuàng)建所需配置條目開始,修改你的 module.config.php 文件,添加一個頂級鍵 db

 <?php
 // 文件名: /module/Blog/config/module.config.php
 return array(
     'db' => array(
         'driver'         => 'Pdo',
         'username'       => 'SECRET_USERNAME',  //edit this
         'password'       => 'SECRET_PASSWORD',  //edit this
         'dsn'            => 'mysql:dbname=blog;host=localhost',
         'driver_options' => array(
             \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
         )
     ),
     'service_manager' => array( /** ServiceManager Config */ ),
     'view_manager'    => array( /** ViewManager Config */ ),
     'controllers'     => array( /** ControllerManager Config */ ),
     'router'          => array( /** Router Config */ )
 );

如您所見我們已經(jīng)添加了 db 鍵,并且在里面我們添加了必要的參數(shù)來創(chuàng)建一個驅(qū)動示例。

注意:一個需要注意的重要事情是,通常你不會希望你的憑證存放在一個普通的配置文件中,而是希望存放在本地配置文件中,例如 /config/autoload/db.local.php。在使用 zend 骨架 .gitignore 文件標(biāo)記時,存放在本地的文件不會被推送到服務(wù)器。當(dāng)您共享您的代碼時請務(wù)必注意這點。參考下例代碼:

<?php
// 文件名: /config/autoload/db.local.php
return array(
        'db' => array(
            'driver'         => 'Pdo',
            'username'       => 'SECRET_USERNAME',  //edit this
            'password'       => 'SECRET_PASSWORD',  //edit this
            'dsn'            => 'mysql:dbname=blog;host=localhost',
            'driver_options' => array(
                \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
        )
    ),
);

接下來我們要做的事情就是利用 AdapterServiceFactory。這是一個 ServiceManager 條目,看上去像下面這樣:

 <?php
 // 文件名: /module/Blog/config/module.config.php
 return array(
     'db' => array(
         'driver'         => 'Pdo',
         'username'       => 'SECRET_USERNAME',  //edit this
         'password'       => 'SECRET_PASSWORD',  //edit this
         'dsn'            => 'mysql:dbname=blog;host=localhost',
         'driver_options' => array(
             \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
         )
     ),
     'service_manager' => array(
         'factories' => array(
             'Blog\Service\PostServiceInterface' => 'Blog\Service\Factory\PostServiceFactory',
             'Zend\Db\Adapter\Adapter'           => 'Zend\Db\Adapter\AdapterServiceFactory'
         )
     ),
     'view_manager'    => array( /** ViewManager Config */ ),
     'controllers'     => array( /** ControllerManager Config */ ),
     'router'          => array( /** Router Config */ )
 );

請注意名為 Zend\Db\Adapter\Adapter 的新 Service?,F(xiàn)在調(diào)用這個 Service 終會得到一個正在運行的,實現(xiàn) Zend\Db\Adapter\AdapterInterface 接口的一個示例,這取決于我們指定的驅(qū)動。

當(dāng) Adapter 就位時,我們現(xiàn)在可以對數(shù)據(jù)庫進(jìn)行查詢了。查詢的構(gòu)造最好通過 Zend\Db\Sql 的 “QueryBuilder” 功能完成,若要進(jìn)行 select 查詢則使用 Zend\Db\Sql\Sql;若要進(jìn)行 insert 查詢則使用 Zend\Db\Sql\Insert;若要進(jìn)行 update 或者 delete 查詢則分別使用 Zend\Db\Sql\UpdateZend\Db\Sql\Delete。這些組件的基本工作流是:

  1. 建立一個使用 SqlInsert、UpdateDelete的查詢
  2. Sql 對象創(chuàng)建一個 SQL 語句
  3. 執(zhí)行查詢
  4. 對結(jié)果做點什么

知道這些之后我們現(xiàn)在可以編寫 PostMapperInterface 接口的實現(xiàn)了。

編寫映射器的實現(xiàn)

我們的映射器實現(xiàn)會存放在和它的接口同樣的名稱空間內(nèi)?,F(xiàn)在開始創(chuàng)建一個類,稱之為 ZendDbSqlMapper 然后 implements PostMapperInterface

現(xiàn)在回想我們之前學(xué)到的東西。為了讓 Zend\Db\Sql 能工作我們需要一個可用的 AdapterInterface 接口的實現(xiàn)。這是一個要求,所以會通過構(gòu)造器注入進(jìn)行注入。創(chuàng)建一個 __construct() 函數(shù)來接收 AdapterInterface 作為參數(shù),并且將其存放在類中:

 <?php
 // 文件名: /module/Blog/src/Blog/Mapper/ZendDbSqlMapper.php
 namespace Blog\Mapper;

 use Blog\Model\PostInterface;
 use Zend\Db\Adapter\AdapterInterface;

 class ZendDbSqlMapper implements PostMapperInterface
 {
     /**
      * @var \Zend\Db\Adapter\AdapterInterface
      */
     protected $dbAdapter;

     /**
      * @param AdapterInterface  $dbAdapter
      */
     public function __construct(AdapterInterface $dbAdapter)
     {
         $this->dbAdapter = $dbAdapter;
     }

     /**
      * @param int|string $id
      *
      * @return PostInterface
      * @throws \InvalidArgumentException
      */
     public function find($id)
     {
     }

     /**
      * @return array|PostInterface[]
      */
     public function findAll()
     {
     }
 }

如您從以往的課程學(xué)到的內(nèi)容,每當(dāng)我們被要求參數(shù),就要為這個類編寫 factory。所以現(xiàn)在去為我們的映射器實現(xiàn)創(chuàng)建一個 factory 吧。

現(xiàn)在我們可以將映射器實現(xiàn)注冊成一個 Service 了。如果你能回想起以往的章節(jié),或者你有去查看一下當(dāng)前的錯誤信息,你就會注意到我們通過調(diào)用 Blog\Mapper\PostMapperInterface Service 來獲取映射器的實現(xiàn)。修改配置文件,以便讓這個鍵能調(diào)用我們剛調(diào)用的 factory 類。

 <?php
 // 文件名: /module/Blog/config/module.config.php
 return array(
     'db'              => array( /** Db Config */ ),
     'service_manager' => array(
         'factories' => array(
             'Blog\Mapper\PostMapperInterface'   => 'Blog\Factory\ZendDbSqlMapperFactory',
             'Blog\Service\PostServiceInterface' => 'Blog\Service\Factory\PostServiceFactory',
             'Zend\Db\Adapter\Adapter'           => 'Zend\Db\Adapter\AdapterServiceFactory'
         )
     ),
     'view_manager'    => array( /** ViewManager Config */ ),
     'controllers'     => array( /** ControllerManager Config */ ),
     'router'          => array( /** Router Config */ )
 );

當(dāng) adapter 就緒之后,你就能刷新博客站點 localhost:8080/blog,然后發(fā)現(xiàn) ServiceNotFoundException 異常已經(jīng)消失,取而代之的是一個 PHP 警告信息:

Warning: Invalid argument supplied for foreach() in /module/Blog/view/blog/list/index.phtml on line 13
 ID  Text    Title

這是因為實際上我們的映射器還不會返回任何東西。讓我們來修改一下 findAll() 函數(shù)來從數(shù)據(jù)庫表中返回所有博客帖子。

 <?php
 // 文件名: /module/Blog/src/Blog/Mapper/ZendDbSqlMapper.php
 namespace Blog\Mapper;

 use Zend\Db\Adapter\AdapterInterface;

 class ZendDbSqlMapper implements PostMapperInterface
 {
     /**
      * @var \Zend\Db\Adapter\AdapterInterface
      */
     protected $dbAdapter;

     /**
      * @param AdapterInterface  $dbAdapter
      */
     public function __construct(AdapterInterface $dbAdapter)
     {
         $this->dbAdapter = $dbAdapter;
     }

     /**
      * @param int|string $id
      *
      * @return \Blog\Entity\PostInterface
      * @throws \InvalidArgumentException
      */
     public function find($id)
     {
     }

     /**
      * @return array|\Blog\Entity\PostInterface[]
      */
     public function findAll()
     {
         $sql    = new Sql($this->dbAdapter);
         $select = $sql->select('posts');

         $stmt   = $sql->prepareStatementForSqlObject($select);
         $result = $stmt->execute();

         return $result;
     }
 }

上述代碼應(yīng)該看上去十分直接??上У氖?,再次刷新頁面會發(fā)現(xiàn)另外一個錯誤信息。

讓我們暫時先不要返回 $result 變量,然后生成一個 dump 來看看到底這里發(fā)生了什么。更改 findAll() 函數(shù)來做一個 $result 變量的數(shù)據(jù) dump:

 <?php
 // 文件名: /module/Blog/src/Blog/Mapper/ZendDbSqlMapper.php
 namespace Blog\Mapper;

 use Blog\Model\PostInterface;
 use Zend\Db\Adapter\AdapterInterface;
 use Zend\Db\Sql\Sql;

 class ZendDbSqlMapper implements PostMapperInterface
 {
     /**
      * @var \Zend\Db\Adapter\AdapterInterface
      */
     protected $dbAdapter;

     /**
      * @param AdapterInterface  $dbAdapter
      */
     public function __construct(AdapterInterface $dbAdapter)
     {
         $this->dbAdapter = $dbAdapter;
     }

     /**
      * @param int|string $id
      *
      * @return PostInterface
      * @throws \InvalidArgumentException
      */
     public function find($id)
     {
     }

     /**
      * @return array|PostInterface[]
      */
     public function findAll()
     {
         $sql    = new Sql($this->dbAdapter);
         $select = $sql->select('posts');

         $stmt   = $sql->prepareStatementForSqlObject($select);
         $result = $stmt->execute();

         \Zend\Debug\Debug::dump($result);die();
     }
 }

刷新應(yīng)用程序后你應(yīng)該能看見以下輸出:

 object(Zend\Db\Adapter\Driver\Pdo\Result)#303 (8) {
   ["statementMode":protected] => string(7) "forward"
   ["resource":protected] => object(PDOStatement)#296 (1) {
     ["queryString"] => string(29) "SELECT `posts`.* FROM `posts`"
   }
   ["options":protected] => NULL
   ["currentComplete":protected] => bool(false)
   ["currentData":protected] => NULL
   ["position":protected] => int(-1)
   ["generatedValue":protected] => string(1) "0"
   ["rowCount":protected] => NULL
 }

如您所見,沒有任何數(shù)據(jù)被返回,取而代之的是我們得到了一些 Result 對象的 dump,并且里面并不包含任何有用的數(shù)據(jù)。不過這是一個錯誤的推論,這個 Result 對象只有在當(dāng)你實際試圖訪問的時候才會擁有信息。所以若要利用 Result 對象內(nèi)的數(shù)據(jù),最佳方案是將 Result 對象傳給 ResultSet 對象,只要查詢成功就可以這樣做。

 <?php
 // 文件名: /module/Blog/src/Blog/Mapper/ZendDbSqlMapper.php
 namespace Blog\Mapper;

 use Blog\Model\PostInterface;
 use Zend\Db\Adapter\AdapterInterface;
 use Zend\Db\Adapter\Driver\ResultInterface;
 use Zend\Db\ResultSet\ResultSet;
 use Zend\Db\Sql\Sql;

 class ZendDbSqlMapper implements PostMapperInterface
 {
     /**
      * @var \Zend\Db\Adapter\AdapterInterface
      */
     protected $dbAdapter;

     /**
      * @param AdapterInterface  $dbAdapter
      */
     public function __construct(AdapterInterface $dbAdapter)
     {
         $this->dbAdapter = $dbAdapter;
     }

     /**
      * @param int|string $id
      *
      * @return PostInterface
      * @throws \InvalidArgumentException
      */
     public function find($id)
     {
     }

     /**
      * @return array|PostInterface[]
      */
     public function findAll()
     {
         $sql    = new Sql($this->dbAdapter);
         $select = $sql->select('posts');

         $stmt   = $sql->prepareStatementForSqlObject($select);
         $result = $stmt->execute();

         if ($result instanceof ResultInterface && $result->isQueryResult()) {
             $resultSet = new ResultSet();

             \Zend\Debug\Debug::dump($resultSet->initialize($result));die();
         }

         die("no data");
     }
 }

再次刷新頁面,你應(yīng)該能看見 ResultSet 對象的 dump 現(xiàn)在擁有屬性 ["count":protected] => int(5),這意味著我們有5列元組在我們的數(shù)據(jù)庫中。

 object(Zend\Db\ResultSet\ResultSet)#304 (8) {
   ["allowedReturnTypes":protected] => array(2) {
     [0] => string(11) "arrayobject"
     [1] => string(5) "array"
   }
   ["arrayObjectPrototype":protected] => object(ArrayObject)#305 (1) {
     ["storage":"ArrayObject":private] => array(0) {
     }
   }
   ["returnType":protected] => string(11) "arrayobject"
   ["buffer":protected] => NULL
   ["count":protected] => int(2)
   ["dataSource":protected] => object(Zend\Db\Adapter\Driver\Pdo\Result)#303 (8) {
     ["statementMode":protected] => string(7) "forward"
     ["resource":protected] => object(PDOStatement)#296 (1) {
       ["queryString"] => string(29) "SELECT `posts`.* FROM `posts`"
     }
     ["options":protected] => NULL
     ["currentComplete":protected] => bool(false)
     ["currentData":protected] => NULL
     ["position":protected] => int(-1)
     ["generatedValue":protected] => string(1) "0"
     ["rowCount":protected] => int(2)
   }
   ["fieldCount":protected] => int(3)
   ["position":protected] => int(0)
 }

另外一個非常有趣的屬性是 ["returnType":protected] => string(11) "arrayobject"。這告訴了我們所有的數(shù)據(jù)庫條目都會以 ArrayObject 的形式返回。這產(chǎn)生了一個小問題,因為 PostMapperInterface 要求我們返回一個 PostInterface 對象數(shù)組,幸運的是這里有非常簡單的辦法讓我們來解決它。在上面的例子中我們使用了默認(rèn)的 ResultSet 對象。其實這里還有 HydratingResultSet 對象來將給出的數(shù)據(jù)充水成給出的對象。意思就是,如果我們告訴 HydratingResultSet 對象來使用數(shù)據(jù)庫數(shù)據(jù)為我們生成 post 對象,那么它就能做到。讓我們修改一下我們的代碼:

 <?php
 // 文件名: /module/Blog/src/Blog/Mapper/ZendDbSqlMapper.php
 namespace Blog\Mapper;

 use Blog\Model\PostInterface;
 use Zend\Db\Adapter\AdapterInterface;
 use Zend\Db\Adapter\Driver\ResultInterface;
 use Zend\Db\ResultSet\HydratingResultSet;
 use Zend\Db\Sql\Sql;

 class ZendDbSqlMapper implements PostMapperInterface
 {
     /**
      * @var \Zend\Db\Adapter\AdapterInterface
      */
     protected $dbAdapter;

     /**
      * @param AdapterInterface  $dbAdapter
      */
     public function __construct(AdapterInterface $dbAdapter)
     {
         $this->dbAdapter = $dbAdapter;
     }

     /**
      * @param int|string $id
      *
      * @return PostInterface
      * @throws \InvalidArgumentException
      */
     public function find($id)
     {
     }

     /**
      * @return array|PostInterface[]
      */
     public function findAll()
     {
         $sql    = new Sql($this->dbAdapter);
         $select = $sql->select('posts');

         $stmt   = $sql->prepareStatementForSqlObject($select);
         $result = $stmt->execute();

         if ($result instanceof ResultInterface && $result->isQueryResult()) {
             $resultSet = new HydratingResultSet(new \Zend\Stdlib\Hydrator\ClassMethods(), new \Blog\Model\Post());

             return $resultSet->initialize($result);
         }

         return array();
     }
 }

我們又進(jìn)行了幾項更改。首先我們將普通的 ResultSet 替換成 HydratingResultSet。這個對象要求兩個參數(shù),第一個是使用的 hydrator 類型,第二個是 hydrator 目標(biāo)對象。hydrator 簡單來說,就是一個對象用來將任意類型的數(shù)據(jù)從一種格式轉(zhuǎn)換成另一種。我們現(xiàn)在的輸入類型是 ArrayObject,但是我們想要 Post 模型。而 ClassMethods hydrator 搞定這個轉(zhuǎn)換問題,通過調(diào)用我們的 Post 模型的 getter 和 setter 函數(shù)。

比起去 dump $result 變量,我們現(xiàn)在直接返回初始化過的 HydratingResultSet 對象,從而得以訪問里面存儲的數(shù)據(jù)。如果我們得到了一些不是 ResultInterface 的實例的返回值,那么就會返回一個空數(shù)組。

刷新頁面,現(xiàn)在你就能看見你所有的博客帖子了,很好!

重構(gòu)隱藏依賴對象

在我們完成的事情中,還有一件事情并沒有做到最佳實踐。我們同時使用 hydrator 和一個對象在下述文件內(nèi):

 <?php
 // 文件名: /module/Blog/src/Blog/Mapper/ZendDbSqlMapper.php
 namespace Blog\Mapper;

 use Blog\Model\PostInterface;
 use Zend\Db\Adapter\AdapterInterface;
 use Zend\Db\Adapter\Driver\ResultInterface;
 use Zend\Db\ResultSet\HydratingResultSet;
 use Zend\Db\Sql\Sql;
 use Zend\Stdlib\Hydrator\HydratorInterface;

 class ZendDbSqlMapper implements PostMapperInterface
 {
     /**
      * @var \Zend\Db\Adapter\AdapterInterface
      */
     protected $dbAdapter;

     /**
      * @var \Zend\Stdlib\Hydrator\HydratorInterface
      */
     protected $hydrator;

     /**
      * @var \Blog\Model\PostInterface
      */
     protected $postPrototype;

     /**
      * @param AdapterInterface  $dbAdapter
      * @param HydratorInterface $hydrator
      * @param PostInterface    $postPrototype
      */
     public function __construct(
         AdapterInterface $dbAdapter,
         HydratorInterface $hydrator,
         PostInterface $postPrototype
     ) {
         $this->dbAdapter      = $dbAdapter;
         $this->hydrator       = $hydrator;
         $this->postPrototype  = $postPrototype;
     }

     /**
      * @param int|string $id
      *
      * @return PostInterface
      * @throws \InvalidArgumentException
      */
     public function find($id)
     {
     }

     /**
      * @return array|PostInterface[]
      */
     public function findAll()
     {
         $sql    = new Sql($this->dbAdapter);
         $select = $sql->select('posts');

         $stmt   = $sql->prepareStatementForSqlObject($select);
         $result = $stmt->execute();

         if ($result instanceof ResultInterface && $result->isQueryResult()) {
             $resultSet = new HydratingResultSet($this->hydrator, $this->postPrototype);

             return $resultSet->initialize($result);
         }

         return array();
     }
 }

現(xiàn)在我們的映射器需要更多的參數(shù)來更新 ZendDbSqlMapperFactory 并且注入那些參數(shù)了。

 <?php
 // 文件名: /module/Blog/src/Blog/Factory/ZendDbSqlMapperFactory.php
 namespace Blog\Factory;

 use Blog\Mapper\ZendDbSqlMapper;
 use Blog\Model\Post;
 use Zend\ServiceManager\FactoryInterface;
 use Zend\ServiceManager\ServiceLocatorInterface;
 use Zend\Stdlib\Hydrator\ClassMethods;

 class ZendDbSqlMapperFactory implements FactoryInterface
 {
     /**
      * Create service
      *
      * @param ServiceLocatorInterface $serviceLocator
      *
      * @return mixed
      */
     public function createService(ServiceLocatorInterface $serviceLocator)
     {
         return new ZendDbSqlMapper(
             $serviceLocator->get('Zend\Db\Adapter\Adapter'),
             new ClassMethods(false),
             new Post()
         );
     }
 }

當(dāng)這些就緒之后,你可以再次刷新應(yīng)用程序,就能看見你的博客帖子再次顯示出來了。我們的映射器現(xiàn)在擁有一個很不錯的架構(gòu),并且沒有更多隱含的依賴對象了。

完成映射器

在我們跨越到下一個章節(jié)之前,先快速地通過為 find() 函數(shù)編寫一個實現(xiàn)來完成映射器。

 <?php
 // 文件名: /module/Blog/src/Blog/Mapper/ZendDbSqlMapper.php
 namespace Blog\Mapper;

 use Blog\Model\PostInterface;
 use Zend\Db\Adapter\AdapterInterface;
 use Zend\Db\Adapter\Driver\ResultInterface;
 use Zend\Db\ResultSet\HydratingResultSet;
 use Zend\Db\Sql\Sql;
 use Zend\Stdlib\Hydrator\HydratorInterface;

 class ZendDbSqlMapper implements PostMapperInterface
 {
     /**
      * @var \Zend\Db\Adapter\AdapterInterface
      */
     protected $dbAdapter;

     /**
      * @var \Zend\Stdlib\Hydrator\HydratorInterface
      */
     protected $hydrator;

     /**
      * @var \Blog\Model\PostInterface
      */
     protected $postPrototype;

     /**
      * @param AdapterInterface  $dbAdapter
      * @param HydratorInterface $hydrator
      * @param PostInterface    $postPrototype
      */
     public function __construct(
         AdapterInterface $dbAdapter,
         HydratorInterface $hydrator,
         PostInterface $postPrototype
     ) {
         $this->dbAdapter      = $dbAdapter;
         $this->hydrator       = $hydrator;
         $this->postPrototype  = $postPrototype;
     }

     /**
      * @param int|string $id
      *
      * @return PostInterface
      * @throws \InvalidArgumentException
      */
     public function find($id)
     {
         $sql    = new Sql($this->dbAdapter);
         $select = $sql->select('posts');
         $select->where(array('id = ?' => $id));

         $stmt   = $sql->prepareStatementForSqlObject($select);
         $result = $stmt->execute();

         if ($result instanceof ResultInterface && $result->isQueryResult() && $result->getAffectedRows()) {
             return $this->hydrator->hydrate($result->current(), $this->postPrototype);
         }

         throw new \InvalidArgumentException("Blog with given ID:{$id} not found.");
     }

     /**
      * @return array|PostInterface[]
      */
     public function findAll()
     {
         $sql    = new Sql($this->dbAdapter);
         $select = $sql->select('posts');

         $stmt   = $sql->prepareStatementForSqlObject($select);
         $result = $stmt->execute();

         if ($result instanceof ResultInterface && $result->isQueryResult()) {
             $resultSet = new HydratingResultSet($this->hydrator, $this->postPrototype);

             return $resultSet->initialize($result);
         }

         return array();
     }
 }

這個 find() 函數(shù)看上去真的很像 findAll() 函數(shù)。這里只有三點簡單的差別:首先我們需要為查詢添加一個條件,讓其只選擇一行,這通過使用 Sql 對象的 where() 函數(shù)實現(xiàn)。然后我們也要檢查 $result 變量內(nèi)是否有元組在內(nèi),這通過 getAffectedRows() 函數(shù)實現(xiàn)。返回語句會被注入的 hydrator 變成同樣被注入的原型中。

這次,但我們找不到任何元組時,會拋出一個 \InvalidArgumentException 異常,以便應(yīng)用程序可以方便的處理這種狀況。

總結(jié)

完成這個章節(jié)之后,你現(xiàn)在了解了如何通過 Zend\Db\Sql 類來查詢數(shù)據(jù),也學(xué)習(xí)了關(guān)于 ZF2 新關(guān)鍵組件之一的 Zend\Stdlib\Hydrator 的知識。而且你再一次證明了你可以駕馭恰當(dāng)?shù)囊蕾噷ο笞⑷搿?/p>

在下一個章節(jié)中,我們會更進(jìn)一步地了解 router,這樣能讓我們在模組內(nèi)做更多動作。