Wallpaper Setup Command - Part 1 - Symfony Commands As a Service


In the previous video we got our Wallpaper entity set up. Now we're going to start using this Wallpaper entity to add wallpapers to our database.

We already have the concept of a category. We have summer, winter, and abstract. At the moment, we don't have the concept of a Category entity, though we will do shortly.

Before we go ahead and create that Category entity, we can do something interesting with the existing wallpaper images we have available. If you recall from when we created our home page we already have a selection of images residing in our web/images directory.

To begin with, we will use a similar tactic to grab (or glob) all of these images and use them to create new Wallpaper entities for us.

There's a problem here though. This is somewhat intentional. Our image file names are similar to:

"abstract-blue-line-background.jpg",
"abstract-red-background-pattern.jpg",
"abstract-shards.jpeg",
"abstract-swirls.jpeg",
"landscape-summer-beach.jpg",
"landscape-summer-field.jpg",
"landscape-summer-flowers.jpg",
"landscape-summer-hill.jpg",
"landscape-summer-mountain.png",
"landscape-winter-high-tatras.jpg",
"landscape-winter-snow-lake.jpg",
"landscape-winter-snow-mountain.jpeg",
"landscape-winter-snow-trees.jpg",

We could try and be smarty pants and determine the category name via a regex match, but that would be going down a path I don't wish to explore.

Instead, for the moment we will not concern ourselves with a category, and just worry about getting Wallpaper entities created and into our database. After this, we will use Doctrine Fixtures to better manage the relationship between the two.

Before we dive into the Doctrine Fixtures approach we are going to create a Symfony console command that will allow us to explore some of the concepts that fixtures will provide for us. Along the way we will get to see some other cool ways that console commands can display data for us.

Really the point of using a Symfony console command here is to demonstrate console commands. It is not intended to be a working solution for the real world. It should be viewed as a learning exercise.

Now, rather than typing out a bunch of boilerplate, Symfony provides us with a handy command line generator for creating console commands. Let's use it:

php bin/console generate:command

  Welcome to the Symfony command generator

First, you need to give the name of the bundle where the command will
be generated (e.g. AppBundle)

Bundle name: AppBundle

Now, provide the name of the command as you type it in the console
(e.g. app:my-command)

Command name: app:setup-wallpapers

  Summary before generation

You are going to generate a app:setup-wallpapers command inside AppBundle bundle.
Do you confirm generation [yes]?
  created ./src/AppBundle/Command/
  created ./src/AppBundle/Command/AppSetupWallpapersCommand.php
Generated the app:setup-wallpapers command in AppBundle

  Everything is OK! Now get to work :).

Super cool, and super easy.

Inside our project we now have some boilerplate to work from. If we open up the generated file, we have the following:

<?php

// /src/AppBundle/Command/AppSetupWallpapersCommand.php

namespace AppBundle\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 AppSetupWallpapersCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this
            ->setFilename('app:setup-wallpapers')
            ->setDescription('...')
            ->addArgument('argument', InputArgument::OPTIONAL, 'Argument description')
            ->addOption('option', null, InputOption::VALUE_NONE, 'Option description')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $argument = $input->getArgument('argument');

        if ($input->getOption('option')) {
            // ...
        }

        $output->writeln('Command result.');
    }

}

This is a good starting point, but not quite what I'm after.

I do not like extending ContainerAwareCommand. I prefer to have my Symfony console commands registered as Symfony services instead.

Also, I dislike the generated name (and file name).

These are simple enough to fix:

<?php

// renamed
// /src/AppBundle/Command/SetupWallpapersCommand.php

namespace AppBundle\Command;

// removed
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
// added
use Symfony\Component\Console\Command\Command;
// * snip *

// renamed class name, and changed
// from extends ContainerAwareCommand
// to extends Command
class SetupWallpapersCommand extends Command
{

If we are using this command as a service we must add in the appropriate service definition:

# /app/config/services.yml

services:
    app.command.wallpaper_setup:
        class: AppBundle\Command\WallpaperSetupCommand
        tags:
            - { name: console.command }

All of this is explained further in the official Symfony docs.

With this config in place we should now be able to run our command and see some output:

php bin/console app:setup-wallpapers

Command result.

We see 'Command result.' as that's the only thing inside our console command's execute method:

$output->writeln('Command result.');

Before we go any further, for complete clarity here is where we are going to start from:

<?php

namespace AppBundle\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class WallpaperSetupCommand extends Command
{
    protected function configure()
    {
        $this
            // the name of the command (the part after "bin/console")
            ->setName('app:setup-wallpapers')

            // the short description shown while running "php bin/console list"
            ->setDescription('Grabs all local wallpapers and creates an entity for each one')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {

    }
}

I've removed the bits and pieces that we won't need (the input arguments and options sections) and stripped out all the contents of execute.

We know that we have some images inside web/images.

The plan of attack is to get hold of all the files inside the web/images directory and loop through all, creating a new Wallpaper() for each.

We can then populate the entities properties and save (persist and flush) the changes off to the database.

We will then spice up the output a touch using the SymfonyStyle class and some other goodies.

Code For This Course

Get the code for this course.

Episodes

# Title Duration
1 Introduction and Site Demo 02:14
2 Setup and a Basic Wallpaper Gallery 08:43
3 Pagination 08:24
4 Adding a Detail View 04:47
5 Creating a Home Page 11:14
6 Creating our Wallpaper Entity 07:50
7 Wallpaper Setup Command - Part 1 - Symfony Commands As a Service 05:57
8 Wallpaper Setup Command - Part 2 - Injection Is Easy 08:53
9 Wallpaper Setup Command - Part 3 - Doing It With Style 05:37
10 Doctrine Fixtures - Part 1 - Setup and Category Entity Creation 08:52
11 Doctrine Fixtures - Part 2 - Relating Wallpapers with Categories 05:56
12 EasyAdminBundle - Setup and Category Configuration 06:02
13 EasyAdminBundle - Wallpaper Setup and List View 07:46
14 EasyAdminBundle - Starting with Wallpaper Uploads 05:57
15 Testing with PhpSpec to Guide Our Implementation 03:39
16 Using PhpSpec to Test our FileMover 05:34
17 Symfony Dependency Testing with PhpSpec 08:47
18 Defensive Counter Measures 06:33
19 No Tests - Part 1 - Uploading Files in EasyAdminBundle 11:01
20 No Tests - Part 2 - Uploading Files in EasyAdminBundle 07:05
21 Don't Mock What You Don't Own 09:36
22 You've Got To Take The Power Back 07:36
23 Making Symfony Work For Us 08:56
24 Testing The Wallpaper File Path Helper 15:11
25 Finally, It Works! 14:56
26 Why I Prefer Not To Use Twig 16:51
27 Fixing The Fixtures 11:20
28 Untested Updates 14:30
29 Untested Updates Part Two - Now We Can Actually Update 06:33
30 Adding an Image Preview On Edit 12:31
31 Delete Should Remove The Wallpaper Image File 11:02
32 Getting Started Testing Wallpaper Updates 10:02
33 Doing The Little Before The Big 08:13
34 Tested Image Preview... Sort Of :) 07:36
35 Finishing Up With a Tested Wallpaper Update 10:41
36 Test Driven Wallpaper Delete - Part 1 11:06
37 Test Driven Wallpaper Delete - Part 2 11:57
38 EasyAdminBundle Login Form Tutorial 08:01