我尝试创建一个登录窗口小部件,可以随意包含在需要登录的应用程序内容之外的任何操作中。我的想法是,我想要一个DRY方法来渲染登录框,其中包含来自先前登录尝试的任何错误消息。为了实现这一点,我使用FlashMessenger来保存登录尝试失败的结果。该小部件还使用Post/Redirect/Get-pattern来避免在使用浏览器后退按钮时多次发布数据的问题。
现在,问题在于我宁愿不使用Zend_View_Helper_Action,因为该帮助程序克隆了请求并创建了另一个dispatch-loop运行,这非常耗费资源。那么,通过查看下面的代码,您能否就如何重构代码给我一些建议:
目前,通过在视图脚本中调用来呈现登录窗口小部件:
echo $this->action('auth', 'login-widget');
AuthController.php:
class AuthController extends Zend_Controller_Action {
// This method is invoked by Zend_View_Helper_Action to render the
// login widget
public function loginWidgetAction () {
$flashMessenger = $this->_helper->flashMessenger->setNamespace('login');
$this->view->messages = $flashMessenger->getMessages();
}
public function loginAction () {
if($this->getRequest()->isPost()) {
$result = Auth::doLogin($this->getRequest()->getPost());
if($result->isValid()) {
$this->_redirect('user');
}
else {
$flashMessenger = $this->_helper->flashMessenger->
setNamespace('login');
foreach($result->getMessages() as $message) {
$flashMessenger->addMessage($message);
}
}
}
// This will be changed to redirect either to the index page,
// or the page the request originated from if available.
$this->_redirect('');
}
[...]
}
/models/Auth.php:
/**
* Model for encapsulating the actions that deals with authentication,
* such as registering and activating users, as well as logging in and
* logging out.
* @todo: Refactor this to remove static methods
*/
class Auth {
/**
*
* @return Zend_Auth_Result
*/
public static function doLogin ($credentials) {
$authAdapter = new Auth_Adapter_DbTable(
Zend_Db_Table::getDefaultAdapter(),
'Users',
'username',
'pwdHash',
'SHA1(CONCAT(?, salt))'
);
$authAdapter->setIdentity($credentials['username']);
$authAdapter->setCredential($credentials['password']);
$auth = Zend_Auth::getInstance();
return $auth->authenticate($authAdapter);
}
[...]
}
/models/Auth/Adapter/DbTable.php:
class Auth_Adapter_DbTable extends Zend_Auth_Adapter_DbTable {
/**
* authenticate() - defined by Zend_Auth_Adapter_Interface. This method
* is called to attempt an authenication. Previous to this call, this
* adapter would have already been configured with all necessary
* information to successfully connect to a database table and attempt
* to find a record matching the provided identity.
*
* @throws Zend_Auth_Adapter_Exception if answering the authentication
* query is impossible
* @see library/Zend/Auth/Adapter/Zend_Auth_Adapter_DbTable#authenticate()
* @return MedU_Auth_Result
*/
public function authenticate() {
return parent::authenticate();
}
/**
* _authenticateValidateResult() - This method attempts to validate that
* the record in the result set is indeed a record that matched the
* identity provided to this adapter.
*
* Additionally it checks that the user account is activated.
*
* @param array $resultIdentity
* @return MedU_Auth_Result
*/
protected function _authenticateValidateResult($resultIdentity)
{
$result = parent::_authenticateValidateResult($resultIdentity);
if(!$result->isValid()) { return $result; }
$this->_checkAccountIsActivated($resultIdentity);
$this->_checkAccountIsSuspended($resultIdentity);
// Overwrite the username supplied by the user and instead
// use the name supplied upon registration, i.e if the
// user signs in as uSERNAME and registered as Username,
// the identity is Username
$this->_authenticateResultInfo['identity'] =
$resultIdentity[$this->_identityColumn];
return $this->_authenticateCreateAuthResult();
}
protected function _checkAccountIsActivated ($resultIdentity) {
if(!$resultIdentity['activated']) {
$this->_authenticateResultInfo['code'] =
MedU_Auth_Result::FAILURE_NOT_ACTIVATED;
$this->_authenticateResultInfo['messages'] =
array('The account has not yet been activated.
Please click on the link provided in the
activation email.');
}
}
protected function _checkAccountIsSuspended ($resultIdentity) {
if($resultIdentity['suspended']) {
$this->_authenticateResultInfo['code'] =
MedU_Auth_Result::FAILURE_SUSPENDED;
$this->_authenticateResultInfo['messages'] =
array('The account has been suspended.
If you feel this is a mistake,
please contact our support: support@meduniverse.com');
}
}
/**
* _authenticateCreateAuthResult() - This method creates a
* MedU_Auth_Result object from the information that has
* been collected during the authenticate() attempt.
*
* @return MedU_Auth_Result
*/
protected function _authenticateCreateAuthResult()
{
return new MedU_Auth_Result(
$this->_authenticateResultInfo['code'],
$this->_authenticateResultInfo['identity'],
$this->_authenticateResultInfo['messages']
);
}
}
/views/scripts/auth/partials/login-widget.phtml
// The fetchForm-view helper returns a Zend_Form object.
// The form definition (xml) is attached below in the
// code box below this one.
<div class="box">
<h3>Login</h3>
<?php if($this->messages) : ?>
<ul class="errors">
<?php foreach($this->messages as $message) : ?>
<li><?php echo $message ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<div>
<?php echo $this->fetchForm('user', 'login') ?>
</div>
</div>
/application/configs/forms.xml
// Configuration of the Login form. 'user' and 'login' are
// just keys for the view helper to return the correct form
<forms>
<user>
<login>
<action value="/auth/login" />
<method value="post" />
<elements>
<username>
<type value="text"></type>
<options>
<label value="Username" />
<required value="true" />
<validators>
<alnum validator="alnum" />
<strlen validator="StringLength">
<options min="6" max="45" />
</strlen>
</validators>
</options>
</username>
<password>
<type value="password" />
<options>
<label value="Password" />
<required value="true" />
<validators>
<strlen validator="StringLength">
<options min="6" max="20" />
</strlen>
</validators>
</options>
</password>
<submit>
<type value="submit" />
<options>
<label value="Log in" />
</options>
</submit>
</elements>
</login>
答案 0 :(得分:1)
据我了解您的解决方案,您当前对Zend_View_Helper_Action
帮助程序的调用只是渲染登录表单。为什么不编写视图助手,从flash-messenger中检索与登录相关的消息并呈现登录表单?
修改强>
您可以从其静态代理中检索flash-messenger:
$flashMessenger = Zend_Controller_Action_HelperBroker::getStaticHelper('FlashMessenger');
$flashMessenger->setNamespace('login');
$messages = $flashMessenger->getMessages();
您不需要在视图助手中设置新的flash-messenger,您只需检索它的当前实例(参见1.)。从设计的角度来看,我认为在视图助手中检索应用程序组件是绝对可行的(例如Zend_View_Helper_Url
从静态前端控制器检索路由器)。为了减少耦合并实现setter-dependency-injection-pattern,我建议如下(Zend_View_Helper_Translate
使用类似的模式来检索翻译器):
class My_View_Helper_Login extends Zend_View_Helper_Abstract
{
// [...]
/**
* @var Zend_Controller_Action_Helper_FlashMessenger
*/
protected $_flash;
/**
* @param Zend_Controller_Action_Helper_FlashMessenger $flash
* @return My_View_Helper_Login Provides a fluent interface
*/
public function setFlashMessenger(Zend_Controller_Action_Helper_FlashMessenger $flash)
{
$this->_flash = $flash;
return $this;
}
/**
* @return Zend_Controller_Action_Helper_FlashMessenger
*/
public function getFlashMessenger()
{
if ($this->_flash === null) {
$this->_flash = Zend_Controller_Action_HelperBroker::getStaticHelper('FlashMessenger');
}
return $this->_flash;
}
/**
*
*/
public function login()
{
$flashMessenger = $this->getFlashMessenger();
$flashMessenger->setNamespace('login');
$messages = $flashMessenger->getMessages();
// [...]
}
}
当flash-messenger提供您需要的功能时。如果您需要区分错误,信息和成功消息,例如本土解决方案可能是更好的方法。
答案 1 :(得分:1)
另一个补充是,您可以使用FlashMessenger来存储 ANY 数据。所以你可以做到
$flash->addMessage(array('status'=>'error','message'=> 'oops'));
然后在foreach中使用
foreach($messages as $message){
echo '<div class="message '. $message['status'] .'">' . $message['message'] . '</div>'
}
答案 2 :(得分:0)
也许您应该使用ViewHelper来呈现表单和ActionController进行身份验证? 这使您免于第二次调度循环。 如果你愿意,我可以告诉你一些小例子。
P.S。您确定需要表单验证器吗?我在你的例子中看不到任何用处。