Wallpaper Setup Command - Part 3 - Doing It With Style
At this stage we have a working Symfony console command which allows us to pull in any wallpaper images we have, and use those images to populate entities which we then save off to our database.
However, as covered in the previous video, the data that is used to populate our table is a little ... poor.
Before we go any further, if you have been following along I would ask that you either truncate your table now, or drop the database entirely, recreate and re-run the migration to get back to a known good state:
php bin/console doctrine:database:drop --force
php bin/console doctrine:database:create
php bin/console doctrine:migrations:migrate -n
Now, let's make this whole process a little smoother.
<?php
namespace AppBundle\Command;
use AppBundle\Entity\Wallpaper;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class WallpaperSetupCommand extends Command
{
/**
* @var string
*/
private $rootDir;
/**
* @var EntityManager
*/
private $entityManager;
public function __construct(string $rootDir, EntityManager $entityManager)
{
parent::__construct();
$this->rootDir = $rootDir;
$this->entityManager = $entityManager;
}
protected function configure()
{
$this
// the name of the command (the part after "bin/console")
->setFilename('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)
{
$wallpapers = glob($this->rootDir . '/../web/images/*.*');
$wallpaperCount = count($wallpapers);
foreach ($wallpapers as $wallpaper) {
[
'basename' => $filename,
'filename' => $slug,
] = pathinfo($wallpaper);
[
0 => $width,
1 => $height
] = getimagesize($wallpaper);
$wp = (new Wallpaper())
->setFilename($filename)
->setSlug($slug)
->setWidth($width)
->setHeight($height)
;
$this->entityManager->persist($wp);
}
$this->entityManager->flush();
}
}
Here we are using PHP 7.1's shorthand array destructuring to extract data from returned arrays, and push that data into variables in 'one line'.
Both pathinfo
and getimagesize
take the full path of the given image and return interesting information that we care about. Watch the video for further info on this.
If we run the command again now, we should see a much nicer looking set of database table entries.
Getting SymfonyStyle
ish
It would be super nice to spruce up our Symfony console command output. Fortunately Symfony offers a SymfonyStyle
class, along with some helpers such as the progress bar, and a nice Table to help us do so.
We're going to add in a progress bar to show how many images have been processed, and then a table output at the end to show each image by name.
Symfony makes it super easy to add these helpers to our console command. The downside is, our commands do get a little bit 'busier' (or messier, depending on your point of view) as a result.
Adding in the progress bar is the easier of the two, so let's do just that:
use Symfony\Component\Console\Style\SymfonyStyle;
// * snip *
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$wallpapers = glob($this->rootDir . '/../web/images/*.*');
$wallpaperCount = count($wallpapers);
$io->progressStart($wallpaperCount);
foreach ($wallpapers as $wallpaper) {
[
'basename' => $filename,
'filename' => $slug,
] = pathinfo($wallpaper);
[
0 => $width,
1 => $height
] = getimagesize($wallpaper);
$wp = (new Wallpaper())
->setFilename($filename)
->setSlug($slug)
->setWidth($width)
->setHeight($height)
;
$this->entityManager->persist($wp);
$io->progressAdvance();
}
$this->entityManager->flush();
$io->progressFinish();
}
Easy enough.
We need to create a new instance of the SymfonyStyle
class. Calling this instance $io
is just something I lifted directly from the docs. This class takes both the $input
and $output
variables as arguments, which come with any execute
function, so we are all good there.
progressStart
needs to know how many entries will be counted over before we are done. If you don't add this, expect some very funky output, especially on very large result sets.
In each iteration of the foreach
loop we must call progressAdvance
to indicate we are incrementing the progress bar by 1. You can pass in an integer here to increment by a different value if needed.
Finally, outside the loop we finish up.
All of this results in a nice looking output:
php bin/console app:setup
22/22 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
Just be careful with the progress bar and any entries such as writeLn
which interrupt the progress bar output and make things look messy.
One thing to note here is that because we have a unique constraint on our table, we cannot keep re-running the console command without first drop / create / migrate our database, or truncating the table.
Adding a Table of results to the output involves a little further effort:
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Helper\Table;
// * snip *
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$wallpapers = glob($this->rootDir . '/../web/images/*.*');
$wallpaperCount = count($wallpapers);
if ($wallpaperCount === 0) {
$io->warning('No wallpapers found');
return false;
}
$io->title('Importing Wallpapers');
$io->progressStart($wallpaperCount);
$fileNames = [];
foreach ($wallpapers as $wallpaper) {
[
'basename' => $filename,
'filename' => $slug,
] = pathinfo($wallpaper);
[
0 => $width,
1 => $height
] = getimagesize($wallpaper);
$wp = (new Wallpaper())
->setFilename($filename)
->setSlug($slug)
->setWidth($width)
->setHeight($height)
;
$this->em->persist($wp);
$fileNames[] = [$filename];
$io->progressAdvance();
}
$io->progressFinish();
$table = new Table($output);
$table
->setHeaders(['Filename'])
->setRows($fileNames)
;
$table->render();
$io->success(sprintf('Added %d wallpapers, nice one.', $wallpaperCount));
$this->em->flush();
}
The gotcha here is to make sure you use an array of arrays for your $filenames
or your output will be really messed up. Also, don't forget to call $table->render()
, or all your hard work is for naught.
And with that, we should have our finished output:
php bin/console app:setup
Importing Wallpapers
====================
22/22 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
+-------------------------------------+
| Filename |
+-------------------------------------+
| abstract-background-pink.jpg |
| abstract-black-and-white-wave.jpg |
| abstract-black-multi-color-wave.jpg |
| abstract-blue-green.jpg |
| 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-summer-sea.jpg |
| landscape-summer-sky.jpg |
| landscape-winter-canada-lake.jpg |
| landscape-winter-high-tatras.jpg |
| landscape-winter-snow-lake.jpg |
| landscape-winter-snow-mountain.jpeg |
| landscape-winter-snow-trees.jpg |
| landscape-winter-snowboard-jump.jpg |
| landscape-winter-snowy-fisheye.png |
+-------------------------------------+
[OK] Added 22 wallpapers, nice one.
As mentioned, this is for learning purposes only. This isn't meant to be a proper solution. We really need to switch over to using fixtures to go much further. And that's exactly what we will do in the very next video.