如何在 Zend Framework 中创建模块化 MVC 组件
我在 Zend Framework 应用程序中创建模块化可重用组件时遇到问题。在本例中,我指的不是 Zend Framework 模块,而是拥有可重用 MVC 小部件的能力(如果您愿意)。我遇到的问题可能对于我的实现来说非常特殊,但如果有人能指出正确的方向,我非常乐意将其扔掉并重新开始。不管怎样,细节和代码有望更好地解释事情,即使我正在做的不是最好的方式,它也应该显示我想要实现的目标:
一个简单的例子是邮件列表注册表单。我想将其包含在使用不同控制器的网站的几个页面上,这在如何处理数据和返回相关消息方面提出了一些问题。我不想执行以下任何操作,因为它们真的很臭:
- 创建一个带有表单处理的基本控制器并扩展(坏)
- 在相关控制器中重复表单处理代码(更糟糕!)
我觉得干净的方式要创建新的控制器来处理邮件列表表单数据,请使用视图助手轻松地将表单和相关标记输出到所需页面,然后在处理表单后重定向回发生注册的页面。但是,我想使用 Zend_Form 提供的表单验证,这意味着如果验证失败但在同一请求中,我需要以某种方式将表单对象传递回视图助手。我目前正在通过将其设置为视图上的变量,然后转发回上一页而不是重定向来实现此目的,这是可以的。如果验证正常,那么我更愿意使用重定向回原始页面。不过,我在执行此操作时遇到了困难,因为我想将有关注册状态的消息传递回组件。通常我会使用 FlashMessenger Action Helper,在这种情况下我可以为其命名,这样消息就不会与其他页面数据冲突,但我无法从 View Helper 中访问它。所以目前我也在转发这种情况。我更喜欢重定向,以防止用户刷新页面时重新提交表单并保持 URL 干净。我意识到我本质上希望在页面内有一个迷你 MVC 调度过程,我认为这就是操作堆栈的用途?我对此确实了解不多,如果有任何指点,我将不胜感激。这是我当前的代码:
Controller:
<?php
class MailingListController extends Zend_Controller_Action {
public function insertAction() {
$request = $this->getRequest();
$returnTo = $request->getParam('return_to');
if(!$request->isPost() || (!isset($returnTo) || empty($returnTo))) {
$this->_redirect('/');
}
$mailingList = new Model_MailingList();
$form = new Form_MailingList();
$returnTo = explode('/', $returnTo);
if($form->isValid($_POST)) {
$emailAddress = $form->getValue('email_address');
$mailingList->addEmailAddress($emailAddress);
$this->view->mailingListMessages = $mailingList->getMessages();
$this->view->mailingListForm = "";
}
else {
$this->view->mailingListForm = $form;
}
$this->_forward($returnTo[2], $returnTo[1], $returnTo[0]);
}
}
return_to 是一个包含当前 URI(模块/控制器/操作)的字符串,它是在视图助手中生成的。我更喜欢在 $form->isValid($_POST) 块内重定向。
View Helper:
<?php
class Zend_View_Helper_MailingList extends Zend_View_Helper_Abstract {
public function mailingList($form, $messages = "") {
if(!isset($form)) {
$request = Zend_Controller_Front::getInstance()->getRequest();
$currentPage = $request->getModuleName() . '/' . $request->getControllerName() . '/' . $request->getActionName();
$form = new Form_MailingList();
$form->setAction('/mailing-list/insert');
$form->setCurrentPage($currentPage);
}
$html = '<div class="mailingList"><h2>Join Our Mailing List</h2>' . $form;
$html .= $messages;
$html .= '</div>';
return $html;
}
}
在 View Helper 中获取 Front Controller 的实例并不理想,但我更愿意尽可能封装。
如果我有一个验证失败的表单对象,我可以将其传递回帮助器以输出错误消息。如果我有一些消息要渲染,我也可以将它们传递给助手。
在我的视图脚本中,我使用如下帮助程序:
<?=$this->mailingList($this->mailingListForm, $this->mailingListMessages);?>
如果 MailingListController 未在视图上设置 mailingListForm 或 mailingListMessages,它将输出一个没有消息的新表单。
非常感谢任何帮助!
I've been having problems created modular reusable components in my Zend Framework app. In this case I'm not referring to Zend Framework modules but rather the ability to have a reusable MVC widgety thing if you like. The problems I'm having may be very particular to my implementation, but I'm completely happy to throw it out and start again if someone can point me in the right direction. Anyway, specifics and code will hopefully explain things better and even if what I'm doing is not the best way it should show what I'm trying to achieve:
A simple example is a Mailing List sign up form. I want to include this on several pages of the site which use different Controllers and this presents a few problems in how to process the data and return relevant messages. I don't want to do either of the following as they really smell:
- Create a base controller with the form processing in and extend (Bad)
- Duplicate form processing code in relevant controllers (Even worse!)
The clean way to go feels to me to create a new Controller to process the mailing list form data, use a View Helper to easily output the form and relevant markup into the desired pages and then redirect back to the page where signup occurred once the form has been processed. However, I'd like to use the form validation provided by Zend_Form, which means I'd need to pass the form object back to the view helper somehow if validation fails but in the same request. I'm currently doing this by setting it as a variable on the view and then forwarding back to the previous page rather than redirecting, which is ok(ish). If validation is ok then I'd prefer to use a redirect back to the original page. I'm having trouble doing this though as I'd like to pass messages back to the component about the state of signup. Normally I'd use the FlashMessenger Action Helper, I could namespace it in this case so messages didn't clash with other page data, but I can't access it from within a View Helper. So currently I'm forwarding in this case too. I'd much prefer a redirect to prevent form resubmissions if a user refreshes the page and to keep the URL clean. I realise I essentially want to have a mini MVC dispatch process within a page and I think that's what the action stack is for? I really don't know much about this though and any pointers would be greatly appreciated. Here's my current code:
Controller:
<?php
class MailingListController extends Zend_Controller_Action {
public function insertAction() {
$request = $this->getRequest();
$returnTo = $request->getParam('return_to');
if(!$request->isPost() || (!isset($returnTo) || empty($returnTo))) {
$this->_redirect('/');
}
$mailingList = new Model_MailingList();
$form = new Form_MailingList();
$returnTo = explode('/', $returnTo);
if($form->isValid($_POST)) {
$emailAddress = $form->getValue('email_address');
$mailingList->addEmailAddress($emailAddress);
$this->view->mailingListMessages = $mailingList->getMessages();
$this->view->mailingListForm = "";
}
else {
$this->view->mailingListForm = $form;
}
$this->_forward($returnTo[2], $returnTo[1], $returnTo[0]);
}
}
return_to is a string containing the current URI (module/controller/action), which is generated in the View Helper. I'd prefer to redirect inside the $form->isValid($_POST) block.
View Helper:
<?php
class Zend_View_Helper_MailingList extends Zend_View_Helper_Abstract {
public function mailingList($form, $messages = "") {
if(!isset($form)) {
$request = Zend_Controller_Front::getInstance()->getRequest();
$currentPage = $request->getModuleName() . '/' . $request->getControllerName() . '/' . $request->getActionName();
$form = new Form_MailingList();
$form->setAction('/mailing-list/insert');
$form->setCurrentPage($currentPage);
}
$html = '<div class="mailingList"><h2>Join Our Mailing List</h2>' . $form;
$html .= $messages;
$html .= '</div>';
return $html;
}
}
Getting an instance of the Front Controller in the View Helper isn't ideal but I'd prefer to encapsulate as much as possible.
If I have a form object where validation has failed I can pass it back into the helper to output with error messages. If I have some messages to render I can also pass them into the helper.
In my view scripts I'm using the helper like so:
<?=$this->mailingList($this->mailingListForm, $this->mailingListMessages);?>
If neither mailingListForm or mailingListMessages has been set on the view by MailingListController, it will output a new form with no messages.
Any help is greatly appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
使用ajax似乎是一种最佳方式。 View Action Helper 仅用于首次加载邮件表单。
控制器
视图脚本 insert.phtml
表单类
视图助手
JS文件邮件列表.js
Using ajax seems to be an optimal way. View Action Helper is used only for the first load of the mailing form.
Controller
view script insert.phtml
Form class
View Helper
JS file mailing-list.js
我认为你的做法与我的做法非常接近。如果你抛开想要在页面中显示 Zend_Form 错误消息的要求,那么你要做的是:
这使一切变得更加简单,唯一问题是如果如果出现任何验证错误,用户就会失去上下文,并得到一个带有表单的普通旧页面,而不是原来的位置。然后,您可以通过更改提交方式来解决此问题(现在或以后)。相反,Ajax,并通过渲染错误。 JS。但这将是一项相当大的工作量。
I think the way you've done it is pretty close to what I would do. If you set aside the requirement of wanting to display the Zend_Form error messages in the page, then what you do instead is:
This makes everything much simpler, the only issue is that if there are any validation errors then the user loses their context and gets a plain old page with the form on instead of where they were. You can then address this (either now or at a later date) by changing the form to submit via. Ajax instead, and rendering the errors via. JS. But this would be a fair amount of work.
好的,我已经想出了一个令我感到高兴的解决方案,并解决了我面临的一些问题。希望这可以帮助面临类似问题的人。现在唯一的缺点是我在视图助手中引用模型。我知道这不是松散耦合,但我之前已经见过几次这样做了,甚至在 ZF 文档作为避免使用“操作”视图助手的方法(这将创建新的 MVC 调度循环)。总的来说,我认为 DRYness 和封装是值得的,可能还有其他合适的术语。
为了能够使用从我的 MailingListController 返回的重定向,但维护来自我的模型的消息和任何表单验证错误,我需要将它们存储在会话中。对于消息,我通常使用 FlashMessenger 操作帮助程序,但由于在视图帮助程序中获取它不是最佳实践,它不会处理我的表单错误,它真正做的只是将内容保存到会话中,无论如何这是不必要的。我可以在 Model_MailingList 中实现我自己的会话存储,我也可以将其用于表单错误。然后,我可以在重定向后使用错误重新填充表单并打印出任何相关消息。不管怎样,代码如下:
控制器:
我已经向我的 Model_MailingList 类添加了一个方法; setFormErrors($errors) 如果验证失败,我会从表单传递错误消息。这会将错误数组保存到会话中。
我通常使用具有 addMessage 和 getMessages 方法的基模型类。它们仅访问受保护的消息数组。在我的 Model_MailingList 中,我重写这些方法以将消息存储在会话中。在 addEmailAddress($emailAddress) 方法中,我已经调用 addMessage 来说明将电子邮件地址插入数据库是否已成功。
模型:
我现在不需要将任何参数传递给视图助手,因为它可以直接从模型查询其状态。 $this->view->messenger 只是另一个将数组转换为无序列表的视图助手。
View Helper:
然后在 Form_MailingList 类中,我只需要添加一个附加方法来重新填充错误消息。虽然 getMessages() 是 Zend_Form 的一个方法,但似乎没有任何相应的 setMessages()。不过,您可以在 Zend_Form_Element 上执行此操作,因此我将以下函数添加到 Form_MailingList 类中:
表单:
我现在可以使用 MailingList 视图帮助程序在站点的任何页面上添加注册表单:
我意识到很多问题我面临的是一系列非常具体的情况,但希望这可以以某种方式帮助其他人!
干杯,
亚历克斯
OK, I've come up with a solution that I feel happier about and solves some of the problems I was facing. Hopefully, this might help someone out who's facing similar issues. The only downside now is that I'm referencing the Model inside the View Helper. Not loose coupling I know but I've seen this done several times before and it's even recommended in the ZF docs as a way to avoid using the 'action' view helper (which will create a new MVC dispatch loop). On the whole, I think the DRYness and encapsulation is worth it, there's probably some other suitable lingo too.
In order to be able to use a redirect back from my MailingListController but maintain the messages from my model and any form validation errors I need to store them in the session. For messages I'd normally use the FlashMessenger action helper, but as getting hold of this in a View Helper is not best practice, it won't handle my form errors and all it's really doing is saving stuff to the session anyway it's unnecessary. I can implement my own session storage in the Model_MailingList, which I can also use for the form errors. I can then repopulate the form with the errors after the redirect and print out any relevant messages. Anyway, here's the code:
Controller:
I've added a method to my Model_MailingList class; setFormErrors($errors) that I pass the error messages from the form if it fails validation. This saves the error array to the session.
I normally use a base model class that has addMessage and getMessages methods. These just access a protected array of messages. In my Model_MailingList I override these methods to store the messages in the session instead. In the addEmailAddress($emailAddress) method I'm already calling addMessage to say whether inserting the email address to the db has been successful.
Model:
I now don't need to pass any params to the view helper as it can query it's state from the Model directly. $this->view->messenger is just another view helper that converts an array to an unordered list.
View Helper:
Then in the Form_MailingList class I just need to add an additional method to repopulate the error messages. Although getMessages() is a method of Zend_Form there doesn't appear to be any corresponding setMessages(). You can do this on a Zend_Form_Element however, so I've added the following function to the Form_MailingList class:
Form:
I can now add a signup form on any page of my site using the MailingList view helper:
I realise a lot of the problems I was facing was down to a very specific set of circumstances, but hopefully this can help some other people out in some way!
Cheers,
Alex