Maker Bundle: Let's Generate Some Code!
Hats off for nearly making it through the first Symfony tutorial. You've taken a huge step toward building whatever you want on the web. To celebrate, I want to play with MakerBundle: Symfony's awesome tool for code generation.
Composer require vs require-dev
Let's get it installed:
composer require symfony/maker-bundle --dev
We haven't seen that --dev
flag yet, but it's not that important. Move over and open composer.json
. Thanks to the flag, instead of symfony/maker-bundle
going under the require
key, it was added down here under require-dev
.
{ | |
// ... lines 2 - 84 | |
"require-dev": { | |
// ... line 86 | |
"symfony/maker-bundle": "^1.52", | |
// ... lines 88 - 89 | |
} | |
} |
By default, when you run composer install
, it will download everything under both require
and require-dev
. But require-dev
is meant for packages that don't need to be available on production: packages that you only need when you're developing locally. That's because, when you do deploy, if you want, you can tell Composer:
Hey! Only install the packages under my
require
key: don't install therequire-dev
stuff.
That can give you a small performance boost on production. But mostly, it's not a big deal.
The Maker Commands
Now, we just installed a bundle. Do you remember the main thing that bundles give us? That's right: services. This time, the services that MakerBundle gave us are services that provide new console commands. Drumroll please. Run:
php bin/console
Or, actually, I'll start running symfony console
, which is the same thing. Thanks to the new bundle, we have a ton of commands that start with make
! Commands for generating a security system, making a controller, generating doctrine entities to talk to the database, forms, listeners, a registration form.... lots and lots of stuff!
Generating a Console Command
Let's use one of these to make our own custom console command. Run:
symfony console make:command
This will interactively ask us about our command. Let's call it: app:ship-report
. Done!
This created exactly one file: src/Command/ShipReportCommand.php
. Let's go check that out!
// ... lines 1 - 2 | |
namespace App\Command; | |
use Symfony\Component\Console\Attribute\AsCommand; | |
use Symfony\Component\Console\Command\Command; | |
use Symfony\Component\Console\Input\InputArgument; | |
use Symfony\Component\Console\Input\InputInterface; | |
use Symfony\Component\Console\Input\InputOption; | |
use Symfony\Component\Console\Output\OutputInterface; | |
use Symfony\Component\Console\Style\SymfonyStyle; | |
( | |
name: 'app:ship-report', | |
description: 'Add a short description for your command', | |
) | |
class ShipReportCommand extends Command | |
{ | |
public function __construct() | |
{ | |
parent::__construct(); | |
} | |
protected function configure(): void | |
{ | |
$this | |
->addArgument('arg1', InputArgument::OPTIONAL, 'Argument description') | |
->addOption('option1', null, InputOption::VALUE_NONE, 'Option description') | |
; | |
} | |
protected function execute(InputInterface $input, OutputInterface $output): int | |
{ | |
$io = new SymfonyStyle($input, $output); | |
$arg1 = $input->getArgument('arg1'); | |
if ($arg1) { | |
$io->note(sprintf('You passed an argument: %s', $arg1)); | |
} | |
if ($input->getOption('option1')) { | |
// ... | |
} | |
$io->success('You have a new command! Now make it your own! Pass --help to see your options.'); | |
return Command::SUCCESS; | |
} | |
} |
Cool! This is a normal class - it is a service, by the way - but with an attribute above: #[AsCommand]
. This tells Symfony:
Yo! See this service? It's not just a service: I would like you to include it in the list of console commands.
The attribute includes the name of the command and a description. Then the class itself has a configure()
method where we can add arguments and options. But the main part is that, when somebody calls this command, Symfony will call execute()
.
This $io
variable is cool. It lets us output things - like $this->note()
or $this->success()
- with different styles. And though we don't see it here, we can also ask the user questions interactively.
The best part? Just by creating this class, it's ready to use! Try it out:
symfony console app:ship-report
That's so cool! The message down here comes from the success message at the bottom of the command. And thanks to configure()
, we have one argument called arg1
. Arguments are string that we pass after the command, like:
symfony console app:ship-report ryan
It says:
You passed an argument: ryan
... which comes from this spot in the command.
Building a Progress Bar
There are a lot of fun things you can do with commands... and I want to play with one of them. One of the superpowers of the $io
object is to create animated progress bars.
Imagine we're building a ship report... and it requires some heavy queries. So we want to show a progress bar on the screen. To do that, say $io->progressStart()
and pass it however many rows of data we're looping through and handling. Let's pretend we're looping over 100 rows of data for this report.
Instead of looping over real data, create a fake loop with for
. I'm even going to include the $i
variable in the middle! Inside, to advance the progress bar, say $io->advance()
. Then, here is where we would do our heavy query or heavy work. Fake that with a usleep(10000)
to create a short pause.
After the loop, finish with $io->progressFinish()
.
// ... lines 1 - 16 | |
class ShipReportCommand extends Command | |
{ | |
// ... lines 19 - 31 | |
protected function execute(InputInterface $input, OutputInterface $output): int | |
{ | |
// ... lines 34 - 44 | |
$io->progressStart(100); | |
for ($i = 0; $i < 100; ++$i) { | |
$io->progressAdvance(); | |
usleep(10000); | |
} | |
$io->progressFinish(); | |
// ... lines 51 - 54 | |
} | |
} |
That's it! Spin over and give that a try:
symfony console app:ship-report ryan
Oh, that is so cool.
And... that's it people! Give yourself a high five... or, better, surprise a co-worker with a jumping high five! Then celebrate with a well-deserved beer, tea, walk around the block or frisbee match with your dog. Because... you did it! You took the first big step into being dangerous with Symfony. Then, come back and try this stuff out: play with it, build a blog, create a few static pages, anything. That will make a huge difference.
And if you ever have any questions, we watch the comment section below each video closely and answer everything. Also keep going! In the next tutorial, we're going to become even more dangerous by diving deeper into Symfony's configuration and services: the systems that drive everything you'll do in Symfony.
Alright, friends, see you next time!
This whole course was incredibly clear and helpful.
Two thumbs up!