1

I want to create a "like/dislike" button in Ajax with Symfony 1.4.

I have these tables :

| Song | ------- n --------------------------- n ---------- | sfGuardUser |
                               |
                          | LikeSong |                 `

I've read symfony AJAX documentation but it is 1.0 documentation. 1.4 is very light. So, here is what I tried to do first.

In /app/frontend/module/likesong/_voting.php :

<?php
    if($song->hasVote())
    {
        jq_link_to_remote('I do not like', array('complete' => '[??? Update my link]', 'url' => 'likesong/notlike?id_song='.$song->getId()));
    }
    else
    {
        jq_link_to_remote('I like', array('complete' => '[??? Update my link]', 'url' => 'likesong/like?id_song='.$song->getId()));
    }
    echo ' - '.$song->getNbVote();
?>

In /app/frontend/config/routing.yml :

song_like:
  url:      /song-like/:id
  param:    { module: song, action: like }

song_notlike:
  url:      /song-not-like/:id
  param:    { module: song, action: notLike }

In /app/frontend/module/likesong/actions.class.php

public function executeLike(sfWebRequest $request)
{
  if ($request->isXmlHttpRequest())
  {                              
    if(USER HAS NOT YET VOTED)
    {
      $this->vote = new LikeSong();

      $this->vote->setSongId($this->song()->getId());
      $this->vote->setSfGuardUserId($this->getUser()->getId());
      $this->vote->save();

      return $this->renderText('notlike');
      else
      {
        // Display flash
      }
    }
 }

public function executeNotLike(sfWebRequest $request)
{
  if ($request->isXmlHttpRequest())
  {                              
     if(USER ALREADY VOTED)
     {
        // Delete form database

        return $this->renderText('like');
        else
        {
          // Display flash
        }
      }
}

When the user click, "I like this song" should be replaced by "I don't like this song".

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
fallais
  • 577
  • 1
  • 8
  • 32
  • 1
    There's nothing about this task that is "like-button" specific. It's simply an ajax call to the server, and a UI change on the client. Go learn to use basic AJAX functionality and the rest will follow. – Diodeus - James MacFarlane Oct 05 '12 at 17:28
  • I'm searching for a tutorial because I use to learn by practicing. I would not have asked it here if I had not read Ajax chapter in symfony documentation. I know my case is very simple and I know it's a database call and a UI update but I would like to create it step by step. – fallais Oct 05 '12 at 17:56
  • You know about jQuery? are you using sfDoctrineGuardPlugin? – glerendegui Oct 05 '12 at 20:16
  • 1
    Check this too: http://code.google.com/p/nsdoctrineactasratableplugin/ – j0k Oct 05 '12 at 21:54
  • Yes I know about jQuery and I use sfDoctrineGuardPlugin. Concerning the plugin, it deals with another function, rating. I think what I want to do is easier, isn't it ? – fallais Oct 06 '12 at 19:12

1 Answers1

3

First, you should not have business logic in your controller.

Your templates code is weird too - I never used jq_link_to_remote() for anything ajax, and quick searching about this function, it seems to have many troubles. You'll probably have a lot of issues solved if you go back on conception.

  1. User like or doesn't like a song should be written in your Song class. I'd write something like this :

    class Song extends BaseSong
    {
    
        public function userLike($user_id)
        {
            return in_array($user_id, $this->getLikers()->getPrimaryKeys()));
        }
    
        public function switchLike($user_id)
        {
            if ($this->userLike($user_id))
            {
                $this->getLikers()->remove($user_id);
                $this->save();
                return 0;
            }
            else
            {
                $this->getLikers()->add(Doctrine::getTable('User')->find($user_id));
                $this->save();
                return 1;
            }
        }
    }
    
  2. You should always write clean controllers which can be called with or without AJAX. The isXmlHttpRequest() function can be very powerful, but it must NOT kill the accessibility of your website. Javascript must stay optional, your links must have functional fallbacks for standard http calls. I'd start with something like this:

    public function executeIndex(sfWebRequest $request)
    {
          // Here the code for the whole song's page, which will include your _vote partial
    }
    
    public function executeSwitchLike(sfWebRequest $request)
    {
          $this->song = Doctrine::getTable('Song')->find($request->getParameter('id'));
          $this->song->switchLike($this->getUser()->getId());
          $this->redirect('song/index?id='.$request->getParameter('id'));
    }
    

Then, write your template with a simple http link for 'like' or 'don't like'. You should have the behavior of reloading the entire page, with just switching the 'like' status.

Finally, overload this link with a simple jquery.load() call, which only replace the HTML element you're aiming to replace:

$('#like_block').load('/switchLike?id=<?php echo $song->id ?>');

It will result in the whole PHP executed, which is a good way to work with AJAX, but only your specific HTML element is reloaded.

fallais
  • 577
  • 1
  • 8
  • 32
BaptN
  • 1
  • 2
  • Thanks. This button will appear many time in one page, the JS code should not be like that, should it ? – fallais Dec 13 '12 at 15:54