This tutorial has a new version, check it out!

Custom Alice Faker Function

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 $12.00

The Faker name() function gives us a poor genus name. Yea, I know - this is just fake data - but it's so wrong that it fails at its one job: to give us some somewhat realistic data to make development easy.

Here's our goal: use a new <genus()> function in Alice and have this return the name of a random ocean-bound genus:

AppBundle\Entity\Genus:
genus_{1..10}:
name: <genus()>
... lines 4 - 7

This shouldn't work yet - but try it to see the error:

./bin/console doctrine:fixtures:load

Unknown formatter "genus"

Faker calls these functions "formatters". Can we create our own formatter? Absolutely.

Adding a Custom Formatter (Function)

In LoadFixtures, break the load() call onto multiple lines to keep things short and civilized. Now, add a third argument - it's sort of an "options" array. Give it a key called providers - these will be additional objects that provide formatter functions - and set it to an array with $this:

... lines 1 - 9
class LoadFixtures implements FixtureInterface
{
public function load(ObjectManager $manager)
{
$objects = Fixtures::load(
__DIR__.'/fixtures.yml',
$manager,
[
'providers' => [$this]
]
);
}
... lines 22 - 45
}

And we're nearly done! To add a new genus formatter, add public function genus(). I've already prepared a lovely list of some fantastic genuses that live in the ocean:

... lines 1 - 9
class LoadFixtures implements FixtureInterface
{
... lines 12 - 22
public function genus()
{
$genera = [
'Octopus',
'Balaena',
'Orcinus',
'Hippocampus',
'Asterias',
'Amphiprion',
'Carcharodon',
'Aurelia',
'Cucumaria',
'Balistoides',
'Paralithodes',
'Chelonia',
'Trichechus',
'Eumetopias'
];
... lines 41 - 44
}
}

Finish this with $key = array_rand($genera) and then return $genera[$key]:

... lines 1 - 9
class LoadFixtures implements FixtureInterface
{
... lines 12 - 22
public function genus()
{
... lines 25 - 41
$key = array_rand($genera);
return $genera[$key];
}
}

Let's give it a try:

./bin/console doctrine:fixtures:load

No errors! Refresh! Ah, so much better.

New Random Boolean Column

Now, hold on, we have a new requirement: we need the ability to have published and unpublished genuses - for those times when we create a new genus, but we're still trying to think of a fun fact before it shows up on the site. With our beautiful migration and fixtures systems, this will be a breeze.

First, open Genus and add a new private property - call it $isPublished. Next, use the "Code"->"Generate" shortcut - or Ctrl+Enter - to generate the annotations:

... lines 1 - 10
class Genus
{
... lines 13 - 39
/**
* @ORM\Column(type="boolean")
*/
private $isPublished = true;
... lines 44 - 89
public function setIsPublished($isPublished)
{
$this->isPublished = $isPublished;
}
}

Hey that was cool! Because the property started with is, PhpStorm correctly guessed that this is a boolean column. Go team!

At the bottom, generate just the setter function. We can add a getter function later... if we need one.

We need to update the fixtures. But first, find the command line and generate the migration:

./bin/console doctrine:migrations:diff

Be a responsible dev and make sure the migration looks right:

... lines 1 - 10
class Version20160207083347 extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE genus ADD is_published TINYINT(1) NOT NULL');
}
/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE genus DROP is_published');
}
}

Actually, it looks perfect. Run it:

./bin/console doctrine:migrations:migrate

Last step: we want to have a few unpublished genuses in the random data set. Open the Faker documentation and search for "boolean". Perfect! There's a built-in boolean() function and we can control the $chanceOfGettingTrue. In the fixtures file, add isPublished and set that to boolean(75) - so that most genuses are published:

AppBundle\Entity\Genus:
genus_{1..10}:
... lines 3 - 6
isPublished: <boolean(75)>

Re-run the fixtures!

./bin/console doctrine:fixtures:load

Hey, no errors! Now, to only show the published genuses on the list page, we need a custom query.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}