If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeCongrats on making it so far! Seriously: your work on this tutorial is going to make everything else you do make a lot more sense. Now, it's time to celebrate!
One of the best parts of Symfony is that it has a killer code generator. It's called "MakerBundle"... because shouting "make me a controller!" is more fun than saying "generate me a controller".
Let's get it installed. Find your terminal and run:
composer require "maker:^1.30" --dev
We're adding --dev
because we won't need the MakerBundle
in production, but that's a minor detail.
As I've mentioned so many times - sorry - bundles give you services. In this case, MakerBundle
doesn't give you services that you will use directly, like in your controller. Nope, it gives you services that power a huge list of new console commands.
When the install finishes, run:
php bin/console make:
Woh! Our app suddenly has a bunch of commands that start with make:
, like make:command
, make:controller
, make:crud
, make:entity
, which will be a database entity and more.
Let's try one of these! Let's make our own custom console command!
Because, in Symfony, you can hook into anything. The bin/console
executable
php bin/console
is no exception: we can add our own command here. I do this all the time for CRON jobs or data importing. To get started, run:
php bin/console make:command
Ok: it asks us for a command name. I like to prefix mine with app
: how about app:random-spell
. Our new command will output a random magical spell - very useful!
And... we're done! You can see that this created a new src/Command/RandomSpellCommand.php
file. Let's go check it out!
... lines 1 - 11 | |
class RandomSpellCommand extends Command | |
{ | |
protected static $defaultName = 'app:random-spell'; | |
protected function configure() | |
{ | |
$this | |
->setDescription('Add a short description for your command') | |
->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 0; | |
} | |
} |
Cool! We can see the name on top, it has a description, some options... and, at the bottom, it ultimately prints a message. We'll start customizing this in a minute.
But before we do that... guess what? The new command already works! Run:
php bin/console app:random-spell
It's alive! This message is coming from the bottom of the new class.
But wait: how did Symfony instantly see our new command class and start using it? Is it because... it lives in a Command/
directory and Symfony scans for classes that live there? Nope! We could rename this to directory to DefinitelyNoCommandsInHere/
and it would still see the command.
The way this works is way cooler. Open up config/services.yaml
and look at the _defaults
section. We talked about what autowire: true
means, but I did not explain the purpose of autoconfigure: true
:
... lines 1 - 8 | |
services: | |
# default configuration for services in *this* file | |
_defaults: | |
autowire: true # Automatically injects dependencies in your services. | |
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. | |
... lines 14 - 33 |
Because this is below _defaults
, autoconfiguration is active on all of our services, including our new command.
When autoconfiguration is enabled for a service, it basically tells Symfony:
Yo! Please look at the base class or interface of this service and if it looks like it should be a command, or an event listener or something else that hooks into Symfony, please automatically integrate it into that system. Thanks!
In other words, Symfony sees our service, notices that it extends Command
:
... lines 1 - 4 | |
use Symfony\Component\Console\Command\Command; | |
... lines 6 - 11 | |
class RandomSpellCommand extends Command | |
{ | |
... lines 14 - 41 | |
} |
And thinks:
I bet this is meant to be a console command. I'll just... hook it into that system automatically.
I love this because it means there is zero configuration needed to get things working. And you'll see this in a bunch of places in Symfony: you create a class, make it implement an interface and... it'll just start working. We'll see another example in a few minutes with a Twig extension.
Ok! Now that our command is working, let's customize it! Playing with console commands is one of my favorite things to do in Symfony. Let's go!
Hey Ruslan I.!
Oh no! Do you have a specific Maker command where you see this? In the MakerBundle test suite, we actually run php-cs-fixer on the code, and if it results in any changes (meaning the generated code did NOT have perfect coding standards), it should fail the test suite. So this is something we actively try to do correctly. If it's wrong somewhere, that's a bug! So let me know :).
Cheers!
I think you misunderstood me. I want to change default Maker's styling.
For example, I want to remove curly braces, when there is only 1 statement in IF block.
So I don't want to stick to the standards.
Hey Ruslan I.!
Ah! I understand better now :). This is not possible in MakerBundle. It's on purpose (it was my decision): trying to make this level of detail configurable would be *super* complex. However, I'd recommend installing php-cs-fixer. If you can find/configure a rule for your coding style, then you could (A) run Maker then (B) run php-cs-fixer to change the code to your style.
Cheers!
// composer.json
{
"require": {
"php": "^7.3.0 || ^8.0.0",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
"sensio/framework-extra-bundle": "^6.0", // v6.2.1
"sentry/sentry-symfony": "^4.0", // 4.0.3
"symfony/asset": "5.0.*", // v5.0.11
"symfony/console": "5.0.*", // v5.0.11
"symfony/debug-bundle": "5.0.*", // v5.0.11
"symfony/dotenv": "5.0.*", // v5.0.11
"symfony/flex": "^1.3.1", // v1.17.5
"symfony/framework-bundle": "5.0.*", // v5.0.11
"symfony/monolog-bundle": "^3.0", // v3.6.0
"symfony/profiler-pack": "*", // v1.0.5
"symfony/routing": "5.1.*", // v5.1.11
"symfony/twig-pack": "^1.0", // v1.0.1
"symfony/var-dumper": "5.0.*", // v5.0.11
"symfony/webpack-encore-bundle": "^1.7", // v1.8.0
"symfony/yaml": "5.0.*" // v5.0.11
},
"require-dev": {
"symfony/maker-bundle": "^1.15", // v1.23.0
"symfony/profiler-pack": "^1.0" // v1.0.5
}
}
Guys, when I use make commands and console binary generates code it
positions curly braces inconsistently. For example class and method
bodies has opening brace on new line, but condition structures have
opening brace on the same line.
I don't like it, because I want to be consistent.
I know that this is the recommended style by PSR2, but can I configure it somehow? Or I can rely only on my editor?