Installation
composer require captjm/storyteller-bundle
Register the bundle in config/app.php under extra.bundles:
Captjm\StorytellerBundle\StorytellerBundle::class => ['all' => true],
Basic Configuration Publish the default config:
php artisan vendor:publish --provider="Captjm\StorytellerBundle\StorytellerBundle" --tag="config"
Update config/storyteller.php with your preferred storage (e.g., database, filesystem, or s3).
First Use Case: Logging a Story
Inject the Storyteller service into a controller or command:
use Captjm\StorytellerBundle\Storyteller;
public function __construct(private Storyteller $storyteller) {}
public function logStory()
{
$this->storyteller->story('user_registration', [
'user_id' => 123,
'ip' => request()->ip(),
]);
}
Define Story Types
Use consistent naming conventions (e.g., user_*., order_*.) for categorization.
$this->storyteller->story('order_placed', ['order_id' => 456, 'amount' => 99.99]);
Tagging for Filtering Attach metadata for later querying:
$this->storyteller->story('payment_failed', [
'user_id' => 123,
'tags' => ['fraud_check', 'stripe'],
]);
Integration with Laravel Events Dispatch events and log stories in listeners:
public function handle(UserRegistered $event)
{
$this->storyteller->story('user_registered', [
'user_id' => $event->user->id,
'email' => $event->user->email,
]);
}
config(['storyteller.driver' => 'database']);
config(['storyteller.driver' => 'filesystem', 'path' => storage_path('logs/stories')]);
config([
'storyteller.driver' => 's3',
'bucket' => 'your-bucket',
'prefix' => 'stories',
]);
Query stories via the Storyteller facade or service:
// Get all stories for a user
$stories = $this->storyteller->getStories(['user_id' => 123]);
// Filter by type and date range
$stories = $this->storyteller->getStories([
'type' => 'order_*',
'from' => now()->subDays(7),
'to' => now(),
]);
Storage Permissions
storage_path('logs/stories')) is writable.Database Schema Mismatch
database driver, run migrations:
php artisan migrate
metadata column).Performance with High Volume
$this->storyteller->batch(function () {
$this->storyteller->story('event_a', [...]);
$this->storyteller->story('event_b', [...]);
});
Check Storage
Verify stories are being written to the expected location (e.g., storage/logs/stories/ or database table storyteller_stories).
Logging Errors
Enable debug mode in config/storyteller.php:
'debug' => env('APP_DEBUG', false),
Errors will appear in Laravel’s log.
Custom Drivers
Extend the Captjm\StorytellerBundle\Contracts\Driver interface to support new storage backends (e.g., Elasticsearch).
Story Validation Add validation rules via a service provider:
Storyteller::extend(function ($story) {
if (empty($story['user_id'])) {
throw new \InvalidArgumentException('User ID is required.');
}
});
Event Dispatching Trigger Laravel events when stories are logged:
Storyteller::extend(function ($story) {
event(new StoryLogged($story));
});
Default Driver
If no driver is configured, the bundle defaults to filesystem with storage_path('logs/stories').
Serialization
Story data is serialized using json_encode(). Ensure payloads are JSON-serializable (no resources or circular references).
Use for Analytics
Combine with Laravel’s analytics package to track user journeys:
$this->storyteller->story('product_viewed', [
'user_id' => auth()->id(),
'product_id' => $product->id,
]);
Cleanup Old Stories Add a scheduled task to purge old stories (e.g., older than 6 months):
// config/schedule.php
$schedule->command('storyteller:prune')->daily();
How can I help you explore Laravel packages today?