国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

Zend Framework 2-應(yīng)用 Form 和 Fieldset

2018-09-28 20:18 更新

應(yīng)用 Form 和 Fieldset

到目前我們已經(jīng)實(shí)現(xiàn)了從數(shù)據(jù)庫讀取數(shù)據(jù)。在現(xiàn)實(shí)生活中的應(yīng)用程序這并不是十分實(shí)用,畢竟多數(shù)情況下我們至少需要實(shí)現(xiàn)完整的增刪改查功能。最普遍的添加數(shù)據(jù)到數(shù)據(jù)庫的方法是讓用戶將數(shù)據(jù)添加到 Web <form> 表單標(biāo)簽內(nèi)提交,然后我們的應(yīng)用程序?qū)⒂脩糨斎氡4娴胶笈_(tái)。

核心組件

我們想要能夠準(zhǔn)確的實(shí)現(xiàn)目標(biāo),而 Zend Framework 提供了所有完成目標(biāo)所需要的工具。在我們直接開始編碼之前,我們有必要了解一下這個(gè)工作的兩個(gè)核心組件。所以我們來看看這些組件是什么,并了解它們是怎么工作的。

Zend\Form\Fieldset (字段集)

首先你需要知道的組件就是 Zend\Form\Fieldset。一個(gè) Fieldset 就是一個(gè)包含可重用的元素集合。你將會(huì)使用 Fieldset 來存儲(chǔ)前臺(tái)輸入以便讓后臺(tái)模型處理。每個(gè) Model 都準(zhǔn)備一個(gè) Fieldset 通常被認(rèn)為是良好實(shí)踐。

Fieldset 組件,然而,并不是 Form 表單本身,意味著你不可以只使用 Fieldset 卻不將其附著在 Form 組件上。這樣子做的優(yōu)勢是你擁有一個(gè)可以重用的元素集合,可以用于任意多個(gè) Form 組件上,而不需要為了 Model 重新聲明所有輸入,因?yàn)?Model 已經(jīng)被 Fieldset 代表。

Zend\Form\Form (表單)

這是你所需要的主要部件,而且你很可能已經(jīng)聽說過了。Form 組件是所有的 Web <form> 元素的主要容器。你可以向其添加單個(gè)元素,也可以通過 Fieldset 的形式添加多個(gè)元素。

創(chuàng)建你的第一個(gè) Fieldset

要解釋 Zend\Form 組件是如何工作的,不如通過你自己實(shí)戰(zhàn)編碼體會(huì)來的深刻。所以我們直接切入正題,創(chuàng)建我們的 Blog 模組所需的所有表單。我們首先創(chuàng)建包含所有需要處理的輸入元素的 Fieldset 用來處理我們的 Blog 數(shù)據(jù)。

  • 你需要為 id 屬性準(zhǔn)備一個(gè)隱藏的輸入域,僅僅在編輯和刪除數(shù)據(jù)時(shí)有用。
  • 你需要一個(gè)文本輸入框來存放 text 屬性。
  • 你需要另一個(gè)文本輸入框來存放 title 屬性。

創(chuàng)建文件 /module/Blog/src/Blog/Form/PostFieldset.php 并且添加下述代碼:

<?php
// 文件名: /module/Blog/src/Blog/Form/PostFieldset.php
namespace Blog\Form;

use Zend\Form\Fieldset;

class PostFieldset extends Fieldset
{
   public function __construct()
   {
      $this->add(array(
         'type' => 'hidden',
         'name' => 'id'
      ));

      $this->add(array(
         'type' => 'text',
         'name' => 'text',
         'options' => array(
           'label' => 'The Text'
         )
      ));

      $this->add(array(
         'type' => 'text',
         'name' => 'title',
         'options' => array(
            'label' => 'Blog Title'
         )
      ));
   }
}

如您所見這個(gè)類是十分有用的。我們做的事情是讓我們的類 extends Zend\Form\Fieldset,然后我們編寫一個(gè) __construct() 函數(shù)并且添加所有我們需要的元素到字段集。這個(gè) fieldset 現(xiàn)在就能隨我們意愿用于任意多個(gè)表單中了。所以接下來讓我們創(chuàng)建第一個(gè) Form 表單吧。

創(chuàng)建 PostForm

現(xiàn)在我們已經(jīng)準(zhǔn)備好了我們的 PostFieldset,還需要在 Form 內(nèi)使用它。我們接下來需要添加一個(gè)表單的提交按鈕,這樣用戶就能夠提交數(shù)據(jù)了。所以在同一個(gè)路徑 /module/Blog/src/Blog/Form/PostForm 下創(chuàng)建 PostForm,并且將 PostFieldset 添加進(jìn)去:

 <?php
 // 文件名: /module/Blog/src/Blog/Form/PostForm.php
 namespace Blog\Form;

 use Zend\Form\Form;

 class PostForm extends Form
 {
     public function __construct()
     {
         $this->add(array(
             'name' => 'post-fieldset',
             'type' => 'Blog\Form\PostFieldset'
         ));

         $this->add(array(
             'type' => 'submit',
             'name' => 'submit',
             'attributes' => array(
                 'value' => 'Insert new Post'
             )
         ));
     }
 }

這就是我們的表單了。并沒有什么特別的,我們添加了 PostFieldset 到表單里,還添加了一個(gè)提交按鈕,然后沒別的了?,F(xiàn)在我們來讓這個(gè)表單發(fā)揮作用。

添加一個(gè)新 Post(帖子)

現(xiàn)在我們已經(jīng)寫好了 PostForm?,F(xiàn)在想要使用它,還有幾個(gè)任務(wù)需要完成。目前你要直接面對(duì)的任務(wù)是:

  • 創(chuàng)建一個(gè)新的控制器 WriteController
  • 添加一個(gè) PostService 服務(wù),將其設(shè)定為 WriteController 的依賴對(duì)象
  • 添加一個(gè) PostForm 表單,將其設(shè)定為 WriteController 的依賴對(duì)象
  • 創(chuàng)建一個(gè)新的路徑 blog/add,并讓其轉(zhuǎn)發(fā)到 WriteController 和它附屬的 addAction()
  • 創(chuàng)建一個(gè)新的視圖用于顯示表單

創(chuàng)建 WriteController(寫控制器)

如您在任務(wù)清單上所見,我們需要一個(gè)新的控制器,而且這個(gè)控制器應(yīng)該擁有兩個(gè)依賴對(duì)象。一個(gè)依賴對(duì)象時(shí) PostService,它也在 ListController 中被使用,而另一個(gè)依賴對(duì)象 PostForm 是全新的。由于在顯示博客數(shù)據(jù)的時(shí)候,PostFrom 是一個(gè) ListController 不需要的依賴對(duì)象,所以我們會(huì)創(chuàng)建一個(gè)新的控制器來讓讀和寫兩邊的事務(wù)分離。首先,在配置文件中注冊(cè)一個(gè)控制器工廠(controller-factory):

 <?php
 // 文件名: /module/Blog/config/module.config.php
 return array(
     'db'              => array( /** DB Config */ ),
     'service_manager' => array( /** ServiceManager Config */),
     'view_manager'    => array( /** ViewManager Config */ ),
     'controllers'     => array(
         'factories' => array(
             'Blog\Controller\List'  => 'Blog\Factory\ListControllerFactory',
             'Blog\Controller\Write' => 'Blog\Factory\WriteControllerFactory'
         )
     ),
     'router'          => array( /** Router Config */ )
 );

下一步就是編寫 WriteControllerFactory。讓 factory 返回 WriteController 并且在構(gòu)造器中添加所需的依賴對(duì)象:

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

 use Blog\Controller\WriteController;
 use Zend\ServiceManager\FactoryInterface;
 use Zend\ServiceManager\ServiceLocatorInterface;

 class WriteControllerFactory implements FactoryInterface
 {
     public function createService(ServiceLocatorInterface $serviceLocator)
     {
         $realServiceLocator = $serviceLocator->getServiceLocator();
         $postService        = $realServiceLocator->get('Blog\Service\PostServiceInterface');
         $postInsertForm     = $realServiceLocator->get('FormElementManager')->get('Blog\Form\PostForm');

         return new WriteController(
             $postService,
             $postInsertForm
         );
     }
 }

在這個(gè)代碼示例中,這里有幾樣事情需要注意。第一件,WriteController 暫時(shí)還不存在,不過我們會(huì)在下一步創(chuàng)建這個(gè)控制器所以我們先假設(shè)其稍后會(huì)存在。第二件事情,我們通過訪問 FormElementManager 來獲得對(duì) PostForm 的讀寫。所有表單都應(yīng)該通過 FormElementManager 來訪問。即使我們還沒有在配置文件中注冊(cè) PostForm,FormElementManager 也會(huì)自動(dòng)認(rèn)出表單并且將其當(dāng)做 invokables 對(duì)象,只要你的對(duì)象沒有依賴對(duì)象,你便不需要顯式地注冊(cè)它。

下一步,創(chuàng)建我們的控制器。請(qǐng)確保通過輸入依賴對(duì)象的接口并且添加 addAction() 函數(shù)來提示依賴對(duì)象。

 <?php
 // 文件名: /module/Blog/src/Blog/Controller/WriteController.php
 namespace Blog\Controller;

 use Blog\Service\PostServiceInterface;
 use Zend\Form\FormInterface;
 use Zend\Mvc\Controller\AbstractActionController;

 class WriteController extends AbstractActionController
 {
     protected $postService;

     protected $postForm;

     public function __construct(
         PostServiceInterface $postService,
         FormInterface $postForm
     ) {
         $this->postService = $postService;
         $this->postForm    = $postForm;
     }

     public function addAction()
     {
     }
 }

接下來創(chuàng)建新路徑:

 <?php
 // 文件名: /module/Blog/config/module.config.php
 return array(
     'db'              => array( /** Db Config */ ),
     'service_manager' => array( /** ServiceManager Config */ ),
     'view_manager'    => array( /** ViewManager Config */ ),
     'controllers'     => array( /** Controller Config */ ),
     'router'          => array(
         'routes' => array(
             'blog' => array(
                 'type' => 'literal',
                 'options' => array(
                     'route'    => '/blog',
                     'defaults' => array(
                         'controller' => 'Blog\Controller\List',
                         'action'     => 'index',
                     )
                 ),
                 'may_terminate' => true,
                 'child_routes'  => array(
                     'detail' => array(
                         'type' => 'segment',
                         'options' => array(
                             'route'    => '/:id',
                             'defaults' => array(
                                 'action' => 'detail'
                             ),
                             'constraints' => array(
                                 'id' => '\d+'
                             )
                         )
                     ),
                     'add' => array(
                         'type' => 'literal',
                         'options' => array(
                             'route'    => '/add',
                             'defaults' => array(
                                 'controller' => 'Blog\Controller\Write',
                                 'action'     => 'add'
                             )
                         )
                     )
                 )
             )
         )
     )
 );

最后我們創(chuàng)建一個(gè)沒實(shí)際作用的模板文件:

 <!-- Filename: /module/Blog/view/blog/write/add.phtml -->
 <h1>WriteController::addAction()</h1>

檢查當(dāng)前狀態(tài)

如果你視圖訪問新的路徑 localhost:8080/blog/add,那么你應(yīng)該會(huì)看見下面這樣的錯(cuò)誤信息:

 Fatal error: Call to a member function insert() on a non-object in
 {libraryPath}/Zend/Form/Fieldset.php on line {lineNumber}

如果你看見的和上面寫出來的不一樣,那么請(qǐng)回過頭認(rèn)真檢查一下先前的步驟是否準(zhǔn)確地跟隨著教程,并且檢查你所有的文件。接下來,假設(shè)你已經(jīng)看見了這個(gè)錯(cuò)誤信息,讓我們來尋找原因并且解決它!

上述錯(cuò)誤是非常常見的,但是它的解決方法卻沒有那么直觀。它看上去像是 Zend/Form/Fieldset.php 中出現(xiàn)了一個(gè)錯(cuò)誤,但實(shí)際上卻不是這個(gè)情況。這個(gè)錯(cuò)誤信息讓你知道了你在創(chuàng)建你的表單的時(shí)候有一些事情出現(xiàn)了差錯(cuò)。事實(shí)上,當(dāng)同時(shí)創(chuàng)建 PostForm 表單和 PostFieldset 的時(shí)候,我們忘記了一些非常,非常重要的事情。

注意:當(dāng)重寫 Zend\Form 組件的 __construct() 函數(shù)的時(shí)候,請(qǐng)永遠(yuǎn)不要忘記調(diào)用 parent::__construct()!

由于缺少了 parent::__construct() 調(diào)用,表單和字段集都不能正確的初始化。讓我們通過在表單和字段級(jí)中調(diào)用父級(jí)構(gòu)造器來修正這個(gè)問題。為了能擁有更好的可伸縮性我們也會(huì)包含能夠接收多個(gè)參數(shù)的 __construct() 函數(shù)的簽名。

 <?php
 // 文件名: /module/Blog/src/Blog/Form/PostForm.php
 namespace Blog\Form;

 use Zend\Form\Form;

 class PostForm extends Form
 {
     public function __construct($name = null, $options = array())
     {
         parent::__construct($name, $options);

         $this->add(array(
             'name' => 'post-fieldset',
             'type' => 'Blog\Form\PostFieldset'
         ));

         $this->add(array(
             'type' => 'submit',
             'name' => 'submit',
             'attributes' => array(
                 'value' => 'Insert new Post'
             )
         ));
     }
 }

如您所見我們的 PostForm 現(xiàn)在接受兩個(gè)參數(shù)分別定義我們的表單的名字和一些列的設(shè)置。兩個(gè)參數(shù)都會(huì)被傳給父對(duì)象。如果你仔細(xì)觀察我們是如何添加 PostFieldset 的,便會(huì)發(fā)現(xiàn)我們?yōu)樽侄渭x予了一個(gè)名字。這些選項(xiàng)都會(huì)在 PostFieldset 創(chuàng)建時(shí)通過 FormElementManager 傳出。不過要讓這些正常工作,我們需要在字段集里面做同樣的工作:

<?php
// 文件名: /module/Blog/src/Blog/Form/PostFieldset.php
namespace Blog\Form;

use Zend\Form\Fieldset;

class PostFieldset extends Fieldset
{
public function __construct($name = null, $options = array())
{
    parent::__construct($name, $options);
    $this->add(array(
        'type' => 'hidden',
        'name' => 'id'
    ));
    $this->add(array(
        'type' => 'text',
        'name' => 'text',
        'options' => array(
            'label' => 'The Text'
        )
    ));
    $this->add(array(
        'type' => 'text',
        'name' => 'title',
        'options' => array(
            'label' => 'Blog Title'
        )
    ));
}
}

重新載入你的應(yīng)用程序,你便可以看見你想要的結(jié)果了。

顯示表單

現(xiàn)在我們?cè)?WriteController 里有了我們的 PostForm,是時(shí)候?qū)⑦@個(gè)表單傳遞給視圖,并讓其通過指定的來自 Zend\Form 組件的 ViewHelpers 來進(jìn)行渲染。首先修改你的控制器,讓表單被傳遞到視圖。

  <?php
 // 文件名: /module/Blog/src/Blog/Controller/WriteController.php
 namespace Blog\Controller;

 use Blog\Service\PostServiceInterface;
 use Zend\Form\FormInterface;
 use Zend\Mvc\Controller\AbstractActionController;
 use Zend\View\Model\ViewModel;

 class WriteController extends AbstractActionController
 {
     protected $postService;

     protected $postForm;

     public function __construct(
         PostServiceInterface $postService,
         FormInterface $postForm
     ) {
         $this->postService = $postService;
         $this->postForm    = $postForm;
     }

     public function addAction()
     {
         return new ViewModel(array(
             'form' => $this->postForm
         ));
     }
 }

然后我們需要修改視圖來讓表單得以正確渲染:

 <!-- Filename: /module/Blog/view/blog/write/add.phtml -->
 <h1>WriteController::addAction()</h1>
 <?php
 $form = $this->form;
 $form->setAttribute('action', $this->url());
 $form->prepare();

 echo $this->form()->openTag($form);

 echo $this->formCollection($form);

 echo $this->form()->closeTag();

首先,我們告訴了表單它應(yīng)該將它的數(shù)據(jù)發(fā)送給目前的 URL 然后我們告訴表單讓其 prepare()(準(zhǔn)備)自己(這個(gè)函數(shù)會(huì)觸發(fā)一些內(nèi)部操作)。

注意: HTML 表單可以通過 POST 或者 GET 方式來進(jìn)行傳輸。 ZF2 默認(rèn)是使用 POST,所以你不需要對(duì)此進(jìn)行顯式的設(shè)定。但是如果你希望使用 GET 方式,只需要在調(diào)用 prepare() 之前設(shè)置好這個(gè)特定的屬性: $form->setAttribute('method', 'GET');

接下來我們會(huì)使用幾個(gè) ViewHelpers 來負(fù)責(zé)幫我們渲染表單。使用 Zend Framework 渲染表單的方法有很多種,不過使用 formCollection() 可能是最快的方法。

刷新您的瀏覽器,現(xiàn)在就能看見你的表單被正確顯示出來了。然而,現(xiàn)在提交表單的話,我們只能看見先前提交的表單原封不動(dòng)的回顯出來。很簡單,這是因?yàn)槲覀冞€沒有為控制器添加任何邏輯。

注意:請(qǐng)記住這個(gè)教程僅僅聚焦于面向?qū)ο缶幊桃暯?。像這樣子渲染表單,不應(yīng)用任何樣式表是無法反映出絕大多數(shù)設(shè)計(jì)師關(guān)于一個(gè)美麗的表單的想法的。您將會(huì)在 Zend\Form\View\Helper 章節(jié)中學(xué)習(xí)到更多關(guān)于表單渲染的內(nèi)容。

幾乎適用于所有類型表單的控制器邏輯

編寫一個(gè)控制器來處理表單工作流是非常簡單的,而且基本上對(duì)于應(yīng)用程序中每一種表單的手段都是一樣的。

  1. 你會(huì)想先檢查目前的請(qǐng)求是否一個(gè) POST 請(qǐng)求,這意味著確認(rèn)表單是否被發(fā)出。
  2. 如果表單已經(jīng)被發(fā)出,你會(huì)想要:
    • 將表單的 POST 數(shù)據(jù)存放起來
    • 檢查這個(gè)表單數(shù)據(jù)是否合法
  3. 如果表單通過了檢測,你會(huì)想要:
    • 將表單數(shù)據(jù)傳給你的服務(wù)進(jìn)行處理
    • 將用戶重定向到剛才輸入的數(shù)據(jù)的詳情頁面或者一些概覽頁面
  4. 針對(duì)所有其他情形,你會(huì)希望表單繼續(xù)被顯示,有時(shí)會(huì)伴隨一些錯(cuò)誤信息

然而要實(shí)現(xiàn)上述所有功能并不需要你想象中那么多的代碼。首先,按照下例修改你的 WriteController 代碼:

 <?php
 // 文件名: /module/Blog/src/Blog/Controller/WriteController.php
 namespace Blog\Controller;

 use Blog\Service\PostServiceInterface;
 use Zend\Form\FormInterface;
 use Zend\Mvc\Controller\AbstractActionController;
 use Zend\View\Model\ViewModel;

 class WriteController extends AbstractActionController
 {
     protected $postService;

     protected $postForm;

     public function __construct(
         PostServiceInterface $postService,
         FormInterface $postForm
     ) {
         $this->postService = $postService;
         $this->postForm    = $postForm;
     }

     public function addAction()
     {
         $request = $this->getRequest();

         if ($request->isPost()) {
             $this->postForm->setData($request->getPost());

             if ($this->postForm->isValid()) {
                 try {
                     $this->postService->savePost($this->postForm->getData());

                     return $this->redirect()->toRoute('blog');
                 } catch (\Exception $e) {
                     // 某些數(shù)據(jù)庫錯(cuò)誤發(fā)生了,記錄并且讓用戶知道
                 }
             }
         }

         return new ViewModel(array(
             'form' => $this->postForm
         ));
     }
 }

這個(gè)示例代碼應(yīng)該十分簡單明了。首先我們將目前的請(qǐng)求保存到一個(gè)本地變量中。然后我們檢查目前的請(qǐng)求是不是一個(gè) POST 請(qǐng)求,如果是,將請(qǐng)求的 POST 數(shù)據(jù)存儲(chǔ)到表單中。如果表單經(jīng)過檢查之后確認(rèn)是有效的,我們就試圖將表單數(shù)據(jù)通過我們的服務(wù)進(jìn)行保存,然后將用戶重定向到路徑 blog。如果在這途中任何時(shí)候出現(xiàn)了任何錯(cuò)誤,我們就再次將表單顯示出來。

現(xiàn)在提交表單的話,我們會(huì)遇到下述錯(cuò)誤:

 Fatal error: Call to undefined method Blog\Service\PostService::savePost() in
 /module/Blog/src/Blog/Controller/WriteController.php on line 33

我們通過拓展 PostService 來修復(fù)這個(gè)錯(cuò)誤,同時(shí)不要忘記更改 PostServiceInterface 的簽名!

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

 use Blog\Model\PostInterface;

 interface PostServiceInterface
 {
     /**
      * Should return a set of all blog posts that we can iterate over. Single entries of the array are supposed to be
      * implementing \Blog\Model\PostInterface
      *
      * @return array|PostInterface[]
      */
     public function findAllPosts();

     /**
      * Should return a single blog post
      *
      * @param  int $id Identifier of the Post that should be returned
      * @return PostInterface
      */
     public function findPost($id);

     /**
      * Should save a given implementation of the PostInterface and return it. If it is an existing Post the Post
      * should be updated, if it's a new Post it should be created.
      *
      * @param  PostInterface $blog
      * @return PostInterface
      */
     public function savePost(PostInterface $blog);
 }

如您所見 savePost() 函數(shù)已經(jīng)被添加,并且需要在 PostService 里被實(shí)現(xiàn)。

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

     /**
      * {@inheritDoc}
      */
     public function savePost(PostInterface $post)
     {
         return $this->postMapper->save($post);
     }
 }

現(xiàn)在我們對(duì) postMapper 做出了假設(shè),所以需要擴(kuò)展我們的 PostMapperInterface 接口以及其實(shí)現(xiàn)。首先我們拓展接口:

 <?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();

     /**
      * @param PostInterface $postObject
      *
      * @param PostInterface $postObject
      * @return PostInterface
      * @throws \Exception
      */
     public function save(PostInterface $postObject);
 }

然后我們實(shí)現(xiàn) save 函數(shù):

<?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\Insert;
use Zend\Db\Sql\Sql;
use Zend\Db\Sql\Update;
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();
   }

   /**
    * @param PostInterface $postObject
    *
    * @return PostInterface
    * @throws \Exception
    */
   public function save(PostInterface $postObject)
   {
      $postData = $this->hydrator->extract($postObject);
      unset($postData['id']); // Insert 和 Update 都不需要數(shù)組中存在 ID

      if ($postObject->getId()) {
         // ID 存在,是一個(gè) Update
         $action = new Update('posts');
         $action->set($postData);
         $action->where(array('id = ?' => $postObject->getId()));
      } else {
         // ID 不存在,是一個(gè)Insert
         $action = new Insert('posts');
         $action->values($postData);
      }

      $sql    = new Sql($this->dbAdapter);
      $stmt   = $sql->prepareStatementForSqlObject($action);
      $result = $stmt->execute();

      if ($result instanceof ResultInterface) {
         if ($newId = $result->getGeneratedValue()) {
            // 每當(dāng)一個(gè)值被生成時(shí),將其賦給對(duì)象
            $postObject->setId($newId);
         }

         return $postObject;
      }

      throw new \Exception("Database error");
   }
}

在這里 save() 函數(shù)處理了兩種情況:insertupdate 流程。首先我們提取 Post 對(duì)象,因?yàn)槲覀冃枰獢?shù)組數(shù)據(jù)來實(shí)現(xiàn) InsertUpdate。然后,我們從數(shù)組中刪除了 id ,因?yàn)閷?duì)一個(gè)元組進(jìn)行更新的時(shí)候,我們不需要更新 id 屬性;同時(shí),我們插入一個(gè)新元組的時(shí)候也不需要 id 字段,所以兩種情況均不需要 id 這個(gè)字段,將其簡單去除即可。

在我們?nèi)コ?id 字段之后,檢查那些動(dòng)作需要被調(diào)用。如果 Post 對(duì)象擁有一個(gè) id 集,我們便創(chuàng)建一個(gè)新的 Update 對(duì)象,否則我們創(chuàng)建一個(gè) Insert 對(duì)象。我們將數(shù)據(jù)傳給合適的 action 然后數(shù)據(jù)會(huì)被傳給 Sql 對(duì)象,最終進(jìn)行真正的數(shù)據(jù)庫操作。

最后,我們檢查我們是否接收到一個(gè)有效的結(jié)果,檢查一下有沒有新產(chǎn)生的 id。如果是的話,我們調(diào)用我們博客的 setId() 函數(shù)并且將對(duì)象返回。

讓我們?cè)俅翁釋⑽覀兊谋韱?,看看這次會(huì)得到什么。

 Catchable fatal error: Argument 1 passed to Blog\Service\PostService::savePost()
 must implement interface Blog\Model\PostInterface, array given,
 called in /module/Blog/src/Blog/Controller/InsertController.php on line 33
 and defined in /module/Blog/src/Blog/Service/PostService.php on line 49

表單,默認(rèn)的時(shí)候,會(huì)將數(shù)據(jù)以數(shù)組形式傳給你。不過我們的 PostService 卻期待接收到的對(duì)象是 PostInterface 的一個(gè)實(shí)現(xiàn)。這意味著我們需要找到一個(gè)方法來將這個(gè)數(shù)組數(shù)據(jù)轉(zhuǎn)換成對(duì)象數(shù)據(jù)。如果你還記得上一章節(jié),就會(huì)知道要通過 hydrators 實(shí)現(xiàn)。

注意:在更新查詢中,你會(huì)注意到我們添加了一個(gè)條件語句讓其只更新與給出的 id 匹配的元組:$action->where(array('id = ?' => $postObject->getId()));你會(huì)看見條件是:id equals ?。這個(gè)問號(hào)代表著 POST 對(duì)象的 id。同樣的,你可以添加一個(gè)條件語句來更新(或者選擇)所有大于給定 id 的元組:$action->where(array('id > ?' => $postObject->getId()));這些操作符可以用于所有類型的條件語句:=>、<、>=<=

讓 Zend\Form 和 Zend\Stdlib\Hydrator 協(xié)調(diào)工作

在我們繼續(xù)前進(jìn)并且將充水器放進(jìn)表單之前,先讓我們對(duì)表單的數(shù)據(jù)做一個(gè) dump。這樣做可以讓我們很方便的注意到充水器做的所有變更。根據(jù)下例修改你的 WriteController

 <?php
 // 文件名: /module/Blog/src/Blog/Controller/WriteController.php
 namespace Blog\Controller;

 use Blog\Service\PostServiceInterface;
 use Zend\Form\FormInterface;
 use Zend\Mvc\Controller\AbstractActionController;
 use Zend\View\Model\ViewModel;

 class WriteController extends AbstractActionController
 {
     protected $postService;

     protected $postForm;

     public function __construct(
         PostServiceInterface $postService,
         FormInterface $postForm
     ) {
         $this->postService = $postService;
         $this->postForm    = $postForm;
     }

     public function addAction()
     {
         $request = $this->getRequest();

         if ($request->isPost()) {
             $this->postForm->setData($request->getPost());

             if ($this->postForm->isValid()) {
                 try {
                     \Zend\Debug\Debug::dump($this->postForm->getData());die();
                     $this->postService->savePost($this->postForm->getData());

                     return $this->redirect()->toRoute('blog');
                 } catch (\Exception $e) {
                     // 某些數(shù)據(jù)庫錯(cuò)誤發(fā)生了,記錄并且讓用戶知道
                 }
             }
         }

         return new ViewModel(array(
             'form' => $this->postForm
         ));
     }
 }

做好之后請(qǐng)?jiān)俅翁峤槐韱?。你現(xiàn)在應(yīng)該能看到數(shù)據(jù) dump,和下例差不多的情形:

 array(2) {
   ["submit"] => string(16) "Insert new Post"
   ["post-fieldset"] => array(3) {
     ["id"] => string(0) ""
     ["text"] => string(3) "foo"
     ["title"] => string(3) "bar"
   }
 }

現(xiàn)在讓你的字段集將數(shù)據(jù)注水成 Post 對(duì)象是非常簡單的。你需要做的事情僅僅是指定注水器和對(duì)象原型,如下例所示:

 <?php
 // 文件名: /module/Blog/src/Blog/Form/PostFieldset.php
 namespace Blog\Form;

 use Blog\Model\Post;
 use Zend\Form\Fieldset;
 use Zend\Stdlib\Hydrator\ClassMethods;

 class PostFieldset extends Fieldset
 {
     public function __construct($name = null, $options = array())
     {
         parent::__construct($name, $options);
         $this->setHydrator(new ClassMethods(false));
         $this->setObject(new Post());
         $this->add(array(
             'type' => 'hidden',
             'name' => 'id'
         ));
         $this->add(array(
             'type' => 'text',
             'name' => 'text',
             'options' => array(
                 'label' => 'The Text'
             )
         ));
         $this->add(array(
             'type' => 'text',
             'name' => 'title',
             'options' => array(
                 'label' => 'Blog Title'
             )
         ));
     }
 }

如您所見我們做了兩件事情,我們告知了字段集使用 ClassMethods hydrator,然后我們還告知了它應(yīng)該返回給我們 Blog 模型、不過,當(dāng)你再次提交表單的時(shí)候你會(huì)注意到什么都沒有改變。我們?nèi)匀恢坏玫綌?shù)組數(shù)據(jù),而不是對(duì)象。

這是因?yàn)槭聦?shí)上表單本身不知道自己需要返回一個(gè)對(duì)象。當(dāng)表單不知道自己要返回什么的時(shí)候就會(huì)默認(rèn)遞歸使用 ArraySeriazable hydrator。要改變這點(diǎn),我們需要讓我們的 PostFieldset 變成所謂的 base_fieldset

base_fieldset 基本上就告訴了表單“這個(gè)表單是關(guān)于我的,請(qǐng)不要操心其他數(shù)據(jù),只操心我就好”。而且當(dāng)表單意識(shí)到這個(gè)字段集是來真的,它就會(huì)乖乖使用字段集提供的 hydrator,并且將我們想要的對(duì)象返回出來。修改你的 PostForm 并且將 PostFieldset 設(shè)置成 base_fieldset

 <?php
 // 文件名: /module/Blog/src/Blog/Form/PostForm.php
 namespace Blog\Form;

 use Zend\Form\Form;

 class PostForm extends Form
 {
     public function __construct($name = null, $options = array())
     {
         parent::__construct($name, $options);

         $this->add(array(
             'name' => 'post-fieldset',
             'type' => 'Blog\Form\PostFieldset',
             'options' => array(
                 'use_as_base_fieldset' => true
             )
         ));

         $this->add(array(
             'type' => 'submit',
             'name' => 'submit',
             'attributes' => array(
                 'value' => 'Insert new Post'
             )
         ));
     }
 }

現(xiàn)在再次提交你的表單。你應(yīng)該能看見如下輸出:

 object(Blog\Model\Post)#294 (3) {
   ["id":protected] => string(0) ""
   ["title":protected] => string(3) "foo"
   ["text":protected] => string(3) "bar"
 }

現(xiàn)在你可以回滾你的 WriteController 到它的先前狀態(tài)了,來讓表單數(shù)據(jù)傳給 PostService

 <?php
 // 文件名: /module/Blog/src/Blog/Controller/WriteController.php
 namespace Blog\Controller;

 use Blog\Service\PostServiceInterface;
 use Zend\Form\FormInterface;
 use Zend\Mvc\Controller\AbstractActionController;
 use Zend\View\Model\ViewModel;

 class WriteController extends AbstractActionController
 {
     protected $postService;

     protected $postForm;

     public function __construct(
         PostServiceInterface $postService,
         FormInterface $postForm
     ) {
         $this->postService = $postService;
         $this->postForm    = $postForm;
     }

     public function addAction()
     {
         $request = $this->getRequest();

         if ($request->isPost()) {
             $this->postForm->setData($request->getPost());

             if ($this->postForm->isValid()) {
                 try {
                     $this->postService->savePost($this->postForm->getData());

                     return $this->redirect()->toRoute('blog');
                 } catch (\Exception $e) {
                     // 某些數(shù)據(jù)庫錯(cuò)誤發(fā)生了,記錄并且讓用戶知道
                 }
             }
         }

         return new ViewModel(array(
             'form' => $this->postForm
         ));
     }
 }

你現(xiàn)在只需要再次發(fā)送表單就能夠隨心所欲任意添加博客帖子了,不錯(cuò)!

總結(jié)

在這個(gè)章節(jié)中,你學(xué)習(xí)了許多關(guān)于 Zend\Form 組件的知識(shí)。你也知道了 Zend\Stdlib\HydratorZend\Form 組件扮演多么重要的角色,并且通過使用這兩個(gè)組件你已經(jīng)能為博客模組創(chuàng)建插入表單。

在下一個(gè)章節(jié)中,我們會(huì)創(chuàng)建更新和刪除程序,從而完成博客模組的所有增刪改查功能。

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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)