What are they Allowable Values in a Symfony 2 Currency Field Type?

500-leonesI love Symfony 2, don’t get me wrong, but the documentation? Lacking.

I know the documentation is always improving, and that essentially no one likes to document when they could be coding, AND that they are always asking for volunteers to help improve the documentation.

The thing is, how do you know what documentation is missing, until you stumble across it during your day-to-day Symfony-ising?

Well, today was one of those days.

I just happened to be using the Money field type for first time ever. And I wanted a few different currency symbols to be made available to my end users.

All good?

Well, not quite.

The documentation lists the default option of Euro. But as we all know, the UK will never enter the Euro. At least, not on my watch.

But what other values are there?

Well, GBP seemed like a good choice. And it’s there. Great.

And there are a few hundred or so more. But where to find them?

Try here.

Just look at that path, Christ on a bike.

Still, at least now you can count all the Albanian Lek your users can throw at you.

How to use Raw SQL Queries in Symfony 2

hashtag-doctrine-dogSometimes, usually when you first start with Symfony 2 (but there are other times too), you just want to get access to good old raw SQL.

I have experimented with a few different ways, and – as with many Symfony issues – the documentation is either shonky, or worst, so cryptic it requires a Mensa-like IQ level to decode just WTF these guys are talking about.

Now, if you are new to Symfony 2, before you go about using raw SQL for everything, do yourself a favour and make sure you learn Doctrine.

And then only if you absolutely must go native SQL, then use the following:

    public function foobar($foobar)
    {
      $stmt = $this->getEntityManager()
                   ->getConnection()
                   ->prepare('SELECT COUNT(id) AS num, foo FROM bar WHERE foobar = :foobar GROUP BY foo');
      $stmt->bindValue('foobar', $foobar);
      $stmt->execute();
      return $stmt->fetchAll();
    }

I love this, and I have used it with great success!, but the credit is not mine, it belongs to a user called althaus on the old symfony forums.

How to render just the CSRF token in Symfony 2

forged-moniesIt’s not often – thankfully – that I need to customise a form to the extent where I can’t use the lovely {{ form_rest(myForm) }} functionality, but when that situation arises, and you still need the CSRF token, you may think you need to go into your form type and add it in there, in some weird hidden field.

The manual page is quite deceptive in that it helps perpetuate this viewpoint.

Thankfully the solution is very simple, simply put: {{ form_row(form._token) }} in your twig template and Symfony’s form creator and rendering processes will handle the rest.

I can’t take the credit for this one, so here’s my original source of knowledge.

Trouble moving your Symfony 2 install to a new server?

nail-biting-nerdI just moved my biggest Symfony 2 site to a new server, and in doing so I made a couple of discoveries.

Discovery #1 – that .git folder? Pretty important.

Yeah, I thought I would save myself the megabytes by not uploading the .git folder. Oopsie.

Turns out that composer throws a fit if you don’t. My bad.

Easily rectified, right? Just upload the damn folder. Ooh the megabytes.

Discovery #2 – that vendors folder? Pretty useless.

The megabytes I saved not uploading the .git folder I figured were well spent on uploading the vendors folder.

Nope.

I ended up getting a funky error:

[RuntimeException]
 Failed to clone http://github.com/fabpot/Twig.git via git, https and http protocols, aborting.
- git://github.com/fabpot/Twig.git
 fatal: No such remote 'composer'

When trying to run composer update.

In the end I had to delete the entire vendors folder (sudo rm -rf vendors/) and re-run composer update.

The conclusion? So many wasted megabytes.

But hey, bandwidth is cheap, right?

How To Use Doctrine Fixtures with Discriminator Maps

my-pet-cat-entityThis is one of those issues where over-thinking the problem is usually the cause of your problems.

When using a discriminator map, for example, if using Single Table Inheritance, your ‘mapped entity’ will be extending a base entity.

For example, we might have the Pet base entity, and the PetCat extended entity.

Inside our fixture, we might do something like this:

namespace MCM\PetBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\Persistence\ObjectManager;

use MCM\PetBundle\Entity\Pet;

class LoadPetCatData extends AbstractFixture implements OrderedFixtureInterface
{
    /**
     * {@inheritDoc}
     */
    public function load(ObjectManager $manager)
    {
        $cat = new Pet();
        
        $cat->setName('Friendly Cat');
        $cat->setFood('Whiskers');

        $manager->persist($cat);

        $manager->flush();
    }

    /**
    * {@inheritDoc}
    */
    public function getOrder()
    {
        return 3; // the order in which fixtures will be loaded
    }
}

But this will throw up an error, as when doctrine tries to execute your SQL, it will be missing the discriminator mapping.

Why?

Well, above on line 7 we are using: use MCM\PetBundle\Entity\Pet;, but we need to use the extended class / entity, so we should be using: use MCM\PetBundle\Entity\PetCat;.

Then instead of instantiating the base class on line 16: $cat = new Pet();, we just instantiate: $cat = new PetCat();, and all our troubles seem so far away.

Hopefully that helps – I found nothing on Google about this when I looked, and it had me scratching my head as to why. But the answer is pretty straightforward once you stop over thinking it.

Change the text of HTML 5 validation messages in Symfony 2

If you want to change the text of the HTML5 validation messages in your Symfony 2 project, you can do it one of two ways.

The controller method:

$builder->add('email', 'email',array(
    'attr'=>array('oninvalid'=>"setCustomValidity('Would you please enter a valid email?')")
));

Or, the Twig template method:

{{ form_row(form.email, { 'attr': {'oninvalid': "setCustomValidity('Please give me a nice email')"} }) }}

Very helpful, and very easy. Thanks to Carlos Granados at StackOverflow for getting me through that the first time.

Symfony 2 Errors and their meanings

Invalid parameter: token tokenName is not defined in the query.

Check your ->setParameter() statements to make sure your tokens (or first part of the setParameter statement) matches the :tokenName you used in your createQuery.

An example:

        $query = $this->getEntityManager()
                ->createQuery('
                    SELECT
                        c
                    FROM
                        MyBigBundle:Cat c
                    WHERE
                        c.petName = :petName
                ')
                ->setParameter( 'peName' , $petName)
        ;

That would throw the error:

Invalid parameter: token peName is not defined in the query.

Because of the typo in the setParameter statement, it should be petName, but I put peName.

It sounds like it should be easy to spot, but when you have a few parameters, or you have been coding for several hours in a row, sometimes it’s easy to miss.

Doctrine Schema Validation Problems, and how to fix them

Doctrine has a habit of throwing strange errors my way – usually when things look like they should just work.

As I encounter more of them, I will add them below.

The referenced column name ‘your_field_name’ has to be a primary key column on the target entity class ‘My\FriendlyBundle\Entity\EntityName’

You may be thinking – well, just add the primary key, as it’s suggesting. But no, that would have been silly.

Instead, as is usually the case, I deleted the problem fields on both ends, and recreated them using my MySQL tool of choice, SQLyog.

After that, the foreign key relationship was recreated. Then I ran:

php app/console doctrine:mapping:import CoreDatabaseBundle annotation

followed by:

php app/console doctrine:schema:validate

And the entities were magically happy again.

This fix works well for many doctrine issues I find.

Symfony 2 Cheat Sheet

This is a work in progress, feel free to add anything useful in the comments below.

Forms

Stupid form, you go squish now!

Adding Javascript to a Form

For example, to add an onchange event to your drop down box, add the following:

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('my_example_dropdown', 'choice', array(
                'choices' => array(
                    'fish' => 'Fish',
                    'chips' => 'Chips'
                ),
                'expanded' => false,
                'multiple' => false,
                'attr' => array( 
                      'class' => 'myFancyCssClassHere',
                      'onchange'  => 'this.form.submit()',
                 ),
            ))
        ;
    }

Entity Management

Please don’t tell me that command just erased all my hard work!?

Remember, unless you specify –no-backup, you can always recover a copy of your entity by finding the “EntityName.php~” file in same directory as your existing entity.

Generating Just One Entity

This is a short How To for generating an entity for a new table you have added to your database.

Important: The filter command requires a camel cased version of your table name. So if your table was called ‘your_table_name’, be sure to use ‘YourTableName’, much like any entity references.

Change annotation to yaml, xml, or whatever you are using.

php app/console doctrine:mapping:convert annotation ./src/Your/BundleName/Resources/config/doctrine --from-database --filter="YourTableName"
php app/console doctrine:mapping:import YourBundleName annotation --filter="YourTableName"
php app/console doctrine:generate:entities YourBundleName:YourTableName --no-backup

You don’t need to take a backup on the last command, as it would create your entity twice.

This is my version of this question from Stack Overflow.

Lifecycle Callbacks

How I do Lifecycle Callbacks (with sample code)

Debugging

Sometimes things get complicated…

Exceptions

A list of all Exceptions in Symfony 2.1.2 (External link)

Doctrine

Helpful for getting a readable var_dump from a Doctrine query:

exit(\Doctrine\Common\Util\Debug::dump($yourDoctrineResult));

Logging

Write to the Symfony log file:

$this->get('logger')->info('Put your helpful debug text here');

Testing

Helpful for identifying what’s actually being seen by your crawler:

var_dump($client->getResponse()->getContent());
exit(var_dump($client->getResponse()->getContent()));

Twig Templates

Don’t bother with Twig’s dump functionality, get the Ladybug Bundle

{{ ladybug_dump(form) }}

Finding your Symfony 2 Version

A nice easy one:

php app/console -V