We sometimes might have different values for same parameter in different environments. This example shows us how to define environment specific parameters in Symfony 4 applications. We will use a realy AWS SES utility class to send emails in prod environment but in dev and test environments we will use the mock utility class instead.


AwsController


namespace App\Controller;

use App\Util\Aws\AwsSesUtil;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

/**
* @Route("/aws")
*/
class AwsController
{
private $awsSesUtil;

public function __construct(AwsSesUtil $awsSesUtil)
{
$this->awsSesUtil = $awsSesUtil;
}

/**
* @Route("/ses")
* @Method({"GET"})
*/
public function send(): Response
{
$to = ['to@email.com'];
$from = 'from@email.com';
$subject = 'Test';
$body = 'This is a test';

$id = $this->awsSesUtil->sendEmail($to, $from, $subject, $body);

return new JsonResponse($id);
}
}

AwsSesUtil


This is the real one.


namespace App\Util\Aws;

use App\Exception\AwsException;
use Aws\Result;
use Aws\Ses\Exception\SesException;
use Aws\Ses\SesClient;

class AwsSesUtil extends AwsAbstractUtil
{
private const CHARSET = 'UTF-8';

public function sendEmail(
iterable $toAddresses,
string $fromAddress,
string $subject,
string $body
): ?string {
try {
/** @var SesClient $client */
$client = $this->sdk->createSes();

/** @var Result $result */
$result = $client->sendEmail([
'Destination' => [
'ToAddresses' => $toAddresses,
],
'Source' => $fromAddress,
'Message' => [
'Subject' => [
'Charset' => self::CHARSET,
'Data' => $subject,
],
'Body' => [
'Text' => [
'Charset' => self::CHARSET,
'Data' => $body,
],
],
],
]);

return $result->get('MessageId');
} catch (SesException $e) {
throw new AwsException(sprintf('[AWS SES] %s', $e->getAwsErrorMessage()));
}
}
}

AwsSesUtilMock


This is the mock version.


declare(strict_types=1);

namespace App\Tests\Mock;

use App\Util\Aws\AwsSesUtil;
use App\Exception\AwsException;

class AwsSesUtilMock extends AwsSesUtil
{
public function sendEmail(
iterable $toAddresses,
string $fromAddress,
string $subject,
string $body
): ?string {
if (
is_array($toAddresses) &&
$fromAddress &&
$subject &&
$body
) {
return 'MESSAGE_ID';
}

throw new AwsException('[AWS SES] Error while sending an email.');
}
}

Configuration files


config/services.yaml


imports:
- { resource: services/* }

parameters:

aws_sdk:
version: 'latest'
region: 'eu-west-1'
credentials:
key: 'AWS_KEY'
secret: 'AWS_SECRET'

services:
_defaults:
autowire: true
autoconfigure: true
public: false

App\:
resource: '../src/*'
exclude: '../src/{Controller,Util,Kernel.php}'

config/services/controllers.yaml


services:
_defaults:
autowire: true
autoconfigure: true
public: true

App\Controller\:
resource: '../../src/Controller'
tags: ['controller.service_arguments']

App\Controller\AwsController:
arguments:
- '@app.util.aws_ses_util'

config/services/utils.yaml


services:
_defaults:
autowire: true
autoconfigure: true
public: false

App\Util\:
resource: '../../src/Util'

Aws\Sdk:
arguments:
- '%aws_sdk%'

app.util.aws_ses_util:
class: '%aws_ses_util_class%'

config/packages/dev/parameters.yaml


This is pointing at the mock class.


parameters:

aws_ses_util_class: 'App\Tests\Mock\AwsSesUtilMock'

config/packages/test/parameters.yaml


This is pointing at the mock class.


parameters:

aws_ses_util_class: 'App\Tests\Mock\AwsSesUtilMock'

config/packages/prod/parameters.yaml


This is pointing at the real class.


parameters:

aws_ses_util_class: 'App\Util\Aws\AwsSesUtil'

Test


If you update the value for APP_ENV variable and call http://localhost.dev/aws/ses, you will see MESSAGE_ID as response for dev and test environments. If you change it to prod you will see the real message id such as 000201642d907400-0e7ea6ae-1111-2222-bc4c-e9039f4d15c5-333333 as the response.