Code snippets for symfony 1.x

Navigation

Snippets by user Olivier Verdier

Install symfony from subversion

You may disregard this snippet if you are working with a recent version of symfony. Installing symfony is now very straightforward, even from subversion.

The problem

I had to install symfony via subversion several times and it is a pain because the paths to several key files are to be manually changed in the files coming from the svn install. I prefer the subversion install over the pear beta install because i often have to fix bugs and patch things here and there in the symfony code.

The solution

1. svn checkout the symfony code somewhere on your server

2. Choose two distinct folder accessible from php where you will put the data and lib folders. Let us call those folders «DATA» and «LIB».

3. Make symbolic links of the data and lib directories inside those two folders. The following command is to be issued from the freshly checked out symfony folder:

ln -s data «DATA»/symfony
ln -s lib «LIB»/symfony

4. Put the following file inside the bin folder:

#!/bin/sh
 
PEAR_DIR= «PAKE» #put here the path to pake.php
DATA_DIR= «DATA» # here is the path to the alias of the symfony data directory
SYMFONY_DIR= «LIB» # here is the path to the alias of the symfony lib directory 
SVN_VERSION=svn # this does not matter
 
sed -i '' -e "s#@PEAR-DIR@#${SYMFONY_DIR}/bin#g" symfony.sh
#sed -i '' -e "s#@PEAR-DIR@#${PEAR_DIR}#g" symfony.bat
sed -i '' -e "s#@PEAR-DIR@#${PEAR_DIR}#g" -e "s#@DATA-DIR@#${DATA_DIR}#g" -e "s#@SYMFONY-VERSION@#${SVN_VERSION}#g"  symfony.php
sed -i '' -e "s#@PEAR-DIR@#${PEAR_DIR}#g" -e "s#@DATA-DIR@#${DATA_DIR}#g" -e "s#@SYMFONY-VERSION@#${SVN_VERSION}#g" ../../lib/pear.php

5. Execute that file. You should be ready to use symfony

Additional remarks

by Olivier Verdier on 2006-08-24, tagged installation  svn 
(2 comments)

Get the module, action and parameters from a url

The problem

You want to get the module, action and parameters associated to a given url, pretty much as the routing system does.

The solution

First you will have to remove the part of the url which is not symfony specific. That part typically looks like yoursite.com/path/to/symfony. Once you've done that execute the following code:

$r = new sfRouting();
$r->setRoutes(sfRouting::getInstance()->getRoutes());
$params = $r->parse($myUrl);
$module = $params['module'];
$action = $params['action'];

Now the module and action associated to this url are as above in $module and $action and the parameters are the remaining elements of the array $params.

by Olivier Verdier on 2006-07-21, tagged action  module  parameters  routing  url 
(12 comments)

Customise propel getters and setters

The problem

The problem is the following. Assume that you have two tables, say Book and Author. Each book has one author so the getter method to get the author of a book is getAuthor().

Now assume that you modify your model to have a second author. You have two references from the Book table to the Author table. Now your code doesn't work anymore because the propel method names have changed! The propel method names are now pretty cumbersome. They look like getAuthorReferencedByAuthor for the first author and getAuthorReferencedBySecondAuthor for the second one. Bleh!

The solution

Here is a very simple solution to customise all the getters and setters of propel. The idea is to give any name you want in the schema.xml file in the following manner (in the book table for instance):

<foreign-key foreignTable="author" localName="AsFirstAuthorBook" foreignName="Author">
  <reference local="author_id" foreign="id"/>
</foreign-key>
<foreign-key foreignTable="author" localName="AsSecondAuthorBook" foreignName="SecondAuthor">
  <reference local="second_author_id" foreign="id"/>
</foreign-key>

Now with the method i suggest propel will generate getters like getAuthor (for the main author) and getSecondAuthor which seems perfectly logical. To get a list of books that an author wrote as second author you would use getAsSecondAuthorBooks which is not so bad.

For this to work you'll have to add one file in the symfony directory. Put the following in the symfony/addon/propel/builder directory under the name of MyObjectBuilder.php.

<?php
require_once 'symfony/addon/propel/builder/SfObjectBuilder.php';
 
class MyObjectBuilder extends sfObjectBuilder
{
    public function getFKPhpNameAffix(ForeignKey $fk, $plural = false)
    {
    $fkName = $fk->getAttribute('foreignName');
    return $fkName . ($plural ? 's' : '');
    }
 
    public function getRefFKPhpNameAffix(ForeignKey $fk, $plural = false)
    {
        $fkName = $fk->getAttribute('localName');
        return $fkName . ($plural ? 's' : '');
    }
 
}

Now if you want to use that customising feature in a project you only need to change propel.ini on the line in front of propel.builder.object.class and replace SfObjectBuilder with MyObjectBuilder and you are done. That project will use the customising feature. Other projects will use the usual propel naming conventions.

Additional remarks

by Olivier Verdier on 2006-07-17, tagged foreignkey  getter  propel  setter 

How to get a propel pager working with a custom SQL query

The problem

Let us take the example of snipeet! :-) The function that selects snippets by tags is a custom SQL query. When filtering by tags there is no pager anymore.

Why is that? The problem is that sfPropelPaginate only works with a Criteria object, not with a raw query. As a result you cannot have pagination with raw SQL query which is a rather severe limitation.

The solution

The solution is to first encode the parameter of the query in the criteria (that's the tricky bit) and then to set up a custom peer method.

As i said earlier we will take a simplified version of snipeet as an example. We assume that we have a function that creates a sql statement from a list of tags.

Here we go about setting the dummy criteria that encodes an array of tags:

getCriteriaFromTags($tags)
{
  $c = new Criteria();
  for ($i = 1; $i <= count($tags); ++$i)
  {
    $prefix = 't'.$i;
    // note that that SnippetTagPeer::TABLE_NAME  SnippetTagPeer::NAME are in fact arbitrary
    // any other table and table.column combination would work as well
    $c->addAlias($prefix.SnippetTagPeer::TABLE_NAME, SnippetTagPeer::TABLE_NAME);
    $c->add($prefix.SnippetTagPeer::NAME, $tags[$i-1]);
  }
  return $c;
}

Now you can initialise a sfPropelPaginate object as usual with that Criteria object. When that is done you tell it to use your custom peer method:

$pager->setPeerMethod('doSelectByTags');

Next you code the doSelectByTags method that performs the actual selection from a criteria object:

function doSelectByTags($c)
{
  // first you fetch the tags from the query
  $tags = array();
  foreach($c->keys() as $key)
    $tags[] = $c->get($key);
 
  // here comes your custom sql query 
  // it basically creates a $statement variable from the $tags array
  // .....
 
  // now you have to add the limit and offset:
 $statement->setLimit($c->getLimit());
 $statement->setOffset($c->getOffset());
 // and the rest of the code is as usual
}
by Olivier Verdier on 2006-05-25, tagged custom  pager  pagination  propel  query  raw  sql 

How to use the admin generator along with propel inheritance

The problem

Say that you have a model object Son that inherits from the model object Father, via propel inheritance. You want to create an admin generator interface for the object Son. The problem is that propel does not generate a SonPeer class, so you'll have to call:

symfony propel-init-admin Father

but the list function will list all the Father objects instead of the Son objects only. Same problem with the create function that will create a Father object, not a Son object.

The solution

Here is a solution. You will have to overload getFatherOrCreate and addFiltersCriteria:

// add this in the actions.class.php of your admin module
  protected function addFiltersCriteria(&$c)
  {
    $c->add(FatherPeer::CLASS_KEY, FatherPeer::CLASSKEY_SON);
    parent::addFiltersCriteria($c);
  }  
 
  protected function getFatherOrCreate ($id = 'id')
  {
    $son = parent::getFatherOrCreate($id);
 
    if ($son->isNew()) // if it is a new one then we create a Son object
      $son = new Son();
   else
     $this->redirect404Unless($son->isSon()); // we check that we really got a son object
 
   return $son;
  }

Now everything will work as if you were using the Son object.

by Olivier Verdier on 2006-05-25, tagged admin  generator  inheritance  propel 
(2 comments)

Simulate a 'owner' security credential

The Problem

Let's say your site manages users who can create posts. You want to restrict the edition of a post to the administrator or the owner of the post. You would therefore like to have a security.yml file that looks like:

edit:
  is_secure:on
  credentials: [[administrator owner]]

but the problem is that the credentials are given to the user statically upon login.

The solution

The solution is therefore to give or take back credentials dynamically. As of now, the only way to achieve that is to “hijack” the getCredential action of the sfAction class.

Write the following in the action file of your post module:

// to put in the actions.class.php file
function getCredential()
  {
    $this->post = $this->_retrievePost(); // retrieving the object based on the request parameters
    if ($this->getUser()->isOwnerOf($this->post))
      $this->getUser()->addCredential('owner');
    else
      $this->getUser()->removeCredential('owner');
 
    // the hijack is over, let the normal flow continue:
    return parent::getCredential();
  }

Of course you will have to write the sfUser::isOwnerOf function, which depends on your data structure. You will also have to write the _retrievePost function that returns null if no post have been found. Watch out for the isOwnerOf function: it has to return true if the post is new, otherwise users will never be able to create new posts.

Now the owner of a post can edit his post but not the others ones, except if he has administrator credentials. Everything is set up in the security.yml file. For example it is now trivial to add the same security check for the delete method.

by Olivier Verdier on 2006-05-25, tagged credentials  dynamiccredentials  owner  security 
(10 comments)