Doctrine postPersist / postUpdate events not firing in Symfony 2?

So let’s say you’ve been tinkering about with the example given on the Symfony 2 manual page for configuring a Doctrine Event Subscriber.

All is good.

Maybe you’ve been through and configured it just like the example, added a subscriber, re-run your fixtures, and everything is tickedyboo.

But then you make a few changes to the subscriber implementation. Maybe it should do something a little differently, and you want to re-save the entity, and get the new implementation to take effect.

Only, it won’t.

You tinker about some more, and everything looks sane. But for some reason, when you go to your form, change nothing, and click the Submit button, the listener just doesn’t seem to take effect.

Strange, eh?

Not exactly.

See, there’s a big ol’ list of lifecycle events that you can hook into, and do funky things with.

But each one has a weird, technical description that doesn’t quite seem to explain what it does in a way that’s easy to comprehend.

If you followed the Symfony documentation example, you might think a postUpdate or postPersist would be triggered whenever you Submit the form. After all, if you stick some debugs in your code, you can see the entity is persisted and the unit of work is flushed.

But yet, your listener doesn’t fire.

Try changing the data in your form, though, and suddenly the listener is triggered.

I’m 100% sure this isn’t a bug. It’s a misunderstanding.

But if you’ve been sat tearing your hair out as to why this damn thing just won’t do your bidding for the last 30 minutes, the only sane solution is to blog about it, and move on.

Hopefully this will save you a headache or two.

How I fixed exception ‘Bad credentials’ with FOSUserBundle

Waaaa, no fair, unexpected nasty bug message on prod? Boo.

Yup, this one just caught me out – and thankfully my good buddy, brother, and friend managed to pick it up whilst doing some ad-hoc Quality Assurance checks.

Disclaimer: This site may have been in prod, but only just, this was the first release to live 🙂 So no major foul.

Disclaimer part 2: Yes, there is now a passing Codeception acceptance test to make sure this never happens again 🙂

Ok, so this is the bug message (mainly for Google users benefit):

UPDATE `your_table_name` SET exception 'Symfony\Component\Security\Core\Exception\BadCredentialsException' with message 'Bad credentials' in /var/www/html/mysite.dev/vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php:73
 Stack trace: #0 /var/www/html/mysite.dev/app/cache/prod/classes.php(120): session_start() #1 /var/www/html/mysite.dev/app/cache/prod/classes.php(198): Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage->start() #2 /var/www/html/mysite.dev/app/cache/prod/classes.php(498): Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage->getBag('attributes') #3 /var/www/html/mysite.dev/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/ContextListener.php(76): Symfony\Component\HttpFoundation\Session\Session->get('_security_main') #4 /var/www/html/mysite.dev/app/cache/prod/classes.php(2463): Symfony\Component\Security\Http\Firewall\ContextListener->handle(Object(Symfony\Component\HttpKernel\Event\GetResponseEvent)) #5 [internal function]: Symfony\Component\Security\Http\Firewall->onKernelRequest(Object(Symfony\Component\HttpKernel\Event\GetResponseEvent), 'kernel.request', Object(Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher)) #6 /var/www/html/mysite.dev/app/cache/prod/classes.php(1750): call_user_func(Array, Object(Symfony\Component\HttpKernel\Event\GetResponseEvent), 'kernel.request', Object(Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher)) #7 /var/www/html/mysite.dev/app/cache/prod/classes.php(1683): Symfony\Component\EventDispatcher\EventDispatcher->doDispatch(Array, 'kernel.request', Object(Symfony\Component\HttpKernel\Event\GetResponseEvent)) #8 /var/www/html/mysite.dev/app/cache/prod/classes.php(1847): Symfony\Component\EventDispatcher\EventDispatcher->dispatch('kernel.request', Object(Symfony\Component\HttpKernel\Event\GetResponseEvent)) #9 /var/www/html/mysite.dev/app/bootstrap.php.cache(2965): Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch('kernel.request', Object(Symfony\Component\HttpKernel\Event\GetResponseEvent)) #10 /var/www/html/mysite.dev/app/bootstrap.php.cache(2938): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1) #11 /var/www/html/mysite.dev/app/bootstrap.php.cache(3087): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #12 /var/www/html/mysite.dev/app/bootstrap.php.cache(2337): Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #13 /var/www/html/mysite.dev/web/app.php(27): Symfony\Component\HttpKernel\Kernel->handle(Object(Symfony\Component\HttpFoundation\Request)) #14 {main}

Lovely job.

Anyway, this was a pretty simple fix, even though there isn’t much about this on Google.

As part of this project, I needed to overwrite the default FOS User Bundle templates with my own. A pretty typical task tbh.

As it happened though, I had removed kept the FOS User Bundle error logic, but removed the translation part.

Something like this:

 {% trans_default_domain 'FOSUserBundle' %}
{% block fos_user_content %} {% if error %}
{{ error.messageKey|trans(error.messageData, 'security') }}
{% endif %}

And I had gone ahead and changed that in my template to something like this:

 {% block content %} {% if error %}
{{ error.messageKey|trans(error.messageData, 'security') }}
{% endif %} {% endblock %}

So I had removed the trans_default_domain, which in turn, was – as far as I can tell – then simply rendering out the full stack trace.

I’m not sure if FOSUserBundle catches that error, and then translates it? That seems kinda unusual to me.

Anyway, hopefully this saves someone some time, someday, some time soon 😉

How to Fix Composer: ErrorException fork failed – cannot allocate memory

If you’re trying to deploy a Symfony project – or Laravel, or pretty much anything using composer – and your deployment server is a little anemic on the memory side of things (Amazon AWS, tiny Digital Ocean droplets, even the basic Linode offerings), then you may well hit an issue when you run composer update during your deploy.

There’s quite a long github issue thread about it, and another one on Stack Overflow.

Anyway, after much frustration, here is my fix – which works for me, hopefully it will work for you too.

This assumes you are developing locally and then deploying to prod using git, which I highly advise. But it will work for any type of deploy if you follow the steps and tweak accordingly.

  1. Do a composer update on your dev box
  2. Commit the updated composer.lock file
  3. Push the commit with the lock file to your prod server (or wherever you’re getting that error)
  4. From the prod server (or wherever), delete your composer cache (rm -rf ~/.composer)
  5. From the prod server (or wherever), delete your vendor folder (rm -rf vendor)
  6. From the prod server (or wherever), run composer install

So, whilst the above is the high level overview, here’s how I did it irl.

  1. Using ssh, I connected to my development virtual machine (VMWare), where from the Symfony root dir (/var/www/html/myproject.dev/), I ran composer update
  2. Wait a while, whilst number 1 finishes.
  3. From my local machine, download the newly updated composer.lock file. Git sees this as a changed file, and so I can then do a git commit on the updated composer.lock file
  4. As I have already set up a git remote for my production server, I can then do a git push for the commit with the lock file to my production server.
  5. When the commit hits the server, the post-receive hook is run automatically, which does all the Symfony deploy stuff like clearing the cache, dumping assets, reinstalling vendors etc.
  6. If all this sounds great, but deploying with Git is something you’re unsure of, I highly recommend this Digital Ocean article, which makes it very easy.

    Hope it helps!

Testing Sessions With Codeception

Testing sessions is a pain.

But it’s better to have pain when testing than pain when the site is live.

So test we must.

He was aiming for the bike

Ok, so as I’m all about Symfony 2, this is going to be an example of testing a Symfony 2 Service that makes use of Sessions.

First of all, we define our service so that the Symfony 2 Session (Symfony\Component\HttpFoundation\Session\Session) is injected for us.

#src/Our/BundleDir/OurBundle/Resources/Config/services.yml
    our.bundle_dir.our_bundle.our_session_manager:
        class: Our\BundleDir\OurBundle\Service\OurSessionManager
        arguments:
            - "@session"

Hoorah, nice and easy. Feel free to use whatever naming convention you like for your service. Personally I like to make it quite verbose, so it’s super easy to track down where that service lives when you return to your project in 6 months time.

If you’re wondering what other things can be injected, then this chapter of the Symfony docs is worth a read. Also, try using:

php app/console container:debug

on your command line.

So, we have our service definition. Now to make our service, and our associated test file.

The service itself:

namespace Our\BundleDir\OurBundle\Service;

use Symfony\Component\HttpFoundation\Session\SessionInterface;

class OurSessionManager
{
    const OUR_THING = 'our_thing';
    private $session;

    public function __construct (SessionInterface $sessionInterface)
    {
        $this->session = $sessionInterface;
    }
    
    public function setOurConstantThing($value)
    {
        $this->session->set(self::OUR_THING, $value);
        $this->session->save();

        return $this->session;
    }

    public function getOurConstantThing()
    {
        return $this->session->get(self::OUR_THING, '');
    }
}

Your implementation can do whatever it likes. This example is deliberately convoluted to show off a few things.

Naughty, we wrote an implementation without writing some tests. Let’s fix that before we get in trouble.

class OurSessionManagerTest extends \Codeception\TestCase\Test
{
    private $serviceContainer;
    private $service;
    protected $codeGuy;

    protected function _before()
    {
        $this->serviceContainer = $this->getModule('Symfony2')->container;
        $this->service = $this->serviceContainer->get('our_session_manager');
    }

    protected function _after()
    {
        session_destroy();
    }


    public function testCanSetOurConstantThing()
    {
        $session = $this->service->setOurConstantThing('my_value'); /** @var $session \Symfony\Component\HttpFoundation\Session\SessionInterface */

        $this->assertInstanceOf(
            '\Symfony\Component\HttpFoundation\Session\SessionInterface',
            $session
        );

        $this->assertEquals(
            'my_value',
            $session->get(\Our\BundleDir\OurBundle\Service\OurSessionManager::EMAIL_TOKEN)
        );
    }

    public function testCanGetOurConstantThingWhenSet()
    {
        $this->service->setOurConstantThing('another_value');

        $this->assertEquals(
            'another_value',
            $this->service->getOurConstantThing()
        );
    }

    public function testGetDefaultValueWhenAValueIsNotSet()
    {
        $this->assertEquals(
            '',
            $this->service->getOurConstantThing()
        );
    }

}

The third test is testing the following:

return $this->session->get(self::OUR_THING, '');

Essentially that’s saying, get me our thing, or return ” if our thing isn’t set. You’re free to return null, a default value, or whatever.

But the critical thing for testing sessions is the _after() method. This is killing off any residual session stuff, and then on each new test, starting with a blank slate – so to speak.

So, there we go. Testing sessions in codeception.

glhf!

Book Review РExtending Symfony2 Web Application Framework by S̩bastien Armand

[Full Disclosure: I was asked to review a copy of Extending Symfony2 Web Application Framework by Sébastien Armand, and put a review on this blog.

The review is my own, and is in no way influenced by Packt Publishing]

The Symfony framework has – in my opinion – a lack of decent third party reading material generally available. One quick search on Amazon will show you what I mean. That’s not to say that there aren’t books available, but many of them are self-published, or published on these new fangled platforms that old-timers like me aren’t aware of.

And you know what, I kind of get that. Many big publishers don’t want to, or can’t afford to take a risk on publishing a title that will, in all likelihood, only appeal to a few thousand-odd developers. So, like for many parts of Symfony 2, if you want to find the juicy bits, you have to do a little bit more than a Google search.

What’s in it for me?

Truthfully this isn’t a book for the beginner. If you want to learn more about Symfony and you are just starting out, try the official manual, or heck, my video tutorials are as good a place as any, even if I do say so myself.

Rather than me trying to explain in great depth what’s in each chapter, I’ve screen-grabbed the contents for your perusal.

toc-1

toc-2

The Delilah Smith Guide To Symfony

Whilst the author doesn’t come right out and say it, the book feels more like a collection of the Symfony 2 cookbook style articles, rather than a deep-dive in to each of the presented topics. And again, the chapter length is a bit of a give-away on this also.

That’s great for developers who have plenty of hands-on experience with the framework, but again, maybe not so great for advanced beginners, or early intermediates.

It is suggested that you code along as you read, and whilst that’s not always practical, being able to run the code in your head (so to speak) is hugely beneficial – but completely impractical if you don’t have that prior experience.

My other gripe with both this and the Symfony cookbook articles is that if you do get stuck, you need a base of reference (Google usually) to get a better understanding of some of the high level concepts of which the author has only scratched the surface.

This can be frustrating.

However, when learning a new framework or concept, having multiple points of reference – videos, written tutorials, checking out others code on github – is always a good idea as you will see different ways of implementing the same idea.

YAML, XML-ding-dong

Oh lordy me, I wish Sébastien had used YAML instead of XML in his examples.

The chapter I was most looking forward to – Chapter 6: Sharing Your Extensions – is a super hard to read on the Kindle with the XML dumps displaying horribly.

As I typed the code out into my IDE and could see each piece on one line instead of five or more, the whole chapter became clearer. Not good for reading on the train though.

That said, I don’t think the config can be done in YAML when sharing bundles (Configuration.php) – but do feel free to correct me if I am wrong on that one?

Altogether though, chapter 6 was super useful to me, and something I had found, shall we say, unenlightening on the Symfony website.

Isn’t this information already available for free elsewhere?

Of course. The beauty of a non-fiction title such as this is rarely that you will learn some obscure tidbit that you cannot find elsewhere. Rather, it’s the time saved by letting the author collate, refine, and re-interpret in one handy place of reference that hopefully saves you a few hours digging around the web to find by yourself.

And in that respect, I think the author has done a really good job. There’s plenty of advice from disparate blogs / websites / mailing lists that can be found here, and with helpful code samples to boot. It’s certainly not always easy to read code on a kindle, but with the code also available for separate download, I think that’s more of a problem with the current state of the Kindle than with the presentation.

Good or Bad, what’s the verdict?

On the whole I enjoyed reading this book, and learned a few things in the process.

My biggest criticism is that it’s a little on the short side for the price.

However, the fact that it was so short also ensured I actually read it, rather than being off-put as sometimes happens when I buy a six or seven hundred page monster – Core Java Fundamentals, I’m looking at you.

The tone is conversational, and at no point did I feel the chapter was too dry, tedious, or overly long, and so I never skipped ahead.

Ultimately, if you can get your company to pay for your learning materials then I would recommend you take that option first.

So tl;dr: yes, it’s a good book for intermediates onwards, but for the price it’s a little expensive considering the page count.

Extending Symfony2 Web Application Framework by Sébastien Armand is £11.89 via Packt’s official website, and on purchase you can download the book in .epub, .mobi, .pdf, and view in browser using their Packt Lib application. All formats are DRM free.

And lastly, a huge thank you to Sébastien Armand, and to Packt Publishing for asking me to review this book, I really enjoyed doing so.

Free Symfony 2.3 Tutorial Videos

If you are thinking of making the switch from Functional to Object Oriented PHP development, or have already ventured down the OOP route and tried a framework (Laravel, Cake, Yii, or similar), chances are you will have hit a few stumbling blocks or worse, given up in despair.

I remember being there.

I also remember wishing someone had come up with some video tutorials which I could watch rather than having to slog through the manuals.

Now, don’t get me wrong – you are still going to need the manual, but hopefully these videos will explain the concepts visually, which you can then use as a base of reference when you start reading further.

There are six videos up in total at the moment with more on the way. If there is a specific topic you have in mind or aren’t sure of, I would be happy to make a video for it. Just leave a comment either here, or on youtube.

Full video list

Symfony 2 and Bootstrap 3 Assetic Config and Base HTML Template

This doesn’t mean much standalone, but it’s here for future reference.

# Assetic Configuration
assetic:
    debug:          %kernel.debug%
    use_controller: false
    bundles:        [ ]
    java: /usr/bin/java
    filters:
        cssrewrite: ~
        cssembed:
          jar: %kernel.root_dir%/Resources/java/cssembed-0.4.5.jar
        yui_js:
          jar: %kernel.root_dir%/Resources/java/yuicompressor.jar
        lessphp:
          file: %kernel.root_dir%/../vendor/leafo/lessphp/lessc.inc.php
          apply_to: "\.less$"
    assets:
        jquery_js:
            inputs:
                - '%kernel.root_dir%/../components/jquery/jquery.min.js'
            filters: [?yui_js]
        bootstrap_js:
            inputs:
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/transition.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/alert.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/modal.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/dropdown.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/scrollspy.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/tab.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/tooltip.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/popover.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/button.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/collapse.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/carousel.js'
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/js/affix.js'
            filters: [?yui_js]
        bootstrap_less:
            inputs:
                - '%kernel.root_dir%/../vendor/twitter/bootstrap/less/bootstrap.less'
            filters: [lessphp,cssembed]

Original source from Joeri Verdeyen, slightly modified for Bootstrap 3.


< html>
< head>
    < title>{% block title %}Bootstrap 101 Template{% endblock %}< /title>
    < meta name="viewport" content="width=device-width, initial-scale=1.0">
    

    {% block stylesheets %}
        {% stylesheets '@bootstrap_less' combine=true %}
            
        {% endstylesheets %}
    {% endblock %}

    
    
< /head>
< body>

    {% block myBody %}
    {% endblock %}

    {% block javascripts %}
        {% javascripts '@jquery_js' '@bootstrap_js' filter='?yui_js' combine=true %}
            
        {% endjavascripts %}
    {% endblock %}
< /body>
< /html>

(Sorry, had to put the spaces in to get the whole thing to show. Silly WordPress. Thanks also Gzom!)

PHPNW2013 is Live

phpnw-2013-logoThe one conference I have been waiting for since I missed out on attending last years due to work, is PHP North West 2013.

I’m really keen to actually speak at this event, but I don’t know what topic to speak about. Something Symfony 2 related would be a good start I should imagine.

When you attend a conference like this, what is the one topic that you would like to learn more about that never seems to be covered?

Please leave your suggestions in the comments box. And if I can come up with something I will post about it here.

How I Use Doctrine Fixtures with Symfony 2 for an Easier Life

John-Bercow

Back in the days of yore, sometime around Yesteryear, I became aware of Doctrine Fixtures.
I’m not going to go into any great depth as to what they are, what they do, or why you should use them, because frankly if you don’t use them you are a silly billy bean.

Anyway, one thing that makes them not so great is that in the default implementation given in the Symfony documentation, you end up with a db table with auto incremented records starting at some crazy number, incrementally higher and higher every time you run the fixtures.

I’m sure there are ways round this, ways I have never imagined. But as I haven’t imagined them, I can’t very well implement them.

So here is my one line wonder (well, one line after a few other lines have been typed once) to always get you back to zero.

Command and ConqueraSillyBean_by_aSillyBean

Sadly this has nothing to do with that bald headed NOD goon Kane, or that commando guy who killed people with one shot. But programming is quite a dull subject when written down, so I have to spice up my posts with references to more fun times.

Anyway, what we actually need is a Symfony Console Command.

Wait, wot? This sounds hard and scary and I can’t really be bothered learning new things right now.

Actually it’s not – it’s really easy, and it saves time. Your time. Time that you can now invest in bettering yourself. Or on Reddit. Whatever.

Ok, I’m just going to paste my code, then walk through it.

namespace MCM\MyExampleBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class PurgeEverythingCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this
            ->setName('mcm:purge')
            ->setDescription('Purges the test database, reloads the fixtures, and triggers a test routine.')
            ->addArgument('confirm', InputArgument::REQUIRED, 'You must confirm you wish to erase everything in your test database!')
            ->addOption('notests', null, InputOption::VALUE_NONE, 'Run without the test suite')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // flatten all the tables
        exec('php /full/path/your/site/root/app/console doctrine:schema:drop --env=test --force');

        // recreate the schema
        exec('php /full/path/your/site/root/app/console doctrine:schema:create --env=test');

        // clear any doctrine caches
        exec('php /full/path/your/site/root/app/console doctrine:cache:clear-metadata --env=test');
        exec('php /full/path/your/site/root/app/console doctrine:cache:clear-query --env=test');
        exec('php /full/path/your/site/root/app/console doctrine:cache:clear-result --env=test');

        // re-create the fixtures
        exec('php /full/path/your/site/root/app/console doctrine:fixtures:load -n --env=test');

        if( !$input->getOption('notests') )
        {
            // re-run the tests
            system('phpunit -c app/');
        }
    }

}

Meep, the computer is now telling you exactly what you could do with a life time supply of chocolate.

Ok – you will have gathered from the first line that Commands go in a command sub folder off your Bundle directory. They don’t have too, but it’s a nice convention.

Make sure you suffix your file name with Command.

Then we get to the configuration:

    protected function configure()
    {
        $this
            ->setName('mcm:purge')
            ->setDescription('Purges the test database, reloads the fixtures, and triggers a test routine.')
            ->addArgument('confirm', InputArgument::REQUIRED, 'You must confirm you wish to erase everything in your test database!')
            ->addOption('notests', null, InputOption::VALUE_NONE, 'Run without the test suite')
        ;
    }

The setName method is saying what will we need to type in after app/console to get this command to run. You can put anything here.

The setDescription method is what you will see as a bit of helper text after your string from setName when you type in just app/console and hit return.

The addArgument method sets an argument on the command line that could allow the user to modify the outcome of your script depending on what arguments they choose. In my case, I hide this command initially so a user doesn’t accidentally type in the command and delete everything.

Lastly, the addOption method again allows more granular configuration of how parts of your command will run. In our case, whether or not to run the test suite after recreating the db.

Click here for a full breakdown of all the available ‘actions’ available when using a Command.

And now the actual guts of the how the command executes:

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // flatten all the tables
        exec('php /full/path/your/site/root/app/console doctrine:schema:drop --env=test --force');

        // recreate the schema
        exec('php /full/path/your/site/root/app/console doctrine:schema:create --env=test');

        // clear any doctrine caches
        exec('php /full/path/your/site/root/app/console doctrine:cache:clear-metadata --env=test');
        exec('php /full/path/your/site/root/app/console doctrine:cache:clear-query --env=test');
        exec('php /full/path/your/site/root/app/console doctrine:cache:clear-result --env=test');

        // re-create the fixtures
        exec('php /full/path/your/site/root/app/console doctrine:fixtures:load -n --env=test');

        if( !$input->getOption('notests') )
        {
            // re-run the tests
            system('phpunit -c app/');
        }
    }

All this is doing is dropping the database in the Test (--env=test) environment; recreating the database schema; clearing any Doctrine caches; and then importing the fixtures into a fresh, empty table structure.

This gets round any pesky foreign key relationship issues when trying to empty tables.

Optionally, the test suite will then be triggered.

The three Doctrine cache clear commands are probably not even needed – but it happens behind the scenes against a test database, so why not?

Downsides

Yeah there are downsides to this sadly.

The biggest downside is that if there are any errors, you will get the error but no context of where the error occurred.

In my case, there are only three possible outcomes.

  1. It all worked so you are just dumped back to your command line
  2. Your fixture import failed – a red box with an error shows up
  3. Your fixture test failed – outputs some combination like …..!…EEEE..

Point one is good. So we move on happier in our lives, more time to post cat pictures to reddit.

Point two is not so good.

In this case you are best to run the schema:drop, schema:create, and fixtures:load commands from the above script again by hand, and see which fixture is bombing out.

Point three is also not so good.

In this case you need to run the test suite manually as it gives a more verbose output of the results.

Lights, Camera, Action-diddly-action-Jackson!

So to finally run this bad boy we just type in:

php app/console mcm:purge --notests confirm

You can omit the --notests option as it’s optional, but the confirm argument is required remember. This is just a fail safe.

So it might seem a little crazy and have a few downsides, but 99% of the time this is a real time saver, so get on it.