到目前我們已經實現(xiàn)了從數(shù)據(jù)庫讀取數(shù)據(jù)。在現(xiàn)實生活中的應用程序這并不是十分實用,畢竟多數(shù)情況下我們至少需要實現(xiàn)完整的增刪改查功能。最普遍的添加數(shù)據(jù)到數(shù)據(jù)庫的方法是讓用戶將數(shù)據(jù)添加到 Web <form>
表單標簽內提交,然后我們的應用程序將用戶輸入保存到后臺。
我們想要能夠準確的實現(xiàn)目標,而 Zend Framework 提供了所有完成目標所需要的工具。在我們直接開始編碼之前,我們有必要了解一下這個工作的兩個核心組件。所以我們來看看這些組件是什么,并了解它們是怎么工作的。
首先你需要知道的組件就是 Zend\Form\Fieldset
。一個 Fieldset
就是一個包含可重用的元素集合。你將會使用 Fieldset
來存儲前臺輸入以便讓后臺模型處理。每個 Model
都準備一個 Fieldset
通常被認為是良好實踐。
Fieldset
組件,然而,并不是 Form
表單本身,意味著你不可以只使用 Fieldset
卻不將其附著在 Form
組件上。這樣子做的優(yōu)勢是你擁有一個可以重用的元素集合,可以用于任意多個 Form
組件上,而不需要為了 Model
重新聲明所有輸入,因為 Model
已經被 Fieldset
代表。
這是你所需要的主要部件,而且你很可能已經聽說過了。Form
組件是所有的 Web <form>
元素的主要容器。你可以向其添加單個元素,也可以通過 Fieldset
的形式添加多個元素。
要解釋 Zend\Form
組件是如何工作的,不如通過你自己實戰(zhàn)編碼體會來的深刻。所以我們直接切入正題,創(chuàng)建我們的 Blog
模組所需的所有表單。我們首先創(chuàng)建包含所有需要處理的輸入元素的 Fieldset
用來處理我們的 Blog
數(shù)據(jù)。
id
屬性準備一個隱藏的輸入域,僅僅在編輯和刪除數(shù)據(jù)時有用。text
屬性。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'
)
));
}
}
如您所見這個類是十分有用的。我們做的事情是讓我們的類 extends Zend\Form\Fieldset
,然后我們編寫一個 __construct()
函數(shù)并且添加所有我們需要的元素到字段集。這個 fieldset
現(xiàn)在就能隨我們意愿用于任意多個表單中了。所以接下來讓我們創(chuàng)建第一個 Form
表單吧。
現(xiàn)在我們已經準備好了我們的 PostFieldset
,還需要在 Form
內使用它。我們接下來需要添加一個表單的提交按鈕,這樣用戶就能夠提交數(shù)據(jù)了。所以在同一個路徑 /module/Blog/src/Blog/Form/PostForm
下創(chuàng)建 PostForm
,并且將 PostFieldset
添加進去:
<?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
到表單里,還添加了一個提交按鈕,然后沒別的了。現(xiàn)在我們來讓這個表單發(fā)揮作用。
現(xiàn)在我們已經寫好了 PostForm
?,F(xiàn)在想要使用它,還有幾個任務需要完成。目前你要直接面對的任務是:
WriteController
PostService
服務,將其設定為 WriteController
的依賴對象PostForm
表單,將其設定為 WriteController
的依賴對象blog/add
,并讓其轉發(fā)到 WriteController
和它附屬的 addAction()
如您在任務清單上所見,我們需要一個新的控制器,而且這個控制器應該擁有兩個依賴對象。一個依賴對象時 PostService
,它也在 ListController
中被使用,而另一個依賴對象 PostForm
是全新的。由于在顯示博客數(shù)據(jù)的時候,PostFrom
是一個 ListController
不需要的依賴對象,所以我們會創(chuàng)建一個新的控制器來讓讀和寫兩邊的事務分離。首先,在配置文件中注冊一個控制器工廠(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
并且在構造器中添加所需的依賴對象:
<?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
);
}
}
在這個代碼示例中,這里有幾樣事情需要注意。第一件,WriteController
暫時還不存在,不過我們會在下一步創(chuàng)建這個控制器所以我們先假設其稍后會存在。第二件事情,我們通過訪問 FormElementManager
來獲得對 PostForm
的讀寫。所有表單都應該通過 FormElementManager
來訪問。即使我們還沒有在配置文件中注冊 PostForm
,FormElementManager
也會自動認出表單并且將其當做 invokables
對象,只要你的對象沒有依賴對象,你便不需要顯式地注冊它。
下一步,創(chuàng)建我們的控制器。請確保通過輸入依賴對象的接口并且添加 addAction()
函數(shù)來提示依賴對象。
<?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)建一個沒實際作用的模板文件:
<!-- Filename: /module/Blog/view/blog/write/add.phtml -->
<h1>WriteController::addAction()</h1>
如果你視圖訪問新的路徑 localhost:8080/blog/add
,那么你應該會看見下面這樣的錯誤信息:
Fatal error: Call to a member function insert() on a non-object in
{libraryPath}/Zend/Form/Fieldset.php on line {lineNumber}
如果你看見的和上面寫出來的不一樣,那么請回過頭認真檢查一下先前的步驟是否準確地跟隨著教程,并且檢查你所有的文件。接下來,假設你已經看見了這個錯誤信息,讓我們來尋找原因并且解決它!
上述錯誤是非常常見的,但是它的解決方法卻沒有那么直觀。它看上去像是 Zend/Form/Fieldset.php
中出現(xiàn)了一個錯誤,但實際上卻不是這個情況。這個錯誤信息讓你知道了你在創(chuàng)建你的表單的時候有一些事情出現(xiàn)了差錯。事實上,當同時創(chuàng)建 PostForm
表單和 PostFieldset
的時候,我們忘記了一些非常,非常重要的事情。
注意:當重寫
Zend\Form
組件的__construct()
函數(shù)的時候,請永遠不要忘記調用parent::__construct()
!
由于缺少了 parent::__construct()
調用,表單和字段集都不能正確的初始化。讓我們通過在表單和字段級中調用父級構造器來修正這個問題。為了能擁有更好的可伸縮性我們也會包含能夠接收多個參數(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)在接受兩個參數(shù)分別定義我們的表單的名字和一些列的設置。兩個參數(shù)都會被傳給父對象。如果你仔細觀察我們是如何添加 PostFieldset
的,便會發(fā)現(xiàn)我們?yōu)樽侄渭x予了一個名字。這些選項都會在 PostFieldset
創(chuàng)建時通過 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'
)
));
}
}
重新載入你的應用程序,你便可以看見你想要的結果了。
現(xiàn)在我們在 WriteController
里有了我們的 PostForm
,是時候將這個表單傳遞給視圖,并讓其通過指定的來自 Zend\Form
組件的 ViewHelpers
來進行渲染。首先修改你的控制器,讓表單被傳遞到視圖。
<?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();
首先,我們告訴了表單它應該將它的數(shù)據(jù)發(fā)送給目前的 URL 然后我們告訴表單讓其 prepare()
(準備)自己(這個函數(shù)會觸發(fā)一些內部操作)。
注意: HTML 表單可以通過
POST
或者GET
方式來進行傳輸。 ZF2 默認是使用POST
,所以你不需要對此進行顯式的設定。但是如果你希望使用GET
方式,只需要在調用prepare()
之前設置好這個特定的屬性:$form->setAttribute('method', 'GET');
接下來我們會使用幾個 ViewHelpers
來負責幫我們渲染表單。使用 Zend Framework 渲染表單的方法有很多種,不過使用 formCollection()
可能是最快的方法。
刷新您的瀏覽器,現(xiàn)在就能看見你的表單被正確顯示出來了。然而,現(xiàn)在提交表單的話,我們只能看見先前提交的表單原封不動的回顯出來。很簡單,這是因為我們還沒有為控制器添加任何邏輯。
注意:請記住這個教程僅僅聚焦于面向對象編程視角。像這樣子渲染表單,不應用任何樣式表是無法反映出絕大多數(shù)設計師關于一個美麗的表單的想法的。您將會在 Zend\Form\View\Helper 章節(jié)中學習到更多關于表單渲染的內容。
編寫一個控制器來處理表單工作流是非常簡單的,而且基本上對于應用程序中每一種表單的手段都是一樣的。
然而要實現(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ù)庫錯誤發(fā)生了,記錄并且讓用戶知道
}
}
}
return new ViewModel(array(
'form' => $this->postForm
));
}
}
這個示例代碼應該十分簡單明了。首先我們將目前的請求保存到一個本地變量中。然后我們檢查目前的請求是不是一個 POST 請求,如果是,將請求的 POST 數(shù)據(jù)存儲到表單中。如果表單經過檢查之后確認是有效的,我們就試圖將表單數(shù)據(jù)通過我們的服務進行保存,然后將用戶重定向到路徑 blog
。如果在這途中任何時候出現(xiàn)了任何錯誤,我們就再次將表單顯示出來。
現(xiàn)在提交表單的話,我們會遇到下述錯誤:
Fatal error: Call to undefined method Blog\Service\PostService::savePost() in
/module/Blog/src/Blog/Controller/WriteController.php on line 33
我們通過拓展 PostService
來修復這個錯誤,同時不要忘記更改 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ù)已經被添加,并且需要在 PostService
里被實現(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)在我們對 postMapper
做出了假設,所以需要擴展我們的 PostMapperInterface
接口以及其實現(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);
}
然后我們實現(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 存在,是一個 Update
$action = new Update('posts');
$action->set($postData);
$action->where(array('id = ?' => $postObject->getId()));
} else {
// ID 不存在,是一個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()) {
// 每當一個值被生成時,將其賦給對象
$postObject->setId($newId);
}
return $postObject;
}
throw new \Exception("Database error");
}
}
在這里 save()
函數(shù)處理了兩種情況:insert
和 update
流程。首先我們提取 Post
對象,因為我們需要數(shù)組數(shù)據(jù)來實現(xiàn) Insert
和 Update
。然后,我們從數(shù)組中刪除了 id
,因為對一個元組進行更新的時候,我們不需要更新 id
屬性;同時,我們插入一個新元組的時候也不需要 id
字段,所以兩種情況均不需要 id
這個字段,將其簡單去除即可。
在我們去除了 id
字段之后,檢查那些動作需要被調用。如果 Post
對象擁有一個 id
集,我們便創(chuàng)建一個新的 Update
對象,否則我們創(chuàng)建一個 Insert
對象。我們將數(shù)據(jù)傳給合適的 action 然后數(shù)據(jù)會被傳給 Sql
對象,最終進行真正的數(shù)據(jù)庫操作。
最后,我們檢查我們是否接收到一個有效的結果,檢查一下有沒有新產生的 id
。如果是的話,我們調用我們博客的 setId()
函數(shù)并且將對象返回。
讓我們再次提將我們的表單,看看這次會得到什么。
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
表單,默認的時候,會將數(shù)據(jù)以數(shù)組形式傳給你。不過我們的 PostService
卻期待接收到的對象是 PostInterface
的一個實現(xiàn)。這意味著我們需要找到一個方法來將這個數(shù)組數(shù)據(jù)轉換成對象數(shù)據(jù)。如果你還記得上一章節(jié),就會知道要通過 hydrators 實現(xiàn)。
注意:在更新查詢中,你會注意到我們添加了一個條件語句讓其只更新與給出的 id 匹配的元組:
$action->where(array('id = ?' => $postObject->getId()));
你會看見條件是:id equals ?。這個問號代表著 POST 對象的 id。同樣的,你可以添加一個條件語句來更新(或者選擇)所有大于給定 id 的元組:$action->where(array('id > ?' => $postObject->getId()));
這些操作符可以用于所有類型的條件語句:=
、>
、<
、>=
和<=
。
在我們繼續(xù)前進并且將充水器放進表單之前,先讓我們對表單的數(shù)據(jù)做一個 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ù)庫錯誤發(fā)生了,記錄并且讓用戶知道
}
}
}
return new ViewModel(array(
'form' => $this->postForm
));
}
}
做好之后請再次提交表單。你現(xiàn)在應該能看到數(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
對象是非常簡單的。你需要做的事情僅僅是指定注水器和對象原型,如下例所示:
<?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,然后我們還告知了它應該返回給我們 Blog
模型、不過,當你再次提交表單的時候你會注意到什么都沒有改變。我們仍然只得到數(shù)組數(shù)據(jù),而不是對象。
這是因為事實上表單本身不知道自己需要返回一個對象。當表單不知道自己要返回什么的時候就會默認遞歸使用 ArraySeriazable
hydrator。要改變這點,我們需要讓我們的 PostFieldset
變成所謂的 base_fieldset
。
base_fieldset
基本上就告訴了表單“這個表單是關于我的,請不要操心其他數(shù)據(jù),只操心我就好”。而且當表單意識到這個字段集是來真的,它就會乖乖使用字段集提供的 hydrator,并且將我們想要的對象返回出來。修改你的 PostForm
并且將 PostFieldset
設置成 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)在再次提交你的表單。你應該能看見如下輸出:
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ù)庫錯誤發(fā)生了,記錄并且讓用戶知道
}
}
}
return new ViewModel(array(
'form' => $this->postForm
));
}
}
你現(xiàn)在只需要再次發(fā)送表單就能夠隨心所欲任意添加博客帖子了,不錯!
在這個章節(jié)中,你學習了許多關于 Zend\Form
組件的知識。你也知道了 Zend\Stdlib\Hydrator
在 Zend\Form
組件扮演多么重要的角色,并且通過使用這兩個組件你已經能為博客模組創(chuàng)建插入表單。
在下一個章節(jié)中,我們會創(chuàng)建更新和刪除程序,從而完成博客模組的所有增刪改查功能。
更多建議: