I already have written some examples similar to this but I just wanted add one more. The difference here is that, the configuration file is optional, config root key name has been overriden, namespace is different so on. This example is a Symfony command that accepts some config parameters and just prints them on terminal. That's all!


Structure


This is what we will end up with.


.
├── .gitignore
├── composer.json
└── src
├── Command
│   └── DictateStructureCommand.php
├── DependencyInjection
│   ├── Configuration.php
│   ├── ExtensionTrait.php
│   └── StructureDictatorExtension.php
├── Resources
│   └── config
│   └── services.yaml
└── StructureDictatorBundle.php

Files


.gitignore


vendor
composer.lock

composer.json


{
"name": "inanzzz/structure-dictator",
"type": "symfony-bundle",
"description": "Ensures that the developers follow a certain application development structure",
"license": "MIT",
"require": {
"php": "^7.1",
"symfony/console": "*",
"symfony/dependency-injection": "*",
"symfony/config": "*",
"symfony/http-kernel": "*"
},
"autoload": {
"psr-4": {
"Inanzzz\\StructureDictator\\": "src/"
}
}
}

DictateStructureCommand.php


declare(strict_types=1);

namespace Inanzzz\StructureDictator\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class DictateStructureCommand extends Command
{
private $controller;
private $repository;
private $factory;

public function __construct(
string $controller,
string $repository,
string $factory
) {
parent::__construct();

$this->controller = $controller;
$this->repository = $repository;
$this->factory = $factory;
}

protected function configure()
{
$this
->setName('inanzzz:dictate-structure')
->setDescription('Verifies application development structure');
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$output->write('Dictated'.PHP_EOL);
$output->write($this->controller.PHP_EOL);
$output->write($this->repository.PHP_EOL);
$output->write($this->factory.PHP_EOL);
}
}

Configuration.php


declare(strict_types=1);

namespace Inanzzz\StructureDictator\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

class Configuration implements ConfigurationInterface
{
use ExtensionTrait;

public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root($this->configRoot);

$rootNode
->children()
->arrayNode($this->configChild)
->addDefaultsIfNotSet()
->children()
->scalarNode($this->configChildController)
->cannotBeEmpty()
->defaultValue('src\Controller')
->end()
->scalarNode($this->configChildRepository)
->cannotBeEmpty()
->defaultValue('src\Repository')
->end()
->scalarNode($this->configChildFactory)
->cannotBeEmpty()
->defaultValue('src\Factory')
->end()
->end()
->end()
->end();

return $treeBuilder;
}
}

ExtensionTrait.php


declare(strict_types=1);

namespace Inanzzz\StructureDictator\DependencyInjection;

trait ExtensionTrait
{
private $configRoot = 'inanzzz';
private $configChild = 'structure_dictator';
private $configChildController = 'controller';
private $configChildRepository = 'repository';
private $configChildFactory = 'factory';
}

StructureDictatorExtension.php


declare(strict_types=1);

namespace Inanzzz\StructureDictator\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

class StructureDictatorExtension extends Extension
{
use ExtensionTrait;

public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);

$container->setParameter(
$this->configRoot.'.'.$this->configChild.'.'.$this->configChildController,
$config[$this->configChild][$this->configChildController]
);
$container->setParameter(
$this->configRoot.'.'.$this->configChild.'.'.$this->configChildFactory,
$config[$this->configChild][$this->configChildFactory]
);
$container->setParameter(
$this->configRoot.'.'.$this->configChild.'.'.$this->configChildRepository,
$config[$this->configChild][$this->configChildRepository]
);

$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yaml');
}

public function getAlias()
{
return $this->configRoot;
}
}

services.yaml


services:
inanzzz.structure_dictator.command.dictate_scructure:
class: Inanzzz\StructureDictator\Command\DictateStructureCommand
tags:
- { name: console.command }
arguments:
$controller: '%inanzzz.structure_dictator.controller%'
$repository: '%inanzzz.structure_dictator.repository%'
$factory: '%inanzzz.structure_dictator.factory%'

StructureDictatorBundle.php


declare(strict_types=1);

namespace Inanzzz\StructureDictator;

use Inanzzz\StructureDictator\DependencyInjection\StructureDictatorExtension;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class StructureDictatorBundle extends Bundle
{
public function getContainerExtension()
{
if (null === $this->extension) {
$this->extension = new StructureDictatorExtension();
}

return $this->extension;
}
}

Main Symfony Application


Setup


Add block below to main application's composer.json file. This is compulsory only if you haven't registered your package with packagist site.


{
"repositories": [
{
"type": "git",
"url": "git@github.com:inanzzz/structure-dictator.git"
}
]
}

Install


composer require "inanzzz/structure-dictator:dev-master"

Verify


bin/console list | grep inanzzz
inanzzz
inanzzz:dictate-structure Verifies application development structure

Test


bin/console inanzzz:dictate-structure
Dictated
src\Controller
src\Repository
src\Factory

You can override values below by creating a configuration file under config/packages directory as seen below.


# config/packages/inanzzz.yaml

inanzzz:
structure_dictator:
controller: 'src\App\Controller'
repository: 'src\App\Repository'
factory: 'src\App\Factory'

Let's test our command again.


bin/console inanzzz:dictate-structure
Dictated
src\App\Controller
src\App\Repository
src\App\Factory

Your config file can be configured in many ways as seen below. If any key is missing the default value will be used by the package.


# Empty file

inanzzz:

inanzzz:
structure_dictator: ~

inanzzz:
structure_dictator:
controller: 'src\App\Controller'

inanzzz:
structure_dictator:
controller: 'src\App\Controller'
repository: 'src\App\Repository'

inanzzz:
structure_dictator:
controller: 'src\App\Controller'
repository: 'src\App\Repository'
factory: 'src\App\Factory'