dibakar/laravel-ownership

Flexible ownership management package for Laravel with single & multiple ownership support. with traits, scopes, and policies.

1.0.0 2025-09-05 10:27 UTC

This package is auto-updated.

Last update: 2025-09-05 10:54:13 UTC


README

Latest Version on Packagist Total Downloads PHP Version License StyleCI

A comprehensive ownership management system for Laravel applications. This package provides an elegant way to handle both single and multiple ownership scenarios with role-based permissions, events, and query scopes.

Features

  • Dual Ownership Modes: Support for both single and multiple ownership models
  • Role-based Access Control: Define custom roles with specific permissions
  • Flexible Configuration: Highly customizable to fit any application needs
  • Event-driven Architecture: Built-in events for all ownership changes
  • Powerful Query Scopes: Filter models by ownership with ease
  • Performance Optimized: Built-in caching for ownership checks
  • Type Safety: Strict type declarations and modern PHP features
  • Laravel Integration: Seamless integration with Laravel's authentication system
  • Morphable Owners: Support for any authenticatable model as an owner
  • Bulk Operations: Manage multiple owners at once with sync methods

Installation

  1. Install the package via Composer:
composer require dibakar/laravel-ownership
  1. Publish the configuration file:
php artisan vendor:publish --provider="Dibakar\\Ownership\\OwnershipServiceProvider" --tag=config
  1. Publish and run the migrations:
php artisan vendor:publish --provider="Dibakar\\Ownership\\OwnershipServiceProvider" --tag=migrations
php artisan migrate

Configuration

The configuration file (config/ownership.php) allows you to customize various aspects of the package. Here are the main configuration options:

return [
    /*
    |--------------------------------------------------------------------------
    | Morph Name
    |--------------------------------------------------------------------------
    |
    | This is the name of the polymorphic relationship used for ownership.
    | You can change this to 'user', 'team', 'organization', etc.
    */
    'morph_name' => 'owner',

    /*
    |--------------------------------------------------------------------------
    | Global Scope
    |--------------------------------------------------------------------------
    |
    | When enabled, a global scope will be applied to automatically scope
    | queries to the current owner in single ownership mode.
    */
    'apply_global_scope' => true,

    /*
    |--------------------------------------------------------------------------
    | Authentication Guard
    |--------------------------------------------------------------------------
    |
    | The authentication guard used to retrieve the currently authenticated user.
    */
    'guard' => 'web',

    /*
    |--------------------------------------------------------------------------
    | Ownership Mode
    |--------------------------------------------------------------------------
    |
    | Set to 'single' for one owner per model or 'multiple' for many owners.
    */
    'mode' => 'single',

    /*
    |--------------------------------------------------------------------------
    | Cache Configuration
    |--------------------------------------------------------------------------
    */
    'cache' => [
        'enabled' => true,
        'ttl' => 3600, // Cache time-to-live in seconds
    ],

    /*
    |--------------------------------------------------------------------------
    | Multiple Ownership Configuration
    |--------------------------------------------------------------------------
    */
    'multiple_ownership' => [
        'table_name' => 'ownerships',
        'roles' => [
            'owner' => [
                'display_name' => 'Owner',
                'permissions' => ['*'], // Wildcard means all permissions
            ],
            'editor' => [
                'display_name' => 'Editor',
                'permissions' => ['edit', 'view'],
            ],
            'viewer' => [
                'display_name' => 'Viewer',
                'permissions' => ['view'],
            ],
        ],
        'default_role' => 'viewer',
    ],
];

Usage

Single Ownership Mode

Add the HasOwnership trait to your model for single ownership:

use Illuminate\Database\Eloquent\Model;
use Dibakar\Ownership\Traits\HasOwnership;

class Post extends Model
{
    use HasOwnership;
    
    // ...
}

Basic Operations

// Creating a new post with the current user as owner
$post = Post::create([
    'title' => 'My First Post',
    'content' => 'This is my first post.'
]);

// Explicitly set the owner
$post->setOwner($user);

// Check ownership
if ($post->isOwnedBy($user)) {
    // User owns the post
}

// Get the owner
$owner = $post->owner;

// Transfer ownership
$post->transferOwnership($currentOwner, $newOwner);

// Clear the owner
$post->clearOwner();

Multiple Ownership Mode

First, update your config to use multiple ownership mode:

// config/ownership.php
return [
    'mode' => 'multiple',
    // ... rest of the config
];

Then add the HasOwnership trait to your model:

use Illuminate\Database\Eloquent\Model;
use Dibakar\Ownership\Traits\HasOwnership;

class Project extends Model
{
    use HasOwnership;
    
    // ...
}

Managing Multiple Owners

// Add an owner with a specific role
$project->addOwner($user, 'owner');

// Add multiple owners at once
$project->addOwners([$user1, $user2, $user3], 'editor', ['custom_permission']);

// Remove an owner
$project->removeOwner($user);

// Check if a user is an owner
if ($project->hasOwner($user)) {
    // User is an owner
}

// Get all owners with their roles
$owners = $project->getOwners();

// Get owners with a specific role
$editors = $project->getOwnersWithRole('editor');

// Check if a user has a specific role
if ($project->hasOwnerWithRole($user, 'admin')) {
    // User has admin role
}

// Update a user's role
$project->updateOwnerRole($user, 'admin');

// Sync owners (removes any owners not in the list)
$project->syncOwners([
    $user1,
    $user2,
    $user3,
], 'owner');

// Clear all owners
$project->clearAllOwners();
}

### Query Scopes

The package provides several query scopes to filter models by ownership:

```php
// Get all posts owned by the current user
$posts = Post::ownedByCurrent()->get();

// Get all posts owned by a specific user
$userPosts = Post::ownedBy($user)->get();

// In multiple ownership mode, get projects where user has a specific role
$projects = Project::whereHasOwnerWithRole($user, 'editor')->get();

// Get models where user has specific permission
$editablePosts = Post::whereUserHasPermission($user, 'edit')->get();

Events

The package dispatches events when ownership changes occur:

use Dibakar\Ownership\Events\OwnershipCreated;
use Dibakar\Ownership\Events\OwnershipUpdated;
use Dibakar\Ownership\Events\OwnershipDeleted;
use Dibakar\Ownership\Events\OwnershipTransferred;

// Listen for ownership events
Event::listen(OwnershipCreated::class, function (OwnershipCreated $event) {
    $model = $event->model;
    $owner = $event->owner;
    $role = $event->role;
    
    // Handle the event
});

Middleware

Protect your routes with the ownership middleware:

// routes/web.php
Route::middleware(['auth', 'ownership:post,edit'])
    ->group(function () {
        Route::get('/posts/{post}/edit', 'PostController@edit');
        Route::put('/posts/{post}', 'PostController@update');
    });

// With custom ownership check
Route::middleware(['auth', 'ownership:post,edit,App\Policies\CustomPostPolicy'])
    ->group(function () {
        // Your routes
    });

Blade Directives

{{-- Check if current user owns the model --}}
@owned($post)
    <a href="{{ route('posts.edit', $post) }}">Edit</a>
@endowned

{{-- Check specific user ownership --}}
@owned($post, $specificUser)
    <span>Owned by {{ $specificUser->name }}</span>
@endowned

{{-- Check if user has specific permission --}}
@can('edit', $post)
    <a href="{{ route('posts.edit', $post) }}">Edit</a>
@endcan

@owned($model, $user = null)
    This content is only visible to the owner
@endowned

@canOwn($model, 'edit', $user = null)
    This content is only visible to users with edit permission
@endCanOwn

@isOwner($model, $user = null)
    This content is only visible to an owner
@endIsOwner

Events

The package dispatches several events that you can listen to:

  • Dibakar\Ownership\Events\OwnershipCreated
  • Dibakar\Ownership\Events\OwnershipUpdated
  • Dibakar\Ownership\Events\OwnershipDeleted
  • Dibakar\Ownership\Events\OwnershipTransferred

Example listener:

namespace App\Listeners;

use Dibakar\Ownership\Events\OwnershipCreated;

class LogOwnershipCreated
{
    public function handle(OwnershipCreated $event)
    {
        // Handle the event
    }
}

Testing

Run the tests with:

composer test

Security

If you discover any security related issues, please email dibakarmitra07@gmail.com instead of using the issue tracker.

License

The MIT License (MIT). Please see License File for more information.

Contributing

Please see CONTRIBUTING for details.

Credits