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.