This tutorial has a new version, check it out!

Creating Unique Files

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $3.00

Tip

A newer version of HauteLookAliceBundle has been released and portions of this tutorial won't apply to that new version.

There's a little issue. These two kittens are the exact same filename. The first is kitten2.jpg and so is the second. That's fine for us, but imagine if we could delete characters, and if doing that deleted the image. If we deleted this character, it would delete the image for the second one too. To be more realistic, each character needs a unique image.

NO problem. Setup a new $targetFilename instead of using the original filename. Set it to fixtures_ then mt_rand() and .jpg:

... lines 1 - 15
public function preProcess($object)
{
... lines 18 - 26
$targetFilename = 'fixtures_'.mt_rand(0, 100000).'.jpg';
... lines 28 - 36
}
... lines 38 - 49

Copy the file to this filename. And make sure that the avatarFilename is our new, random thing:

... lines 1 - 15
public function preProcess($object)
{
... lines 18 - 26
$targetFilename = 'fixtures_'.mt_rand(0, 100000).'.jpg';
... lines 28 - 29
$fs->copy(
$projectRoot.'/resources/'.$object->getAvatarFilename(),
$projectRoot.'/web/uploads/avatars/'.$targetFilename,
true
);
$object->setAvatarFilename($targetFilename);
}
... lines 38 - 49

Time to reload those fixtures:

php app/console doctrine:fixtures:load

In web/uploads/avatars, we see a bunch of random filenames. And when we refresh, they're all using different filenames.

Accessing the container in a Processor

You can do whatever you want inside a Processor, but with a glaring limitation so far: you don't have access to the container or any of your services.

Let's try to log the random filenames being used for each Character. That means we'll need the logger service, and right now we don't have access to anything. To get it, we'll treat AvatarProcessor like any other service and use dependency injection. Create a __construct() function, and type-hint the argument with LoggerInterface from PSR. That'll add my use statement. Now, set that on a logger property:

... lines 1 - 6
use Psr\Log\LoggerInterface;
... lines 8 - 9
class AvatarProcessor implements ProcessorInterface
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
... lines 18 - 61
}

Before worrying about how we'll pass in the logger, go down below and log a debug message. Fill in the placeholders with the object's name, the $targetFilename and then the original avatarFilename:

... lines 1 - 9
class AvatarProcessor implements ProcessorInterface
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
... lines 18 - 23
public function preProcess($object)
{
... lines 26 - 43
$this->logger->debug(sprintf(
'Character %s using filename %s from %s',
$object->getName(),
$targetFilename,
$object->getAvatarFilename()
));
$object->setAvatarFilename($targetFilename);
}
... lines 52 - 61
}

This class is not registered as a service - we just create it manually in AppFixtures:

... lines 1 - 7
class AppFixtures extends DataFixtureLoader
{
... lines 10 - 48
protected function getProcessors()
{
return array(
new AvatarProcessor()
);
}
}

Passing the logger in is simple. The base DataFixturesLoader class has the container and puts it on a $container property, just like a Controller. So we can say $this->container->get('logger'):

... lines 1 - 7
class AppFixtures extends DataFixtureLoader
{
... lines 10 - 48
protected function getProcessors()
{
return array(
new AvatarProcessor($this->container->get('logger'))
);
}
}

To test this out, open up a new tab and let's tail the app/logs/dev.log directory, because app/console runs in the dev environment by default. And let's grep it for the word Character:

tail -f app/logs/dev.log | grep "Character"

Now reload the fixtures!

php app/console doctrine:fixtures:load

No errors, AND we get our log messages. Btw, you can also see log messages directly when running a command by passing the -vvv option:

php app/console doctrine:fixtures:load -vvv

This can be pretty handy.

This means that there's nothing you can't do with a Processor. Need a service? Just use normal dependency injection, pass it in, do awesome things with your fixtures, then celebrate.

Cheers!

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "2.5.*", // v2.5.5
        "doctrine/orm": "~2.2,>=2.2.3", // v2.4.6
        "doctrine/doctrine-bundle": "~1.2", // v1.2.0
        "twig/extensions": "~1.0", // v1.1.0
        "symfony/assetic-bundle": "~2.3", // v2.5.0
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.7
        "symfony/monolog-bundle": "~2.4", // v2.6.1
        "sensio/distribution-bundle": "~3.0", // v3.0.6
        "sensio/framework-extra-bundle": "~3.0", // v3.0.2
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "hautelook/alice-bundle": "~0.1" // 0.1.5
    },
    "require-dev": {
        "sensio/generator-bundle": "~2.3" // v2.4.0
    }
}