symfonynerds.com

Nerds who love the symfony-project

Archive for the ‘1.2’ Category

This tip is a quick one for newbies. The Symfony book has some great tips as to when you would want to use a Symfony forward, vs a Symfony redirect. This quick post will show you the difference between the two and give you some guidelines to know when to use redirect or forward.

So what’s the main difference?

Symfony Redirect: This simply does a HTTP header(”location: “)
Symfony Forward:
This is complete custom code that comes with the Symfony framework that forwards you within the Symfony application.

Use Symfony Forward if:

  • If the action needs to forward a call to another action
    This will mean you can keep all the action class’ objects in scope and still have access to them
  • You want to forward the user to an action within the application, and keep it transparent to the user.
    From a users perspective - the displayed URL stays the same.

Use a Symfony Redirect if:

  • You need to go to an external website (http://google.com for example)
  • You don’t want the user to re-submit a form (if you are doing a form POST)
    It is generally good practice to do a redirect after a POST to ensure the user does not re-submit the form.

Conditional Forwarding/redirect
Quite often you need to forward or redirect if a condition has been met. Be sure to checkout the other Symfony functions that help you do this (forwardIf(), forwardUnless(), forward404If(), forward404Unless(), redirectIf(), and redirectUnless())

What about AJAX calls?
You can check if a HTTP request is an AJAX call before deciding what to do with it by calling calling  $request->isXmlHttpRequest() in your actions class. Once you have done this you can choose what you want to do; redirect or forward.

Symfony and pChart

I’ve been making some changes to an app that was using the Google Charts API to draw a couple of line and pie charts in a ‘reporting’ section. Sherif suggested while I was at it, I may as well look for some sexier charts, and a few Googles later came up with pChart. A quick look at their site will tell you it definitely fits the ’sexy’ criteria, and has some very cool features like caching (but who cares about, that main thing is it’s sexy :) ).

There are two ways to get pChart going with Symfony. Probably the easiest is to use xsPchartPlugin which was written specifically for the job and is as simple as running ./symfony plugin:install xsPChartPlugin within your app and creating and chmoding the web/images/tmp/xspchart/ directory it is configured to use.

xsPChartPlugin takes care of a few otherwise manual steps and includes some additional features like a function to clear the image directory. I think it’s a great plugin but I was just a little concerned I then had two things I’d have to keep up to date, the pChart lib and the plugin, or the plugin might fall behind and I’d have to remove it anyway.

Fortunately after installing xsPChartPlugin and seeing how it worked, I could now see easily how pChart would need to be installed for Symfony use, so here’s the steps for using it by itself:

  • Download the latest version (1.27d at time of writing) from here
  • Extract the RAR (warning, it doesn’t extract to it’s own folder by default, so first create a folder eg: pChart-1.27d and put the RAR in there then extract - or do it manually)
  • Move your ‘pChart-1.27d’ folder to ‘lib/vendor/’ under your project
  • In ‘pChart-1.27d/pChart/’ you’ll find the three class files that do all the work. So these get picked up by the Symfony Autoloader, you need to rename them with .php on the end. (eg: pChart.class becomes pChart.class.php)
  • Run a ./symfony cc to clear the cache and autoload the classes
  • Have a look in that folder for some example code to test. Example10.php is good, the bit you need is below. This should be placed in your executeXXX (probably exectureIndex) function in the action.class.php of your module.
     // Dataset definition 
     $DataSet = new pData;
     $DataSet->AddPoint(array(10,2,3,5,3),"Serie1");
     $DataSet->AddPoint(array("January","February","March","April","May"),"Serie2");
     $DataSet->AddAllSeries();
     $DataSet->SetAbsciseLabelSerie("Serie2");
     
     // Initialise the graph
     $Test = new pChart(420,250);
     $Test->drawFilledRoundedRectangle(7,7,413,243,5,240,240,240);
     $Test->drawRoundedRectangle(5,5,415,245,5,230,230,230);
     $Test->createColorGradientPalette(195,204,56,223,110,41,5);
     
     // Draw the pie chart
     $Test->setFontProperties("Fonts/tahoma.ttf",8);
     $Test->AntialiasQuality = 0;
     $Test->drawPieGraph($DataSet->GetData(),$DataSet->GetDataDescription(),180,130,110,PIE_PERCENTAGE_LABEL,FALSE,50,20,5);
     $Test->drawPieLegend(330,15,$DataSet->GetData(),$DataSet->GetDataDescription(),250,250,250);
     
     // Write the title
     $Test->setFontProperties("Fonts/MankSans.ttf",10);
     $Test->drawTitle(10,20,"Sales per month",100,100,100);
     
     $Test->Render("example10.png");

    You don’t need to include the

     // Standard inclusions   
     include("pChart/pData.class");
     include("pChart/pChart.class");

    lines obviously as Symfony has taken care of that for us.

  • IMPORTANT: We need to tell pChart more specifically where it’s fonts and output image directories are (this is one of the things xsPChartPlugin takes care of itself if you use it). So in the above example code you need to make the following changes:
      $Test->setFontProperties("Fonts/tahoma.ttf",8);

    becomes (in both spots)

     $Test->setFontProperties(sfConfig::get('sf_lib_dir')."/vendor/pChart-1.27d/Fonts/tahoma.ttf",8);

    Then

     $Test->Render("example10.png");

    becomes

     $Test->Render(sfConfig::get('sf_web_dir') . "/images/pchart/example1.png");

    For the lazy people like me who just want something to cut and paste, the whole thing might look something like:

     public function executeIndex(sfWebRequest $request) {   
     // Dataset definition    
     $DataSet = new pData;
     $DataSet->AddPoint(array(10,2,3,5,3),"Series1");
     $DataSet->AddPoint(array("January","February","March","April","May"),"Series2");
     $DataSet->AddAllSeries();
     $DataSet->SetAbsciseLabelSerie("Series2");
     
     // Initialise the graph
     $Test = new pChart(420,250);
     $Test->drawFilledRoundedRectangle(7,7,413,243,5,240,240,240);
     $Test->drawRoundedRectangle(5,5,415,245,5,230,230,230);
     $Test->createColorGradientPalette(195,204,56,223,110,41,5);
     
     // Draw the pie chart
     $Test->setFontProperties(sfConfig::get('sf_lib_dir')."/vendor/pChart-1.27d/Fonts/tahoma.ttf",8);
     $Test->AntialiasQuality = 0;
     $Test->drawPieGraph($DataSet->GetData(),$DataSet->GetDataDescription(),180,130,110,PIE_PERCENTAGE_LABEL,FALSE,50,20,5);
     $Test->drawPieLegend(330,15,$DataSet->GetData(),$DataSet->GetDataDescription(),250,250,250);
     
     // Write the title
     $Test->setFontProperties(sfConfig::get('sf_lib_dir')."/vendor/pChart-1.27d/Fonts/tahoma.ttf",12);
     $Test->drawTitle(10,20,"Sales per month",100,100,100);
     
     $Test->Render(sfConfig::get('sf_web_dir') . "/images/pchart/example1.png");
    }

    All that’s left to do now is show the image by using a normal image tag in your template:

    echo image_tag('pchart/example1.png')

    Now this post doesn’t show you how to use the pChart cache and a lot of other things, but it does get you up and running! It’s personal choice as to whether you want to use the plugin or do it by hand, but either way have fun with these sexy charts! :)

  • 1 Comment
  • Filed under: 1.0, 1.1, 1.2, plugins, tip
  • We came across an interesting problem today when setting up an application we’d built earlier in a fresh dev environment to add some features…

    Because we’re big fans of MySQL Workbench we made our initial schema changes there, generated the SQL to update the DB, then used a symfony propel:build-schema to generate an updated schema.yml followed by a symfony propel:build-model to update the model…

    All good so far, until I began work on the first change and tried to fire off the task I’d just written…

    Fatal error: Class 'BaseSMMConfig' not found in /usr/local/projects/smm/lib/model/SMMConfig.php on line 3

    After some looking around, I realised my base class was called BaseSmmConfig - spot the problem? Yes, the ‘mm’ was lower case, while my class was looking for ‘MM’ in capitals…

    So what caused this?

    The application had originally been created by writing the schema.yml file (including the phpName being specified) then doing a symfony propel:build-all to create the DB and the model together. This is fine as the capitalisation used in the hand written schema.yml flowed through to the model. When I had done the reverse, creating a schema.yml from the DB, all it has to go on is the names of the tables, using camel case to identify where an underscore exists in the table name, so the ’smm_config’ table becomes SmmConfig in the schema.yml and model, not SMMConfig as originally typed. Because your extendable model classes don’t get updated (so as not to overwrite your changes) your app then breaks down.

    So the lesson:

    • Always observe strict camel case if you specify phpName
    • Don’t specify it, let it be generated
    • Work backwards by creating your DB first - using Workbench makes this an attractive option, saves you drawing up the schema later…

    Submit on Select Drop Down

    Just a symfonynerds quick tip on getting a drop down select of names & URLs to submit when the userselects an item on it… One of those rare occasions when the first thing that came to mind seemed to work :)


    <form action="<?php echo url_for('yesbar/submit') ?>" method="post">

    <?php echo select_tag('links',objects_for_select($links, 'getLink', 'getName'), array("onchange" => "this.form.submit();")) ?>

    </form>

  • 1 Comment
  • Filed under: 1.2, tip
  • The guys at UI Studio have done a really nice job of publishing http://symfony-check.org .

    It’s a simple site that goes through and helps you check if your symfony application is ready for deploymen. Be sure it check it out.

    This is a quick tip that will hopefully save you some time. The sfFormExtraPlugin is an excellent plugin with lots of extra features to help you build your forms. This tip is regarding the sfWidgetFormJQueryDate widget. This Widget lets you create a jQuery date selector. Setting up this widget is simple, but not well documented. Here are the basic steps of getting this started in your project forms:

    1. Install the sfFormExtraPlugin
    ./symfony symfony plugin:install sfFormExtraPlugin

    2. Install relevant jQuery libraries:
    a) Download jQuery AND
    b) Download jQueryUI

    3. Setup jQuery in your project
    Extract the JavaScript libs to your project js folder, then modify your app view.yml to include them:

    javascripts: [jquery-1.3.2.min.js, jquery-ui-1.7.1.custom.min.js]

    Note: The order in which the jQuery libs are included are important and can cause a conflict if they are not in the order above.

    4. Setup the CSS for jQuery UI
    When you download the jQuery UI, extract the smoothness theme into your web css directory. IE:

    /web/css/smoothness

    Now include the relevant CSS in your app view.yml:

    stylesheets: [main, smoothness/jquery-ui-1.7.1.custom.css]

    5. Setup the Widget your form class:
    Note in my example below, I will have an image icon as the date selector (/images/icons/calendar_view_month.gif). This is what the form class will look like:

    class myForm extends sfForm {
      public function configure()
      {
        $this->setWidgets(array(
          'from_date'    => new sfWidgetFormJQueryDate(array(
                 'image'=>'/images/icons/calendar_view_month.gif', 
                 'format' => '%day%/%month%/%year%')
                 ),
         //...other widgets
        ));
     
        $this->widgetSchema->setLabels(array(
      		'from_date'    => 'From Date',
                    //...other labels
        ));
      }
    }

    6. Display it in your view:
    Action:

      public function executeSearch(sfWebRequest $request){    
        $this->form = new myForm();
        return sfView::SUCCESS; 
      }

    View:

    ....
    echo $form['from_date']->renderLabel().":".$form['from_date'];
    ....

    And that’s it! You should get something looking like this:

    Hope this helps.

    In general, you will always run into issues trying to use the ‘/’ character as part of a parameter in your URL in web development. Recently I’ve run into issues trying to pass a parameter in my URL that has the forwardslash character. For example - trying to pass a URL within a URL:

    mymodule/myaction/url=http://google.com

    In the case above, Apache will return a 404 as it cannot find that URL (it things that http://google.com is part of the URL).

    This is a known issue that can be resolved in two ways:
    1) urlencode() + ApacheEncodedSlashes
    You could use urlencode() to escape your http:// slashes, and then change you’re ApacheEncodedSlashes directive to allow this (further reading). However this does require a Apache change.

    2) The quick fix is to simply tell Apache that it is a parameter, not part of the URL, by changing the / to a ?
    mymodule/myaction?url=http://google.com

    This works fine. However, in Symfony you can’t get this to work if you are trying to use the link_to_remote Ajax Helper function.

    The following code:

    $url = "http://google.com";
    echo link_to_remote($topic->getName(), array(
      'update'   => array('success' => 'topic_detail', 'failure' => 'topic_detail_err'),
      'url'      => "mymodule/myaction?t="$url,
      'script'   => 'true',
      'loading'  => visual_effect('appear', 'loading_animation'),
      'complete' => visual_effect('appear', 'topic_detail')
    ));

    Will produce this URL:

    ....mymodule/myaction/url=http://google.com

    Notice that Symfony converts the ? to a /. This is because we are using link_to_remote. link_to_remote goes through the routing layer that converts all ? to / characters. If we were using url_for, this would not be an issue.

    So how do we overcome this problem if we are using link_to_remote? Well it’s simple - use both the Symfony url_for() helper function as well as the PHP http_build_query function. The http_build_query function encodes the URL for us (so ‘/’ get converted to %2F, alongside other characters).

    So the end result looks like this:

    $url=  url_for("mymodlue/myaction")."?".http_build_query(Array("t"=>$topic->getName()));
    echo link_to_remote($topic->getName(), array(
      'update'   => array('success' => 'topic_detail', 'failure' => 'topic_detail_err'),
       'url'      => $url,
       'script'   => 'true',
       'loading'  => visual_effect('appear', 'loading_animation'),
       'complete' => visual_effect('appear', 'topic_detail')
    ));

    This forces link_to_remote to use the ? instead of replacing it with a /. Hope this helps!

    Symfony application configuration can be configured for each application in the app.yml file. One of they key problems with this is if you want an end-user to be able to change some of this application configuration, they can’t. You would need to do that for them.

    A good way to fix this is to store your applications configuration in the database. This comes with a performance cost (extra calls to your database) - so its up to you to decide if this is the most efficient solution for you.

    Here is an outline on how to get started building your application’s config in the database.

    Step 1: Define your model
    Create the columns you want to store for your application config. In this instance, we are just going to store name and value pairs (ie Attribute & Value). Here is a sample below:

    propel:
      #Application configuration table
      my_app_config:
        _attributes:                { phpName: MyAppConfig }
        id:                         { type: integer, required: true, primaryKey: true, autoIncrement: true }
        attribute:                  { type: varchar(255) }
        value:                      { type: varchar(255) }
    ...

    Once you have finished that, go ahead and build your model

    Step 2: Extend Your MyAppConfig class
    So the next step is for us to make it easy to access application configuration. For us to do this, go to your lib/model/MyAppConfig.class.php and extend your base class as follows:

    <?php
    class MyAppConfig extends BaseMyAppConfig
    {
     
      /**
       * Returns a value of an attribute string for my app's config table
       * @param string attribute name
       * @return string The value of that attribute
       * @author eHabib
       */
    	public function lookupValueFromAttribute($attribute){
      		$c = new Criteria();
      		$c->add(MyAppConfigPeer::ATTRIBUTE, $attribute);
      		$result = new MyAppConfig();
      		$result = MyAppConfig::doSelectOne($c);
    		return ($result->getValue());
    	}
    }

    Step 3: Use it!
    Let’s say that in our database we have the following attribute/value paris:

    Attribute: app_support_email, Value: myemail@mydomain.com
    Attribute: app_base_url, Value: http://whatever

    You could access this data in your code by doing this:

    $myConfig = new MyAppConfig();
    $appEmail = $smyConfig->lookupValueFromAttribute("app_support_email");
    $appURL = $smyConfig->lookupValueFromAttribute("app_base_url");
    ...
    echo $appEmail.",".$appURL; //lets print it

    That’s it!

    Step 4: Configure it via your front-end
    The last step (if you wish) is to allow the end-user to update the value of these fields. To do this, you can build a form to update these fields, or use Symfony’s admin generator.

    One of the things that might not be clear to Symfony developers is when to use app.yml for your application config and when to use project-level configuration for all your applications.

    If your configuration parameter is for all your applications you should place this configuration setting within your config/ProjectConfiguration.class.php (Symfony 1.2.x). This will save you duplicating the config within each app’s app.yml and you only have to specify it once.

    Setting up project-level config
    You can set project-wide config by editing this file.

    ....
    class ProjectConfiguration extends sfProjectConfiguration
    {
      public function setup()
      {
        //In my example below I want to tell my app if it should use a proxy or not 
        sfConfig::set('use_proxy', '1');	//1 = True, 0=False
        $this->enableAllPluginsExcept(array('sfDoctrinePlugin', 'sfCompat10Plugin'));
      }
    }

    Anything you place in your project-level config can be accessed from any application (or Symfony task) by using the standard sfConfig::get() static method.

    Note: You would normally have to get the application context if you want to read an application config within a Symfony Task. In this case, you wouldn’t because the config is set at a project level.

    In previous Symfony versions it was quite difficult and painful to link your applications together (eg frontend and backend).

    Yesterday the Symfony crew posted a tutorial on how easy it is in Symfony 1.2. In essence, its a simple config change and a change to your sfApplicationConfiguration class.

    However, this method does still require an absolute hard-code of your application URL within your class (or you could put it in a config setting in your app.yml and read it off there). Even though it required some hard-coding, it still is much simpler and easier to use than Symfony 1.0 and 1.1 methods.

    For more information - check out the Symfony blog post.

    Symfony nerds tip: To avoid hard-coding as much as possible, you could in your app.yml define your application URL’s for each environment (eg. DEV, TEST and PROD). Once you have, in your application configuration class (which extends sfApplicationConfiguration), you could read the config variable for your application URL, but automate that via sfConfig::get(’sf_environment’). And depending on what environment you are in, you will select the application URL that corresponds to that environment.