Zend Framework 2-為不同的數(shù)據(jù)庫后臺做準備

2018-09-28 20:18 更新

為不同的數(shù)據(jù)庫后臺做準備

再上一個章節(jié)我們創(chuàng)建了一個 PostService 類來返回博客帖子的數(shù)據(jù)。雖然那個章節(jié)作為一個簡單易懂的教程十分稱職,但是在現(xiàn)實世界應(yīng)用中卻是十分不使用的。沒有人會想要每次有一個新帖子產(chǎn)生就去修改一次源代碼。幸運的是我們都了解數(shù)據(jù)庫。我們所需要的就是去學(xué)習(xí)如何通過 ZF2 應(yīng)用程序和數(shù)據(jù)庫進行互動。

不過這里有一個問題。目前有許多數(shù)據(jù)庫后臺系統(tǒng),例如 SQL 類數(shù)據(jù)庫和非 SQL 類數(shù)據(jù)庫。雖然在現(xiàn)實世界中你會直接使用一個你認為最合適的解決方案,但是在實際數(shù)據(jù)庫操作前面創(chuàng)建多一個抽象層來抽象化數(shù)據(jù)庫操作會是更好的實踐。我們將這層稱之為映射層(Mapper-Layer)。

什么是數(shù)據(jù)庫抽象化?

術(shù)語“數(shù)據(jù)庫抽象化”聽上去好像挺令人困惑的,實際上這是一個非常簡單的概念。假設(shè)有一個 SQL 數(shù)據(jù)庫和一個非 SQL 數(shù)據(jù)庫。兩者都有增刪改查操作所對應(yīng)的函數(shù)。例如要在數(shù)據(jù)庫中查詢某列數(shù)據(jù),在 MySQL 中你會使用這個命令 mysqli_query('SELECT foo FROM bar');但如果你是用的是 ORM for MongoDB,那么你就要使用類似這樣的命令 $mongoODM->getRepository('bar')->find('foo')。兩種數(shù)據(jù)庫引擎都會給你同樣的結(jié)果但是其執(zhí)行方法確實完全不同的。

所以如果我們一開始使用一個 SQL 數(shù)據(jù)庫,然后將這些代碼直接寫進我們的 PostService 中。一年之后,卻決定遷移到一個非 SQL 數(shù)據(jù)庫上,那么我們將不得不刪除所有之前的代碼并且重新編寫新代碼。再過幾年,新的狀況又出現(xiàn)了,然后我們又要刪除所有代碼然后重新編寫...這顯然不是最好的實現(xiàn)方法,這也正說明了為何數(shù)據(jù)庫抽象化/映射層是如此的實用。

我們要做的事情根本上就是創(chuàng)建一個新的接口。這個接口定義了數(shù)據(jù)庫操作應(yīng)該如何運作,但是實際實現(xiàn)是留空的。我們不要停留在理論上,現(xiàn)在開始進行編碼實踐吧。

創(chuàng)建 PostMapperInterface

首先我們來思考一下有什么可能的數(shù)據(jù)庫操作,我們需要:

  • 尋找一個博客帖子
  • 尋找所有博客帖子
  • 插入新的博客帖子
  • 更新已有的博客帖子
  • 刪除已有的博客帖子

上面提到的這些功能都是目前我想到的最重要的功能??紤]到 insert()update 函數(shù)都是對數(shù)據(jù)庫進行寫入,所以將兩者合并到一個 save() 函數(shù)是個不錯的主意,讓 save() 函數(shù)根據(jù)情況調(diào)用合適的函數(shù)。

首先我們在 Blog\Mapper 名稱空間下創(chuàng)建一個新文件,叫做 PostMapperInterface.php,然后參考下例添加內(nèi)容:

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

 use Blog\Model\PostInterface;

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

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

如您所見我們定義了兩個不同的函數(shù)。我們覺得一個映射實現(xiàn)(mapper-implementation)應(yīng)該擁有一個 find() 函數(shù)來返回一個實現(xiàn)了 PostInterface 的對象。然后還要擁有一個叫做 findAll() 的函數(shù)來返回一個實現(xiàn)了 PostInterface 的對象的數(shù)組。在這里沒有添加 save()delete() 函數(shù),因為我們目前只考慮只讀部分的功能,當(dāng)然稍后這些功能也會被補全。

重構(gòu) PostService

現(xiàn)在我們定義了我們的映射層應(yīng)該如何工作,我們可以在 PostService 內(nèi)對其進行調(diào)用。要開始重構(gòu),我們要先清空我們的類并且刪除所有現(xiàn)有的內(nèi)容,然后實現(xiàn) PostServiceInterface 接口,現(xiàn)在你的 PostService 應(yīng)該看上去像這樣:

 <?php
 // 文件名: /module/Blog/src/Blog/Service/PostService.php
 namespace Blog\Service;

 use Blog\Mapper\PostMapperInterface;

 class PostService implements PostServiceInterface
 {
     /**
      * @var \Blog\Mapper\PostMapperInterface
      */
     protected $postMapper;

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

     /**
      * {@inheritDoc}
      */
     public function findAllPosts()
     {
     }

     /**
      * {@inheritDoc}
      */
     public function findPost($id)
     {
     }
 }

第一件你需要牢記于心的事情是這個接口并不是在 PostService 中實現(xiàn)的,而是在這里用作依賴對象,一個被要求的依賴對象,所以我們需要創(chuàng)建 __construct() 函數(shù)來接收任意這個接口所需的實現(xiàn)作為參數(shù)。同時你也要創(chuàng)建一個 protected 變量來存放參數(shù)。

當(dāng)你完成上述內(nèi)容之后,我們需要一個 PostMapperInterface 接口的實現(xiàn)來讓我們的 PostService得以運作。由于什么都不存在,所以我們現(xiàn)在是沒法讓我們的應(yīng)用程序運作的,刷新您的瀏覽器就能看見如下 PHP 錯誤:

 Catchable fatal error: Argument 1 passed to Blog\Service\PostService::__construct()
 must implement interface Blog\Mapper\PostMapperInterface, none given,
 called in {path}\module\Blog\src\Blog\Service\PostServiceFactory.php on line 19
 and defined in {path}\module\Blog\src\Blog\Service\PostService.php on line 17

不過我們的正在做的東西的權(quán)力取決于我們可以做出的假設(shè)。這個 PostService 總會接收到一個映射器作為參數(shù)。所以在我們的 find*() 函數(shù)中我們可以假設(shè)其存在。回想 PostMapperInterface 定義了一個 find($id) 函數(shù)和一個 findAll() 函數(shù)。讓我們在 Service 函數(shù)里面上面提到的函數(shù):

 <?php
 // 文件名: /module/Blog/src/Blog/Service/PostService.php
 namespace Blog\Service;

 use Blog\Mapper\PostMapperInterface;

 class PostService implements PostServiceInterface
 {
     /**
      * @var \Blog\Mapper\PostMapperInterface
      */
     protected $postMapper;

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

     /**
      * {@inheritDoc}
      */
     public function findAllPosts()
     {
         return $this->postMapper->findAll();
     }

     /**
      * {@inheritDoc}
      */
     public function findPost($id)
     {
         return $this->postMapper->find($id);
     }
 }

看著這些代碼,你就能發(fā)現(xiàn)我們使用 postMapper 來獲取我們所需要的數(shù)據(jù)。這個過程是如何發(fā)生的再也和 PostService 沒有任何關(guān)系。PostService 只知道他會接收到什么類型的數(shù)據(jù),而這是唯一重要的事情。

PostService 擁有依賴對象

現(xiàn)在我們介紹了 PostMapperInterfacePostService 的一個依賴對象,我們再也沒辦法將這個 Service 定義為 invokable 了,因為它有了依賴對象。所以我們需要為這個 Service 創(chuàng)建一個 Factory,就和我們?yōu)?ListController 做的一樣。首先更改配置文件,將其從 invokable 數(shù)組中移動至 factories 數(shù)組,然后賦予合適的 factory 類,如下例:

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

完成上述配置文件之后我們需要創(chuàng)建 Blog\Factory\PostServiceFactory 類,所以現(xiàn)在我們來實現(xiàn)它:

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

 use Blog\Service\PostService;
 use Zend\ServiceManager\FactoryInterface;
 use Zend\ServiceManager\ServiceLocatorInterface;

 class PostServiceFactory implements FactoryInterface
 {
     /**
      * Create service
      *
      * @param ServiceLocatorInterface $serviceLocator
      * @return mixed
      */
     public function createService(ServiceLocatorInterface $serviceLocator)
     {
         return new PostService(
             $serviceLocator->get('Blog\Mapper\PostMapperInterface')
         );
     }
 }

這個工作完成之后你現(xiàn)在應(yīng)該能看到 ServiceNotFoundException 異常了,由 ServiceManager 拋出,告訴你所請求的 Service 無法被找到。

 Additional information:
 Zend\ServiceManager\Exception\ServiceNotFoundException
 File:
 {libraryPath}\Zend\ServiceManager\ServiceManager.php:529
 Message:
 Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Blog\Mapper\PostMapperInterface

總結(jié)

我們在此寫上本章節(jié)的最終結(jié)語,事實上我們已經(jīng)成功的將數(shù)據(jù)庫操作邏輯隔離在我們的 Service 之外。現(xiàn)在,若情況需要的話,我們可以根據(jù)我們的需求來方便的對實際數(shù)據(jù)庫操作進行變更了。

在下一章節(jié)我們會通過 Zend\Db\Sql 來創(chuàng)建關(guān)于 PostMapperInterface 的實現(xiàn)。

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號