Laravel 11 vs Symfony 7 dependency injection

Guillermo Leyendeker
4 min readApr 3, 2024

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.

--

--