Buy

For our last trick, I want to introduce a bundle that's going to make our life awesome. And, for the first time, we are going to hook into Symfony.

Installing Maker Bundle

First, find your terminal, and install that bundle:

composer require maker --dev

Yep! That's a Flex alias for symfony/maker-bundle. And, in this case, "make" means - "make your life easier by generating code".

We know that the main purpose of a bundle is to give us services. And, that's true in this case too... but the purpose of these services isn't for us to use them directly, like in our controller. Nope, the purpose of these services is that they give us new bin/console commands:

php bin/console

Nice! About 10 new commands, capable of generating all kinds of things. And, more make commands are still being added.

Generating a new Command

So... let's try one! Let's use the MakerBundle to create our very own, custom bin/console command. Use:

php bin/console make:command

This will ask us for a command name - how about article:stats - we'll create a command that will return some stats about an article. And... it's done! We now have a shiny new src/Command/ArticleStatsCommand.php file. Open it!

37 lines src/Command/ArticleStatsCommand.php
... lines 1 - 2
namespace App\Command;
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;
class ArticleStatsCommand extends Command
{
protected static $defaultName = 'article:stats';
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)
{
$io = new SymfonyStyle($input, $output);
$argument = $input->getArgument('arg1');
if ($input->getOption('option1')) {
// ...
}
$io->success('You have a new command! Now make it your own! Pass --help to see your options.');
}
}

Hey! It even added some example code to get us started! Run:

php bin/console

And on top... yes! Symfony already sees our new article:stats command. Sweet! Um... so... let's try it!

php bin/console article:stats

It doesn't do much... yet - but it's already working.

Service autoconfigure

But... how does Symfony already know about this new command? I mean, is it scanning all of our files looking for command classes? Actually, no! And that's a good thing - that would be super slow!

Here's the answer. Remember: all of our classes in src/ are loaded as services:

37 lines config/services.yaml
... lines 1 - 5
services:
... lines 7 - 22
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{Entity,Migrations,Tests}'
... lines 28 - 37

Notice that our new class extends Symfony's base Command class:

37 lines src/Command/ArticleStatsCommand.php
... lines 1 - 4
use Symfony\Component\Console\Command\Command;
... lines 6 - 11
class ArticleStatsCommand extends Command
{
... lines 14 - 35
}

When the service was registered, Symfony noticed this and made sure that it included it as a command. This nice feature has a name - autoconfigure. It's not too important, but just like autowiring, this is activated thanks to a little bit of config in our services.yaml file:

37 lines config/services.yaml
... lines 1 - 5
services:
# default configuration for services in *this* file
_defaults:
... line 9
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
... lines 11 - 37

It's just another way that you can avoid configuration, and keep working!

Next, let's have fun and make our command much more awesome!

Leave a comment!