danilovl/render-service-twig-extension-bundle
Installation
composer require danilovl/render-service-twig-extension-bundle
Add to config/bundles.php (Symfony) or config/app.php (Laravel via Symfony bridge):
return [
// ...
Danilovl\RenderServiceTwigExtensionBundle\RenderServiceTwigExtensionBundle::class => ['all' => true],
];
First Use Case Define a service with a method to render:
// src/Service/RenderableService.php
namespace App\Service;
class RenderableService {
public function renderPartial(string $template, array $data = []): string {
return \Twig\Environment::createLoader(...)->render($template, $data);
}
}
Register it as a service (Symfony/Laravel):
# config/services.yaml
services:
App\Service\RenderableService: ~
Use in Twig:
{{ render_service('App\Service\RenderableService', 'renderPartial', ['template': 'partials/_card.html.twig', 'data': {'title': 'Hello'}]) }}
Replacing render(controller())
{{ render(controller('App\Controller\CardController::showCardAction')) }}public function renderCard(array $data): string {
return $this->twig->render('card.html.twig', $data);
}
Use in Twig:
{{ render_service('App\Service\CardService', 'renderCard', {'data': {'title': 'Card'}}) }}
Dynamic Service Resolution Use dependency injection to pass services dynamically:
{% set service = service('App\Service\DynamicRenderer') %}
{{ render_service(service, 'render', ['template': 'dynamic.html.twig']) }}
Integration with Laravel
laravel/symfony):
// config/app.php
'extra' => [
'bundles' => [
Danilovl\RenderServiceTwigExtensionBundle\RenderServiceTwigExtensionBundle::class => ['all' => true],
],
];
@php
$rendered = \Twig\Environment::createLoader(...)->render(
'partials/_sidebar.blade.twig',
['user' => $user]
);
@endphp
{!! $rendered !!}
Or create a custom Twig extension in Laravel:
// app/Providers/AppServiceProvider.php
public function boot() {
$twig = app('view.factory')->getEngine();
$twig->addExtension(new \Danilovl\RenderServiceTwigExtensionBundle\Twig\RenderServiceExtension());
}
Caching Rendered Output Cache service method results to avoid redundant rendering:
public function renderCached(string $template, array $data, string $cacheKey): string {
return Cache::remember($cacheKey, 3600, function() use ($template, $data) {
return $this->twig->render($template, $data);
});
}
Service Autowiring
$this->app->bind('App\Service\RenderableService', function ($app) {
return new RenderableService($app['twig']);
});
Circular Dependencies
{{ render_service('A', 'render', {'data': render_service('B', 'render', {})}) }} <!-- Risky -->
Pre-render data in PHP before passing to Twig.Performance Overhead
render(controller()), excessive use of render_service can still impact performance. Benchmark critical paths.Twig Environment Configuration
Service Not Found
php bin/console debug:container App\Service\RenderableService
php artisan container:list | grep RenderableService
Method Not Callable
public function renderPartial() { ... } // Must be public
Template Loading Issues
'@App/partials/_card.html.twig') to avoid loader conflicts.Customizing the Twig Extension Override the default extension in Symfony:
# config/packages/danilovl_render_service_twig_extension.yaml
danilovl_render_service_twig_extension:
extension_class: App\Twig\CustomRenderServiceExtension
Or in Laravel:
$twig->addExtension(new CustomRenderServiceExtension());
Adding Validation Extend the bundle to validate service/method/data before rendering:
class ValidatingRenderServiceExtension extends RenderServiceExtension {
public function renderService(ServiceInterface $service, string $method, array $arguments) {
if (!$this->isValidService($service)) {
throw new \RuntimeException('Invalid service');
}
return parent::renderService($service, $method, $arguments);
}
}
Laravel-Specific Tips
view()->make() for partials and cache them:
public function renderPartial(string $view, array $data = []): string {
return Cache::remember("view_{$view}", 3600, function() use ($view, $data) {
return view($view, $data)->render();
});
}
use Purifier;
return Purifier::clean($this->twig->render($template, $data));
Security
if (!preg_match('/^[a-zA-Z0-9_]+$/', $method)) {
throw new \InvalidArgumentException('Invalid method name');
}
How can I help you explore Laravel packages today?