Laravel 11 vs Symfony 7 dependency injection
I made a comparison between how both frameworks made the dependency injection. Just for curiosity.
To make this comparison I made a command that ask the ratio between Argentinian currency (ARS) and USD from the Argentinian National Bank (BCRA).
In both frameworks, I created a service and used it to from a command.
Symfony
The files we need to create or modify are:
tm/src/Command/TmGetUsdArsRatioCommand.php
tm/src/Service/UsdArsRatio.php
tm/config/services.yml
# tm/src/Command/TmGetUsdArsRatioCommand.php
<?php
namespace App\Command;
use App\Service\UsdArsRatio;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
#[AsCommand(
name: 'tm:getUsdArsRatio',
description: 'Add a short description for your command',
)]
class TmGetUsdArsRatioCommand extends Command
{
protected $params;
protected $usdArsRatio;
public function __construct(ParameterBagInterface $params, UsdArsRatio $usdArsRatio )
{
$this->params = $params;
$this->usdArsRatio = $usdArsRatio;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->info('Getting USD to ARS conversion ratio...');
$ratio = $this->usdArsRatio->getLastRatio();
$io->success('USD to ARS ratio is: ' . $ratio);
return Command::SUCCESS;
}
}
# tm/src/Service/UsdArsRatio.php
<?php
namespace App\Service;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\RequestException;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
class UsdArsRatio
{
private $httpClient;
private $parameters;
public function __construct(ClientInterface $httpClient, ParameterBagInterface $parameters)
{
$this->httpClient = $httpClient;
$this->parameters = $parameters;
}
public function getLastRatio(): float
{
$token = $this->parameters->get('bcra_token');
$endpoint = $this->parameters->get('bcra_endpoint');
try {
$response = $this->httpClient->request('GET', $endpoint, [
'headers' => [
'Authorization' => 'Bearer ' . $token
]
]);
$data = json_decode($response->getBody()->getContents(), true);
$lastValue = array_pop($data)['v'];
return (float) $lastValue;
} catch (RequestException $e) {
throw new \Exception('Error getting data from the Usd/Ars ratio service: ' . $e->getMessage());
}
}
}
parameters:
bcra_token: '%env(BCRA_TOKEN)%'
bcra_endpoint: '%env(BCRA_ENDPOINT)%'
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.
# 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/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
# Guzzle with no arguments
GuzzleHttp\Client: ~
# Bind the interface to the class
GuzzleHttp\ClientInterface: '@GuzzleHttp\Client'
Laravel
The files we need to create or modify are:
tm/app/Console/Commands/TmGetUsdArsRatioCommand.php
tm/app/Console/Services/UsdArsRatio.php
tm/app/Console/Providers/AppServiceProvider.php
# tm/app/Console/Commands/TmGetUsdArsRatioCommand.php
<?php
namespace App\Console\Commands;
use App\Services\UsdArsRatio;
use Illuminate\Console\Command;
class TmGetUsdArsRatioCommand extends Command
{
protected $signature = 'tm:getUsdArsRatio';
protected $description = 'Command description';
// UsdArsRatio service
protected $usdArsRatio;
// Inject UsdArsRatio service
public function __construct(UsdArsRatio $usdArsRatio)
{
$this->usdArsRatio = $usdArsRatio;
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$this->info('Getting USD to ARS conversion ratio...');
$lastRatio = $this->usdArsRatio->getLastRatio();
$this->info('The last USD to ARS ratio is: ' . $lastRatio);
}
}
# tm/app/Console/Services/UsdArsRatio.php
<?php
namespace App\Services;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\RequestException;
class UsdArsRatio
{
private $httpClient;
// Inyect Guzzle in constructor
public function __construct(ClientInterface $client)
{
$this->httpClient = $client;
}
function getLastRatio()
{
$token = config('app.parameters.bcra_token');
$endpoint = config('app.parameters.bcra_endpoint');
try {
$response = $this->httpClient->request('GET', $endpoint, [
'headers' => [
'Authorization' => 'Bearer ' . $token
]
]);
$data = json_decode($response->getBody()->getContents(), true);
$lastValue = array_pop($data)['v'];
return (float) $lastValue;
} catch (RequestException $e) {
throw new \Exception('Error getting data from the Usd/Ars ratio service: ' . $e->getMessage());
}
}
}
# tm/app/Console/Providers/AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton('GuzzleHttp\ClientInterface', 'GuzzleHttp\Client');
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
}
}
Differences
There are no big differences between them, just syntax and some helpers available in Laravel that simplify a liitle bit the code.
Writing messages to the console
Laravel:
// We can use just $this->info()
public function handle()
{
$this->info('Getting USD to ARS conversion ratio...');
Symfony:
// We have to inyect intput and output interface and create
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->info('Getting USD to ARS conversion ratio...');
Point to Laravel. Show messages in Laravel is clearer and easier to do than Symfony.
Service registration
Laravel:
$this->app->singleton('GuzzleHttp\ClientInterface', 'GuzzleHttp\Client');
Symfony:
services:
GuzzleHttp\Client:
class: GuzzleHttp\Client
# other service configurations
# Bind the interface to the service 'GuzzleHttp\Client'. The @ is a reference to a service.
GuzzleHttp\ClientInterface: '@GuzzleHttp\Client'
Point to Symfony. You can “bind” an interface with a service with it own configuration. In Laravel you can do it too, but the notation is not so clear:
use GuzzleHttp\Client;
...
$this->app->bind('GuzzleHttp\Client', function ($app) {
return new Client([
'base_uri' => 'example.com',
// other configurations
]);
});
Parameters
Symfony:
<?php
...
public function __construct(ClientInterface $httpClient, ParameterBagInterface $parameters)
{
$this->httpClient = $httpClient;
$this->parameters = $parameters;
}
public function getLastRatio(): float
{
$token = $this->parameters->get('bcra_token');
$endpoint = $this->parameters->get('bcra_endpoint');
Laravel:
<?php
...
function getLastRatio()
{
$token = config('app.parameters.bcra_token');
$endpoint = config('app.parameters.bcra_endpoint');
Point to Laravel. You can get the parameters from the command without inject the parameterBagInterface
Conclusion
Both frameworks are great. Laravel is a little bit clearer sometimes, and Symfony is clearer in others. In my next entry I will compare the difference creating a model.