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

Vessel Laravel Package

epessine/vessel

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation: Add via Composer:

    composer require epessine/vessel
    

    No additional configuration is required.

  2. Define a Vessel Class: Create a class extending Vessel\BaseVessel in app/Vessels (or your preferred namespace). Example:

    namespace App\Vessels;
    
    use Vessel\BaseVessel;
    
    class CartVessel extends BaseVessel
    {
        public int $itemCount = 0;
        public array $items = [];
    }
    
  3. Register the Vessel: Bind the vessel in a service provider (e.g., AppServiceProvider):

    public function boot()
    {
        $this->app->singleton(CartVessel::class);
    }
    
  4. Use in Livewire: Inject the vessel into your Livewire component:

    use App\Vessels\CartVessel;
    
    public function mount(CartVessel $cartVessel)
    {
        $this->cart = $cartVessel;
    }
    

    Access properties directly:

    $this->cart->itemCount++;
    
  5. Share Across Components: All components with the same vessel instance will reflect changes in real-time.


First Use Case

Scenario: A multi-component dashboard where a sidebar filters content in a main panel.

  • Define a FilterVessel with filter criteria (e.g., selectedCategory, searchQuery).
  • Inject into both the sidebar (filter component) and main panel (results component).
  • Updates in the sidebar automatically reflect in the panel without manual event dispatching.

Implementation Patterns

Core Workflows

  1. State Centralization:

    • Use vessels for global, shared state (e.g., user preferences, UI toggles, form data).
    • Avoid prop drilling or excessive Livewire events.
  2. Initialization: Override init() to set default values or load from a database:

    public function init()
    {
        $this->selectedUserType = auth()->user()->defaultRole;
    }
    
  3. Type Safety: Leverage PHP types (e.g., public string $name) for IDE autocompletion and runtime checks.

  4. Component Integration:

    • Inject via Constructor: Preferred for dependency injection.
      public function __construct(public CartVessel $cart) {}
      
    • Access via App Container: For one-off usage.
      $vessel = app(CartVessel::class);
      
  5. Reactivity: Vessel properties are automatically reactive in Livewire. Changes trigger UI updates in all components using the vessel.


Advanced Patterns

  1. Scoped Vessels: Bind vessels to specific requests or sessions for isolation:

    $this->app->when(Request::create('/admin'))
        ->singleton(AdminVessel::class);
    
  2. Validation: Add validation logic in init() or a custom method:

    public function validate()
    {
        if ($this->itemCount < 0) {
            $this->itemCount = 0;
        }
    }
    
  3. Persistence: Save/load state to/from a database or cache:

    public function init()
    {
        $this->items = Cache::get('cart_items', []);
    }
    
    public function save()
    {
        Cache::put('cart_items', $this->items, now()->addHours(1));
    }
    
  4. Nested Vessels: Compose vessels for hierarchical state:

    class UserVessel extends BaseVessel
    {
        public ProfileVessel $profile;
        public SettingsVessel $settings;
    }
    
  5. Testing: Mock vessels in tests:

    $vessel = Mockery::mock(CartVessel::class);
    $vessel->shouldReceive('getItemCount')->andReturn(5);
    $this->app->instance(CartVessel::class, $vessel);
    

Gotchas and Tips

Pitfalls

  1. Singleton Scope: Vessels are application-wide singletons by default. Overuse can lead to unintended state leakage.

    • Fix: Use scoped bindings or request-specific vessels for isolated state.
  2. Reactivity Quirks:

    • Changes to nested objects/arrays may not trigger reactivity. Use array_push() or [] = $value instead of direct assignment.
    • Workaround: Reset the entire property or use wire:model for complex state.
  3. Circular Dependencies: Avoid circular references between vessels and Livewire components (e.g., a vessel holding a Livewire component instance).

    • Fix: Use IDs or weak references.
  4. Type Coercion: Vessel properties are strictly typed. Assigning null to a non-nullable property will throw an error.

    • Tip: Use ?string or initialize with null.
  5. Livewire Hydration: Vessels are not hydrated by default. For form-like state, use wire:model or manual hydration:

    public function hydrate()
    {
        $this->items = $this->all();
    }
    

Debugging Tips

  1. Log State: Add a debug() method to vessels:

    public function debug()
    {
        \Log::debug('Vessel state:', [
            'itemCount' => $this->itemCount,
            'items' => $this->items,
        ]);
    }
    
  2. Check Bindings: Verify the vessel is bound correctly:

    php artisan container:list | grep Vessel
    
  3. Clear Cache: If changes aren’t reflecting, clear Livewire and config caches:

    php artisan optimize:clear
    php artisan view:clear
    

Extension Points

  1. Custom Initialization: Override initialize() for one-time setup:

    protected function initialize()
    {
        $this->loadFromSession();
    }
    
  2. Events: Dispatch events on state changes:

    public function setItemCount(int $count)
    {
        $this->itemCount = $count;
        event(new CartUpdated($this));
    }
    
  3. Middleware: Restrict vessel access via middleware:

    $this->app->when(CartVessel::class)
        ->needs('auth')
        ->give(fn () => new CartVessel());
    
  4. Serialization: Implement JsonSerializable for API responses:

    public function jsonSerialize()
    {
        return [
            'count' => $this->itemCount,
            'items' => array_keys($this->items),
        ];
    }
    
  5. Blade Directives: Create a Blade directive for quick access:

    Blade::directive('vessel', function ($vessel) {
        return "<?php echo app({$vessel})->{$args[0]} ?? ''; ?>";
    });
    

    Usage:

    @vessel('itemCount')
    
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.
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table