PHPackages                             yannelli/entry-vault-laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. yannelli/entry-vault-laravel

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

yannelli/entry-vault-laravel
============================

A Laravel 12 package for building a backend-only entry/resource library system with multi-tenancy, versioning, and state management

v1.4.1(4mo ago)142[3 PRs](https://github.com/yannelli/entry-vault-laravel/pulls)MITPHPPHP ^8.2CI passing

Since Dec 26Pushed 1mo agoCompare

[ Source](https://github.com/yannelli/entry-vault-laravel)[ Packagist](https://packagist.org/packages/yannelli/entry-vault-laravel)[ Docs](https://github.com/yannelli/entry-vault-laravel)[ GitHub Sponsors](https://github.com/:vendor_name)[ RSS](/packages/yannelli-entry-vault-laravel/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (17)Versions (19)Used By (0)

Entry Vault
===========

[](#entry-vault)

A Laravel 12 package for building a backend-only entry/resource library system with multi-tenancy, versioning, and state management.

[![Latest Version on Packagist](https://camo.githubusercontent.com/1723d837f49c0c56401c28ef6069d1f891c36fea5cc152caefbfee7b3c0ffcb2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f79616e6e656c6c692f656e7472792d7661756c742d6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/yannelli/entry-vault-laravel)[![GitHub Tests Action Status](https://camo.githubusercontent.com/b6b294b173861449d9ba093826f7b4964c73a6074d611a409a3f91693d2f1c04/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f79616e6e656c6c692f656e7472792d7661756c742d6c61726176656c2f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/yannelli/entry-vault-laravel/actions?query=workflow%3Arun-tests+branch%3Amain)

Features
--------

[](#features)

- **Entry Management** - Full CRUD operations with metadata (title, description, keywords)
- **Flexible Content Storage** - Separate content table with support for multiple content types (markdown, HTML, JSON, text)
- **Multi-tenancy Support** - Polymorphic ownership (user, team, or custom model)
- **Visibility Controls** - Public, private, and team visibility options
- **Draft/Published Workflow** - State machine with draft, published, and archived states
- **Version History** - Built-in versioning with revert capabilities
- **Category System** - System, team, or user-owned categories
- **Template System** - Create entries from templates with featured/starter templates
- **Filament 4 Integration** - Optional admin panel for managing entries, categories, and content

**No UI components are included by default.** This is a pure backend/API package with optional Filament admin panel integration.

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require yannelli/entry-vault-laravel
```

Run the installation command:

```
php artisan entry-vault:install
```

This will:

1. Publish the configuration file
2. Publish and run migrations
3. Optionally seed default categories

### Manual Installation

[](#manual-installation)

If you prefer manual installation:

```
# Publish config
php artisan vendor:publish --tag="entry-vault-config"

# Publish migrations
php artisan vendor:publish --tag="entry-vault-migrations"

# Run migrations
php artisan migrate

# Seed default categories (optional)
php artisan entry-vault:seed-categories
```

Configuration
-------------

[](#configuration)

The configuration file is published to `config/entry-vault.php`:

```
return [
    // Table names
    'tables' => [
        'entries' => 'entries',
        'contents' => 'entry_contents',
        'categories' => 'entry_categories',
    ],

    // Model classes (for extending)
    'models' => [
        'entry' => \Yannelli\EntryVault\Models\Entry::class,
        'content' => \Yannelli\EntryVault\Models\EntryContent::class,
        'category' => \Yannelli\EntryVault\Models\EntryCategory::class,
        'version' => \Yannelli\EntryVault\Models\EntryVersion::class,
    ],

    // User and team models
    'user_model' => \App\Models\User::class,
    'team_model' => null,

    // Defaults
    'default_visibility' => 'private',
    'default_state' => 'draft',

    // Versioning
    'versioning' => [
        'enabled' => true,
        'strategy' => 'snapshot',
        'keep_versions' => 50,
    ],
];
```

Filament Admin Panel (Optional)
-------------------------------

[](#filament-admin-panel-optional)

Entry Vault includes optional Filament 4 admin panel integration for managing entries, categories, and content.

### Installing Filament

[](#installing-filament)

First, ensure you have Filament 4 installed in your Laravel application:

```
composer require filament/filament:"^4.0"
php artisan filament:install --panels
```

### Registering the Plugin

[](#registering-the-plugin)

Register the `EntryVaultPlugin` in your Filament panel provider:

```
use Yannelli\EntryVault\Filament\EntryVaultPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ... other configuration
        ->plugins([
            EntryVaultPlugin::make(),
        ]);
}
```

### Plugin Configuration

[](#plugin-configuration)

The plugin can be configured with various options:

```
use Yannelli\EntryVault\Filament\EntryVaultPlugin;

EntryVaultPlugin::make()
    // Customize navigation group
    ->navigationGroup('Content Management')

    // Set navigation sort order
    ->navigationSort(10)

    // Disable specific resources
    ->entryResource(false)           // Disable Entry resource
    ->entryCategoryResource(false)   // Disable Category resource

    // Use custom resource classes
    ->usingEntryResource(CustomEntryResource::class)
    ->usingEntryCategoryResource(CustomCategoryResource::class)
```

### Configuration File Options

[](#configuration-file-options)

You can also configure Filament settings in `config/entry-vault.php`:

```
'filament' => [
    // Enable the Filament integration (disabled by default)
    'enabled' => false,

    // Navigation group for Entry Vault resources
    'navigation_group' => 'Content',

    // Navigation sort order (null for default)
    'navigation_sort' => null,

    // Customize resource labels
    'entry_label' => 'Entry',
    'entry_plural_label' => 'Entries',
    'category_label' => 'Category',
    'category_plural_label' => 'Categories',
],
```

### Enabling Filament Integration

[](#enabling-filament-integration)

The Filament integration is disabled by default. To enable it, set `enabled` to `true` in your config:

```
// config/entry-vault.php
'filament' => [
    'enabled' => true,
    // ...
],
```

Or use an environment variable:

```
'filament' => [
    'enabled' => env('ENTRY_VAULT_FILAMENT_ENABLED', false),
    // ...
],
```

When disabled, `EntryVaultPlugin::make()` will not register any resources even if added to your panel.

### Features

[](#features-1)

The Filament integration provides:

**Entry Resource:**

- Full CRUD for entries with inline content editing
- State management actions (Publish, Unpublish, Archive, Restore)
- Category assignment and filtering
- Visibility controls
- Template management (mark as template, featured)
- Soft delete with restore/force delete
- Contents relation manager for managing content blocks

**Category Resource:**

- Full CRUD for categories
- System/default category flags
- Color and icon customization
- Display order (drag-and-drop reordering)
- Entry count display
- Soft delete support

**Content Blocks Relation Manager:**

- Add/edit/remove content blocks for entries
- Support for all content types (Markdown, HTML, JSON, Text)
- Appropriate editor for each content type
- Drag-and-drop reordering

### Extending Resources

[](#extending-resources)

You can extend the default Filament resources:

```
namespace App\Filament\Resources;

use Yannelli\EntryVault\Filament\Resources\EntryResource as BaseEntryResource;

class EntryResource extends BaseEntryResource
{
    // Add custom columns, filters, or actions
    public static function table(Table $table): Table
    {
        return parent::table($table)
            ->columns([
                // Add custom columns
                ...parent::table($table)->getColumns(),
                Tables\Columns\TextColumn::make('custom_field'),
            ]);
    }
}
```

Then register your custom resource:

```
EntryVaultPlugin::make()
    ->usingEntryResource(App\Filament\Resources\EntryResource::class)
```

Basic Usage
-----------

[](#basic-usage)

### Creating Entries

[](#creating-entries)

```
use Yannelli\EntryVault\Models\Entry;
use Yannelli\EntryVault\Facades\EntryVault;

// Create a basic entry
$entry = Entry::create([
    'title' => 'My First Entry',
    'description' => 'A sample entry',
    'keywords' => ['sample', 'first'],
]);

// Create with the facade
$entry = EntryVault::create([
    'title' => 'Another Entry',
    'visibility' => 'public',
]);

// Create with an owner
$entry = Entry::create([
    'title' => 'User Entry',
    'owner_type' => $user->getMorphClass(),
    'owner_id' => $user->id,
]);
```

### Adding Content

[](#adding-content)

```
$entry->contents()->create([
    'type' => 'markdown',
    'body' => '# Hello World\n\nThis is my content.',
    'order' => 0,
]);

// Multiple content sections
$entry->contents()->create([
    'type' => 'html',
    'body' => 'Additional content',
    'order' => 1,
]);
```

### State Management

[](#state-management)

```
use Yannelli\EntryVault\Transitions\PublishTransition;
use Yannelli\EntryVault\Transitions\UnpublishTransition;
use Yannelli\EntryVault\Transitions\ArchiveTransition;

// Publish an entry
$transition = new PublishTransition($entry);
$transition->handle();

// Unpublish (back to draft)
$transition = new UnpublishTransition($entry);
$transition->handle();

// Archive
$transition = new ArchiveTransition($entry);
$transition->handle();

// Check state
$entry->isDraft();      // true/false
$entry->isPublished();  // true/false
$entry->isArchived();   // true/false
```

### Visibility

[](#visibility)

```
// Create with visibility
$entry = Entry::create([
    'title' => 'Team Entry',
    'visibility' => 'team',
    'team_type' => $team->getMorphClass(),
    'team_id' => $team->id,
]);

// Query by visibility
Entry::public()->get();
Entry::private()->get();
Entry::teamVisible()->get();

// Get entries visible to a user
Entry::visibleTo($user)->get();
EntryVault::accessibleBy($user)->get();

// Check access
$entry->isAccessibleBy($user); // true/false
```

Authorization Resolvers
-----------------------

[](#authorization-resolvers)

Entry Vault provides a flexible authorization system that allows you to define custom authorization logic in your service provider. This gives you full control over how ownership and team access are determined.

### Registering Resolvers

[](#registering-resolvers)

Register resolvers in your `AppServiceProvider` boot method:

```
use App\Models\Team;
use App\Models\User;
use Yannelli\EntryVault\Facades\EntryVault;
use Yannelli\EntryVault\Models\Entry;

public function boot(): void
{
    // Global authorization callback
    EntryVault::authorize(function (Entry $entry) {
        // Custom global auth logic
        return $entry->owner_id === auth()->id() || auth()->user()->isAdmin();
    });

    // Owner resolver with custom authorization
    EntryVault::resolveOwner(
        model: User::class,
        authorize: function (User $user, Entry $entry) {
            return $user->id === $entry->owner_id;
        }
    );

    // Team resolver with custom authorization
    EntryVault::resolveTeam(
        model: Team::class,
        authorize: function (Team $team, Entry $entry) {
            return auth()->user()->currentTeam?->id === $entry->team_id
                || $entry->owner_id === auth()->user()->current_team_id;
        }
    );
}
```

### Global Authorization

[](#global-authorization)

The `authorize()` method registers a global callback that is checked before any other authorization:

```
EntryVault::authorize(function (Entry $entry) {
    // Return false to deny access to any entry
    // Return true to allow (subject to other checks)
    return $entry->visibility !== 'archived';
});
```

### Owner Resolver

[](#owner-resolver)

Register your user model and optional authorization logic:

```
// Simple registration (uses default ownership check)
EntryVault::resolveOwner(model: User::class);

// With custom authorization logic
EntryVault::resolveOwner(
    model: User::class,
    authorize: function (User $user, Entry $entry) {
        // Allow if owner OR if user is admin
        return $user->id === $entry->owner_id || $user->hasRole('admin');
    }
);
```

### Team Resolver

[](#team-resolver)

Register your team model with optional authorization logic:

```
EntryVault::resolveTeam(
    model: Team::class,
    authorize: function (Team $team, Entry $entry) {
        // Custom team access logic
        return $team->id === $entry->team_id;
    }
);
```

### Custom Resolvers

[](#custom-resolvers)

For more complex authorization scenarios, register custom resolvers:

```
EntryVault::resolveCustom(
    name: 'organization',
    model: Organization::class,
    authorize: function (Organization $org, Entry $entry) {
        return $org->entries()->where('id', $entry->id)->exists();
    }
);

// Check custom resolver
$entry->isAuthorizedFor($user); // Checks all resolvers including custom
```

### Checking Authorization

[](#checking-authorization)

```
// Check global authorization
EntryVault::checkAuthorization($entry);

// Check owner authorization
EntryVault::checkOwnerAuthorization($user, $entry);

// Check team authorization
EntryVault::checkTeamAuthorization($team, $entry);

// Check custom resolver
EntryVault::checkCustomAuthorization('organization', $org, $entry);

// Check all resolvers (on entry model)
$entry->isAuthorizedFor($user);
```

### Visibility

[](#visibility-1)

```
// Create with visibility
$entry = Entry::create([
    'title' => 'Team Entry',
    'visibility' => 'team',
    'team_type' => $team->getMorphClass(),
    'team_id' => $team->id,
]);

// Query by visibility
Entry::public()->get();
Entry::private()->get();
Entry::teamVisible()->get();

// Get entries visible to a user
Entry::visibleTo($user)->get();
EntryVault::accessibleBy($user)->get();

// Check access
$entry->isAccessibleBy($user); // true/false
```

### Categories

[](#categories)

```
use Yannelli\EntryVault\Models\EntryCategory;

// Create a system category
$category = EntryCategory::create([
    'name' => 'Documentation',
    'is_system' => true,
    'is_default' => true,
]);

// Create a user category
$category = EntryCategory::create([
    'name' => 'My Personal Category',
    'owner_type' => $user->getMorphClass(),
    'owner_id' => $user->id,
]);

// Assign entry to category
$entry->update(['category_id' => $category->id]);

// Query by category
Entry::inCategory($category)->get();
Entry::inCategory('documentation')->get(); // by slug

// Get accessible categories for user
EntryCategory::accessibleBy($user)->ordered()->get();
EntryVault::categoriesFor($user)->get();
```

### Templates

[](#templates)

```
// Create a template
$template = Entry::create([
    'title' => 'Blog Post Template',
    'is_template' => true,
    'is_featured' => true, // Make it a starter
]);

$template->contents()->create([
    'type' => 'markdown',
    'body' => '# Title\n\n## Introduction\n\n...',
]);

// Create entry from template
$entry = Entry::createFromTemplate($template, [
    'title' => 'My Blog Post',
    'owner' => $user,
]);

// Query templates
Entry::templates()->get();
Entry::systemTemplates()->get();
Entry::starters()->get(); // Featured system templates

// Via facade
EntryVault::templates()->get();
EntryVault::starters()->get();
EntryVault::startersInCategory('onboarding')->get();
```

Adding Traits to Your Models
----------------------------

[](#adding-traits-to-your-models)

### HasEntries

[](#hasentries)

Add to models that own entries (User, Team, etc.):

```
use Yannelli\EntryVault\Traits\HasEntries;

class User extends Model
{
    use HasEntries;
}

// Usage
$user->entries;
$user->draftEntries;
$user->publishedEntries;
$user->entryTemplates;
```

### HasEntryCategories

[](#hasentrycategories)

Add to models that own categories:

```
use Yannelli\EntryVault\Traits\HasEntryCategories;

class User extends Model
{
    use HasEntryCategories;
}

// Usage
$user->entryCategories;
$user->defaultEntryCategory();
```

### HasEntryContent

[](#hasentrycontent)

Add to models that can be associated with entry content:

```
use Yannelli\EntryVault\Traits\HasEntryContent;

class Document extends Model
{
    use HasEntryContent;
}

// Usage
$document->entryContent;
$document->entryContents;
```

Events
------

[](#events)

The package dispatches the following events:

- `EntryCreated` - When an entry is created
- `EntryUpdated` - When an entry is updated
- `EntryDeleted` - When an entry is deleted
- `EntryPublished` - When an entry is published
- `EntryUnpublished` - When an entry is unpublished
- `EntryArchived` - When an entry is archived
- `EntryRestored` - When an entry is restored from archive
- `EntryCreatedFromTemplate` - When an entry is created from a template
- `EntryCategoryCreated` - When a category is created
- `EntryCategoryUpdated` - When a category is updated
- `EntryCategoryDeleted` - When a category is deleted

Extending Models
----------------

[](#extending-models)

You can extend the default models by updating the config:

```
// config/entry-vault.php
'models' => [
    'entry' => \App\Models\Entry::class,
    'content' => \App\Models\EntryContent::class,
    'category' => \App\Models\EntryCategory::class,
],
```

```
// app/Models/Entry.php
namespace App\Models;

use Yannelli\EntryVault\Models\Entry as BaseEntry;

class Entry extends BaseEntry
{
    // Your customizations
}
```

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Credits
-------

[](#credits)

- [Ryan Yannelli](https://ryanyannelli.com)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance84

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

10

Last Release

134d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/53b64331d4de8c9cef47bc20707ed728c0e900cd2bda4ed7d95359ced8b9adf1?d=identicon)[yannelli](/maintainers/yannelli)

---

Top Contributors

[![yannelli](https://avatars.githubusercontent.com/u/59575788?v=4)](https://github.com/yannelli "yannelli (44 commits)")

---

Tags

laravellibraryversioningresourcesmulti-tenancyentriesyannellientry-vault

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/yannelli-entry-vault-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/yannelli-entry-vault-laravel/health.svg)](https://phpackages.com/packages/yannelli-entry-vault-laravel)
```

###  Alternatives

[spatie/laravel-enum

Laravel Enum support

3655.4M31](/packages/spatie-laravel-enum)[psalm/plugin-laravel

Psalm plugin for Laravel

3274.9M308](/packages/psalm-plugin-laravel)[spatie/laravel-livewire-wizard

Build wizards using Livewire

4061.0M4](/packages/spatie-laravel-livewire-wizard)[laracraft-tech/laravel-useful-additions

A collection of useful Laravel additions!

58109.4k](/packages/laracraft-tech-laravel-useful-additions)[nativephp/mobile

NativePHP for Mobile

82724.0k43](/packages/nativephp-mobile)[tonysm/importmap-laravel

Use ESM with importmap to manage modern JavaScript in Laravel without transpiling or bundling.

148399.8k1](/packages/tonysm-importmap-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
