2009-06-08 4 views
2

Ich habe versucht, ein Login-Widget zu erstellen, das in jeder Aktion enthalten sein kann, die außerhalb des Inhalts der Anwendung ist, die Anmeldung erfordert. Mein Gedanke hier war, dass ich einen DRY-Ansatz wünschte, um die Login-Box mit allen Fehlermeldungen eines vorherigen Anmeldeversuchs zu rendern. Um dies zu aktivieren, habe ich die FlashMessenger zum Speichern des Ergebnisses eines fehlgeschlagenen Anmeldeversuchs verwendet. Das Widget verwendet auch die Post/Redirect/Get-pattern, um das Problem zu vermeiden, Daten mehrfach zu posten, wenn Sie die Zurück-Schaltfläche des Browsers verwenden.Refactoring eines Zend_View_Helper_Action Widget mit Zend_Controller_Action_Helper_FlashMessenger, in ein Modell

Nun, das Problem ist, dass ich lieber nicht die Zend_View_Helper_Action verwenden würde, da dieser Helfer die Anfrage klont und einen weiteren Lauf der Dispatch-Schleife erstellt, die ziemlich ressourcenintensiv ist. Also, indem Sie auf den Code suchen, könnten Sie mir einen Rat geben, wie man den Code Refactoring, so dass:

  1. Das Widget kann in einer beliebigen Ansicht Skript
  2. Ergebnisse aus einer früheren aufgenommen werden Anmeldeversuch gemacht wird
  3. das Widget nicht einen Lauf in der Dispatch-Schleife aufrufen

Derzeit ist die Login-Widget ist durch den Aufruf, in den View Skripten gemacht:

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: [email protected]'); 
     } 
    } 

    /** 
    * _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'] 
      ); 
    } 
} 

/ansichten/scripts/auth/partials/logi n-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, Interessante Frage, aber warum sollte man nicht eine einfache Umleitung ausgeben, um, sagen wir, AuthController-> loginaction, wenn ein nicht eingeloggten versucht der Benutzer, auf geschützte Inhalte zuzugreifen? Gibt es einen besonderen Grund? – karim79

+0

Natürlich muss ich das noch tun, aber ich möchte dem Benutzer die Möglichkeit geben, sich einzuloggen, wenn er Lust dazu hat, ohne ihn dazu zu zwingen, zuerst auf einen Link zu klicken, der eine Anmeldung erfordert. Zum Beispiel, um eine Login-Box in einer Seitenleiste zu haben, wenn der Benutzer nicht eingeloggt ist. – PatrikAkerstrand

Antwort

1

Soweit ich Ihre Lösung verstanden, tut Ihr aktueller Anruf an die Zend_View_Helper_Action Helfer nichts mehr als das Rendern des Anmeldeformulars. Warum schreiben Sie nicht Ihren On-View-Helfer, der die loginbezogenen Nachrichten vom Flash-Messenger abruft und das Login-Formular rendert?

EDIT:

  1. Sie den Flash-messenger von seinem statischen Broker abrufen können:

    $flashMessenger = Zend_Controller_Action_HelperBroker::getStaticHelper('FlashMessenger'); 
    $flashMessenger->setNamespace('login'); 
    $messages = $flashMessenger->getMessages(); 
    
  2. Sie brauchen keine neuen Flash-Bote in der Ansicht instatiate Helfer, Sie können nur die aktuelle Instanz davon abrufen (siehe 1.). Aus der Sicht des Designs würde ich sagen, dass es absolut machbar ist, Anwendungskomponenten in Ihren View-Helfern zu finden (die Zend_View_Helper_Url zum Beispiel ruft den Router vom statischen Front-Controller ab). Zur Kopplung zu reduzieren und eine Setter-Abhängigkeit-Injektion-Muster zu implementieren, ich würde vorschlagen, die folgende (Zend_View_Helper_Translate verwendet ein ähnliches Muster der Übersetzer abrufen):

    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(); 
         // [...] 
        } 
    } 
    
  3. Nicht, wenn der Blitz-messenger die Funktionalität ermöglicht Ihnen brauchen. Wenn Sie zwischen Fehler-, Informations- und Erfolgsmeldungen unterscheiden müssen, wäre zum Beispiel eine selbstgewählte Lösung vielleicht der bessere Weg.

+0

Es tut ein bisschen mehr als das. Es ruft auch die Fehlermeldungen ab und schiebt sie in die Ansicht. Ich dachte an einen View-Helfer anstelle einer Controller-Aktion, aber ich zögerte in diesen Punkten: * Ist es richtig, einen Zend_Controller_Action_Helper in einem View Helper zu instanziieren? (Der FlashMessenger). * Wie instanziiere ich es ordnungsgemäß? * Sollte ich meinen eigenen Fehlerübermittlungsmechanismus bauen, anstatt den Flash Messenger zu verwenden? – PatrikAkerstrand

+0

Antworten bearbeitet mit Antworten auf die gestellten Fragen. –

+0

Ich glaube du vergisst entweder ein! oder leer ($ this -> _ flash) in getFlashMessenger. Abgesehen davon funktioniert diese Lösung genauso gut wie die Verwendung des Aktionsansicht-Helfers. Ich mag Ihre Idee eines Abhängigkeitsinjektormusters, obwohl es in diesem Szenario vielleicht übertrieben ist. Danke für deine Hilfe und deine Ideen! – PatrikAkerstrand

0

Vielleicht sollten Sie verwenden View Helfer Form und Action für die Authentifizierung zu machen? Dies befreit Sie von der zweiten Dispatch-Schleife. Ich kann Ihnen ein kleines Beispiel zeigen, wenn Sie wollen.

P.S. Sie benötigen sicher Validatoren für das Formular? Ich sehe in Ihrem Beispiel keine Verwendung.

+0

Ich brauche wahrscheinlich keine Validatoren, und ich benutze sie im Moment nicht. Ich habe sie beim Konfigurieren des Formulars hinzugefügt, aber Sie haben Recht. Ich sollte sie wahrscheinlich herausnehmen, wenn ich keinen Nutzen für sie finde. – PatrikAkerstrand

1

Eine weitere Ergänzung ist, dass Sie Flashmessenger können ANY Daten zu speichern. So können Sie

$flash->addMessage(array('status'=>'error','message'=> 'oops')); 

tun und dann in foreach verwenden

foreach($messages as $message){ 
echo '<div class="message '. $message['status'] .'">' . $message['message'] . '</div>' 
}