Search code examples
phpfrontenddoctrinecrudsymfony1

Taking advantage of Doctrine relations in frontend applications in Symfony


Let's consider the following simple schema (in Doctrine, but Propel users are welcome too):

User:
  columns:
    name: string
Article:
  columns:
    user_id: integer
    content: string
  relations:
    User:
      local: user_id
      foreign: id

Now, if you create a route for Article model and generate a module via doctrine:generate-module-for-route frontend @article_route you get a CRUD application that manages all the articles. But in frontend you would normally want to manage objects related to signed-in User, so you have to manually get the id of the User, pass id to the model and write a bunch of methods that would retrieve objects related to this User, for example:

  public function executeIndex(sfWebRequest $request)
  {
    $this->articles = Doctrine::getTable('Articles')
      ->getUserArticles(this->getUser());
  }
  public function executeShow(sfWebRequest $request)
  {
    $this->article = $this->getRoute()->getObject();

    if (!$this->article->belongsToUser($this->getUser()))
    {
      $this->redirect404();  
    }
  }  

and model:

class ArticleTable extends Doctrine_Table
{
    public function getUserArticles(sfUser $user)
    {
      $q = $this->createQuery('a')
        ->where('a.user_id = ?', $user->getId());

      return $q->execute();
    }   
}

class Article extends BaseArticle
{
  public function belongsToUser(sfUser $user)
  {
    return $this->getUserId() == $user->getId();
  }
}

This is trivial stuff and yet you have to manually write this code for each new relation. Am I missing some kind of way to take advantage of Doctrine relations? Anyways, how would you do it? Thank you.


Solution

  • I believe you should be able to do this with a custom routing class. I have never done this, but there is a tutorial in the More with Symfony book: Advanced Routing. My guess is that it should look something like this:

    class objectWithUserRoute extends sfDoctrineRoute
    {
      public function matchesUrl($url, $context = array())
      {
        if (false === $parameters = parent::matchesUrl($url, $context))
        {
          return false;
        }
    
        $parameters['user_id'] = sfContext::getInstance()->getUser()->getId();
    
        return array_merge(array('user_id' => sfContext::getInstance()->getUser()->getId()), $parameters);
      }
    
      protected function getRealVariables()
      {
        return array_merge(array('user_id'), parent::getRealVariables());
      }
    
      protected function doConvertObjectToArray($object)
      {
        $parameters = parent::doConvertObjectToArray($object);
    
        unset($parameters['user_id']);
    
        return $parameters;
      }
    }
    

    You would then need to set the routing class in routing.yml to use objectWithUserRoute. I haven't tested this, but I think it is the best way to go about solving the problem.