| 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')) |
|---|
| | 399 | The `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 |
|---|
| | 401 | details page. At the moment, however, the comments edit page still offers a form element |
|---|
| | 402 | to select which post to relate a comment to. What we want is for that field to be |
|---|
| | 403 | replaced by a hidden field (containg the post primary key) if the comments edit page |
|---|
| | 404 | URL is called specifying that key. |
|---|
| | 405 | |
|---|
| | 406 | Before we can do that, we should say a few words about how forms work in symfony 1.1. |
|---|
| | 407 | Rather than specifying all the information about the various form elements (input |
|---|
| | 408 | boxes, tickable boxes, select menus, radio buttons and so forth) in an HTML template, |
|---|
| | 409 | some of this detail is delegated to a "form class". This encapsulates all the |
|---|
| | 410 | information required to render a basic version of an HTML form, making them (or at |
|---|
| | 411 | least a prototype version) very easy to get working quickly. |
|---|
| | 412 | |
|---|
| | 413 | As you would expect, form classes are used to create form objects. In common with |
|---|
| | 414 | standard object-orientation principles, when an object is instantiated, a constructor |
|---|
| | 415 | is called. Therefore, when we create the comments form object, we can read the blog post |
|---|
| | 416 | it is to relate to (if any) and pass that to the constructor to determine whether to |
|---|
| | 417 | swap the post menu for a hidden field. |
|---|
| | 418 | |
|---|
| | 419 | In fact, the CRUD generator we used previously to create our post and comment systems |
|---|
| | 420 | makes 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 |
|---|
| | 422 | is defined. The most important method is setup(), which contains (amongst other items) |
|---|
| | 423 | the 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 | |
|---|
| | 449 | There are two things being specified here: first are the "widgets", which are the HTML |
|---|
| | 450 | elements used in the form, and second, validators, which are used to check user input |
|---|
| | 451 | against a set of correctness checks. Symfony 1.0 users will notice a change of approach |
|---|
| | 452 | here: form elements were previously defined in the template, and validators were |
|---|
| | 453 | defined in YAML files (and sometimes also programmatically in actions). |
|---|
| | 454 | |
|---|
| | 455 | Now that we have examined the base definition of the comments form, we can make some |
|---|
| | 456 | changes. 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 |
|---|
| | 458 | BaseBlogCommentForm, which means we can take advantage of that definition and add our |
|---|
| | 459 | own modifications as required. |
|---|
| | 460 | |
|---|
| | 461 | By default, the BlogCommentForm class has an empty configure method. We want to add |
|---|
| | 462 | a constructor that accepts both the blog comment object to edit (this is a Propel |
|---|
| | 463 | database object) and a reference to the post to attach it to. Then, when the configure() |
|---|
| | 464 | method is called, we need to change the blog post element from a menu (as defined in the |
|---|
| | 465 | base class) to a hidden field containing an automatically-filled value. Lets do that |
|---|
| | 466 | now; 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) |
|---|
| 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 | |
|---|
| | 497 | To use this new class, we need to find the place where this form is created, and |
|---|
| | 498 | add 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 |
|---|
| | 500 | the executeEdit method which reads thus: |
|---|
| | 501 | |
|---|
| | 502 | [php] |
|---|
| | 503 | $this->form = new BlogCommentForm(BlogCommentPeer::retrieveByPk($request->getParameter('id'))); |
|---|
| | 504 | |
|---|
| | 505 | Change that to the following: |
|---|
| | 506 | |
|---|
| | 507 | [php] |
|---|
| | 508 | $this->form = new BlogCommentForm(BlogCommentPeer::retrieveByPk($request->getParameter('id')), $request->getParameter('post_id')); |
|---|
| | 509 | |
|---|
| | 510 | After you have made these changes, you will now be able to add a comment directly to a |
|---|
| | 511 | post without having to explicitly specify the post to attach it to. |
|---|
| | 512 | |
|---|
| | 513 | Next, after adding a comment, we want the user to come back to the post it relates to, |
|---|
| | 514 | rather that remaining on the comment editing page. To accomplish this, we need to edit |
|---|
| | 515 | executeEdit 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()); |
|---|
| 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 | |
|---|
| | 529 | And change the redirect line so it reads thus: |
|---|
| | 530 | |
|---|
| | 531 | [php] |
|---|
| | 532 | $this->redirect('post/show?id='.$blog_comment->getBlogPostId()); |
|---|
| | 533 | |
|---|
| | 534 | This will ensure that when a comment is saved, the user is returned to the post that |
|---|
| | 535 | the comment is related to. There are two things here that are worthy of note: firstly, |
|---|
| | 536 | the save is achieved simply by calling the save method on the form object (this is |
|---|
| | 537 | possible as a result of the form being a child of sfFormPropel, which is a special |
|---|
| | 538 | symfony form class that understands database tables). Secondly, we redirect immediately |
|---|
| | 539 | after the save, so that if the page is subsequently refreshed, the user is not asked if |
|---|
| | 540 | they wish to repeat the POST action again. |
|---|
| | 541 | |
|---|
| | 542 | Okay, so that wraps up this part of the tutorial. You wanted a blog? You have a blog. |
|---|
| | 543 | Incidentally, since we've covered symfony actions a lot here, you may wish to find out |
|---|
| | 544 | more about them from |
|---|
| | 545 | [the manual](http://www.symfony-project.org/book/1_1/06-Inside-the-Controller-Layer). |
|---|