Development

Changeset 11142

You must first sign up to be able to contribute.

Changeset 11142

Show
Ignore:
Timestamp:
08/26/08 00:32:22 (3 months ago)
Author:
halfer
Message:

Substantial changes to 1.1 forms code

Files:

Legend:

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

    r11082 r11142  
    141141>`/config/propel.ini` and ensure that the following lines have 7 sets of '..' in them: 
    142142> 
    143 >    propel.database.createUrl  = sqlite://./../../../../../../../data/sandbox.db 
    144 >    propel.database.url        = sqlite://./../../../../../../../data/sandbox.db 
     143>    propel.database.createUrl  = sqlite://./../../../../../../../data/sandbox.db 
     144>    propel.database.url        = sqlite://./../../../../../../../data/sandbox.db 
    145145> 
    146146>If they do not, copy the lines as they appear here, and save the file. 
     
    386386-------------------------------------- 
    387387 
    388 >**Caution** 
    389 >The following section refers to code that would appear in the 1.0 version of the 
    390 >sandbox, however symfony 1.1 uses a new Forms system. It will take a while longer 
    391 >to convert this section. Please bear with us! 
    392  
    393 When adding a comment, you can choose the `id` of the related post. 
    394 That's not very user-friendly. Let's change this, and make sure that the user 
    395 comes back to the post he was looking at after adding a comment. 
    396  
    397 First, in the `modules/post/templates/showSuccess.php` template, 
    398 add a line at the bottom: 
     388Currently we can't add comments to posts directly; if we're editing a post, we 
     389have to go to the comments editing section, create a new one, then select the post we 
     390want to comment on using the drop-down menu. It would be better if there was a link 
     391on each post editing page to go straight to the comment editing facility. 
     392 
     393So let's arrange that first. In the `modules/post/templates/showSuccess.php` template, 
     394add this line at the bottom: 
    399395 
    400396    [php] 
    401397    <?php echo link_to('Add a comment', 'comment/edit?post_id='.$blog_post->getId()) ?> 
    402398 
    403 The `link_to()` helper creates a hyperlink pointing to the `edit` action of 
    404 the `comment` module, so you can add a comment directly from the post details page. 
    405 Next, open the `modules/comment/templates/editSuccess.php` and replace the 
    406 following lines: 
    407  
    408     [php] 
    409     <tr> 
    410       <th>Post:</th> 
    411       <td><?php echo object_select_tag($comment, 'getPostId', array ( 
    412       'related_class' => 'Post', 
    413     )) ?></td> 
    414     </tr> 
    415  
    416 By: 
    417  
    418     [php] 
    419     <?php if ($sf_params->has('post_id')): ?> 
    420       <?php echo input_hidden_tag('post_id',$sf_params->get('post_id')) ?> 
    421     <?php else: ?> 
    422       <tr> 
    423         <th>Post*:</th> 
    424         <td><?php echo object_select_tag($comment, 'getPostId', array('related_class' => 'Post')) ?></td> 
    425       </tr> 
    426     <?php endif; ?> 
    427  
    428 The form in the `comment/create` page points to a `comment/update` action, which 
    429 redirects to `comment/show` when submitted (this is the default behaviour in generated CRUDs). 
    430 For the blog, that means that after adding a comment to a post, the detail of the 
    431 comment is displayed. It is better to display the post with the comments at that point. 
    432 So open the `modules/comment/actions/actions.class.php` and look for the `executeUpdate()` 
    433 method. Note that the `created_at` field is not defined by the action: symfony knows 
    434 that a field named `created_at` has to be set to the system time when a record is created. 
    435 The final redirect of the action has to be modified to point to the correct action. 
    436 Change it to: 
    437  
    438     [php] 
    439     public function executeUpdate($request) 
    440     { 
    441       if (!$request->getParameter('id')) 
     399The `link_to()` function (which we call a "helper") creates a hyperlink pointing to the 
     400`edit` action of the `comment` module, so you can add a comment directly from the post 
     401details page. At the moment, however, the comments edit page still offers a form element 
     402to select which post to relate a comment to. What we want is for that field to be 
     403replaced by a hidden field (containg the post primary key) if the comments edit page 
     404URL is called specifying that key. 
     405 
     406Before we can do that, we should say a few words about how forms work in symfony 1.1. 
     407Rather than specifying all the information about the various form elements (input 
     408boxes, tickable boxes, select menus, radio buttons and so forth) in an HTML template, 
     409some of this detail is delegated to a "form class". This encapsulates all the 
     410information required to render a basic version of an HTML form, making them (or at 
     411least a prototype version) very easy to get working quickly. 
     412 
     413As you would expect, form classes are used to create form objects. In common with 
     414standard object-orientation principles, when an object is instantiated, a constructor 
     415is called. Therefore, when we create the comments form object, we can read the blog post 
     416it is to relate to (if any) and pass that to the constructor to determine whether to 
     417swap the post menu for a hidden field. 
     418 
     419In fact, the CRUD generator we used previously to create our post and comment systems 
     420makes use of a form class to define the form required in each case. Let's open up 
     421`sf_sandbox/lib/form/base/BaseBlogCommentForm.class.php` together to see how the form 
     422is defined. The most important method is setup(), which contains (amongst other items) 
     423the following code: 
     424 
     425    [php] 
     426    public function setup() 
     427    { 
     428      $this->setWidgets(array( 
     429        'id'           => new sfWidgetFormInputHidden(), 
     430        'blog_post_id' => new sfWidgetFormPropelSelect(array('model' => 'BlogPost', 'add_empty' => true)), 
     431        'author'       => new sfWidgetFormInput(), 
     432        'email'        => new sfWidgetFormInput(), 
     433        'body'         => new sfWidgetFormTextarea(), 
     434        'created_at'   => new sfWidgetFormDateTime(), 
     435      )); 
     436 
     437      $this->setValidators(array( 
     438        'id'           => new sfValidatorPropelChoice(array('model' => 'BlogComment', 'column' => 'id', 'required' => false)), 
     439        'blog_post_id' => new sfValidatorPropelChoice(array('model' => 'BlogPost', 'column' => 'id', 'required' => false)), 
     440        'author'       => new sfValidatorString(array('max_length' => 255, 'required' => false)), 
     441        'email'        => new sfValidatorString(array('max_length' => 255, 'required' => false)), 
     442        'body'         => new sfValidatorString(array('required' => false)), 
     443        'created_at'   => new sfValidatorDateTime(array('required' => false)), 
     444      )); 
     445 
     446      // ... 
     447    } 
     448 
     449There are two things being specified here: first are the "widgets", which are the HTML 
     450elements used in the form, and second, validators, which are used to check user input 
     451against a set of correctness checks. Symfony 1.0 users will notice a change of approach 
     452here: form elements were previously defined in the template, and validators were 
     453defined in YAML files (and sometimes also programmatically in actions). 
     454 
     455Now that we have examined the base definition of the comments form, we can make some 
     456changes. Changes however are not best made in the base form, so close that file and open 
     457`sf_sandbox/lib/form/BlogCommentForm.class.php`. This inherits from 
     458BaseBlogCommentForm, which means we can take advantage of that definition and add our 
     459own modifications as required. 
     460 
     461By default, the BlogCommentForm class has an empty configure method. We want to add 
     462a constructor that accepts both the blog comment object to edit (this is a Propel 
     463database object) and a reference to the post to attach it to. Then, when the configure() 
     464method is called, we need to change the blog post element from a menu (as defined in the 
     465base class) to a hidden field containing an automatically-filled value. Lets do that 
     466now; change the code in this file to the following: 
     467 
     468    [php] 
     469    class BlogCommentForm extends BaseBlogCommentForm 
     470    { 
     471      // Place to store related post PK class-wide 
     472      private $idPost; 
     473 
     474      /** 
     475       * Constructor method for this class 
     476       */ 
     477      public function __construct($comment, $idPost) 
    442478      { 
    443         $comment = new Comment(); 
     479        $this->idPost = $idPost; 
     480        parent::__construct(); 
    444481      } 
    445       else 
     482 
     483      /** 
     484       * Configure method, called automatically by ancestor class 
     485       */ 
     486      public function configure() 
    446487      { 
    447         $comment = CommentPeer::retrieveByPk($request->getParameter('id')); 
    448         $this->forward404Unless($comment); 
     488        // If there is a specified related post, swap menu for hidden field 
     489        if ($this->idPost) 
     490        { 
     491          $this->widgetSchema[$name = 'blog_post_id'] = new sfWidgetFormInputHidden(); 
     492          $this->setDefault($name, $this->idPost); 
     493        } 
     494      }   
     495    } 
     496 
     497To use this new class, we need to find the place where this form is created, and 
     498add the post primary key as a new parameter. This is stored in 
     499`sf_sandbox/apps/frontend/modules/comment/actions/actions.class.php`. Find the code in 
     500the executeEdit method which reads thus: 
     501 
     502    [php] 
     503    $this->form = new BlogCommentForm(BlogCommentPeer::retrieveByPk($request->getParameter('id'))); 
     504 
     505Change that to the following: 
     506 
     507    [php] 
     508    $this->form = new BlogCommentForm(BlogCommentPeer::retrieveByPk($request->getParameter('id')), $request->getParameter('post_id')); 
     509 
     510After you have made these changes, you will now be able to add a comment directly to a 
     511post without having to explicitly specify the post to attach it to. 
     512 
     513Next, after adding a comment, we want the user to come back to the post it relates to, 
     514rather that remaining on the comment editing page. To accomplish this, we need to edit 
     515executeEdit again. Find the following code: 
     516 
     517    [php] 
     518    if ($request->isMethod('post')) 
     519    { 
     520      $this->form->bind($request->getParameter('blog_comment')); 
     521      if ($this->form->isValid()) 
     522      { 
     523        $blog_comment = $this->form->save(); 
     524 
     525        $this->redirect('comment/edit?id='.$blog_comment->getId()); 
    449526      } 
    450  
    451       $comment->setId($request->getParameter('id')); 
    452       $comment->setPostId($request->getParameter('post_id')); 
    453       $comment->setAuthor($request->getParameter('author')); 
    454       $comment->setEmail($request->getParameter('email')); 
    455       $comment->setBody($request->getParameter('body')); 
    456  
    457       $comment->save(); 
    458  
    459       return $this->redirect('post/show?id='.$comment->getPostId()); 
    460     } 
    461  
    462 Users can now add comments to a post and come back to the post afterwards. 
    463 You wanted a blog? You have a blog. 
    464  
    465 Find more about [actions](http://www.symfony-project.org/book/1_1/06-Inside-the-Controller-Layer). 
     527    } 
     528 
     529And change the redirect line so it reads thus: 
     530 
     531    [php] 
     532    $this->redirect('post/show?id='.$blog_comment->getBlogPostId()); 
     533     
     534This will ensure that when a comment is saved, the user is returned to the post that 
     535the comment is related to. There are two things here that are worthy of note: firstly, 
     536the save is achieved simply by calling the save method on the form object (this is 
     537possible as a result of the form being a child of sfFormPropel, which is a special 
     538symfony form class that understands database tables). Secondly, we redirect immediately 
     539after the save, so that if the page is subsequently refreshed, the user is not asked if 
     540they wish to repeat the POST action again. 
     541 
     542Okay, so that wraps up this part of the tutorial. You wanted a blog? You have a blog. 
     543Incidentally, since we've covered symfony actions a lot here, you may wish to find out 
     544more about them from 
     545[the manual](http://www.symfony-project.org/book/1_1/06-Inside-the-Controller-Layer). 
    466546 
    467547Form Validation 
    468548--------------- 
     549 
     550>**Caution** 
     551>The following part of the tutorial refers to code that would have appeared in the 1.0 
     552> version of the sandbox, and has not been updated to take account of symfony 1.1. It will 
     553>take a while longer to convert this section. Please bear with us! 
    469554 
    470555Visitors can enter comments, but what if they submit the form without any data