Weave Code
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

Contracts Laravel Package

symfony/contracts

Symfony Contracts provides small, domain-focused PHP interfaces, traits, and normative docblocks extracted from Symfony components. Use them as stable type hints for loose coupling, interoperability, and easy DI/autowiring with battle-tested implementations.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the package:

    composer require symfony/contracts
    

    (No configuration needed—just type-hint interfaces where required.)

  2. First use case: Replace Laravel’s Cache facade with CacheInterface in a service:

    use Symfony\Contracts\Cache\CacheInterface;
    
    class UserService
    {
        public function __construct(private CacheInterface $cache) {}
    }
    
  3. Bridge Laravel services: In AppServiceProvider@boot():

    $this->app->alias(
        CacheInterface::class,
        Illuminate\Cache\Repository::class
    );
    
  4. Autowire with Laravel: Ensure config/app.php has "autowire": true and "autowire_cache": true.


Implementation Patterns

Core Workflows

  1. Infrastructure Abstraction:

    • Cache: Use CacheInterface for storage-agnostic caching (e.g., Redis, file, database).
      $item = $cache->get('key', fn() => $expensiveOperation());
      
    • Events: Replace Event facade with EventDispatcherInterface:
      $dispatcher->dispatch(new UserRegisteredEvent($user));
      
  2. Cross-Framework Libraries:

    • Build reusable components (e.g., payment processors) that inject HttpClientInterface (Symfony) or GuzzleClient (Laravel) interchangeably.
  3. Service Layer Decoupling:

    • Before: use Illuminate\Mail\Mailer;
    • After: use Symfony\Contracts\Mailer\MailerInterface;
    • Bind in AppServiceProvider:
      $this->app->bind(MailerInterface::class, function ($app) {
          return $app->make(Illuminate\Mail\Mailer::class);
      });
      
  4. Testing:

    • Mock contracts directly:
      $cache = $this->createMock(CacheInterface::class);
      $cache->method('get')->willReturn('mocked-value');
      

Integration Tips

  • Laravel Facades → Contracts: Replace Cache::store() with injected CacheInterface in new code. Use adapters for legacy facades:

    class CacheFacadeAdapter implements CacheInterface {
        public function get($key, Closure $default) {
            return Cache::store('default')->get($key, $default());
        }
        // ... other methods
    }
    
  • Autowiring: Laravel’s autowiring ignores Symfony interfaces by default. Explicitly bind or use bind():

    $this->app->bind(Symfony\Contracts\Cache\CacheInterface::class,
        fn($app) => $app->make(Illuminate\Cache\Repository::class));
    
  • Traits for Partial Compliance: Extend CacheItemInterface in custom cache items:

    class LaravelCacheItem implements CacheItemInterface {
        use Symfony\Contracts\Cache\CacheItemTrait;
        // ...
    }
    

Gotchas and Tips

Pitfalls

  1. Autowiring Failures:

    • Symptom: Target class [Symfony\Contracts\Cache\CacheInterface] does not exist.
    • Fix: Bind the interface explicitly or ensure Laravel’s alias() is configured.
  2. Semantic Mismatches:

    • Laravel’s Cache::remember() uses a callback, while CacheInterface::get() may expect a default value.
    • Solution: Wrap Laravel methods in adapters or use traits to bridge gaps.
  3. Version Conflicts:

    • Symfony Contracts v3.x requires PHP 8.1+. Laravel 9.x may need v2.x, risking deprecation.
    • Tip: Pin versions in composer.json:
      "require": {
          "symfony/contracts": "^3.0"
      }
      
  4. Missing Implementations:

    • Not all Laravel services have direct contract implementations (e.g., Queue).
    • Workaround: Build adapters or use PSR-15 (MessageBusInterface) for queues.

Debugging

  • Check Bindings:

    php artisan container:inspect Symfony\Contracts\Cache\CacheInterface
    

    (Ensures the interface is properly bound.)

  • Mocking Issues:

    • If mocks fail, verify the contract’s methods match Laravel’s implementation (e.g., get() vs. remember()).

Extension Points

  1. Custom Implementations:

    • Create a RedisCache class implementing CacheInterface for Redis support:
      class RedisCache implements CacheInterface {
          use CacheItemTrait;
          // ...
      }
      
  2. Composite Contracts:

    • Combine interfaces for richer semantics (e.g., CacheInterface & TaggableInterface).
  3. Service Tags:

    • Use Symfony’s #[AsService] attribute (if using Symfony’s DI) or Laravel’s bindIf() for conditional binding.

Pro Tips

  • Start Small: Begin with CacheInterface or EventDispatcherInterface—low-risk domains.
  • Document Contracts: Add PHPDoc @method annotations for unsupported methods in adapters.
  • Leverage provide: Declare implementations in composer.json for tooling compatibility:
    "provide": {
        "symfony/cache-implementation": "1.0"
    }
    
  • Avoid Facades: Prefer constructor injection over facades when using contracts to enforce DI.
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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi