Development

Changeset 11369

You must first sign up to be able to contribute.

Changeset 11369

Show
Ignore:
Timestamp:
09/08/08 14:26:35 (3 months ago)
Author:
halfer
Message:

Added custom form for security module, and changed backend action code accordingly

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • doc/branches/1.1/tutorial/my-first-project.txt

    r11244 r11369  
    33 
    44>**Caution** 
    5 >This tutorial is presently being converted from the 1.0 version. It is entirely possible 
    6 >to get a basic version running on 1.1, but you may need to get a few hints from more 
    7 >experienced users in the [users' forum](http://www.symfony-project.org/forum/index.php/f/15/). 
     5>This tutorial is presently being converted from the 1.0 version. The instructions 
     6>should be sufficient to get things running on 1.1, but the images relate to the 
     7>previous version. If you need assistance, post a message in the 
     8>[users' forum](http://www.symfony-project.org/forum/index.php/f/15/). 
    89 
    910So, you want to give it a go? Let's build together a fully-functional web app in 
     
    101102classes of the object-relational mapping system, which allows us to have access to a relational 
    102103database from within an object-oriented code without writing a single SQL query. 
    103 By default, symfony uses the Propel library for this purpose. Theses classes are 
     104By default, symfony uses the Propel library for this purpose. These classes are 
    104105part of the **model** of our application 
    105106(find more in the [model chapter](http://www.symfony-project.org/book/1_1/08-Inside-the-Model-Layer)). 
     
    836837    $ php symfony generate:module backend security 
    837838 
    838 >**Caution** 
    839 >The following part of the tutorial refers to code that would have appeared in the 1.0 
    840 > version of the sandbox, and has not been updated to take account of symfony 1.1. It will 
    841 >take a while longer to convert this section. Please bear with us! 
    842  
    843 This new module will be used to handle the login form and the request. 
    844 Edit the `apps/backend/modules/security/templates/indexSuccess.php` to 
    845 create the login form: 
    846  
    847     [php] 
    848     <h2>Authentication</h2> 
    849  
    850     <?php if ($sf_request->hasErrors()): ?> 
    851       Identification failed - please try again 
    852     <?php endif; ?> 
    853  
    854     <?php echo form_tag('security/login') ?> 
    855       <label for="login">login:</label> 
    856       <?php echo input_tag('login', $sf_params->get('login')) ?> 
    857  
    858       <label for="password">password:</label> 
    859       <?php echo input_password_tag('password') ?> 
    860  
    861       <?php echo submit_tag('submit', 'class=default') ?> 
    862     </form> 
    863  
    864 Add the `login` action that is called by the form to the `security` module 
    865 (in the `apps/backend/modules/security/actions/actions.class.php` file): 
    866  
    867     [php] 
    868     public function executeLogin($request) 
    869     { 
    870       if ($request->getParameter('login') == 'admin' && $request->getParameter('password') == 'password') 
     839This new module will be used to handle the login form. Rather than generate the form 
     840automatically, as we did with posts and comments, we will create this manually. To do 
     841this, create a new file in `/sf_sandbox/lib/form/LoginForm.class.php` and enter the 
     842following code: 
     843 
     844    [php] 
     845    class LoginForm extends sfForm 
     846    { 
     847      private $accounts; 
     848 
     849      /** 
     850       * Associative array of users => passwords is passed to the form 
     851       */ 
     852      public function __construct($accounts) 
    871853      { 
    872         $this->getUser()->setAuthenticated(true)
    873  
    874         return $this->redirect('post/list'); 
     854        $this->accounts = $accounts
     855     
     856        parent::__construct(); 
    875857      } 
    876       else 
     858     
     859      /** 
     860       * Called by symfony to configure the form elements and validators 
     861       */ 
     862      public function setup() 
    877863      { 
    878         $request->setError('login', 'incorrect entry'); 
    879  
    880         return $this->forward('security', 'index'); 
     864        $this->setWidgets(array( 
     865          'username' => new sfWidgetFormInput(), 
     866          'password' => new sfWidgetFormInputPassword(), 
     867        )); 
     868     
     869        $this->setValidators(array( 
     870          'username' => new sfValidatorString(array('required' => true), array('required' => 'A username is required')), 
     871          'password' => new sfValidatorString(array('required' => true), array('required' => 'A password is required')), 
     872        )); 
     873     
     874        // We also want to check the password 
     875        $this->validatorSchema->setPostValidator(new sfValidatorCallback(array('callback' => array($this, 'checkPassword')))); 
     876 
     877        $this->widgetSchema->setNameFormat('login[%s]'); 
     878             
     879        parent::setup(); 
    881880      } 
    882     } 
    883  
    884 Like for the `main` module, remove the default code in the `index` action: 
    885  
    886     [php] 
    887     public function executeIndex() 
    888     { 
    889     } 
     881 
     882       /** 
     883       * Callback function that lets us create our own customised validator 
     884       */     
     885      public function checkPassword($validator, $values) 
     886      { 
     887        // Get the values we need 
     888        $username = $values['username']; 
     889        $password = $values['password']; 
     890     
     891        // Only validate if a username and password have been supplied 
     892        if ($username && $password) 
     893        { 
     894          if (!array_key_exists($username, $this->accounts) || $this->accounts[$username] != $password) 
     895          { 
     896            $error = new sfValidatorError($validator, 'Password wrong - try again'); 
     897     
     898            // Bind the error to the password field 
     899            throw new sfValidatorErrorSchema($validator, array('password' => $error)); 
     900          } 
     901        } 
     902 
     903        // Note that we can clean some values here if we wish 
     904        return $values; 
     905      } 
     906    } 
     907 
     908After adding the class, clear your cache again (`php symfony cc`), so symfony rescans 
     909its lib folders for classes it 'knows' about. 
     910 
     911Okay, so let's look at this code section by section. Firstly, when this class is 
     912instantiated, we have to pass to the constructor an associated array of users and 
     913passwords. This is then saved in the object for later reference. Of course in a real 
     914system this would not be necessary, since we'd just query a user table in a database 
     915instead, or call some kind of authentication service. 
     916 
     917In the same way as the CRUD-generated forms, we have a setup() method, which is called 
     918by symfony automatically. This creates the necessary elements in the form (username and 
     919password) and sets up some basic validators. Most of this is fairly self-explanatory: 
     920the elements are set to have an appropriate type (input box and password box 
     921respectively) and both require some input to validate ('required' => true). 
     922 
     923The last validator, which checks the password, is worth some extra explanation. 
     924Firstly, rather than using a standard validator, it uses a "callback" validator, which 
     925allows the programmer the flexibility of defining their own validation test. Secondly, 
     926in our example we have called this validator as part of the "post validator" sequence, 
     927which is called once all the individual element validators have been executed. This is 
     928useful as at this later stage, we get access to cleaned copies of all the values in 
     929the form. 
     930 
     931We can also see from the code that the password will only be checked if the username 
     932and password have been supplied: if they are not, the form will be invalid anyway 
     933from the 'required' rules. However if we do have a username and password, the 
     934existence of the user and the correctness of the password can be checked, and a 
     935validation error thrown if either check fails. 
     936 
     937So, now we can plug the new form into our system. In the actions class of your backend 
     938security module (`/sf_sandbox/apps/backend/modules/security/actions/actions.class.php`), 
     939paste this class definition (overwriting the existing contents): 
     940 
     941    [php] 
     942    class securityActions extends sfActions 
     943    { 
     944     /** 
     945      * Executes index action 
     946      * 
     947      * @param sfRequest $request A request object 
     948      */ 
     949      public function executeIndex($request) 
     950      { 
     951        $this->form = new LoginForm(array('admin' => 'password')); 
     952     
     953        if ($request->isMethod('post')) 
     954        { 
     955          $this->form->bind($request->getParameter('login')); 
     956          if ($this->form->isValid()) 
     957          { 
     958            // Login here 
     959            $this->getUser()->setAuthenticated(true); 
     960     
     961            // Go to the post module 
     962            $this->redirect('post/index'); 
     963          } 
     964        } 
     965         
     966      } 
     967    } 
     968 
     969The action code works in much the same way as the generated actions we looked at 
     970previously. A form instance is instantiated, then if the operation is a POST *and* 
     971the form is valid then the system authorises the user and redirects them to the 
     972post page. 
     973 
     974Let's now make the post administration module the default one in our backend system. To 
     975do this, open up the `apps/backend/config/routing.yml` file and locate the 
     976'homepage' key. Change the module from 'default' to 'post'. 
    890977 
    891978The last thing to do is to set the `security` module as the default module to 
     
    899986 
    900987At that point, if you try to access the Posts management, you will have to enter 
    901 a login and a password
     988a username and a password (which is 'admin' and 'password' respectively)
    902989 
    903990![login form](/images/tutorials/first_login.gif)