dibakar/laravel-ownership

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

Installs: 2

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/dibakar/laravel-ownership

v1.0.1 2025-10-16 11:26 UTC

This package is auto-updated.

Last update: 2025-10-16 11:27:21 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 model as an owner
  • Bulk Operations: Manage multiple owners at once with sync methods
  • Bidirectional Relationships: Both owners and ownable models have access to each other

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

IsOwner Trait Methods

Check Ownership

// Check if the user owns a document
$user->owns($document);

// Check if the user owns a document with a specific role
$user->owns($document, 'admin');

Get Owned Items

// Get all documents owned by the user
$documents = $user->getOwnedItems(Document::class);

// Get all documents where user has a specific role
$adminDocuments = $user->getOwnedItems(Document::class, 'admin');

Transfer Ownership

// Transfer all documents to another user
$user->transferOwnershipTo($newUser);

// Transfer only specific type of items
$user->transferOwnershipTo($newUser, Document::class);

Count Owned Items

// Count all owned items
$count = $user->countOwnedItems();

// Count items of a specific type
$docCount = $user->countOwnedItems(Document::class);

// Count items with a specific role
$adminDocCount = $user->countOwnedItems(Document::class, 'admin');

Ownership Relationship

// Get all ownership records
$ownerships = $user->ownerships;

// Get all owned items through the relationship
$items = $user->ownerships->map->ownable;

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

Basic Usage

For Ownable Models (Models that can be owned)

Add the HasOwnership trait to your model:

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

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

For Owner Models (Models that can own other models)

Add the IsOwner trait to models that can own other models (like User, Team, etc.):

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

class User extends Authenticatable
{
    use IsOwner;
    // ...
}

class Post extends Model { use HasOwnership;

// ...

}


#### Basic Operations

```php
// 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

Example Usage

User Model

use Illuminate\Foundation\Auth\User as Authenticatable;
use Dibakar\Ownership\Traits\IsOwner;

class User extends Authenticatable
{
    use IsOwner;
    // ...
}

Document Model

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

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

Controller Example

class DocumentController extends Controller
{
    public function store(Request $request)
    {
        $document = Document::create($request->all());
        
        // Set the current user as the owner with admin role
        $document->addOwner(auth()->user(), 'admin');
        
        return response()->json($document, 201);
    }
    
    public function transfer(Document $document, User $newOwner)
    {
        $this->authorize('update', $document);
        
        // Transfer ownership
        $document->getOwner()->transferOwnershipTo($newOwner);
        
        return response()->json(['message' => 'Ownership transferred']);
    }
}

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

Contributing

Please see CONTRIBUTING for details.

Credits