Symfony 1.4
Symfony – Doctrine – Call to a member function evictAll on a non-object
1If 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)
5The 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
0If 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: app/<app_name>/config/<old_app_name>Configuration.class.php TO app/<app_name>/config/<new_app_name>Configuration.class.php
Edit: app/<app_name>/config/<new_app_name>Configuration.class.php to:
class <new_app_name>Configuration extends sfApplicationConfiguartion
Now just clear cache and load your site through the new controller and it should work as expected.
Symfony – Autoload.yml – Basic Usage
21Autoloading 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
1Seven 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.
