Type confusions in Symfony

July 24th, 2008

I post this merely to point out how easy it is to forget to check EVERYTHING when you appear to have a bug.

I’ve been trying unsuccessfully for the last hour to save a string to my database, like

$myString = 'test';
 
$object = new MyObject();
 
$object->setName($myString);
 
$object->save();

No matter how often I checked, including the requested value from the form submitting the data, the data held once in the object etc, the database only ever saved a 0.

So for example:

$myString = 'test';
 
$object = new MyObject();
 
$object->setName($myString);
 
$object->save();
 
echo $object->getName();

would give

test

Yet the value in the MySQl DB says 0, despite being set to a varchar(255).

So what was it? Propel. When I initially built the model, my schema.yml had defined ‘name’ as being an int. Of course, I had decided to change this, and changed it in the schema.yml… but forgot to rebuild the model… doh! The ORM was stepping in and saying “no, screw you buddy, you said this was an Int”

Written by baj | Posted on July 24th, 2008 in symfony | 3 Comments »

Just a quicky, as I had to spend 10 mins looking this up hopefully it’ll save someone else time later.

I need to retrieve a random user from the users table, but didn’t know how to express a random record… this method will return a single random user, or can take an argument to return more than 1
UserPeer

	public static function getRandomUsers($num=1){
		$c = new Criteria();
		$c->addAscendingOrderByColumn('rand()');
		$c->setLimit($num);
		$users = UserPeer::doSelect($c);
		return $users;	
	}

Simple. (and probably documented better somewhere else)

Written by baj | Posted on April 21st, 2008 in symfony | 3 Comments »

Had an issue on Acme Video Website where I want to prepopulate some content on a form element after making an ajax call to a 3rd party API.

Problems comes, in that observe_field (part of the prototype javascript library), is designed to update a div, so I was incorrectly trying to do this:

echo observe_field('hint', array(
'update' => "tags",
'url' => '@embargo_helper',
'with' => "'hint=' + $('hint').value",
'loading' => "'Loading...'"
));

So it should take the value of the hints input element, go off to the 3rd party api, grab the info I need then pop it into the input that has an id of tags … only that doesn’t work, of course.

So what’s the solution?
Ok, a 3 step process, firstly we need to add script = true in our observe_field parameters, letting the observe_field know that it’s going to have script returned that it will need to execute

echo observe_field('hint', array(
'update' => "tags",
'url' => '@embargo_helper',
'with' => "'hint=' + $('hint').value",
'loading' => "'Loading...'",
'script' => true
));

note: true without quotes.

Next, here’s a look at my action, of course it’s not really that important, you do whatever you need to do here:

Action

  public function executeHelper(){
  	$this->description = myTools::getDescriptionFromHint($this->getRequestParameter('hint'));
  }

Then you need to edit the view to execute some javascript:

View

<?php $sf_context->getResponse()->setContentType('text/javascript') ?>
$('tags').value = '<?php echo $description;?>';

And there we go…

…now, I’m not entirely happy with this for 2 reasons. Firstly, the update parameter of the observe_field call is now redundant, and secondly, the id of the input we want to update is now harded in the view, so to at least get around that, we can add an extra parameter in the with part of observe_field like this:

echo observe_field('hint', array(
'update' => "tags",
'url' => '@embargo_helper',
'with' => "'hint=' + $('hint').value+ '&updateid=tags'",
'loading' => "'Loading...'",
'script' => true
));

…so now we are letting our symfony code know what ID we want to update, so lets re-edit the action to take this into account:

Action

  public function executeHelper(){
  	$this->description = myTools::getDescriptionFromHint($this->getRequestParameter('hint'));
        $this->updateid = $this->getRequestParameter('updateid');
  }

and then let the view know which id it needs to update:

View

<?php $sf_context->getResponse()->setContentType('text/javascript') ?>
$('<?php echo $updateid;?>').value = '<?php echo $description;?>';

I’m sure there’s a neater way of doing this, but hell, got it working anyway.

Written by baj | Posted on April 19th, 2008 in symfony | 1 Comment »

Before and After Actions

April 18th, 2008

Ever wanted to run some functions or create some variables before every action in a module? Or ever wanted to do something crafty after every action and before the template gets displayed?

Even if your answer is no, its good to be aware for future projects, that Symfony is hiding some very valuable methods of doing such things. Read on…

For doing stuff before every action in your module, just create a new function called preExecute() like so:

class demomoduleActions extends sfActions
{
  public function preExecute()
  {
    // ... put your stuff here that you want to run before every Action ...
  }
}

Equally for doing anything you like after every action in your module just create a new function called postExecute() like so:

class demomoduleActions extends sfActions
{
  public function postExecute()
  {
    // ... put your stuff here that you want to run after every Action ...
  }
}

It really is as simple as that. I stumbled across a little while ago whilst working on a project for a client of mine. So I thought I’d share this with you all.

Cheers,
Rob

Written by Rob | Posted on April 18th, 2008 in symfony | 4 Comments »

Weird one this, or so I thought.

Search functionality on “acme video website” was throwing up some odd errors.

View:
echo form_tag('/search');

Routing:
search:
url: /search/*
params: { module: video, action: search }

Performing a search, well, It didn’t break, but it went to:
http://www.acmevideowebsite.com/frontend_dev.php/search/module/search/action/index

all together now, “wtf?”

So, tried referencing the route with @
echo form_tag('@search');

Same result, so checking the generated HTML, we see…
action="/frontend_dev.php/search/module/default/action/index"

We now cross over live to the MSN conversation with Rob:

Baj:// says:
search:
url: /search/*
params: { module: video, action: search }
Baj:// says:
params:? or param:
Rob says:
lol

Grr, as usually with my coding, it’s something stupid… what confused me most was that it didn’t error on an incorrectly configured YAML file… probably because params: could be a legal term?

Anyway, back to work!

Written by baj | Posted on April 16th, 2008 in symfony | No Comments »

AJAX Auto Complete

April 4th, 2008

I recently came across the problem of using multiple input_auto_complete_tag’s. The problem was I needed to store the ID of my first category somewhere, so I could filter the sub-categories returned in the second input_auto_complete_tag. Also, just to make things a little more complicated, there is a third input_auto_complete_tag that needs results filtered by the previous IDs.

Follow me, as I take you through a quick tutorial of how I did it:

Form Template:

Firstly we need create our form with the three auto completing inputs. To enable us to store the IDs that are returned we’ll put in a hidden tag for each input tag. Also to enable us to get the ID of the selected result, we’ll need a tiny bit of Javascript which runs after selecting the result.

/apps/app/modules/module/templates/formSuccess.php

<?php use_helper('Javascript'); ?>
<p><?php
  // SELECT A
  echo
    input_hidden_tag('select_a_id', '').
    input_auto_complete_tag(
      'select_a',
      '',
      'module/autoComplete?select=a',
      array('autocomplete' => 'off'),
      array(
        'use_style'             => true,
        'after_update_element'  => "function (inputField, selectedItem) { $('select_a_id').value = selectedItem.id; }"
      )
    );
?></p>
<p><?php
  // SELECT B
  echo
    input_hidden_tag('select_b_id', '').
    input_auto_complete_tag(
      'select_b',
      '',
      'module/autoComplete?select=b',
      array('autocomplete' => 'off'),
      array(
        'use_style'             => true,
        'after_update_element'  => "function (inputField, selectedItem) { $('select_b_id').value = selectedItem.id; }",
        'with'                  => " value+'&amp;select_a_id='+$('select_a_id').value"
      )
    );
?></p>
<p><?php
  // SELECT C
  echo
    input_hidden_tag('select_c_id', '').
    input_auto_complete_tag(
      'select_c',
      '',
      'module/autoComplete?select=c',
      array('autocomplete' => 'off'),
      array(
        'use_style'             => true,
        'after_update_element'  => "function (inputField, selectedItem) { $('select_c_id').value = selectedItem.id; }",
        'with'                  => " value+'&amp;select_a_id='+$('select_a_id').value+'&amp;select_b_id='+$('select_b_id').value"
      )
    );
?></p>

The ‘after_update_element’ simply takes the ID from the element we’ve clicked on, in the results of the auto complete function and places it into the hidden tag.

Select B and Select C use the ‘with’ argument to send the ID of the previous selects to our auto complete function (module/autoComplete) enabling us to filter the results we’ll display in the input_auto_complete_tag.

Auto Complete Function:

The auto complete function will be called everytime we’re typing into one of the input_auto_complete_tag’s. As you can see in the code above, we’re also passing through a parameter (eg. module/autoComplete?select=a) specifying which input is calling the function.

/apps/app/modules/module/actions/actions.class.php

<?php
  public function executeAutoComplete()
  {
    // Get which input called the function
    $input = $this->getRequestParameter('select');
    // Get what the user has typed
    $typed = $this->getRequestParameter('select_'.$input);
 
    // Depending what input called this generate $db_results for the template
    switch ( $input )
    {
      case 'a':
 
        //... generate $db_results
 
        break;
 
      case 'b':
        // Get the IDs of previous inputs
        $a_id = $this->getRequestParameter('select_a_id');
 
        //... generate $db_results using $a_id to filter results
 
        break;
 
      case 'c':
        // Get the IDs of previous inputs
        $a_id = $this->getRequestParameter('select_a_id');
        $b_id = $this->getRequestParameter('select_b_id');
 
        //... generate $db_results using $a_id and $b_id to filter results
 
        break;
    }
 
    // Create array with the ID and Name
    $results = array();
    foreach ( $db_results as $row )
    {
      $results[ $row->getId() ] = $row->getName();
    }
 
    // Pass results to the template
    $this->results = $results;
  }
?>

I’ll leave you to generate the database results, as this is very specific to your database model. Although as a hint, it’s usually best to use an LIKE query:

$criteria->add( YourTablePeer::NAME, $typed.'%', Criteria::LIKE );

Auto Complete Template:

/apps/app/modules/module/templates/autoCompleteSuccess.php

  <ul>
    <?php foreach ( $results as $key => $value ): ?>
      <li id="<?php echo $key ?>"><?php echo $value ?></li>
    <?php endforeach; ?>
  </ul>

The auto complete template simply returns a unordered list which will be displayed on our form for the user to select from.

It’s really as easy as that, but if you’ve got any questions or queries about how to get it working for you, please leave a comment and I’ll try and help you out.

Written by Rob | Posted on April 4th, 2008 in symfony | 14 Comments »

Colons in YAML (view.yml)

April 4th, 2008

Having just launched “Acme Video Website”, I tinkered with a few things, re-synced and got…

Warning: htmlentities() expects parameter 1 to be string, array given in /var/www/acme_video/docs/lib/symfony/config/sfViewConfigHandler.class.php on line 229

Ouchy. So I checked sfViewConfigHandler.class.php to see what was causing the fuss…

$data[] = sprintf(" \$response->addMeta('%s', '%s', false, false);", $name, str_replace('\'', '\\\'', preg_replace('/&(?=\w+;)/', '&', htmlentities($content, ENT_QUOTES, sfConfig::get('sf_charset')))));

Sadly, my regex is non-existent so I’m not entirely sure what’s being replace, however, it was obvious there was an illegal character defined in my meta keyword/description in my View.yml (actual title changed to protect anonimity of website)

title: Lorem ipsum dolor sit amet: consectetuer adipiscing elit.

Hmm, completely forgot about the colon there (after “amet”), whatever’s parsing the yml is obviously having a fit, and I need to leave that in there as the site owner wants to keep the original meta stuff for now.

So, tried \: to escape it, no deal! Time for a quick google, obviously…

According to the specifications for yml,
“Within double-quotes, special characters may be represented with c-style escape sequences starting with a blackslash”

title: "Lorem ipsum dolor sit amet\: consectetuer adipiscing elit."

Which physically put \: in the html that was generated, so removing the \ gave be the desired result. All I had to do was encase the entire line in quotes, (and checking the source of the generated html it was fine too, not doing anything funky like 2 sets of quotes, always worth checking!).

Sure enough, refering to the excellent “The Definitive Guide To Symfony” book, page 65 “Help, a YAML File Killed My App” shows exactly what I should have done anyway…

Todays lesson, don’t be so lazy, the book is only 2 meters across the corridor!

Written by baj | Posted on April 4th, 2008 in symfony | 4 Comments »