21torr/bundle-helpers
Helpers for Symfony bundles to streamline implementation. Provides shared utilities and conveniences for other bundles, reducing boilerplate and keeping bundle setup consistent and easier to maintain. Docs available online.
Installation:
composer require 21torr/bundle-helpers
Ensure your project meets the requirements: Symfony 7+ (or 8) and PHP 8.3+.
First Use Case: Create a basic bundle extension with autogenerated aliases and configuration support:
use TwoOneTorr\BundleHelpers\Bundle\Extension\BundleExtension;
class MyBundleExtension extends BundleExtension
{
public function load(array $configs, ContainerBuilder $container): void
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// Register services or configurations here
}
}
BundleExtension base class handles:
@my_bundle.service).getConfiguration() setup (no manual overrides needed).Where to Look First:
src/Extension/BundleExtension.php for core functionality.tests/ for real-world usage examples (e.g., dependency injection, configuration merging).Use BundleExtension to define services dynamically based on user config:
public function load(array $configs, ContainerBuilder $container): void
{
$config = $this->processConfiguration(new Configuration(), $configs);
foreach ($config['services'] as $id => $serviceConfig) {
$container->register($id, $serviceConfig['class'])
->setArguments($serviceConfig['arguments'] ?? []);
}
}
Configuration class for validation:
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('my_bundle');
$treeBuilder->getRootNode()
->children()
->arrayNode('services')
->useAttributeAsKey('id')
->arrayPrototype()
->children()
->scalarNode('class')->isRequired()->end()
->arrayNode('arguments')->useAttributeAsKey('name')->end()
->end()
->end()
->end();
return $treeBuilder;
}
}
Avoid manual alias definitions:
// In MyBundleExtension.php
protected function getAliases(): array
{
return [
'my_bundle.service' => static::class . '::getServiceAlias',
];
}
public static function getServiceAlias(ContainerBuilder $container, string $id): string
{
return 'my_bundle.' . $id; // e.g., "my_bundle.user_repository"
}
Use BundleExtension to inject dependencies into services:
$container->register('my_bundle.logger', LoggerInterface::class)
->setPublic(true)
->setArgument('$logger', new Reference('logger'));
Attach subscribers without manual service registration:
public function load(array $configs, ContainerBuilder $container): void
{
$container->register('my_bundle.event_subscriber', EventSubscriber::class)
->addTag('kernel.event_subscriber', ['event' => 'my_event']);
}
config/packages/my_bundle.yaml.autoload-dev for local testing:
{
"autoload-dev": {
"psr-4": {
"TwoOneTorr\\BundleHelpers\\": "vendor/21torr/bundle-helpers/src/"
}
}
}
BundleExtension in PHPUnit:
$extension = $this->createMock(BundleExtension::class);
$extension->method('processConfiguration')->willReturn(['enabled' => true]);
Namespace Mismatch:
Class 'TwoOneTorr\BundleHelpers\...' not found.composer.json autoloads the package correctly. For local dev:
composer dump-autoload
Configuration Overrides:
processConfiguration() may silently ignore invalid configs if ConfigurationInterface isn’t implemented.Configuration and define a TreeBuilder:
class Configuration extends ContainerConfiguration
{
// ...
}
Symfony Version Conflicts:
Method "load()" must be of type void (Symfony 7+).BundleExtension extends TwoOneTorr\BundleHelpers\Bundle\Extension\BundleExtension (not Symfony’s base class).Alias Collisions:
@my_bundle.service) may conflict with Symfony’s built-in aliases.@my_bundle.my_service).var_dump($this->processConfiguration(...)) to inspect merged configs.php bin/console debug:container --parameters | grep my_bundle
php bin/console debug:configurator my_bundle
Custom Configuration:
Extend Configuration to add validation:
$treeBuilder->getRootNode()
->children()
->booleanNode('debug')
->defaultFalse()
->info('Enable debug mode')
->end();
Dynamic Service Loading:
Use ContainerBuilder::register() with closures for lazy-loading:
$container->register('my_bundle.dynamic_service', function (ContainerInterface $container) {
return new DynamicService($container->get('some.dependency'));
});
Environment-Specific Configs:
Override load() to conditionally load services:
if ($container->getParameter('kernel.environment') === 'dev') {
$container->loadFromExtension('my_bundle', ['debug' => true]);
}
array_key_first() with Configuration to enforce required keys:
$config = $this->processConfiguration(new Configuration(), $configs);
if (!array_key_first($config['services'])) {
throw new \InvalidArgumentException('At least one service must be defined.');
}
var/cache/dev/ for faster dev cycles.Configuration with PHPDoc for IDE hints:
/**
* @property array<string, mixed> $services
*/
class Configuration implements ConfigurationInterface { ... }
How can I help you explore Laravel packages today?