Code snippets for symfony 1.x

Navigation

Refine Tags

Snippets tagged "filter"

Forward in filters

The regular use of forward is causing double content rendering and it took me a while to find the proper solution so here it is:

<?php
 
class SiteFilter extends sfFilter
{
 
  public function execute($filterChain)
  {
    $context = $this->getContext();
 
    if($this->isFirstCall()){
      if(true){ // put some condition in here
        $context->getController()->forward('module', 'action');
        throw new sfStopException(); // the solution
      }
    }
 
    $filterChain->execute();
  }
}
 

As you can see sfStopException() is the key.

by rozwell on 2012-06-08, tagged content  double  filter  filters  forward  rendering 

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)

Javascript loading optimalization

Hi!

I am using a lot of hand written jscript codes due to the required behavior not found in any javascript framework. I've created my Objects in distinct files, although some of them are quite small ( <1kb ). So to make it fast, I've written a filter class that actually parses the files found in the /js directories having *.js suffix. It does not only parse them together but removes whitespaces, comments, indentations so the sum filesize gets a bit compressed. The filter also includes the final file into the response so there is no need to include any *.js ( inside /js ) in a template.

<?php
 
    class JavascriptParser extends sfFilter
    {
      public function execute($filterChain)
      {
        $filterChain->execute();
 
 
        $fp = fopen( "js/compiled.js", "w" );
 
        JavascriptParser::getJSFiles( "js", $fp );
 
        fclose ( $fp );
 
        $response = $this->getContext()->getResponse();
        $content = $response->getContent();
        if (false !== ($pos = strpos($content, '</head>')))
        {
          $html = "<script type='text/javascript' src='/js/compiled.js'></script>";
 
          if ($html)
          {
            $response->setContent(substr($content, 0, $pos).$html.substr($content, $pos));
          }
        }
 
      }
 
      public function getJSFiles( $dir, &$fp )
      {
        $hDir = opendir( $dir );
        while( ( $filename = readdir( $hDir ) ) !== false )
        {
            if ( is_dir( $filename ) )
            {
            }
            else if ( is_dir( $dir."/".$filename ) )
                JavascriptParser::getJSFiles( $dir."/".$filename, $fp );                
            else if ( strpos( $filename, ".js" ) !== false && $filename != "compiled.js" )
            {
                $tmpFile = fopen( $dir."/".$filename, "r" );
                $data = fread( $tmpFile, filesize( $dir."/".$filename ) );
 
                $data = preg_replace( "'\/\*.*?\*\/'si", "", $data  );
                $data = preg_replace( "'//.*?\n'si", "", $data );
                $data = preg_replace( "'[ \t]+'", " ", $data );
 
                fwrite( $fp, " \n".$data );
                fclose( $tmpFile );
            }
        }
        closedir( $hDir );  
      }
    }
 
?>
 

The same should be done with *.css files, since they are even smaller and loading many small files takes much more time then loading one big.

Best Regards

by Kormany Gabor on 2008-04-13, tagged filter  javascript 
(1 comment)

Disable fillin filter after validation success

When using fillin filter, after validation success (all form fields are ok) if you want display page without form, fillin filter is still enabled and throws exception "Exception - No form found in this page". Use this snippet to prevent it.

in sfFillInFormFilter.class.php file

  public function execute($filterChain)
  {
    // execute next filter
    $filterChain->execute();
 
    $context  = $this->getContext();
    $response = $context->getResponse();
    $request  = $context->getRequest();
 
    // add these two lines
    if(! $request->hasErrors())
        return;
 
by jarecky on 2007-11-20, tagged fillin  filter  form  validation 

Filtering with rows from another table

By default, the admin generator allows to filter the data with the rows from the table currently listed.

Here's how to extend this to data from other linked tables.

We'll consider the following example : a table "command" linked to a table "user". This very simple schema.xml shows the relation between these two tables :

<table name="buyer" phpName="BtqBuyer" >
  <column name="buyer_id" type="BIGINT" required="true" primaryKey="true"/>
  <column name="buyer_name" type="VARCHAR" size="255" required="true"/>
</table>
 
<table name="command" phpName="BtqCommand" >
  <column name="com_id" type="BIGINT" required="true" primaryKey="true"/>
  <column name="com_ref" type="VARCHAR" size="6" required="true"/>
  <column name="com_buyer_id" type="BIGINT" required="true"/>
  <foreign-key foreignTable="buyer" onDelete="" onUpdate="">
    <reference local="com_buyer_id" foreign="buyer_id"/>
  </foreign-key>
</table>

In the file generator.yml, add a partial in the filters parameter to print our specific filter :

filters: [com_ref, _btq_buyer]

The source code for the partial _btq_buyer.php (located in the templates directory) is :

<?php echo input_tag('filters[buyer]', isset($filters['buyer']) ? $filters['buyer'] : '') ?>

Now we have to add our specific filter in the filter process. To do this, we extend the addFiltersCriteria from the admin generator. This is done in the file actions.class.php by adding :

protected function addFiltersCriteria (&$c)
{
  if (isset($this->filters['buyer']) && $this->filters['buyer'] != '')
  {
    $c->add(BtqBuyerPeer::BUYER_NAME, strtr($this->filters['buyer'], '*', '%'), Criteria::LIKE);
    $c->addJoin(BtqBuyerPeer::BUYER_ID, BtqCommandPeer::COM_BUYER_ID);
  }
}

As you can see, we've even allowed the use of wildcard in our filter. Nice ;)

by marc Hugon on 2006-06-21, tagged admin  filter  generator 
(5 comments)

Populate Object Filter

If you're like me, you hate having to populate your objects (from the model) with parameters from the request. Wouldn't it be nice to specify a little nugget of configuration, and then never worry about it again? Consider the following, which is tedious and boring:

Old Example

class mymoduleActions extends sfActions
{
  ...
 
  public function executeUpdate()
  {
    $employee = new Employee();
    $employee->setFirstName($this->getRequestParameter('first_name'));
    $employee->setLastName($this->getRequestParameter('last_name'));
    $employee->setSsn($this->getRequestParameter('ssn'));
    $employee->setDob($this->getRequestParameter('dob'));
    ...
    $this->employee = $employee;
  }
}

And the list goes on. Imagine, however, that you have the following in your module filters.yml file:

myPopulateObjectFilter:
  class: myPopulateObjectFilter
  param:
    use_database:  on
    model_object:  employee
    model_class:   Employee
    exclude:       [ssn]
    defaults:
      first_name:  example default name

If you had a filter that automatically created an object, and based upon the previous yaml, populated that object with data from the request (after a user hit submit on a form containing this information), then subsequently set that object for retrieval as a request attribute, you'd be a pretty happy person right?

New Example

class mymoduleActions extends sfActions
{
  ...
 
  public function executeUpdate()
  {
    $this->employee = $this->getRequest()->getAttribute('employee');
    ...
  }
}

Of course, you'd be free to do whatever you want with the results of the prepopulated object.

A great application would be an alternative to the fillin form filter. If you have your template set up to populate inputs from an object from the model, this filter can populate that object for you, and you can simply pass it on to the template.

The Code

<?php
 
/**
 * myPopulateObjectFilter
 * Uses configuration in filters.yml to create an object,
 * populate it with data from the request,
 * and set a request attribute with the result.
 *
 * @author  Stephen Riesenberg
 */
class myPopulateObjectFilter extends sfFilter
{
    protected
        $defaults       = null,
        $controller     = null,
        $request        = null;
 
    public function initialize($context, $parameters = array())
    {
        parent::initialize($context, $parameters);
 
        // get defaults from filters.yml (if any) and create new parameter holder to store them
        $this->defaults = new sfParameterHolder();
        $this->defaults->add($this->getParameter('defaults', array()));
 
        // get controller and request
        $this->controller   = $this->getContext()->getController();
        $this->request      = $this->getContext()->getRequest();
    }
 
    public function execute($filterChain)
    {
        // get request variable list
        $vars = $this->request->getParameterHolder()->getNames();
        $exclude = array_merge($this->getParameter('exclude', array()), array('module', 'action'));
        $vars = array_diff($vars, $exclude);
 
        $funcs = array();
        foreach ($vars as $var)
        {
            $funcs[$var] = 'set'.ucfirst(sfInflector::camelize($var));
        }
 
        // get model_object and model_class
        $object = $this->getParameter('model_object');
        $class = $this->getParameter('model_class');
 
        // fetch from the database or create the object to fill in
        if ($this->getParameter('use_database', false) && null !== ($id = $this->request->getParameter('id')))
        {
            $peer_class = $class . "Peer";
            $record = $peer_class::retrieveByPk($id);
        }
        else
        {
            $record = new $class();
        }
 
        // something like: array('id' => 'setId', 'my_field' => 'setMyField', ...)
        foreach ($funcs as $var => $func)
        {
            if (is_callable(array($record, $func)))
            {
                $record->$func($this->getValue($var));
            }
        }
 
        // set the updated record into a request attribute
        $this->request->setAttribute($object, $record);
 
        // execute next filter
        $filterChain->execute();
    }
 
    protected function getDefault($var)
    {
        return $this->defaults->get($var);
    }
 
    protected function getValue($var)
    {
        return $this->request->getParameter($var) != '' ? $this->request->getParameter($var) : $this->getDefault($var);
    }
}

NOTE: Place this in a file called myPopulateObjectFilter.class.php in the myproject/apps/myapp/lib/ directory.

by Stephen Riesenberg on 2007-01-06, tagged filter  model  object 
(2 comments)

Sending application/xhtml+xml

All web browsers support the 'text/html' content type. However, since the introduction of XHTML, a new content type has been available that brings the benefits of XML, which is 'application/xhtml+xml' (more info, why do this)

Although not all browsers support 'application/xhtml+xml', fortunately, the designers of the HTTP protocol provided a good solution: the browser tells the server what content types it can accept and which it prefers. So the server is able to choose which content type to serve.

Firefox is one of the browsers that supports 'application/xhtml+xml' and with the new Firefox3 comes incremental rendering, which means that it has fixed the earlier disadvantage in Firefox2 that 'text/html' was faster to render (more info).

The most noticeable advantage of XHTML rendering is that errors are displayed by the browser, meaning that it's easier to identify and fix layout problems caused by muddled content. If your content is ill-formed, Firefox displays a yellow error page indicating where the error was located. This can be a most useful development tool.

So how can we serve 'application/xhtml+xml' content?

  1. Your content has to be written to be XHTML instead of HTML (the W3C website provides helpful information in explaining how to do this). Your pages must include the correct DOCTYPE (how to do this).

  2. Your application has to change the content-type header depending on whether the browser can accept XHTML or not. Obviously, it's useless to send 'application/xhtml+xml' to older browsers that don't support it.

This snippet helps with the second issue. I have written a filter (shown below) to change the response headers depending on what each browser is able to handle.

Create a new class in your lib folder called XHTMLFilter.class.php thus:

<?php
 
class XHTMLFilter extends sfFilter
{
  public function execute ($filterChain)
  {
    $applyIn = $this->getParameter('environments', array('dev'));
    if (!is_array($applyIn))
    {
      throw new sfException("XHTMLFilter param: not an array ".print_r($applyIn, true));
    }
 
    if (in_array( SF_ENVIRONMENT, $applyIn ))
    {
      $accept = $this->context->getRequest()->getAcceptableContentTypes();
      if (in_array('application/xhtml+xml', $accept))
      {
        $this->context->getResponse()->setContentType( 'application/xhtml+xml' );
      }
    }
 
    $filterChain->execute();
  }
}
 

Now add it to your app/config/filters.yml:

rendering: ~
web_debug: ~
security:  ~
 
XHTMLFilter:
  class: XHTMLFilter
  param:
    environments:    [ dev ]
 
cache:     ~
common:    ~
flash:     ~
execution: ~
 

There is one configuration parameter (environments) that contains an array of the environments to which the filter will apply. By default, only the 'dev' environment is included but you can add any or all of your environments here.

by Rick Beton on 2008-05-25, tagged contenttype  filter  header  xhtml  xml 
(2 comments)

Environment-dependent layouts (themes)

A possible way to implement themes in symfony is to use a different layout depending on the environment that is active. Although that's obviously not the original purpose of symfony's environment model, this solution offers great flexibility which is actually superior to regular themes. It is superior because you may have your page completely rearranged and layouted differently without changing one single line of code in your actions.

Let's say we have a website which shall be displayed nicely on mobile devices as well. For this purpose we create two environments: 'frontend' and 'mobile' (names are arbitrary).

We add the following filter class to the /lib directory of our application:

class myEnvironmentLayoutFilter extends sfFilter {
 
    public function execute($filterChain) {
 
        switch (SF_ENVIRONMENT) {
            case 'frontend':
                $this->getContext()->getController()->getActionStack()->getFirstEntry()->getActionInstance()->setLayout('layout_frontend');
                break;
            case 'mobile':
                $this->getContext()->getController()->getActionStack()->getFirstEntry()->getActionInstance()->setLayout('layout_mobile');
                break;
        }
 
        $filterChain->execute();
 
        switch (SF_ENVIRONMENT) {
            case 'frontend':
                $this->getContext()->getResponse()->addStyleSheet('frontend');
                break;
            case 'mobile':
                $this->getContext()->getResponse()->addStyleSheet('mobile');
                break;
        }
 
    }
}

Note the environment-dependent style sheets.

And in filters.yml:

# generally, you will want to insert your own filters here
 
myEnvironmentLayoutFilter:
  class: myEnvironmentLayoutFilter

The advantages should be obvious: performance, as no filesystem access is necessary (no need to check for certain directories or template files). flexibility and expandability - features can be easily added to just one theme/layout, components can be arranged as needed, feature-stripped versions of a website may be realized, too. Less redundant template code. Easy to maintain.

by dante on 2007-06-14, tagged environment  filter  layout  themes 

Automatic Javascript Include

If you're like me and prefer to code your own Unobtusive Javascript, you'll probably find this snippet handy. It will search through your project's /web/js folder for any .js files that match the current Symfony action.

For example, the snippet/new action would look for /web/js/snippet/snippet.js and /web/js/snippet/new.js, and add them to the response if either exist.

You can also define an optional subfolder within /web/js where you want the filter to look (i.e. "backend").

The Code

<?php
 
/**
 * Looks for Javascript files based on current action/module and adds them to
 * the current response object.
 * 
 * Add this filter to the end of your filter chain in filters.yml, after the 
 * execution filter:
 * 
 * <code>
 *     rendering: ~
 *     web_debug: ~
 *     security:  ~
 *     
 *     # generally, you will want to insert your own filters here
 *     
 *     cache:     ~
 *     common:    ~
 *     flash:     ~
 *     execution: ~
 *     
 *     auto_javascript_include:
 *       class: myAutoJavascriptIncludeFilter
 * </code>
 *
 * @package     Automatic Javascript Include (AJI)
 * @subpackage  filter
 * @author      Kris Wallsmith <kris [dot] wallsmith [at] gmail [dot] com>
 * @version     SVN: $Id$
 * @copyright   Have at it ...
 */
class myAutoJavascriptIncludeFilter extends sfFilter
{
    /**
     * Include external Javascript files based on last action called.
     * 
     * This kicks in on the way back down the filter chain, so we're sure to
     * catch the last action. Looks through the web/js folder for files that
     * match the naming syntax and adds them to the response. 
     * 
     * You can specify a subfolder of the web/js folder for the filter to 
     * search in app.yml.
     * 
     * @author  Kris Wallsmith <kris [dot] wallsmith [at] gmail [dot] com>
     * @see     sfConfig::get('app_aji_subfolder')
     * @param   sfFilterChain $filterChain
     */
    public function execute($filterChain)
    {
        $filterChain->execute();
 
        $sf_context  = sfContext::getInstance();
        $sf_response = $sf_context->getResponse();
        $sf_web_dir  = sfConfig::get('sf_web_dir');
 
        $module = $sf_context->getModuleName();
        $action = $sf_context->getActionName();
 
        $sub_folder = sfConfig::get('app_aji_subfolder');
        if($sub_folder && $sub_folder{0} != '/') $sub_folder = '/' . $sub_folder;
 
        $fmt = '/js%s/%s/%s.js';
 
        $mod_mod_js = sprintf($fmt, $sub_folder, $module, $module);
        $mod_act_js = sprintf($fmt, $sub_folder, $module, $action);
 
        if(file_exists($sf_web_dir . $mod_mod_js)) $sf_response->addJavascript($mod_mod_js);
        if(file_exists($sf_web_dir . $mod_act_js)) $sf_response->addJavascript($mod_act_js);
    }
}
 
?>
by Kris Wallsmith on 2007-04-24, tagged filter  javascript 
(6 comments)

Complimentary Filters

A common modification to a CRUD module is filtering records on the list page. There are probably several nice ways to do this, including drop-downs, forms, etc. depending on the situation. While some of these approaches are nice, they are often not minimal... We like minimal!

Discussion

Say you have a book table, with a related author and genre. You want to filter the list of books by those related tables with links, paginate your list, and make it sortable via links in the header of the table.

To do this traditionally, you'll need lots of nasty little if statements in your template code, to check for all of these parameters (author_id, genre_id, title, num_pages, page, etc.) and combine them all together into a meaningful uri for your link_to function (something like "book/list?author_id=10&genre_id=5&sort=title&page=5"). We still want that link in the end, but generating it... How about a better way!

Followup Example

<!-- listSuccess.php -->
<?php use_helpers('Filter', 'Pagination') ?>
<h2>Books</h2>
<h3>Filter By</h3>
<table class="filters">
  <tbody>
    <tr>
      <th>Author:</th>
      <td><?php echo filter_navigation(objects_for_filter($authors), 'author_id', $author_id) ?></td>
    </tr>
    <tr>
      <th>Genre:</th>
      <td><?php echo filter_navigation(objects_for_filter($genres), 'genre_id', $genre_id) ?></td>
    </tr>
  </tbody>
</table>
<hr />
<table class="list">
  <thead>
    <tr>
      <th><?php link_to_unless($sort == 'id', 'Id', filter_url('sort', 'id')) ?></th>
      <th><?php link_to_unless($sort == 'title', 'Title', filter_url('sort', 'title')) ?></th>
      <th><?php link_to_unless($sort == 'author_id', 'Author', filter_url('sort', 'author_id')) ?></th>
      <th><?php link_to_unless($sort == 'genre_id', 'Genre', filter_url('sort', 'genre_id')) ?></th>
    </tr>
  </thead>
  <tbody>
    <?php foreach ($pager->getResults() as $book): ?>
      <tr>
        <td><?php echo $book->getId() ?></td>
        <td><?php echo $book->getTitle() ?></td>
        <td><?php echo $book->getAuthor()->getName() ?></td>
        <td><?php echo $book->getGenre()->getName() ?></td>
      </tr>
    <?php endforeach; ?>
  </tbody>
</table>
<?php echo pager_navigation($pager) ?>

NOTE: pager_navigation is covered at http://www.symfony-project.com/snippets/snippet/4, but needs to guess the uri, which is done at http://www.symfony-project.com/snippets/snippet/59.

NOTE: filter_navigation returns an unordered list, so you'll need css to display the <li> tags inline, and remove <ul> padding, margin, etc.

I'll leave it as an exercise to the reader to create the controller (action) code for this template, but it should be obvious.

The Code

<?php
 
/**
 * Generate a url using the current internal uri, but replaces a param with a new value.
 * If the param is not in the current uri's query string, it is added instead.
 *
 * This is useful for a page that uses several filters to record sets,
 * and needs all the filters to work together, instead of blasting each
 * other away when a new link is clicked.
 *
 * <strong>Examples:</strong>
 * <code>
 *  // with current uri => mymodule/myaction?author=10&genre=3
 *
 *  echo link_to('new author', filter_url('author', 5));
 *  // uri when clicked => mymodule/myaction?author=5&genre=3
 *
 *  echo link_to('new genre', filter_url('genre', 1));
 *  // uri when clicked => mymodule/myaction?author=10&genre=1
 *
 *  // with current uri => mymodule/myaction
 *
 *  echo link_to('an author', filter_url('author', 10));
 *  // uri when clicked => mymodule/myaction?author=10
 * </code>
 *
 * @param string the name of the parameter to replace
 * @param string the value to replace the current value with
 * @param boolean use route name
 * @return string the url with the parameter replaced
 * @see link_to
 */
function filter_url($param, $new_value, $with_route_name = false)
{
    // fetch params from query string
    $params = _get_params(_get_query_string());
 
    // replace param with new value
    $params[$param] = $new_value;
 
    return _get_uri($with_route_name) . '?' . _build_query_string($params);
}
 
/**
 * Removes a parameter from the current uri and returns the resulting url.
 *
 * @see filter_url
 */
function remove_filter_url($param, $with_route_name = false)
{
    // fetch params from query string
    $params = _get_params(_get_query_string());
 
    // remove param
    unset($params[$param]);
 
    return _get_uri($with_route_name) . '?' . _build_query_string($params);
}
 
/**
 * Generates an unordered list of links to filter the current record set by.
 * Multiple sets of filter_navigation links will work together, using the current uri.
 *
 * <strong>Examples:</strong>
 * <code>
 *  echo filter_navigation(array(10=>'Jones', 12=>'Smith, J.', 13=>'Darby'), 'author_id', 13);
 *  echo filter_navigation(objects_for_filter($authors), 'author_id', 13);
 * </code>
 *
 * @param array list of key=>value pairs of ids and strings
 * @param string the name of the parameter for this filter
 * @param string the selected id (or null, if none selected)
 * @param string the text to use for the "all" link
 * @see filter_url
 */
function filter_navigation($list, $param, $selected = null, $all_text = 'All')
{
    $html = '';
 
    $html .= content_tag('li', link_to_unless($selected === null, $all_text, remove_filter_url($param)));
    foreach ($list as $key => $value)
    {
        $html .= content_tag('li', link_to_unless($selected == $key, $value, filter_url($param, $key)));
    }
 
    return content_tag('ul', $html);
}
 
/**
 * Generates a simple list from a record set of propel objects.
 * Expects a getId function and a toString function.
 *
 * @param array objects to be converted to a list
 * @see filter_navigation
 */
function objects_for_filter($objects)
{
    $list = array();
 
    foreach ($objects as $object)
    {
        $list[$object->getId()] = $object->toString();
    }
 
    return $list;
}
 
function _get_uri($with_route_name = false)
{
    $internal_uri = sfRouting::getInstance()->getCurrentInternalUri($with_route_name);
    $ar = explode('?', $internal_uri);
 
    return ($with_route_name ? '@' : '') . $ar[0];
}
 
function _get_query_string()
{
    $internal_uri = sfRouting::getInstance()->getCurrentInternalUri();
    $ar = explode('?', $internal_uri);
 
    return isset($ar[1]) ? $ar[1] : '';
}
 
function _get_params($query_string)
{
    // parse query string into associative array
    $params = array();
    if ($query_string != '')
    {
        foreach (explode('&', $query_string) as $kvpair)
        {
            list($key, $value) = explode('=', $kvpair);
            $params[$key] = $value;
        }
    }
 
    return $params;
}
 
function _build_query_string($params)
{
    // build list of key=value strings
    $ar = array();
    foreach ($params as $key => $value)
    {
        $ar[] = $key . '=' . $value;
    }
 
    return implode('&', $ar);
}

NOTE: Place this in apps/myapp/lib/helper/FilterHelper.php.

Additional Usage Examples

<?php echo link_to('new author', filter_url('author_id', 10)) ?>
<?php echo url_for(filter_url('author_id', 10)) ?>
<?php echo link_to_if($condition, 'new author',  filter_url('author_id', 10)) ?>
<?php echo link_to_unless($condition, 'new author',  filter_url('author_id', 10)) ?>
<?php echo button_to('new author',  filter_url('author_id', 10)) ?>

Additional Helpers

<?php
 
/**
 * Shortcut combining link_to and filter_url into single function.
 *
 * @see link_to
 * @see filter_url
 */
function link_to_filter($name, $param, $new_value, $options = array())
{
    return link_to($name, filter_url($param, $new_value), $options);
}
 
/**
 * Shortcut combining url_for and filter_url into single function.
 *
 * @see url_for
 * @see filter_url
 */
function filter_url_for($param, $new_value)
{
    return url_for(filter_url($param, $new_value));
}
 
/**
 * Shortcut combining link_to_if and filter_url into single function.
 *
 * @see link_to_if
 * @see filter_url
 */
function link_to_filter_if($condition, $name, $param, $new_value, $options = array())
{
    return link_to_if($condition, $name, filter_url($param, $new_value), $options);
}
 
/**
 * Shortcut combining link_to_unless and filter_url into single function.
 *
 * @see link_to_unless
 * @see filter_url
 */
function link_to_filter_unless($condition, $name, $param, $new_value, $options = array())
{
    return link_to_unless($condition, $name, filter_url($param, $new_value), $options);
}
 
/**
 * Shortcut combining button_to and filter_url into single function.
 *
 * @see button_to
 * @see filter_url
 */
function button_to_filter($name, $param, $new_value, $options = array())
{
    return button_to($name, filter_url($param, $new_value), $options);
}
by Stephen Riesenberg on 2007-02-03, tagged css  filter  helper  pager  pagination  template  view 

Set create default value to filter defined value for a specific field

This is a very usefull and simple tip if you want to defined default value in edit form (only on creation of a new record) with the value that the user defined in the list filter.

An exemple for a product module which define the category select field with the value of the same field in the list filter.

action.class.php

  public function executeEdit ()
  {
    $filters = $this->getUser()->getAttributeHolder()->getAll('sf_admin/product/filters');
    if (!$this->getRequestParameter('product_id', 0) && isset($filters['category_id']))
    {
      $this->product = new Product();
      $this->produit->setCatergoryId($filters['category_id']);
    }
    parent::executeEdit();
  }
 
  protected function getProductOrCreate ($product_id = 'product_id')
  {
    if (isset($this->product)) return $this->product;
    else return parent::getProductOrCreate($product_id);
by Sylvain PAPET on 2006-10-20, tagged admin  filter  generator 
(3 comments)

TCPDF filter to transform pages to PDF

[DEPRECATED SEE COMMENTS]

{-- DEPRECATED SEE COMMENTS --}

---DEPRECATED SEE COMMENTS---

This basic filter can transform pages into pdf using the sfTCPDFPlugin. You can also save the generated PDF to the database using the sfPropelFileStoragePlugin.

this is the first version so please post comments if you like it or use it !

Create a file in apps/myapp/lib/pdfFilter.class.php

<?php
/**
 * Filter for redirecting to PDF for the pages that need it
 *
 * @author Laurent Marchal
 * @version 1
 */
 
class pdfFilter extends sfFilter
{
    /**
     * Execute filter
     *
     * @param FilterChain $filterChain The symfony filter chain
     */
 
    public function execute ($filterChain)
    {
 
        if ($this->getContext()->getActionName() == 'pdf')
        {
        $pdf = $this->initPDF();
 
        // Next filter
        $filterChain->execute();
 
        $rawpdf = $this->writePDF($pdf, $this->getContext()->getResponse()->getContent());
 
        $pdf->Output(); //shows the pdf to the user
        //$this->savePdfToDatabase($rawpdf);
        return;
        }
        $filterChain->execute();
    }
 
    /**
     * initialyse the TCPDF object, must be instanciated before
     * filterChain->execute() in order to analyse the request.
     *
     */
    protected function initPDF()
    {
        //create new PDF document
        $pdf = new sfTCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true);
        $pdf->SetFont("FreeSerif", "", 12);
        return $pdf;
    }
 
    /**
     * write the contents to the PDF generally
     * getContext()->getResponse()->getContent()
     *
     *@param sfTCPDF $pdf the TCPDF object created with function initPDF()
     *@param stream $html_content the contents to transform to PDF
     */
    protected function writePDF($pdf, $html_content)
    {
        $pdf->AddPage();
        $pdf->writeHTML($html_content, true, 0);
        //$this->logMessage('ficheduActions::createPDF '.$html_content , 'info');
        //$document = $pdf->Output('test.pdf', 's');
        return $pdf->Output('test.pdf', 's');
    }
 
    /**
     * Save the PDF in the database using sfPropelFileStoragePlugin
     *
     *@param stream $pdf_content output of function writePDF()
     */
    protected function  savePdfToDatabase($pdf_content)
    {
        //File Info
      $file_info = new sfPropelFileStorageInfo();
      $file_info->setName('DU_Entreprise_v'.$fichedu->getFdVersion().'.pdf');
      $file_info->setSize(null);
      $file_info->setMimeType('application/pdf');
        //File Data
      $file_data = new sfPropelFileStorageData();
      $file_data->setBinaryData($pdf_content);
      $file_info->addsfPropelFileStorageData($file_data);
      $file_info->save();
    }
 
}

and in apps/myapp/config/filters.yml

rendering: ~
web_debug: ~
security:  ~
 
pdfFilter:
    class:  pdfFilter
 
cache:     ~
common:    ~
flash:     ~
execution: ~

then in your module,you will have to create this function in the action.class.php :

  public function executePdf()
  {
    $this->renderPDF = true;
    //use a special layout to clean the pdf
    //remove if you want your entire layout to be generated into pdf
    $this->setLayout ('pdf');
    //the show action generally fit
    $this->setTemplate ('show');
  }

then go to http://mywebsite/yourmodule/pdf to generate the pdf

H@ve Fun !

by Laurent Marchal on 2007-05-07, tagged database  filter  pdf  plugin  sfpropelfilestorageplugin  sftcpdfplugin  tcpdf 
(4 comments)

Server-side transparent PNG hack for IE

I've found a very nice and easy method to add transparent PNG image support to IE. This filter is an easy alternative from using Javascript or server-side php scripts. Original code from http://koivi.com/ie-png-transparency/

Add code below to your lib folder with filename pngAlphaFilter.class.php

<?php
 
class pngAlphaFilter extends sfFilter
{
  public function execute ($filterChain)
  {
    $filterChain->execute();
  }
 
  public function executeBeforeRendering ($filterChain)
  {
    $response = $this->getContext()->getResponse();
 
    $content = $response->getContent();
    $newContent = $this->replacePngTags($content, '/images/');
    $response->setContent($newContent);
 
    $filterChain->execute();
  }
 
  /**
  *  KOIVI PNG Alpha IMG Tag Replacer for PHP (C) 2004 Justin Koivisto
  *  Version 2.0.12
  *  Last Modified: 12/30/2005
  *  
  *  Modifies IMG and INPUT tags for MSIE5+ browsers to ensure that PNG-24
  *  transparencies are displayed correctly.  Replaces original SRC attribute
  *  with a binary transparent PNG file (spacer.png) that is located in the same
  *  directory as the orignal image, and adds the STYLE attribute needed to for
  *  the browser. (Matching is case-insensitive. However, the width attribute
  *  should come before height.
  *  
  *  Also replaces code for PNG images specified as backgrounds via:
  *  background-image: url(image.png); or background-image: url('image.png');
  *  When using PNG images in the background, there is no need to use a spacer.png
  *  image. (Only supports inline CSS at this point.)
  *  
  *  @param string $x  String containing the content to search and replace in.
  *  @param string $img_path   The path to the directory with the spacer image relative to
  *                      the DOCUMENT_ROOT. If none os supplied, the spacer.png image
  *                      should be in the same directory as PNG-24 image.
  *  @param string $sizeMeth   String containing the sizingMethod to be used in the
  *                      Microsoft.AlphaImageLoader call. Possible values are:
  *                      crop - Clips the image to fit the dimensions of the object.
  *                      image - Enlarges or reduces the border of the object to fit
  *                              the dimensions of the image.
  *                      scale - Default. Stretches or shrinks the image to fill the borders
  *                              of the object.
  *  @param bool   $inScript  Boolean flag indicating whether or not to replace IMG tags that
  *                      appear within SCRIPT tags in the passed content. If used, may cause
  *                      javascript parse errors when the IMG tags is defined in a javascript
  *                      string. (Which is why the options was added.)
  *  @return string
  */
  public function replacePngTags($x,$img_path='',$sizeMeth='scale',$inScript=FALSE){
      $arr2=array();
      // make sure that we are only replacing for the Windows versions of Internet
      // Explorer 5.5+
      $msie='/msie\s(5\.[5-9]|[6]\.[0-9]*).*(win)/i';
      if( !isset($_SERVER['HTTP_USER_AGENT']) ||
          !preg_match($msie,$_SERVER['HTTP_USER_AGENT']) ||
          preg_match('/opera/i',$_SERVER['HTTP_USER_AGENT']))
          return $x;
 
      if($inScript){
          // first, I want to remove all scripts from the page...
          $saved_scripts=array();
          $placeholders=array();
          preg_match_all('`<script[^>]*>(.*)</script>`isU',$x,$scripts);
          for($i=0;$i<count($scripts[0]);$i++){
              $x=str_replace($scripts[0][$i],'replacePngTags_ScriptTag-'.$i,$x);
              $saved_scripts[]=$scripts[0][$i];
              $placeholders[]='replacePngTags_ScriptTag-'.$i;
          }
      }
 
      // find all the png images in backgrounds
      preg_match_all('/background-image:\s*url\(([\\"\\\']?)([^\)]+\.png)\1\);/Uis',$x,$background);
      for($i=0;$i<count($background[0]);$i++){
          // simply replace:
          //  "background-image: url('image.png');"
          // with:
          //  "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(
          //      enabled=true, sizingMethod=scale, src='image.png');"
          // I don't think that the background-repeat styles will work with this...
          $x=str_replace($background[0][$i],'filter:progid:DXImageTransform.'.
                  'Microsoft.AlphaImageLoader(enabled=true, sizingMethod='.$sizeMeth.
                  ', src=\''.$background[2][$i].'\');',$x);
      }
 
      // find all the IMG tags with ".png" in them
      $pattern='/<(input|img)[^>]*src=([\\"\\\']?)([^>]*\.png)\2[^>]*>/i';
      preg_match_all($pattern,$x,$images);
      for($num_images=0;$num_images<count($images[0]);$num_images++){
          // for each found image pattern
          $original=$images[0][$num_images];
          $quote=$images[2][$num_images];
          $atts=''; $width=0; $height=0; $modified=$original;
 
          // We do this so that we can put our spacer.png image in the same
          // directory as the image - if a path wasn't passed to the function
          if(empty($img_path)){
              $tmp=split('[\\/]',$images[3][$num_images]);
              $this_img=array_pop($tmp);
              $img_path=join('/',$tmp);
              if(empty($img_path)){
                  // this was a relative URI, image should be in this directory
                  $tmp=split('[\\/]',$_SERVER['SCRIPT_NAME']);
                  array_pop($tmp);    // trash the script name, we only want the directory name
                  $img_path=join('/',$tmp).'/';
              }else{
                  $img_path.='/';
              }
          }else if(substr($img_path,-1)!='/'){
              // in case the supplied path didn't end with a /
              $img_path.='/';
          }
 
          // If the size is defined by styles, find them
          preg_match_all(
              '/style=([\\"\\\']).*(\s?width:\s?([0-9]+(px|%));).*'.
              '(\s?height:\s?([0-9]+(px|%));).*\\1/Ui',
                 $images[0][$num_images],$arr2); 
          if(is_array($arr2) && count($arr2[0])){
              // size was defined by styles, get values
              $width=$arr2[3][0];
              $height=$arr2[6][0];
 
              // remove the width and height from the style
              $stripper=str_replace(' ','\s','/('.$arr2[2][0].'|'.$arr2[5][0].')/');
              // Also remove any empty style tags
              $modified=preg_replace(
                  '`style='.$arr2[1][0].$arr2[1][0].'`i',
                  '',
                  preg_replace($stripper,'',$modified));
          }else{
              // size was not defined by styles, get values from attributes
              preg_match_all('/width=([\\"\\\']?)([0-9%]+)\\1/i',$images[0][$num_images],$arr2);
              if(is_array($arr2) && count($arr2[0])){
                  $width=$arr2[2][0];
                  if(is_numeric($width))
                      $width.='px';
 
                  // remove width from the tag
                  $modified=str_replace($arr2[0][0],'',$modified);
              }
              preg_match_all('/height=([\\"\\\']?)([0-9%]+)\\1/i',$images[0][$num_images],$arr2);
              if(is_array($arr2) && count($arr2[0])){
                  $height=$arr2[2][0];
                  if(is_numeric($height))
                      $height.='px';
 
                  // remove height from the tag
                  $modified=str_replace($arr2[0][0],'',$modified);
              }
          }
 
          if($width==0 || $height==0){
              // width and height not defined in HTML attributes or css style, try to get
              // them from the image itself
              // this does not work in all conditions... It is best to define width and
              // height in your img tag or with inline styles..
              if(file_exists($_SERVER['DOCUMENT_ROOT'].$img_path.$images[3][$num_images])){
                  // image is on this filesystem, get width & height
                  $size=getimagesize($_SERVER['DOCUMENT_ROOT'].$img_path.$images[3][$num_images]);
                  $width=$size[0].'px';
                  $height=$size[1].'px';
              }else if(file_exists($_SERVER['DOCUMENT_ROOT'].$images[3][$num_images])){
                  // image is on this filesystem, get width & height
                  $size=getimagesize($_SERVER['DOCUMENT_ROOT'].$images[3][$num_images]);
                  $width=$size[0].'px';
                  $height=$size[1].'px';
              }
          }
 
          // end quote is already supplied by originial src attribute
          $replace_src_with=$quote.$img_path.'spacer.png'.$quote.' style="width: '.$width.
              '; height: '.$height.'; filter: progid:DXImageTransform.'.
              'Microsoft.AlphaImageLoader(src=\''.$images[3][$num_images].'\', sizingMethod='.
              $sizeMeth.');"';
 
          // now create the new tag from the old
          $new_tag=str_replace($quote.$images[3][$num_images].$quote,$replace_src_with,
              str_replace('  ',' ',$modified));
          // now place the new tag into the content
          $x=str_replace($original,$new_tag,$x);
      }
 
      if($inScript){
          // before the return, put the script tags back in. (I was having problems when there was
          // javascript that had image tags for PNGs in it when using this function...
          $x=str_replace($placeholders,$saved_scripts,$x);
      }
 
      return $x;
  }
}
 
?>

add this to your apps/your_app/config/filters.yml

pngAlphaFilter:
  class: pngAlphaFilter

and put a spacer.png with 1 by 1 pixel with binary opacity in images/ directory. Or simply download one from the url below. http://koivi.com/ie-png-transparency/spacer.png

And now you are able to use the transparent PNG files as if IE fully supports it! The rest of templates need no change.

by Sung-Jin Hong on 2006-05-26, tagged filter  hack  png 
(4 comments)

Browser culture detection

This filter use the accepted-languages browser setting to setup the user culture. The autodetection is run once by session.

This filter is sponsored by Dorigo consultants.

It check app.yml for accepted_languages setting in order to fit the available application locales.

It allow the use of sf_culture request parameter overide (this overide is avaible by default in symfony afaik). So you can use language changing links in your application.

It may be enhanced by a check of the really available locales but i don't know how it can be done.

To use this filter, create a file named SwitchLanguageFilter.class.php in lib/ or app/frontend/lib and fill it with this code and follow its documentation :

<?php
/**
 * Setup user culture from request
 * 
 * from http://www.symfony-project.com/snippets/snippet/80
 * Thanks to François Zaninotto
 * Thanks to Garfield-fr on #symfony-fr
 * 
 * Add the following lines in your app.yml to configure your application available languages.
 *   all:
 *     accepted:
 *       languages: [en, fr]
 * 
 * Then add this to your filters.yml
 * 
 *   # Filter that setup user culture
 *   mySwitchLanguageFilter:
 *     class: SwitchLanguageFilter
 * 
 * @package    SwitchLanguageFilter
 * @subpackage filter
 * @author     Pierre-Yves Landuré <py.landure@dorigo.fr>
 */
 
class SwitchLanguageFilter extends sfFilter
{
 
  /**
   * Check that the language is a valid application culture.
   * @param string $language The tested language code
   * @param string $default_language The default language code
   * 
   * @return string $language if no accepted languages is set,
   *                else $language if it is in accepted languages
   *                else $default_language
   */
  private function getAvailableCulture($language, $default_language = null)
  {
    $all_languages = sfConfig::get('app_accepted_languages', array());
 
    if(count($all_languages))
    {
      if(in_array($language, $all_languages)) // Test if language is available
      {
        return $language;
      }
      else // Else test if first part of language is available
      {
        $language_parts = explode('_', $language);
        if(count($language_parts))
        {
          if(in_array($language_parts[0], $all_languages))
          {
            return $language_parts[0];
          }
        }
      }
      return $default_language;
    }
 
    return $language;
  }
 
  /**
   * The filter call.
   */
  public function execute ($filterChain)
  {
    $context = $this->getContext();
    $user = $context->getUser();
 
    $default_culture = sfConfig::get('sf_i18n_default_culture');
    $selected_culture = $user->getCulture();
 
    if(!$user->getAttribute('sf_culture_autodetected', false))
    {
      $browser_languages = $context->getRequest()->getLanguages();
 
      foreach($browser_languages as $language)
      {
        $allowed_culture = $this->getAvailableCulture($language);
        if($allowed_culture)
        {
          $selected_culture = $allowed_culture;
          break;
        }
      }
 
      $user->setAttribute('sf_culture_autodetected', true);
    }
 
    $selected_culture = $context->getRequest()->getParameter('sf_culture', $selected_culture);
    $selected_culture = $this->getAvailableCulture($selected_culture, $default_culture);
    if($selected_culture != $user->getCulture())
    {
      // The user wants to see the page in another language
      $user->setCulture($selected_culture);
    }
 
    $filterChain->execute();
  }
}
 
by Pierre-Yves Landuré on 2007-10-09, tagged culture  filter  i18n  request 

AutoSet the title of the page

It's so long to set the title page per page and i forgot it frequently. With this filter it's the end of no titled pages.

Copy into lib/filter/sfAutoMetaFilter.class.php :

<?php
/**
 * Manage MetaTags 
 * @author Cédric Lombardot <cedric.lombardot@spyrit.net>
 */
 
class sfAutoMetaFilter extends sfFilter{
    public function execute ($filterChain)
      {
        $filterChain->execute();
        /*
         * Recherche le h1 pour mettre le title
         */
 
 
        $html=$this->getContext()->getResponse()->getContent(); 
        $title=$this->get_title($html);
        $h1s=$this->get_h1($html);
        if($h1s[1]==0){
            $h2s=$this->get_h2($html);
            if($h2s[1]!=0){
                $this->getContext()->getResponse()->setContent($this->set_title($html,(($title)?($title.' - ' ):'').strip_tags($h2s[0][0])));
            }
        }else{
            $this->getContext()->getResponse()->setContent($this->set_title($html,(($title)?($title.' - ' ):'').strip_tags($h1s[0][0])));
        }
 
 
 
 
      }
 
      // retrieve all h1 tags
        protected function get_h1($html){
            $h1tags = preg_match_all("/(<h1.*>)(.*)(<\/h1>)/isxmU",$html,$patterns);
            $res = array();
            array_push($res,$patterns[2]);
            array_push($res,count($patterns[2]));
            return $res;
        } 
        protected function get_h2($html){
            $h2tags = preg_match_all("/(<h2.*>)(.*)(<\/h2>)/isxmU",$html,$patterns);
            $res = array();
            array_push($res,$patterns[2]);
            array_push($res,count($patterns[2]));
            return $res;
        } 
        protected function get_title($html){
            $title = preg_match("/(<title>)(.*)(<\/title>)/isxmU",$html,$patterns);
            return isset($patterns[2])?$patterns[2]:'';
        } 
        protected function set_title($html,$title){
            return preg_replace("/(<title>)(.*)(<\/title>)/isxmU",'<title>'.$title.'</title>',$html);
        } 
}
?>
 

And in your filters.yml

autometa: 
  class: sfAutoMetaFilter
 
by cedric lombardot on 2010-04-29, tagged filter  metas  title 
(2 comments)

Google analytics code by filter

This simple filter automatically adds google analytics code for all pages. It's no longer required to modify all the layouts, just put google code to app.yml and this filter does everything for you.

Create a lib/filters/gaFilter.class.php file:

<?php
class gaFilter extends sfFilter
{
  public function execute($filterChain)
  {
    // Nothing to do before the action
    $filterChain->execute();
    // Find google code and check if current module is not disabled
    if(($gaCode = sfConfig::get("app_ga_code",false)) !== false
    && !in_array($this->getContext()->getModuleName(),sfConfig::get("app_ga_disabled_modules",array()))) {
      //Decorate the response with the tracker code
      $response = $this->getContext()->getResponse();
      $response->setContent(str_ireplace('</body>', $gaCode.'</body>',$response->getContent())); 
    }
   }
}
 

Add the filter to filters.yml :

# insert your own filters here
 
# Google analytics filter
ga_filter:
  class: gaFilter
 

Now you are able to insert GA code into your page. Simply copy the whole code and add it as ga_code option into your app.yml file:

all:
  ...
  ga:
    code: >
      <script type="text/javascript">
      try {
      var pageTracker = _gat._getTracker("...");
      pageTracker._trackPageview();
      } catch(err) {}</script>
 

It works on fact, that you probably will not have a </body> tag in other place except at the end of your page and you will not use it in other content type than text/html.

Optionally you may disable GA filter in specific modules, such as administration is. To do this, add this option to your app.yml:

all:
  ...
  ga:
    disabled_modules: [administration]
    code: >
      ...
 
by Radek Petráň on 2010-03-07, tagged analytics  filter  google 

Set user culture from domain name

This filter tries to set the user culture by reading the domain part of the url. I use it for SEO, giving a different domain for each lang of my site.

Add this to your settings.yml :

    domain_culture:
       fr:   [.net, .fr, fr.mydomain.info]
       it:   [it.mydomain.info]
       en:   [.co.uk, .com, .mydomain.info]
 

Order is important : first match will assign the corresponding culture.

Create a lib/filters/domainCultureFilter.class.php file :

<?php
 
class domainCultureFilter extends sfFilter
{
    public function execute($filterChain)
    {
        $user = $this->getContext()->getUser();
        $request = $this->getContext()->getRequest();
        if (!$request->getParameter('sf_culture'))
        {
            if ($user->getAttribute('first_request', true))
            {
                $user->setAttribute('first_request', false);
                $domainCulture=sfConfig::get('sf_domain_culture');
                foreach ((array)$domainCulture as $culture=>$domains)
                {
                    foreach ((array)$domains as $domain)
                    {
                        $pattern = '/'.$domain.'/';
                        if (preg_match( $pattern ,  $_SERVER['HTTP_HOST']) )
                        {
                            $user->setCulture($culture);
                        }
                        break 2;
                    }
                }
            }
        }
        // Execute next filter
        $filterChain->execute();
    }
}
 

Add the filter to filters.yml :

# insert your own filters here
culture:
   class: domainCultureFilter
 

Comments are very welcome.

by Pierre Bastoul on 2009-04-10, tagged culture  domain  filter 
(3 comments)

JsModuleActionFilter

A simple filter for Symfony 1.2.* which embed a js per request named after /js/modules/:module_:action.js rule.

<?php
 
class JsModuleActionFilter extends sfFilter
{
    public function execute($filterChain)
    {
        if ($this->isFirstCall()) {
            $params = $this->getContext()->getRequest()->getRequestParameters();
 
            //gets a module action specific js file
            $this->getContext()->getResponse()->addJavascript('modules/' . $params['module'] . '_' . $params['action'] , 'last');
        }
 
        $filterChain->execute();
    }
}
 
?>
 
by excessive demon on 2009-03-15, tagged 12  filter  peytz 
(2 comments)

module.css

This filter will automatically add css file with the name of current module.

It's great to have these files saved separately that to have one huge file. (And demo of CSSEdit doesn't allow saving files bigger than 2.5k chars :D)

Just create your cssAdderFilter.class.php, and add it to your filters.yml. Clear cache and you are ready to go.

<?php 
 
class cssAdderFilter extends sfFilter
{
 
    public function execute( $filterChain )
    {
        $context = $this->getContext();
        $request = $context->getRequest();
        $response = $context->getResponse();
 
        $module = $request->getParameter("module");
 
        $path_to_web = sfConfig::get("sf_web_dir");
        $path_to_css = $path_to_web . "/css/" . $module . ".css";
 
        if (file_exists($path_to_css))
        {
            $response->addStylesheet($module.".css");
        }
 
        $filterChain->execute();
    }
 
}
 
by zero0x on 2008-05-10, tagged asset  css  filter 
(3 comments)

Making variables available to all Actions via Filters

I had an urgent need to assign a variable across all actions, and copy and pasting the same snippet of code just didn't appeal to me.

Failing to find the answer in the forums or in the Snippets, I decided to roll up my sleeves and dig into creating it as a filter.

After much experimentation and documentation re-reading, I think I finally found a solution.

First you have to set up your own filter. Since it's fairly well laid out in the Handbook, I won't go over it here. (http://www.symfony-project.org/book/1_0/06-Inside-the-Controller-Layer#Filters)

In your filter code, put the following:

class myFilter extends sfFilter
{
  public function execute($filterChain)
  {
    // Remove this if condition if you want the variable available to AJAX actions
    if (!$this->getContext()->getRequest()->isXmlHttpRequest()) {      
      for ($i=0; $i < $this->getContext()->getActionStack()->getSize(); $i++) { 
        $this->getContext()->getActionStack()->getEntry($i)->getActionInstance()->foo = 'foo';
      }
    }
 
    // Execute next filter
    $filterChain->execute();
  }
}
 

Replace foo with the variable name you want and assign it the data you need. You should now be able to access it in every action that you call.

For example in your action:

class indexAction extends sfAction
{
  public function execute()
  {
    strtoupper($this->foo);
  }
}
 

or in your view:

  <?php echo $foo ?>
 
by Joe Guetler on 2008-03-12, tagged action  actions  filter  view 
(3 comments)

Filter item in admin_check_list using admin generator.

Hi, this is my first snippet (in my first Symfony project), so I hope this is useful and not boring for you. I show you my code to solve a problem: how to filter an admin_check_list using a criteria.

In my project I have in the schema also this tables:

  role:
    type:                { primaryKey: true, type: char }
    descritpion:         { type: varchar(50), required: true }
  user:
    id:
    username:            { type: varchar(100), index: unique, required: true }
    role_type:           { type: char, foreignTable: role, foreignReference: type, required: true }
  course:
    id:
    name:                { type: varchar(255), required: true }
    research_proposal:   { type: longvarchar }
  course_teacher:
    course_id:
    user_id:
 

Little explanation: Each user have a role. Each course can have more teacher; each teacher is an user with "T" role_type.

In the generator.yml I can write

edit:
  fields:
    course_teachers:         { type: admin_check_list, params: through_class=CourseTeacher }
 

then in the course creation page I have a list of all users (and not a list of the teacher).

To reach my scope I need to override the ObjectAdminHelper. I create a file named ObjectAdminHelper.php and put it into the /lib/helper folder of my Symfony folder. To override successfully an helper it is necessary to overwrite (or write) all its method. So just copy all from the original ObjectAdminHelper.php file and paste it into this new file. The only method we need to override is "_get_object_list($object, $method, $options, $callback)": it use by default the method "_get_propel_object_list" to get the list to show... But if we would like to modify that list how can we do?

function _get_object_list($object, $method, $options, $callback){
  $object = get_class($object) == 'sfOutputEscaperObjectDecorator' ? $object->getRawValue() : $object;
  // the default callback is the propel one
  if (!$callback) {
    $callback = _get_option($options, 'callback');
    if (!$callback) {
        $callback = '_get_propel_object_list';
    }
  }
  return call_user_func($callback, $object, $method, $options);
}
 

This method get an option called "callback" to get the name of the callback method. If a callback method is not present it use the standard "_get_propel_object_list".

So if I would like to get only the teacher (role_type="T") i can write this function (very similar than _get_propel_object_list):

function _get_teacher_from_users($object, $method, $options){
    $criteria = new Criteria();
    $criteria->add(UserPeer::ROLE_TYPE, "T");
    $through_class = _get_option($options, 'through_class');
    $objects = sfPropelManyToMany::getAllObjects($object, $through_class, $criteria);
    $objects_associated = sfPropelManyToMany::getRelatedObjects($object, $through_class, $criteria);
    $ids = array_map(create_function('$o', 'return $o->getPrimaryKey();'), $objects_associated);
    return array($objects, $objects_associated, $ids);
}
 

Now you can write all the function you need to filter the lists.

Last step: how can my generator.yml say to the helper which method it must use? Simply modify the course_teacher row:

edit:
  fields:
    course_teachers:         { type: admin_check_list, params: through_class=CourseTeacher callback=_get_teacher_from_users }
 

I hope this is useful for you (and sorry for my english).

Pierpaolo Cira

by Pierpaolo Cira on 2008-01-21, tagged adminchecklist  filter  helper