Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Etagging Laravel Package

adsazad/symfony-etagging

Symfony bundle that adds easy ETag support to any Response. Inject EtaggingInterface and wrap rendered responses with etagResponse() to enable caching/conditional requests. Supports custom headers plus configurable max-age and shared max-age.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require adsazad/symfony-etagging
    

    Add the bundle to config/bundles.php:

    return [
        // ...
        Adsazad\SymfonyEtaggingBundle\SymfonyEtaggingBundle::class => ['all' => true],
    ];
    
  2. First Use Case Inject EtaggingInterface into a controller and wrap your response:

    use Adsazad\SymfonyEtaggingBundle\Util\EtaggingInterface;
    
    public function index(EtaggingInterface $etag)
    {
        $response = $this->render('home.html.twig');
        return $etag->etagResponse($response);
    }
    
  3. Where to Look First

    • README for core usage.
    • src/Util/EtaggingInterface.php for method signatures.
    • Symfony’s HTTP Cache docs for caching theory.

Implementation Patterns

Core Workflow

  1. Etagging Responses Use etagResponse() on any Symfony Response object (e.g., render(), json(), redirect()).

    $response = new Response('Content');
    return $etag->etagResponse($response);
    
  2. Custom Headers Extend functionality by adding custom headers before calling etagResponse():

    $etag->addCustom('X-My-Header', 'value');
    return $etag->etagResponse($response);
    
  3. Cache Control

    • Private Caching (per-user):
      $etag->setMaxAge(3600); // 1 hour
      
    • Shared Caching (CDN/proxy):
      $etag->setSharedMax(86400); // 1 day
      
  4. Integration with Twig Dynamically generate ETags based on template content:

    {% set contentHash = render(contentBlock) | hash('sha256') %}
    

    Then pass to etagResponse() via controller logic.

  5. Middleware Approach For global ETagging, create a middleware:

    public function handle(Request $request, callable $next)
    {
        $response = $next($request);
        $etag->etagResponse($response);
        return $response;
    }
    

    Register in config/routes.php:

    $app->middleware(EtagMiddleware::class)->group(...);
    

Gotchas and Tips

Pitfalls

  1. ETag Collisions

    • Default ETag generation uses Response content + headers. If two responses differ only in non-cached headers (e.g., Last-Modified), collisions may occur.
    • Fix: Explicitly include critical headers in ETag logic:
      $etag->addCustom('X-Content-Hash', hash('sha256', $response->getContent()));
      
  2. Shared vs. Private Max-Age

    • setSharedMax() overrides setMaxAge() if both are called. Test edge cases:
      $etag->setMaxAge(3600)->setSharedMax(1800); // Shared wins!
      
  3. Conditional Requests

    • Browsers/CDNs send If-None-Match with ETags. Ensure your app handles 304 Not Modified:
      if ($request->headers->has('If-None-Match') && $etag->isNotModified($request)) {
          return new Response('', 304);
      }
      
  4. Debugging ETags

    • Check headers in browser dev tools (Network tab). Missing ETag or incorrect Cache-Control indicates misconfiguration.
    • Log ETag values for debugging:
      $etagValue = $etag->etagResponse($response)->headers->get('ETag');
      $this->logger->info('Generated ETag:', ['etag' => $etagValue]);
      

Tips

  1. Leverage Symfony’s Cache System Combine with Cache component for dynamic ETag generation:

    $cacheKey = 'response_'.md5($request->getPathInfo());
    $content = $this->cache->get($cacheKey, function() use ($request) {
        return $this->render('page.html.twig', ['data' => $this->fetchData()]);
    });
    
  2. Extension Points

    • Override ETag generation by implementing a custom EtaggingInterface:
      class CustomEtagger implements EtaggingInterface {
          public function etagResponse(Response $response): Response {
              // Custom logic (e.g., database versioning)
              return parent::etagResponse($response);
          }
      }
      
    • Bind your service in services.yaml:
      Adsazad\SymfonyEtaggingBundle\Util\EtaggingInterface: '@custom_etagger'
      
  3. Performance Tuning

    • For static assets, use setSharedMax() with long durations (e.g., 31536000 for 1 year).
    • For dynamic content, use short setMaxAge() (e.g., 60 seconds) with must-revalidate.
  4. Testing

    • Mock EtaggingInterface in PHPUnit:
      $mockEtag = $this->createMock(EtaggingInterface::class);
      $mockEtag->method('etagResponse')->willReturn(new Response('Mocked'));
      
    • Test If-None-Match headers:
      $client->request('GET', '/page', [], [], [
          'HTTP_IF_NONE_MATCH' => 'expected-etag',
      ]);
      $this->assertEquals(304, $client->getResponse()->getStatusCode());
      
  5. Laravel-Specific Notes

    • Service Provider Binding: Register the bundle in AppServiceProvider:
      $this->app->bind(EtaggingInterface::class, function($app) {
          return new \Adsazad\SymfonyEtaggingBundle\Util\Etagging();
      });
      
    • Route Middleware: Apply globally via HandleEtagMiddleware in app/Http/Kernel.php:
      protected $middleware = [
          \App\Http\Middleware\HandleEtagMiddleware::class,
      ];
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log