Symfony is a powerful PHP based application framework, and has continued to improve through the hard work and support of its authors and contributors. However - there are a couple things that could be added that would greatly simplify the life of the user(me :)). Once you carefully define your relational database schema, you run a series of commands to create your object model - a set of PHP based objects that map to your database tables; and also create your admin scaffolding - a set of CRUD pages that provide web access to maintain your database model. All of this happens within minutes. Pretty powerful.
Once you’ve gotten this far, though, all foreign key references in your data will show up in your CRUD pages with their given row id’s, not the name or title that you’ve given that object in its table. You can then create a __toString() method in that objects class file to return a human readable string for listings and edit pages. This is relatively straightforward, but if you have a large model, you may begin to find this somewhat tedious - as described in Michael Kimsal’s blog.
So - as many of the tables that I utilize in Symfony will typically have either a ‘name’ or ‘title’ field, I’d benefit from having the __toString() method automatically generated if my object has either the ‘name’ or ‘title’ field. Turns out this is not that difficult to accomplish. I’ve made the following modifications to a Symfony 1.2.4 install.
By adding an addToStringAccessor() method to /usr/share/pear/symfony/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/builder/om/php5/PHP5ObjectBuilder.php:
/**
* Adds a __toString method in order to provide default human readable
* representation of any table that has a field named 'name' or 'title'.
* @param string &$script The script will be modified in this method.
* @param Column $col The current column.
*/
protected function addToStringAccessor(&$script, $column_name)
{
$script .= "
public function __toString()
{
return \$this->$column_name;
}
";
}
and then by modifying the addColumnAccessorMethods() method in /usr/share/pear/symfony/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/builder/om/ObjectBuilder.php from:
protected function addColumnAccessorMethods(&$script)
{
$table = $this->getTable();
foreach ($table->getColumns() as $col) {
// if they're not using the DateTime class than we will generate "compatibility" accessor method
if ($col->getType() === PropelTypes::DATE || $col->getType() === PropelTypes::TIME || $col->getType() === PropelTypes::TIMESTAMP) {
$this->addTemporalAccessor($script, $col);
} else {
$this->addDefaultAccessor($script, $col);
}
if ($col->isLazyLoad()) {
$this->addLazyLoader($script, $col);
}
}
}
to:
protected function addColumnAccessorMethods(&$script)
{
$table = $this->getTable();
$to_string_column = '';
foreach ($table->getColumns() as $col) {
$column_name = strtolower($col->getName());
if(($column_name == 'name' || $column_name == 'title') && $to_string_column != 'name'){
$to_string_column = $column_name;
}
// if they're not using the DateTime class than we will generate "compatibility" accessor method
if ($col->getType() === PropelTypes::DATE || $col->getType() === PropelTypes::TIME || $col->getType() === PropelTypes::TIMESTAMP) {
$this->addTemporalAccessor($script, $col);
} else {
$this->addDefaultAccessor($script, $col);
}
if ($col->isLazyLoad()) {
$this->addLazyLoader($script, $col);
}
}
if($to_string_column != ''){
$this->addToStringAccessor($script, $to_string_column);
}
}
you’ll have your Symfony installation automatically generating __toString() methods in whichever objects have a name or title field. If you notice, I prefer the name field if there are both name and title fields in the table. Of course, you can change those keywords to whatever you use in your database schema. A useful modification would be to key off of an array that you’d defined in the settings.yml file, instead of having these column names hardcoded here.
This is part one of this solution. The second part involves automatically referencing these in your listing pages. Typically a listing, when automatically generated, will display the row id of any foreign key references in your table and corresponding PHP object.
If you utilize the powerful Admin Generator, you can quickly indicate which fields you want displayed(and how) in each edit or listing page. Utilizing this and a separate little PHP file called a partial, you can reference the object pointed to by your foreign key reference by calling its __toString() method. Each partial looks something like this:
$customer = CustomerPeer::retrieveByPk($customer_communication->getCustomerId()); echo $customer->__toString()
So - in an effort to not have to write a partial for each and every foreign key reference that you want to display in a listing, you can add the following clause to the getColumnListTag() method in /usr/share/pear/symfony/plugins/sfPropelPlugin/lib/generator/sfPropelCrudGenerator.class.php
else if($column->isForeignKey())
{
$getter = 'get'.$this->getRelatedClassName($column);
return sprintf('$%s->%s()', $this->getSingularName(), $getter);
}
Once you have this in place, your listing will automatically now look like you intended:
Caveats: For any foreign key references whose tables do NOT have a name or title field, you will get an error when trying to display the listing, until you define a __toString() method.
These two changes are mutually exclusive, although they do work well together.

