c33s/parameter-objects-bundle
Installation
composer require c33s/parameter-objects-bundle
Add the bundle to config/bundles.php in Symfony:
return [
// ...
C33s\ParameterObjectsBundle\C33sParameterObjectsBundle::class => ['all' => true],
];
Define a Parameter Object
Create a class in src/ParameterObject/ (or your preferred namespace) with a constructor accepting the raw parameter value:
namespace App\ParameterObject;
class UserLimitParameters
{
public function __construct(
private int $maxResults,
private ?int $offset = null,
) {}
}
First Use Case
Replace a raw Symfony parameter (e.g., in config/packages/app.yaml):
# Before
services:
App\Service\UserService:
arguments:
$maxResults: '%app.user_limit%'
# After
services:
App\Service\UserService:
arguments:
$userLimitParameters: '@App\ParameterObject\UserLimitParameters'
Define the parameter in config/packages/app.yaml:
parameters:
app.user_limit.max_results: 100
app.user_limit.offset: null
class UserService
{
public function __construct(
private UserLimitParameters $userLimitParameters,
) {}
}
parameter_objects key in config/services.yaml:
services:
App\ParameterObject\UserLimitParameters:
arguments:
$maxResults: '%app.user_limit.max_results%'
$offset: '%app.user_limit.offset%'
#[Assert\Type]) or custom logic in the constructor:
public function __construct(
private int $maxResults,
private ?int $offset = null,
) {
if ($maxResults <= 0) {
throw new \InvalidArgumentException('Max results must be positive.');
}
}
# config/validator/validation.yaml
App\ParameterObject\UserLimitParameters:
properties:
maxResults:
- Positive:
message: 'Max results must be greater than 0.'
%kernel.environment% to switch parameter sets:
class DatabaseConfig
{
public function __construct(
private string $dsn,
private array $options,
) {}
public static function createFromEnvironment(): self
{
return new self(
dsn: '%env(DB_DSN)%',
options: [
'driver' => '%env(DB_DRIVER)%',
'port' => '%env(int:DB_PORT)%',
],
);
}
}
Register as a service:
services:
App\ParameterObject\DatabaseConfig:
factory: ['App\ParameterObject\DatabaseConfig', 'createFromEnvironment']
# config/packages/app.yaml
parameters:
app.legacy_max_results: 50 # Old way
app.user_limit.max_results: 100 # New way
services:
App\Service\UserService:
arguments:
$legacyMaxResults: '%app.legacy_max_results%' # Temporary
$userLimitParameters: '@App\ParameterObject\UserLimitParameters' # New
Circular Dependencies:
class PaginationParameters {
public function __construct(private UserLimitParameters $userLimit) {}
}
Parameter Name Mismatches:
%app.user_limit.max_results%) match the constructor argument names exactly. The bundle does not auto-map by type alone.bin/console debug:container App\ParameterObject\* to verify service configuration.Immutable Objects:
null) may lead to ArgumentCountError.public function __construct(
private int $maxResults = 10,
private ?int $offset = null,
) {}
Caching Quirks:
php bin/console cache:clear
dump-autoload after modifying parameter objects:
composer dump-autoload
Service Dumping: Inspect the resolved parameter object:
php bin/console debug:container App\ParameterObject\UserLimitParameters
Look for arguments to verify injected values.
Parameter Validation: Use Symfony’s parameter validator to catch issues early:
php bin/console debug:config parameter_objects
Logging: Add temporary logging in the constructor to trace values:
public function __construct(private int $maxResults) {
\Symfony\Component\Debug\Debug::assert(
$maxResults > 0,
sprintf('Invalid maxResults: %d', $maxResults)
);
}
Custom Factories: Override parameter object creation via a factory service:
services:
App\ParameterObject\DynamicUserLimitParameters:
factory: ['App\Factory\UserLimitFactory', 'createFromRequest']
arguments:
$requestStack: '@request_stack'
Event Listeners: Validate or modify parameter objects before injection using Symfony events:
namespace App\EventListener;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
class ParameterValidatorListener
{
public function onKernelController(ControllerEvent $event): void
{
$userLimit = $event->getController()[0]?->getUserLimitParameters();
if ($userLimit->getMaxResults() > 1000) {
throw new \RuntimeException('Max results too high!');
}
}
}
Register the listener in config/services.yaml:
services:
App\EventListener\ParameterValidatorListener:
tags:
- { name: kernel.event_listener, event: kernel.controller }
Compiler Passes: Dynamically generate parameter objects at compile time using Symfony’s compiler passes. Example:
namespace App\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ParameterObjectPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
$definition = $container->findDefinition('App\ParameterObject\DynamicConfig');
$definition->addMethodCall('setEnvironment', ['%kernel.environment%']);
}
}
Register the pass in src/Kernel.php:
protected function build(ContainerBuilder $container): void
{
$container->addCompilerPass(new ParameterObjectPass());
}
How can I help you explore Laravel packages today?