Code snippets for symfony 1.x

Navigation

Snippets by user Nicolas Chambrier

Auto-detect format (from URL and/or Acceptable content types)

As of Symfony 1.1, using alternative formats has become simple. However, automatic use of these formats is now disabled by default.

In the URL

Add support for sf_format in your routing factory :

/apps/myapp/config/factories.yml

all:
  routing:
    ...
    param:
      ...
      suffix: .:sf_format
 

This way, each time you go to http://mysite/module.format, the corresponding format will be detected and used.

Following URLs will automatically work and be rendered as XML : - http://mysite/module.xml # renders indexSuccess.xml.php - http://mysite/module/action.xml # renders actionSuccess.xml.php - http://mysite/module/action/param/value.xml # equals http://mysite/module/action.xml?param=value

If you want the suffix not to be mandatory (e.g. http://mysite/module/action == http://mysite/module/action.html) you'll have to add "sf_format: html" in you routing rules parameters. This is required if you want your old calls to link_to() and url_for() (who don't specifies sf_format parameter) still work.

/apps/myapp/config/routing.yml

default:
  url:   /:module/:action/*
 

myTemplate.php

link_to('hey !', 'module/action') // Produces an error !
link_to('hey !', 'module/action?sf_format=html') // Works, but hey, you'll have to add this to *all* your links :/
 

Add the default format :

/apps/myapp/config/routing.yml

default:
  url:   /:module/:action/*
  param: { sf_format: html }
 

myTemplate.php

link_to('hey !', 'module/action') // Works :)
link_to('hey !', 'module/action?sf_format=xml') // Works too, of course :)
 

Using the Accept HTTP header

We can use a filter, or plug to an even in the ProjectConfiguration class. My choice was a filter.

/apps/myapp/config/filters.yml

# insert your own filters here
detect_format:
  class: DetectFormatFilter
 

/lib/DetectFormatFilter.php

class DetectFormatFilter extends sfFilter
{
 
  public $default_formats = array(
    'html' => array('text/html', 'application/xhtml+xml'),
  );
 
  public function execute($filterChain)
  {
    $request = $this->context->getRequest();
 
    // Only if format is not already defined
    if (!$request->getRequestFormat()) {
 
      // Complete with the filter default formats
      foreach ($this->default_formats as $format => $mimeTypes) {
        foreach ($mimeTypes as $mimeType) {
          if (!$request->getFormat($mimeType)) {
            $request->setFormat($format, $mimeType);
          }
        }
      }
 
      // Detect format depending on acceptable contant types : first is prefered
      foreach ($request->getAcceptableContentTypes() as $mimeType) {
        if ($format = $request->getFormat($mimeType)) {
          $request->setRequestFormat($format);
          break;
        }
      }
 
    }
 
    // execute next filter
    $filterChain->execute();
  }
 
}
 

Take a look at the fact we automatically add "html" to the formats, attached to the two standard mime types. This could be done by adding the corresponding options to the request part of factories.yml, but I wanted this filter to be "ready-to-use" without touching other parts of configuration. "text/html" must be handled apart because as "html" is the default format, Symfony just does not handle it. So when we browse "Accept" headers, we will pass on "text/html", and just skip it because it's not configured. Without adding this part, "html" format would never been rendered, quite annoying isn't it ;)

With this configuration, you can just add the "Accept" header to say you want an XML content, and your ".xml.php" templates will then be rendered.

An interesting evolution of this filter would be to allow it to test if the format can be rendered, and if not, fall back to a default one.

Conclusion

It's quite easy to enable back auto-detection of the format. However, as Fabien says in the ticket 4920, Accept header is sometimes unreliable, and some badly configured browsers could make you think they prefer XML, which will not be the case of the user behind this browser ;)

That said, even if the URL suffix method is probably the best choice, this auto-detection method works and could be useful in some projects...

by Nicolas Chambrier on 2009-03-21, tagged accept  filter  format  routing 
(2 comments)

Automatically create symlinks to plugins' web directories

I use this small bash script to automatically detect /plugins/*/web directories and symlink them in /web folder.

This is particularly useful for SVN installations of plugins, which do not handle this for you.

#!/bin/bash
 
if [ ! -d web -o ! -d plugins ]
then
  echo "You must be in a Symfony project root"
  exit 1
fi
 
# Create links from /web directory
cd web
 
# List plugins' web directories
ls -d "../plugins/*/web" | while read plugin_web
do
  plugin=$(basename $(dirname $plugin_web))
  if [ ! -d $plugin ]
  then
    ln -f -s -v $plugin_web $plugin
  fi
done
 
# Go back to project's root
cd ..
 

You could use a "compact" version as an alias ;)

alias ln_plugins_web="cd web && ls ../plugins/*/web | while read plugin_web; do plugin=$(basename $(dirname $plugin_web)); if [ ! -d $plugin ]; then ln -f -s -v $plugin_web $plugin; fi; done; cd .."
 

Note : if your bash installation does not provide "basename" and "dirname" commands, you can add them manually with those aliases

function dirname() {
  echo "$1" | sed 's/\/[^\/]*\(\/*\)\?$//';
}
 
function basename() {
  echo "$1" | sed 's/^.*\/\([^\/]\+\)\/*$/\1/
}
by Nicolas Chambrier on 2008-03-23, tagged bash  linux  plugins  web 

Bash script to init a subversionned Symfony project

I share here the script I use to initialize a new Symfony project, as I always subversion them.

This script handles the following default scenario :

You can customize the behaviour of the script defining environment variables. This is an unusual way to do, but it has two advantages :

Here is what you can change in the default scenario :

Here is what is always done, and the really goal of this script (to simplify your life) :

Full usage :

Usage : new-symfony-project PROJECT-NAME
You can define following variables to customize the creation :
- SYMFONY_BRANCH : The Symfony branch used
- SYMFONY_REPOS : The full Symfony SVN repository URL, if set, SYMFONY_BRANCH is ignored
- SYMFONY_DATA : The path to the data folder of your Symfony installation. If not left
  empty, no SVN version will be downloaded (using local installation) and two precedent
  variables will be ignored
- TRUNK_URL : The trunk of your project (default : repos created)
- SVNADMIN_REPOS : Local root for SVN repositories
- WEB_DIR : Local root for Web directories
 

Examples :

  # Start a new project using default options
$ new-symfont-project MyProject
  # Start a new project embedding 1.1 Symfony branch
$ SYMFONY_BRANCH=1.1 new-symfont-project MyProject
  # Start a new project embedding 1.1 Symfony branch and versionned in a remote repository (already initialized)
$ SYMFONY_BRANCH=1.1 TRUNK_URL=my-svn-host/my-svn-repos/trunk new-symfont-project MyProject
  # Start a new project using system-wide install
$ SYMFONY_DATA=/usr/share/php/data new-symfont-project MyProject
 

Here is the code, you must customize the first 5 lines to fit your default environment options :

#!/bin/bash
 
DEFAULT_WEB_DIR=/home/naholyr/www
DEFAULT_SVNADMIN_REPOS=/home/naholyr/svn
DEFAULT_SYMFONY_BRANCH=1.0
 
 
PROJECT=$1
 
 
if [ "$PROJECT" = "" ]; then
  # Show help and exit
  echo "Usage : new-symfony-project PROJECT-NAME"
  echo "You can define following variables to customize the creation :"
  echo "- SYMFONY_BRANCH : The Symfony branch used (default : $DEFAULT_SYMFONY_BRANCH)"
  echo "- SYMFONY_REPOS : The full Symfony SVN repository URL, if set, SYMFONY_BRANCH is ignored"
  echo "- SYMFONY_DATA : The path to the data folder of your Symfony installation. If not left"
  echo "  empty, no SVN version will be downloaded (using local installation) and two precedent"
  echo "  variables will be ignored"
  echo "- TRUNK_URL : The trunk of your project (default : repos created)"
  echo "- SVNADMIN_REPOS : Local root for SVN repositories (default : $DEFAULT_SVNADMIN_REPOS)"
  echo "- WEB_DIR : Local root for Web directories (default : $DEFAULT_WEB_DIR)"
fi
 
 
if [ "$SVNADMIN_REPOS" = "" ]; then
  SVNADMIN_REPOS=$DEFAULT_SVNADMIN_REPOS
fi
 
if [ "$WEB_DIR" = "" ]; then
  WEB_DIR=$DEFAULT_WEB_DIR
fi
 
if [ "$SYMFONY_BRANCH" = "" ]; then
  SYMFONY_BRANCH=$DEFAULT_SYMFONY_BRANCH
fi
 
if [ "$SYMFONY_REPOS" = "" ]; then
  SYMFONY_REPOS=http://svn.symfony-project.com/branches/$SYMFONY_BRANCH
fi
 
 
if [ "$TRUNK_URL" = "" ]; then
  SVN_REPOS="file://$SVNADMIN_REPOS"
  SVN_PROJECT=$SVN_REPOS/$PROJECT
  # Create repository
  svnadmin create $SVNADMIN_REPOS/$PROJECT
  svn mkdir -m "Standard structure" $SVN_PROJECT/trunk $SVN_PROJECT/branches $SVN_PROJECT/tags
  svn mkdir -m "Create folder for Symfony" $SVN_PROJECT/trunk/lib $SVN_PROJECT/trunk/lib/vendor
  TRUNK_URL=$SVN_PROJECT/trunk
fi
 
 
# Create web folder
cd $WEB_DIR
svn co $TRUNK_URL $PROJECT
cd $PROJECT
 
if [ "$SYMFONY_DATA" = "" ]; then
  # Download Symfony
  svn propset svn:externals "symfony $SYMFONY_REPOS" lib/vendor
  svn commit -m "External link to Symfony"
  svn update
  SYMFONY_DATA=$(pwd)/lib/vendor/symfony/data
fi
 
# Create Symfony project
$SYMFONY_DATA/bin/symfony init-project $PROJECT
 
# Link to Symfony web elements
ln -s $SYMFONY_DATA/web/sf web/sf
 
# Commit the new project
svn status | grep ^? | sed "s/? *//" | xargs svn add
svn mkdir lib/model/om lib/model/map
svn propset svn:ignore "*" log
svn propset svn:ignore "*" cache
svn propset svn:ignore "*schema-transformed.xml" config
svn propset svn:ignore "lib.model.schema.sql
plugins.*.sql
sqldb.map" data/sql
svn propset svn:ignore "*" lib/model/om
svn propset svn:ignore "*" lib/model/map
svn commit -m "Project initialized"
 
# Conclusion
./symfony -V
echo "Working directory : $WEB_DIR/$PROJECT"
echo "You should be ready to go :)"
 

Note that if you work this way, intensively take advantage of Subversion and install plugins as svn:externals ;)

by Nicolas Chambrier on 2008-02-13, tagged bash  initialize  script  subversion 
(1 comment)

Work with IE conditional comments

sometimes you may need to include a stylesheet only for IE, the best effort you give, this box-model gives you headaches and there is no other solution. Same issue for JS files (but it should not happen anymore thanks to prototype).

In these cases you can simply add this in your <head> section

<head>

<?php include_http_metas() ?>
<?php include_metas() ?>

<?php include_title() ?>

<link rel="shortcut icon" href="/favicon.ico" />
<?php include_javascripts() ?>
<?php include_stylesheets() ?>

<!--[if IE 7]>
<?php echo stylesheet_tag('my-css-for-IE7') ?>
<![endif]-->
<!--[if lte IE 6]>
<?php echo stylesheet_tag('my-css-for-IE6-or-lower') ?>
<![endif]-->
</head>

I made a helper for this, so your header will look more like this :

<?php use_helper('IE') ?>
<?php use_ie_stylesheet('my-css-for-IE7', 'version=7') ?>
<?php use_ie_stylesheet('my-css-for-IE6-or-lower', 'version=6 operator=lte') ?>
...
<head>

<?php include_http_metas() ?>
<?php include_metas() ?>

<?php include_title() ?>

<link rel="shortcut icon" href="/favicon.ico" />
<?php include_javascripts() ?>
<?php include_stylesheets() ?>
<?php include_ie_metas() ?>
</head>

Code is not shorter, but it's more "symfony-like" as you don't explicitly write yourself the strange-syntax comments and don't have to remember their (strange) syntax anymore.

The ideal way would be adding a filter inspired by "lib/symfony/sfCommonFilter.class.php" so we would not even have to call the include_* functions ;)

IMPORTANT :

If you do this (using IE helper or not) to OVERRIDE some CSS rules, don't forget that Symfony automagically adds your CSS and JS files just before </head>, so your CSS will be inserted BEFORE all the CSS included via "use_stylesheet()". So you have to call

<?php include_stylesheets() ?>
<?php include_javascripts() ?>

before adding your conditional comments.

And the code of the helper, to be written in "lib/helper/IEHelper.php"

<?php
 
function get_opening_ie_conditional_comments($version = null, $operator = null) {
    return '<!--[if ' . ($version ? $operator.' ' : '') . 'IE' . ($version ? ' '.$version : '') . ']>';
}
 
function get_closing_ie_conditional_comments($version = null, $operator = 'lte') {
    return '<![endif]-->';
}
 
function get_ie_conditional_comments($version = null, $operator = null) {
    return array(
        get_opening_ie_conditional_comments($version, $operator),
        get_closing_ie_conditional_comments($version, $operator)
    );
}
 
function get_ie_metas($type = null) {
    if ($type == 'javascripts' || $type == 'stylesheets') {
        $metas = '';
        $already_seen = array();
        // Stylesheets
        $assets = sfContext::getInstance()->getResponse()->getParameter($type, array(), 'ie');
        foreach ($assets as $asset_info) {
            list($asset, $options) = $asset_info;
            $file = $type == 'stylesheets' ? stylesheet_path($asset) : javascript_path($asset);
            if (isset($already_seen[$file])) {
                continue;
            } else {
                $already_seen[$file] = true;
            }
            // IE version (default : none)
            $version = isset($options['version']) ? $options['version'] : null;
            unset($options['version']);
            // IE version comparison operator (default : "le")
            $operator = isset($options['operator']) ? $options['operator'] : null;
            unset($options['operator']);
            // get corresponding comments
            list($start_comment, $end_comment) = get_ie_conditional_comments($version, $operator);
            $include = $type == 'stylesheets' ? stylesheet_tag($file, $options) : javascript_include_tag($file, $options);
            $metas .= $start_comment . "\n" . $include . $end_comment . "\n";
        }
        return $metas;
    } elseif (is_null($type)) {
        return get_ie_metas('javascripts') . "\n" . get_ie_metas('stylesheets');
    }
}
 
function include_ie_metas() {
    echo get_ie_metas();
}
 
function use_ie_stylesheet($stylesheet, $options = array()) {
    $stylesheets = sfContext::getInstance()->getResponse()->getParameter('stylesheets', array(), 'ie');
    if (!is_array($options)) $options = sfToolkit::stringToArray($options);
    $stylesheets[] = array($stylesheet, $options);
    sfContext::getInstance()->getResponse()->setParameter('stylesheets', $stylesheets, 'ie');
}
 
function include_ie_stylesheets() {
    echo get_ie_stylesheets();
}
 
function get_ie_stylesheets() {
    return get_ie_metas('stylesheets');
}
 
function use_ie_javascript($js, $options = array()) {
    $javascripts = sfContext::getInstance()->getResponse()->getParameter('javascripts', array(), 'ie');
    if (!is_array($options)) $options = sfToolkit::stringToArray($options);
    $javascripts[] = array($javascript, $options);
    sfContext::getInstance()->getResponse()->setParameter('javascripts', $javascripts, 'ie');
}
 
function include_ie_javascripts() {
    echo get_ie_javascripts();
}
 
function get_ie_javascripts() {
    return get_ie_metas('javascripts');
}
by Nicolas Chambrier on 2007-07-27, tagged css  helper  ie  view 
(3 comments)