我有一个看起来像这样的表格:
class Cas_Form_Company extends Zend_Form
{
/**
* @param Cas_Model_Company|null $company
*/
public function __construct(Cas_Model_Company $company = null)
{
parent::__construct();
$id = new Zend_Form_Element_Hidden('id');
$name = new Zend_Form_Element_Text('name');
$name->addValidator('stringLength', false, array(2,45));
$name->addValidator(new Cas_Model_Validate_CompanyUnique());
$name->setLabel('Name');
$submit = new Zend_Form_Element_Submit('Submit');
if ($company)
{
$id->setValue($company->GetId());
$name->setValue($company->GetName());
}
$this->addElements(array($id, $name, $submit));
$this->setMethod('post');
$this->setAction(Zend_Controller_Front::getInstance()->getBaseUrl() . '/Asset/company');
}
public function Commit()
{
if (!$this->valid())
{
throw new Exception('Company form is not valid.');
}
$data = $this->getValues();
if (empty($data['id']))
{
Cas_Model_Gateway_Company::Create($data['name']);
}
else
{
$company = Cas_Model_Gateway_Company::FindById((int)$data['id']);
$company->SetName($data['name']);
Cas_Model_Gateway_Company::Commit($company);
}
}
}
现在,这种形式依赖于一个控制器,它看起来像这样:
public function companyAction()
{
if ($this->getRequest()->isPost())
{
if ($this->getRequest()->getParam('submit') == 'Delete')
{
Cas_Model_Gateway_Company::Delete(Cas_Model_Gateway_Company::FindById((int)$this->getRequest()->getParam('id')));
$this->_helper->redirector->setCode(303)->gotoSimple('companies');
}
$form = new Cas_Form_Company();
if ($form->isValid($this->getRequest()->getParams()))
{
$form->Commit();
$this->_helper->redirector->setCode(303)->gotoSimple('index');
}
$this->view->form = $form;
}
else if ($id = $this->getRequest()->getParam('id'))
{
$form = new Cas_Form_Company(Cas_Model_Gateway_Company::FindById($id));
$this->view->form = $form;
}
else
{
$this->view->form = new Cas_Form_Company();
}
$this->_helper->viewRenderer->setScriptAction('formrender');
}
看起来控制器动作在这里“做得太多”,但我看不到解决这个问题的简单方法。一般来说,我认为表单应该是担心它是添加,编辑还是删除操作的人。但我似乎无法找到一个很好的方法去做。
对于使用Zend_Form
的人或我做错了什么,这是正常模式吗?
答案 0 :(得分:3)
这非常好,您所做的就是向表单发送值并处理响应,重定向并处理视图。
这些东西都很合适,如果你把它们放在别处,那将是一件很麻烦的事情。我不会想到“胖控制器”,因为“需要比其他控制器/操作更多的LOC”,而是“包含业务逻辑”。如果您觉得圈复杂度变得太大,请尝试将您的行动分成更小的行动。
编辑:实际上,我会将$form->Commit();
部分重构为类似$repository->create($form->getValues()
的内容,因为a)Cas_Form_Company::Commit()
中没有使用内部内容,并且; b)您希望将与存储相关的功能与验证&显示你的表格。考虑调试/改变数据的存储方式,现在你可以查看所有f.ex.包含查询的类正在执行此操作,只执行该操作 - 处理DAL。
答案 1 :(得分:2)
似乎你在表单,模型和控制器中混淆了一些责任。通常,当我根据表单数据与控制器中的模型进行一些交互时,我会引入一个服务层。
另外,我看到你在一个动作中处理多个功能,我将在不同的动作之间进行分割。如果您使用的是Company
模型,我建议您创建一个CompanyController:
class CompanyController extends Zend_Controller_Action {}
接下来,您可能希望在一个页面上查看公司详细信息,在另一个页面上修改这些详细信息并在第三页上创建新实例。我通常使用viewAction()
,editAction()
和newAction()
:
public function indexAction ()
{
$service = new Cas_Service_Company;
$companies = $service->getAllCompanies();
if (!count($companies)) {
throw new Zend_Controller_Action_Exception('No companies found');
}
$this->view->companies = $companies;
}
public function viewAction ()
{
$service = new Cas_Service_Company;
$company = $service->getCompany($this->getRequest()->getQuery('id'));
if (false === $company) {
throw new Zend_Controller_Action_Exception('Company not found');
}
$this->view->company = $company;
}
public function editAction ()
{
$request = $this->getRequest();
$service = new Cas_Service_Company;
$company = $service->getCompany($request->getQuery('id'));
if (false === $company) {
throw new Zend_Controller_Action_Exception('Company not found');
}
$form = new Cas_Form_Company(array('company' => $company));
if ($request->isPost() && $form->isValid($request->getPost())) {
$service->updateCompany($company, $form);
// redirect here to company view for example
}
$this->view->form = $form;
$this->view->company = $company;
}
public function newAction ()
{
$request = $this->getRequest();
$form = new Cas_Form_Company;
if ($request->isPost() && $form->isValid($request->getPost())) {
$company = $service->createCompany($form);
// redirect here to company view for example
}
$this->view->form = $form;
}
public function deleteAction ()
{
$request = $this->getRequest();
$form = new Cas_Form_DeleteCompany;
if ($request->isPost() && $form->isValid($request->getPost())) {
$service->deleteCompany($form);
// redirect here to index for example
}
$this->view->form = $form;
}
现在您对所有功能都有单独的操作,为Company
设置表单非常简单:
class Cas_Form_Company extends Zend_Form
{
protected $_company;
public function init ()
{
$this->addElement('text', 'name', array(
'label' => 'Name'
));
// More elements here
if (null !== $this->_company) {
$this->populate($this->_company->toArray());
}
}
public function setCompany (Cas_Model_Company $company)
{
$this->_company = $company;
}
}
在表单中我使用了几个简洁的功能:
init()
代替__construct()
,无需致电parent::__construct()
并调用setOptions()
。这对#2有益:Cas_Model_Company
的setter。当您需要更多依赖项时,只需在构造表单时将它们放入数组中(请参阅editAction()
作为控制器中的示例)$this->_company
,只需通过检查即可注入表单数据。在我的情况下,我使用Doctrine,所以toArray()
已经存在。否则,您需要自己创建此方法。将所有内容放在一起的最后一块是服务层。 Martin Fowler也在他的EAA书和他的网站http://martinfowler.com/eaaCatalog/serviceLayer.html
中描述了这一点典型的服务类在我看来总是这样:
class Cas_Service_Company
{
public function getCompany ($id)
{
// Get a Cas_Model_Company and load data from database
// Example for Doctrine:
$company = new Cas_Model_Company;
$company = $company->find($id);
return $company;
}
public function getAllCompanies ()
{
// Get a Cas_Model_Company and load all data from database
// Example for Doctrine:
$company = new Cas_Model_Company;
$companies = $company->findAll();
return $companies;
}
public function updateCompany (Cas_Model_Company $company, Cas_Form_Company $form)
{
// Update model with new form data
// Example for Doctrine:
$company->fromArray($form->toArray());
$company->save();
return $company;
}
public function createCompany (Cas_Form_Company $form)
{
// Create model with form data
// Example for Doctrine:
$company = new Cas_Model_Company;
$company->fromArray($form->toArray());
$company->save();
return $company;
}
public function deleteCompany (Cas_Form_DeleteCompany $form)
{
// Get a Cas_Model_Company and load data from database
// Example for Doctrine:
$company = new Cas_Model_Company;
$company = $company->find($form->getValue('id'));
// Remove this instance
if (false !== $company) {
$company->delete();
return true;
} else {
return false;
}
}
}
使用这种服务层,您可以保留所有代码以在一个位置访问和修改模型。当您需要另一个查询(根据特定条件查找公司)时,您只需将该方法添加到服务器类并在(例如)控制器中调用该方法。它会使您的代码非常保持清洁。
总结:使用服务层并只使用控制器作为信息代理:从源A获取数据并将其带到B点。为表单创建一个setter以接受模型并将其用作装饰器的一种图案