Symfony 1.4

symfony_logo

Symfony – Share Template Across Multiple Apps

0

I am currently working on a new project where I wanted multiple apps (frontend (fe) and backend (be)) to have the same exact appearance, and without having to duplicate the code in both template/layout.php files. So what I did is chose the fe layout.php file as the master template, then made the following changes:

// config/ProjectConfiguration.class.php
public function setup()
{
    ....

    sfConfig::set('masterTemplateUri', sprintf('%s/apps/fe/templates/layout.php', sfConfig::get('sf_root_dir'));

    ....
}

// apps/be/templates/layout.php
<?php include sfConfig::get('masterTemplateUri') ?>

Now I can share the same template across both fe and be apps and only have to make code changes in one location.

symfony_logo

PHP – Symfony – Speed up CLI Commands – Do Not Load Web Plugins

0

Below is a code snippet which I use to ensure that only necessary plugins are loaded when running symfony commands via Command Line Interface (CLI).

// config/ProjectConfiguration.class.php
class ProjectConfiguration extends sfProjectConfiguration
{
    public function setup()
    {
        ....

        //-----
        // Plugins
        //-----
        $basePlugins = array(
            'sfDoctrinePlugin',
            'userPlugin'
        );

        // Only add web plugins if we are not being accessed via cli
        if (php_sapi_name() === 'cli') {
            $webPlugins = array();
        } else {
            $webPlugins = array(
                'sfFormExtraPlugin',
                'sfJqueryReloadedPlugin',
                'icsicsBreadcrumbsPlugin'
            );
        }

        $this->enablePlugins(
            array_merge($basePlugins, $webPlugins)
        );
        //------

        ....
    }
}

Code is trivial, basically any plugins you need to add for CLI and web should be added to $basePlugins array, any web only plugins should be added to $webPlugins array as long as PHP is not running in CLI mode.

symfony_logo

PHP – Symfony – Build Model – No yml schema found

0

Working on a new project which requires more complex user account functionality than the current sfDoctrineGuard provides and stumbled upon this error message when attempting to build the model: “No yml schema found in /tmp/doctrine_schema_81014.yml”. There were two issues I had, which after resolving, allowed me to build my model as expected.

First, I added some custom code to my ProjectConfiguration.class.php which will only load certain plugins based upon whether I was running symfony from the Command Line Interface (CLI) or not. For example, when running commands from CLI, I don’t want to load sfFormExtraPlugin or sfJqueryReloadedPlugin, so I had to make sure that my custom plugin was added to the array. You can view the code snippet here (demonstrative only, has no impact on problem outlined in article).

Second, I had the schema.yml located @ plugins/userPlugin/config/doctrine/schema.yml, a uri which Symfony is unaware of. To make symfony aware of the custom location for my config files, I had to add the following code to plugins/userPlugin/config/app.yml:

# plugins/userPlugin/config/app.yml
all:
  userPlugin:
    config_dir: %SF_PLUGINS_DIR%/userPlugin/config
    recursive: true

The important settings is the `recursive: true` setting. After resolving both issues I cleared cache and was able to successfully build the model.

symfony_logo

Symfony – Doctrine – Call to a member function evictAll on a non-object

1

If you are working with Symfony and Doctrine and encounter a “Call to a member function evictAll() on a non-object” error message, check to see if you are using DQL for any delete queries. I just came across this error and after some googling (mostly about bad schema files) I couldn’t fix the issue. I then checked a custom DQL statement which unlinks rows and saw that I had aliased the main table with ‘caa’, but the other references were ‘ca’ (only one a, instead of two). Once I fixed the alias issue, everything worked as expected.

Symfony – Add ReCaptcha to JQuery Dialog (Lightbox)

5

The following solution of adding ReCaptcha to a JQuery Dialog (Lightbox) makes use of the sfFormExtraPlugin, not the sfReCaptchaPlugin. The reason is because the following solution was developed in Symfony 1.2+, and we had access to the Symfony form architecture.

Out of the box, the sfFormExtraPlugin sfWidgetFormReCaptcha::render() will not work in lightboxes because it results in a document.write(). Consider the following:

// sfWidgetFormReCaptcha::render() produces the following javascript
<script type="text/javascript" src="http://api.recaptcha.net/challenge?k=6Let7r0SAAAAAJFp77tW4gRze7r5y89Cql_msPcm"></script>

// Which produces the following javascript
var RecaptchaState = {
    site : '6Let7r0SAAAAAJFp77tW4gRze7r5y89Cql_msPcm',
    challenge : '03AHJ_VusnO7jeI3VF-cHgOIv9RY9RIpYl2QWea_LULIdL8xrM9PZdcZZ7I9gFf4gSbr1fxGCSQjQZPJQ1sa6p1oEI9U_nkU8f2SjczxjH6nzmy43Q-m_8rnxWhhRUIDa7iTPEwo4-dwi-FipyMHsSAz-nE5yfFQfZog',
    is_incorrect : false,
    programming_error : '',
    error_message : '',
    server : 'http://www.google.com/recaptcha/api/',
    timeout : 18000
};

document.write('<scr'+'ipt type="text/javascript" s'+'rc="' + RecaptchaState.server + 'js/recaptcha.js"></scr'+'ipt>');</pre>

Note the resulting document.write() code produced by recaptcha.

Recaptha offers multiple ways to access their api, so instead of the solution implemented by the devs of the sfWidgetFormReCaptcha plugin, I opted to introduce custom code which will produce the same result, except it will be lightbox friendly.  As with any Symfony plugin, I made sure not to overwrite core logic so it doesn’t increase technical debt with respect to future plugin upgrades etc. (unless the core logic changes such that the custom code must follow suit).

First, we need to override the default plugin code so we can add our custom code, and thanks to Symfony autoloading, the following changes should work out of the box. Also note, the myWidgetFormCaptcha class name follows symfony naming conventions to indicate Symfony core code vs custom code.

We need to create a new file which will extend sfWidgetFormReCaptcha;

mkdir -p /path/to/symfony_code/lib/form/myExtraFormPlugin/lib/widget
touch /path/to/symfony_code/lib/form/myExtraFormPlugin/lib/widget/myWidgetFormReCaptcha.class.php #lol @ touch myWidget

You can use whichever path you wish, just make sure it’s in the /path/to/symfony_code/lib dir so it will be included when Symfony autoloads files. I chose the above path to maximize flexibility if I ever need to override any other sfFormExtraPlugin widgets.

Now that we have our override class file in place, lets add the code:

<?php
// /path/to/symfony_code/lib/form/myExtraFormPlugin/lib/widget/myWidgetFormReCaptcha.class.php

/**
 * Extending plugin functionality, see parent class for full docs
 */
class myWidgetFormReCaptcha extends sfWidgetFormReCaptcha
{
 /**
 * @see sfWidgetForm
 */
public function render($name, $value = null, $attributes = array(), $errors = array())
{
    $server    = $this->getServerUrl();
    $key    = $this->getOption('public_key');

    if ((array_key_exists('context', $attributes)) &&
        ($attributes['context'] == 'lightbox')) {

        // Arbitrary flag, unset it
        unset($attributes['context']);

        return javascript_tag("
            Recaptcha.create('".$key."', 'captchaWrap', {custom_translations: {instructions_visual:'" . __('recaptcha_instructions_visual') . "',instructions_audio:'" . __('recaptcha_instructions_audio') . "',play_again:'" . __('recaptcha_play_again') . "', cant_hear_this:'" . __('recaptcha_cant_hear_this') . "', visual_challenge:'" . __('recaptcha_visual_challenge') . "', audio_challenge:'" . __('recaptcha_audio_challenge') . "', refresh_btn:'" . __('recaptcha_refresh_btn') . "', help_btn:'" . __('recaptcha_help_btn') . "', incorrect_try_again:'" . __('recaptcha_incorrect_try_again') . "'}, theme:'". sfConfig::get('recaptchaTheme', sfConfig::get('clientid'), 'text', 'clean') ."', lang:'en'});
        ") . '<div id="captchaWrap"></div>';
    }

    parent::render($name, $value, $attributes, $errors);
}

Ya I know, a lot of javascript, but nothing overly complicated just a bunch of flags dictating to recaptcha how the element should be rendered. I added an arbitary flag to the attributes array, so I could still call the same render() methods.  This allows the calling code to tell the widget how it should be rendered. If the context flag is not set to ‘lightbox’ then it simply calls on parent::render() which will default to normal behavior for the class.

Now we just need to add the following code to the lightbox template (the template that your form data is located in):

<?php echo $form['captcha']->renderRow(array('context' => 'lightbox')); ?>

And of course we need to bring in the ReCaptcha js library (view.yml):

javascripts: [http://www.google.com/recaptcha/api/js/recaptcha_ajax.js]

Now, clear symfony cache and click the link to activate your lightbox, it should render your form AND the recaptcha form element. Yay!

In a future post I will write-up how to check lightbox ajax responses using JSON.

Symfony 1.4 – Rename Application

0

If you ever need to rename an application you can follow the directions given here: http://fabien.potencier.org/article/22/a-symfony-tip-rename-a-symfony-application. The directions work well for Symfony version 1.2 but if you are running 1.3+ you will also need to follow these extra steps:

Rename Config File:

 app/app_name/config/<old_app_name>Configuration.class.php TO app/app_name/config/<new_app_name>Configuration.class.php

Edit Config File:

// app/app_name/config/<new_app_name>Configuration.class.php
class <new_app_name>Configuration extends sfApplicationConfiguration

Now just clear cache and load your site through the new controller (new_app_name.php) and it should work as expected.

Symfony – Autoload.yml – Basic Usage

21

Autoloading is the term used to describe the process where class files are loaded at the first http request (and cached for subsequent requests). The main benefit of autoloading is that  you don’t need to add a bunch of ‘require_once’ calls to your classes to get things working, they are all autoloaded for you. Some may see this autoloading as ‘bloat’, but if strategically cached, it does allow for easier coding within the symfony framework.

Chances are you have some custom classes which are specific to your actual business needs. Rather than stuffing these custom classes in the core symfony directories (lib/model, lib/forms, lib/filters), which they don’t belong, you can create custom directories and then tell symfony about them so symfony can autoload them as if they were the core symfony directories.

Check out the following example:


custom_app: #Unique identifier for autoloading anything in given dir

  name:      Custom app classes #Something that makes sense to you

  ext:       php #You can load files with specific extensions

  path:      %SF_LIB_DIR%/app #The directory from which symfony should autoload files, in this case lib/app

  recursive: true #Whether to load files within nested dirs

As you can see, autoloading in symfony allows for flexible lib structures, which allows you to better structure your classes for faster development and easier maintenance.

Edit (2.16.11)

Thanks to Tix comment, you want to add autoload.yml files to /apps/<app_name>/config/autoload.yml

Edit (9.17.11)

Thanks to German Rumm’s comment, be sure that autoload: is the first line in your autoload.yml file.

Also, here is an actual example:

autoload:
  sfcustom_api:
    name:      Symfony specific custom apis
    ext:       php 
    path:      %SF_LIB_DIR%/MyCustom/api
    recursive: true

Symfony – sfWidgetFormDoctrineChoice vs sfWidgetFormChoice – When to Use Which

1

Seven months ago I started working with Doctrine (vs. Propel) and missed some of the benefits Doctrine provides,  due to lack of experience working with it. Over time I finally picked up on some of the benefits of doctrine and one of these benefits relates to the differences between sfWidgetFormDoctrineChoice and sfWidgetFormChoice (and their associated validators). This article is intended to demonstrate these differences.

sfWidgetFormDoctrineChoice

The main benefit of using this widget vs sfWidgetFormChoice, is that you can specify an object from your model and Doctrine will automagically know how to retreive the data. You can also specify other options to fully configure your choice options.  For example:

// lib/doctrine/form/BookForm.class.php
public function configure()
{
// This will create a single select drop downbox using data stored in Category model. It will also add an empty select option as the first option and sort the categories
// by name, asc
$this->widgetSchema['category_list']    = new sfWidgetFormDoctrineChoice(array('model' => 'Category', 'multiple' => false, 'add_empty' => true, 'order_by' => array('name', 'asc')));

// Or maybe you want a multiple select option, no problem.

// Notice the multiple option is set to true and the add_empty option has been removed, useful for admin double lists
$this->widgetSchema['category_list']    = new sfWidgetFormDoctrineChoice(array('model' => 'Category', 'multiple' => true, 'order_by' => array('name', 'asc')));
}

There are other options you can pass to sfWidgetFormDoctrineChoice which can be found via Symfony’s api documentation.

If you are a propel user, don’t fret, propel follows the same logic but uses sfWidgetFormPropelChoice.

Ok so we got sfWidgetFormDoctrineChoice setup, but what about an accompanying validator? Again, no problem, check out the following example:

// Notice that sfValidatorDoctrineChoice takes the same two arguments that we had in sfWidgetFormDoctrineChoice; model and multiple
// The only difference is the 'column' option which allows us to tell doctrine which column from the Category model will be used for validating
// submitted values
$this->validatorSchema['category_list']     = new sfValidatorDoctrineChoice(array('model' => 'Category', 'column' => 'id', 'multiple' => true));

Also, just like sfWidgetFormDoctrineChoice, there are other options  you can use when instantiating a new sfValidatorDoctrineChoice object; refer to the for all the options: api documentation.

So far I’ve only discussed sfWidgetFormDoctrineChoice, but what about sfWidgetFormChoice, when should we use it? I’ve found the best scenarios for using sfWidgetFormChoice occur when you have a list of options that are not supplied by a model object. For example, say you wanted to maintain a list of static options for book types; ‘format’ (i.e. paperback, hardcover).

Because there are only a handful of options which won’t change much over time, its better to make ‘format’ options class constants rather than create a table, add them to schema, and rebuild the model.

To display these options as a select list you would use sfWidgetFormChoice b/c there is no model which will automagically provide the needed data for your select list.

Lets see it in action:

// An example of using class constants to set values

// lib/model/doctrine/BookFormat.class.php
const FORMAT_TYPE_PAPERBACK = 1;
const FORMAT_TYPE_HARDCOVER = 2;

public static $formatTypes = array(
    self::FORMAT_TYPE_PAPERBACK => 'paperback',
    self::FORMAT_TYPE_HARDCOVER => 'hardcover'
);

// lib/form/doctrine/BookForm.class.php

$formatTypes = BookFormat::$formatTypes;

// I want to add an empty element so user is aware they must select something
$formatTypes = array('' => '') + $formatTypes

// Now pass to sfWidgetFormChoice
$this->widgetSchema['book_formats'] = new sfWidgetFormChoice(array('choices' => $formatTypes));

// Notice we calling array_keys so when form validates it will validate against 1,2,3 vs the string (paperback, hardcover)
$this->validatorSchema['book_formats'] = new sfValidatorChoice(array('choices' => array_keys($formatTypes), 'required' => false));

One thing to remember with the sfWidgetFormChoice example, you still must have a column in your object model to house the submitted value, otherwise the submitted value will be redirected to /dev/null.

Hopefully this article clearly demonstrated the differences between sfWidgetFormDoctrineChoice and sfWidgetFormChoice and when to use which. Thanks for reading and HTH.

Go to Top