<?php
namespace Potherca\Example;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class Website extends Controller
{
/**
* Comment out the line below (and restart the server) and the logger will
* not be added to this class by the dependency injection container
*/
use LoggerAwareTrait;
/**
* @Sensio\Bundle\FrameworkExtraBundle\Configuration\Route("/")
*
* @return Response
*
* @throws \InvalidArgumentException
*/
final public function homepage(): Response
{
$loggerName = 'undefined';
if (isset($this->logger)) {
$loggerName = is_object($this->logger)
? get_class($this->logger)
: gettype($this->logger);
}
return new Response(
sprintf(
'<p>Logger = <code>%s</code></p>',
$loggerName
)
);
}
}
/*EOF*/
<?php
namespace Potherca\Example;
use Psr\Log\LoggerInterface;
/**
* Basic Implementation of LoggerAwareInterface.
*/
trait LoggerAwareTrait
{
/**
* The logger instance.
*
* @var LoggerInterface
*/
protected $logger;
/**
* Sets a logger.
*
* @required
*
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
/*EOF*/
<?php
/**
* This file can be ignored, it is just here to provide a working example using Katwizy
*/
$loader = require __DIR__.'/vendor/autoload.php';
Potherca\Katwizy\Bootstrap::run(
$loader,
Symfony\Component\HttpFoundation\Request::createFromGlobals()
);
/*EOF*/
## Introduction
Example of how a trait can be used to add secondary functionality to classes in Symfony.
<sup>
(The names of the class files have been changed to lower-case so the this
file is shown first in the gist.)
</sup>
## Rational
The Symfony framework offers functionality to "autowire" objects. This means that
the framework creates objects and automatically injects any dependencies the
object might need.
The most common form is simply to add dependencies as a parameter to a class'
constructor with a type-hint.
However, over time, the constructor can become rather large and the object
becomes polluted with properties that do not really influence it's behaviour.
A common example of this is logging.
Instead of adding yet another parameter, a trait can be used.
That way the class code and constructor parameters are kept clean.
Then, using Symfony's dependency-injection container, the logger can be added to
the object when instantiated automagically.
## How it works
The `services.yml` configuration tells Symfony to auto-wire the main "controller" style class in this example.
That class uses the `LoggerAwareTrait`.
The `LoggerAwareTrait` contains a setter. There are several ways auto-wiring
for setter calls can be achieved. The easiest is to simply add a `@required`
annotation to the setter.
Auto-wiring will automatically call any method with the `@required` annotation
above it, auto-wiring each argument. That way it is not necessary to manually
wire each class that uses the `LoggerAwareTrait`.
For full details see:
https://symfony.com/doc/current/service_container/autowiring.html#autowiring-other-methods-e-g-setters
The `LoggerAwareTrait` is an ad-verbatim copy of `Psr\Log\LoggerAwareTrait`, except for
the `@required` annotation on the `setLogger` method.
## Running the example
In order to keep this gist both work and small and to the point, [Katwizy](https://github.com/Katwizy/katwizy) is used.
To see the example in action:
1. Clone this repository
2. Step into the repository folder
3. Install the dependencies
4. Start a web-server
These steps can be achieved with the following commands:
```bash
git clone git@gist.github.com:7b1ffe7e18098bc89931fe59c30642ff.git symfony-trait-autowiring-example
cd $_
composer install
php -S localhost:8080 -t ./
```
Visiting [http://localhost:8080/](http://localhost:8080/) in the browser will
show a page stating
> Logger = Symfony\Bridge\Monolog\Logger
To remove the logger from the main class:
1. Stop the web-server
2. Remove (or comment out) the line stating:
use LoggerAwareTrait;
3. Remove the class cache `\rm -rdf ./var/`
4. Start the web-server again `php -S localhost:8080 -t ./`