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