<!---
# This file is part of the ChillDev DependencyInjection Extra library.
#
# @author Rafał Wrzeszcz <rafal.wrzeszcz@wrzasq.pl>
# @copyright 2013 © by Rafał Wrzeszcz - Wrzasq.pl.
# @version 0.0.1
# @since 0.0.1
# @package ChillDev\DependencyInjection
-->

# Usage

## TagGrabbingPass

`ChillDev\DependencyInjection\Compiler\TagGrabbindPass` is a compiler pass class that automates grabbing all services tagged with specified tag name and passing them to aggregating container. Consider following DI services:

```yaml
services:
    repository:
        class: "YourAdaptersRepository"
    first:
        class: "YourFirstAdapter"
        tags:
            - { name: "your.adapter.tag", adapter: "first" }
    second:
        class: "YourSecondAdapter"
        tags:
            - { name: "your.adapter.tag", adapter: "second" }
    third:
        class: "YourThirdAdapter"
        tags:
            - { name: "your.adapter.tag", adapter: "third" }
```

Let's say your repository class looks as follows:

```php
class YourAdaptersRepository extends ArrayObject
{
    public function registerAdapter($key, $adapter)
    {
        $this[$key] = $adapter;
    }
}
```

Your repository class doesn't have to extend `ArrayObject`, it's used in example code just for simplicity. They only requirement for repository class is to provide registration method. Arguments order for this method is also important - first comes registration key, then service reference.

It is a very common setup - now you want to add all services tagged with `your.adapter.tag` to `repository` service. Instead of writing your own custom DI compiler pass, you can use ready implementation that will do exactly what you want. It's as easy as:

```php
use ChillDev\DependencyInjection\Compiler\TagGrabbingPass;

use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class YourBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        $container->addCompilerPass(
            new TagGrabbingPass(
                // this is tag name for which you want to look
                'your.adapter.tag',
                // this is ID of container service
                'repository',
                // this is name of method for registering new adapters
                'registerAdapter',
                // this is tag attribute name to be used as key identifier
                'adapter'
            ),
            PassConfig::TYPE_OPTIMIZE
        );
    }
}
```

By default `key` attribute is used, so you can ommit last argument if you stay with that name:

```yaml
services:
    first:
        class: "YourFirstAdapter"
        tags:
            - { name: "your.adapter.tag", key: "first" }
```

You can also register service with multiple keys:

```yaml
services:
    first:
        class: "YourFirstAdapter"
        tags:
            - { name: "your.adapter.tag", key: "first" }
            - { name: "your.adapter.tag", key: "default" }
```

**Note:** no action is taken by compiler pass if specified repository service doesn't exist in container.

You can also add more then one compiler pass to handle different tags, repositories and any combinations of them:

```php
class YourBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        $container->addCompilerPass(
            new TagGrabbingPass('tag.adapter', 'repository.adapters', 'registerAdapter', 'adapter'),
            PassConfig::TYPE_OPTIMIZE
        );
        $container->addCompilerPass(
            new TagGrabbingPass('tag.formatters', 'repository.formatters', 'registerFormatter', 'formatter'),
            PassConfig::TYPE_OPTIMIZE
        );
        $container->addCompilerPass(
            new TagGrabbingPass('tag.channel', 'repository.formatters', 'registerChannler', 'channel'),
            PassConfig::TYPE_OPTIMIZE
        );
    }
}
```

## Validation

Debugging DI is very hard if you misconfigure it. Errors which occur at runtime can have various reasons and exception messages won't tell you much since DI configuration may be 100% valid, from technical point of view. **ChillDevDependencyInjectionExtra** allows you to plug in validation for tagged services to detect errors before it's too late and you have all source DI stuff in your hand. Most common check which you may to perform is to check if service is defined with a class that implements certain interface:

```php
use ChillDev\DependencyInjection\Compiler\TagGrabbingPass;
use ChillDev\DependencyInjection\Validator\InterfaceValidator;

class YourBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        // ensure that services defined as adapters define adapter interface
        $pass = new TagGrabbingPass('tag.adapter', 'repository.adapters', 'registerAdapter', 'adapter');
        $pass->addValidator(new InterfaceValidator('YourAdapterInterface'));

        $container->addCompilerPass(
            $pass,
            PassConfig::TYPE_OPTIMIZE
        );
    }
}
```

### Creating custom validators

You can also implement your own validator by implementing `ChillDev\DependencyInjection\Validator\ValidatorInterface` (don't confuse with `InterfaceValidator` described above!).
