Installation: Add via Composer:
composer require epessine/vessel
No additional configuration is required.
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 = [];
}
Register the Vessel:
Bind the vessel in a service provider (e.g., AppServiceProvider):
public function boot()
{
$this->app->singleton(CartVessel::class);
}
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++;
Share Across Components: All components with the same vessel instance will reflect changes in real-time.
Scenario: A multi-component dashboard where a sidebar filters content in a main panel.
FilterVessel with filter criteria (e.g., selectedCategory, searchQuery).State Centralization:
Initialization:
Override init() to set default values or load from a database:
public function init()
{
$this->selectedUserType = auth()->user()->defaultRole;
}
Type Safety:
Leverage PHP types (e.g., public string $name) for IDE autocompletion and runtime checks.
Component Integration:
public function __construct(public CartVessel $cart) {}
$vessel = app(CartVessel::class);
Reactivity: Vessel properties are automatically reactive in Livewire. Changes trigger UI updates in all components using the vessel.
Scoped Vessels: Bind vessels to specific requests or sessions for isolation:
$this->app->when(Request::create('/admin'))
->singleton(AdminVessel::class);
Validation:
Add validation logic in init() or a custom method:
public function validate()
{
if ($this->itemCount < 0) {
$this->itemCount = 0;
}
}
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));
}
Nested Vessels: Compose vessels for hierarchical state:
class UserVessel extends BaseVessel
{
public ProfileVessel $profile;
public SettingsVessel $settings;
}
Testing: Mock vessels in tests:
$vessel = Mockery::mock(CartVessel::class);
$vessel->shouldReceive('getItemCount')->andReturn(5);
$this->app->instance(CartVessel::class, $vessel);
Singleton Scope: Vessels are application-wide singletons by default. Overuse can lead to unintended state leakage.
Reactivity Quirks:
array_push() or [] = $value instead of direct assignment.wire:model for complex state.Circular Dependencies: Avoid circular references between vessels and Livewire components (e.g., a vessel holding a Livewire component instance).
Type Coercion:
Vessel properties are strictly typed. Assigning null to a non-nullable property will throw an error.
?string or initialize with null.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();
}
Log State:
Add a debug() method to vessels:
public function debug()
{
\Log::debug('Vessel state:', [
'itemCount' => $this->itemCount,
'items' => $this->items,
]);
}
Check Bindings: Verify the vessel is bound correctly:
php artisan container:list | grep Vessel
Clear Cache: If changes aren’t reflecting, clear Livewire and config caches:
php artisan optimize:clear
php artisan view:clear
Custom Initialization:
Override initialize() for one-time setup:
protected function initialize()
{
$this->loadFromSession();
}
Events: Dispatch events on state changes:
public function setItemCount(int $count)
{
$this->itemCount = $count;
event(new CartUpdated($this));
}
Middleware: Restrict vessel access via middleware:
$this->app->when(CartVessel::class)
->needs('auth')
->give(fn () => new CartVessel());
Serialization:
Implement JsonSerializable for API responses:
public function jsonSerialize()
{
return [
'count' => $this->itemCount,
'items' => array_keys($this->items),
];
}
Blade Directives: Create a Blade directive for quick access:
Blade::directive('vessel', function ($vessel) {
return "<?php echo app({$vessel})->{$args[0]} ?? ''; ?>";
});
Usage:
@vessel('itemCount')
How can I help you explore Laravel packages today?