Installation:
composer require ekino/drupal-bundle
Ensure your project structure matches:
SymfonyRoot/
├── app/
├── vendor/
├── src/
└── web/ # Drupal 7 source (document root)
Configure index.php:
Replace Drupal’s index.php with the provided snippet to share the Symfony container:
require __DIR__.'/../app/bootstrap.php.cache';
$kernel = new AppKernel('prod', false);
$kernel->boot();
$container = $kernel->getContainer();
\Drupal::setContainer($container); // Critical for service sharing
First Use Case:
Use Symfony’s services in a Drupal command (e.g., drush):
// In a custom Drush command (e.g., `app/console drush my:command`)
use Symfony\Component\DependencyInjection\ContainerInterface;
$container = \Drupal::getContainer();
$logger = $container->get('logger'); // Access Symfony service
$logger->info('Drupal + Symfony integration works!');
Drupal → Symfony:
Inject Symfony services into Drupal modules/services via hook_service_alter():
function MYMODULE_service_alter(&$services) {
$services['my.drupal_service'] = array(
'class' => 'My\Service',
'arguments' => ['@logger'], // Inject Symfony service
);
}
Symfony → Drupal: Access Drupal services from Symfony controllers/commands:
// In a Symfony controller
$drupalDatabase = \Drupal::database();
$users = $drupalDatabase->query("SELECT * FROM {users}")->fetchAll();
// src/AppBundle/Command/DrupalBackupCommand.php
namespace AppBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class DrupalBackupCommand extends Command {
protected function execute(InputInterface $input, OutputInterface $output) {
$backup = \Drupal::service('backup');
$backup->create();
$output->writeln('Backup done!');
}
}
Register in app/config/commands.yml:
commands:
app:drupal_backup: DrupalBackupCommand
Symfony Events in Drupal: Listen to Drupal events from Symfony:
// In a Symfony service
$eventDispatcher = $container->get('event_dispatcher');
$eventDispatcher->addListener('hook_cron_run', function() {
// Handle Drupal cron events
});
Drupal Events in Symfony:
Use \Drupal::moduleHandler()->invokeAll() to trigger Drupal hooks:
\Drupal::moduleHandler()->invokeAll('my_custom_hook');
routing.yml and reuse them in Drupal templates:
{{
path({
'_route': 'app.my_route',
'_parameters': { 'id': node.id }
})
}}
Container Initialization Order:
Drupal::service() fails with ServiceNotFoundException.index.php calls $kernel->boot() before \Drupal::setContainer().AppKernel is properly loaded by inspecting var/cache/dev/appDevProjectContainer.xml.Circular Dependencies:
$service = $container->get('service_id')->setDrupalDependency(\Drupal::service('drupal_service'));
Drush Compatibility:
drush commands fail with Class not found.vendor/bin/drush is used (not global Drush). Add to composer.json:
"bin-dir": "vendor/bin"
Caching Conflicts:
php app/console cache:clear
drush cc all
Symfony Debug Toolbar:
Enable in config_dev.yml to inspect services:
framework:
profiler: { only_exceptions: false }
Access at /_profiler.
Drupal Logs: Redirect Symfony logs to Drupal’s watchdog:
$logger = $container->get('logger');
$logger->addHandler(new \Drupal\Core\Logger\DrupalLoggerHandler('drupal_logger'));
Dependency Injection: Dump services for debugging:
$container->get('debug.container')->dump();
Custom Service Providers: Extend Drupal’s service container by creating a Symfony service provider:
// src/AppBundle/Drupal/DrupalServiceProvider.php
namespace AppBundle\Drupal;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
class DrupalServiceProvider {
public function load(ContainerBuilder $container) {
$definition = new Definition('My\DrupalService');
$definition->addArgument('@drupal.database');
$container->setDefinition('my.drupal_service', $definition);
}
}
Register in app/config/config.yml:
imports:
- { resource: drupal-services.yml }
Override Drupal Services: Use Symfony’s compiler passes to modify Drupal services:
// src/AppBundle/DependencyInjection/Compiler/DrupalPass.php
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class DrupalPass implements CompilerPassInterface {
public function process(ContainerBuilder $container) {
$definition = $container->findDefinition('drupal.service');
$definition->addMethodCall('setCustomOption', ['value']);
}
}
Tag in services.yml:
services:
app.drupal_pass:
class: AppBundle\DependencyInjection\Compiler\DrupalPass
tags: [compiler_pass]
Environment-Specific Config:
Use Symfony’s %kernel.environment% to switch Drupal behaviors:
# app/config/config_dev.yml
drupal:
debug_mode: true
cache: false
How can I help you explore Laravel packages today?