Installation
composer require captjm/story-entity-bundle
Add to config/app.php under providers:
Captjm\StoryEntityBundle\StoryEntityServiceProvider::class,
Publish the config (if needed):
php artisan vendor:publish --provider="Captjm\StoryEntityBundle\StoryEntityServiceProvider" --tag="config"
Basic Usage Define a Story Entity via a trait:
use Captjm\StoryEntityBundle\Traits\StoryEntity;
class Post extends Model
{
use StoryEntity;
// Your model fields
}
The trait automatically adds story_id and story_version columns to your database table.
First Use Case: Versioned Content
$post = new Post(['title' => 'Hello World']);
$post->save(); // Creates initial story version
$post->title = 'Updated Title';
$post->save(); // Creates new story version (story_version increments)
Versioned Model Operations
save() on a StoryEntity creates a new version if changes are detected.$post->getStoryVersion(1); // Returns version 1's data
$post->rollback(1); // Reverts to version 1
Querying Versions
// Get all versions of a story
$versions = $post->getStoryVersions();
// Filter versions by date range
$versions = $post->getStoryVersions()
->where('created_at', '>=', now()->subDays(7));
Soft Deletes with Versioning
$post->delete(); // Soft deletes current version but keeps history
$post->restore(); // Restores latest version
story.created or story.updated events:
StoryEntity::created(function ($story) {
// Log version changes
});
toArray() or toJson():
return $post->getStoryVersions()->values()->all();
story_id and story_version columns. Customize via:
Schema::table('posts', function (Blueprint $table) {
$table->unsignedBigInteger('story_id')->after('id');
$table->unsignedInteger('story_version')->after('story_id');
});
Database Schema Conflicts
story_id and story_version columns are unsigned to avoid overflow.Version Comparison Quirks
getStoryVersions() returns current version first. Use ->reverse() for chronological order.rollback() does not trigger model events (e.g., saved or updated).Performance
with('storyVersions') in large datasets. Use lazy loading:
$posts = Post::where(...)->get();
foreach ($posts as $post) {
$post->load('storyVersions'); // Load per model
}
story_id is being set correctly. Override getStoryId() in your model if using custom keys:
public function getStoryId()
{
return $this->id; // or custom logic
}
fresh() to reload the model after rollback:
$post->rollback(1);
$post->fresh(); // Ensures data matches version 1
getStoryTable() to use a separate table:
protected $storyTable = 'custom_story_versions';
StoryVersion model:
class StoryVersion extends \Captjm\StoryEntityBundle\Models\StoryVersion
{
protected $casts = [
'metadata' => 'array',
];
}
bootStoryEntity():
protected static function bootStoryEntity()
{
static::creating(function ($model) {
// Pre-save logic
});
}
story_id = model id. Disable auto-creation with:
'story_entity' => [
'auto_create_story' => false,
]
bootStoryEntity() to prevent bloat.How can I help you explore Laravel packages today?