Laravel Ownable

v1.0

Laravel Ownable Package

A powerful Laravel package to handle ownership relationships between Eloquent models with flexibility, history tracking, and automatic cleanup.

Key Features

Everything you need to manage ownership relationships in your Laravel application

Flexible Ownership

Any model can own any other model with polymorphic relationships

Ownership Transfer

Seamlessly transfer ownership between different owners

Ownership History

Keep track of ownership changes over time with historical records

Current Owner

Easily retrieve the current owner of any ownable item

Automatic Cleanup

Automatically clean up ownership records when models are deleted

Facade Support

Use the convenient Owner facade for ownership operations

Requirements

Make sure your environment meets these requirements

PHP Version

PHP ^8.0 or higher

Laravel Version

Laravel ^9.0, ^10.0, ^11.0, or ^12.0

Installation

Get started with Laravel Ownable in just a few steps

Step 1

Install via Composer

Add the package to your Laravel project using Composer:

composer require sowailem/ownable
Step 2

Publish Migration

Publish the migration file to create the ownerships table:

php artisan vendor:publish --provider="Sowailem\Ownable\OwnableServiceProvider" --tag="ownable-migrations"
Step 3

Run Migration

Execute the migration to create the database table:

php artisan migrate

Usage Guide

Learn how to implement ownership relationships in your models

1. Setting up Models

First, implement the contracts and use the traits in your models:

Owner Model (e.g., User)

<?php

use Sowailem\Ownable\Traits\HasOwnables;
use Sowailem\Ownable\Contracts\Owner as OwnerContract;

class User extends Authenticatable implements OwnerContract
{
    use HasOwnables;
    
    // Your existing model code...
}

Ownable Model (e.g., Post)

<?php

use Sowailem\Ownable\Traits\IsOwnable;
use Sowailem\Ownable\Contracts\Ownable as OwnableContract;

class Post extends Model implements OwnableContract
{
    use IsOwnable;
    
    // Your existing model code...
}

2. Basic Operations

Giving Ownership

There are multiple ways to assign ownership:

$user = User::first();
$post = Post::first();

// Method 1: Using owner model
$user->giveOwnershipTo($post);

// Method 2: Using ownable model
$post->ownedBy($user);

// Method 3: Using facade
Owner::give($user, $post);

Checking Ownership

Verify if a user owns a specific item:

// Method 1: Using owner model
if ($user->owns($post)) {
    // User owns this post
}

// Method 2: Using ownable model
if ($post->isOwnedBy($user)) {
    // Post is owned by this user
}

// Method 3: Using facade
if (Owner::check($user, $post)) {
    // Check ownership using facade
}

Transferring Ownership

Transfer ownership from one user to another:

$newOwner = User::find(2);

// Method 1: Using current owner
$user->transferOwnership($post, $newOwner);

// Method 2: Using ownable model
$post->transferOwnershipTo($newOwner);

// Method 3: Using facade
Owner::transfer($user, $newOwner, $post);

3. Advanced Usage

Retrieving Owned Items and Owners

// Get all items owned by a user
$ownedItems = $user->ownables()->get();

// Get current owner of an item
$currentOwner = $post->currentOwner();

// Get all owners (including historical)
$allOwners = $post->owners()->get();

// Get only current owners
$currentOwners = $post->owners()->wherePivot('is_current', true)->get();

// Get ownership history
$ownershipHistory = $post->ownerships()->with('owner')->get();

Removing Ownership

// Remove ownership completely
$user->takeOwnershipFrom($post);

// This will delete the ownership record entirely

API Reference

Complete reference for all available methods and classes

HasOwnables Trait

Methods available on owner models that use the HasOwnables trait:

possessions()

Get all ownership records where this model is the owner.

Returns: \Illuminate\Database\Eloquent\Relations\MorphMany

ownables()

Get all ownable entities owned by this owner.

Returns: \Illuminate\Database\Eloquent\Relations\MorphToMany

owns($ownable)

Check if this owner owns the given ownable entity.

Parameters: $ownable - The ownable entity to check
Returns: bool - True if owns the entity, false otherwise

giveOwnershipTo($ownable)

Give ownership of an ownable entity to this owner.

Parameters: $ownable - The ownable entity
Returns: bool - True if ownership was given successfully, false if already owned

takeOwnershipFrom($ownable)

Remove ownership of an ownable entity from this owner.

Parameters: $ownable - The ownable entity
Returns: bool - True if ownership was removed successfully, false if not owned

transferOwnership($ownable, $newOwner)

Transfer ownership of an ownable entity to a new owner.

Parameters: $ownable - The ownable entity, $newOwner - The new owner
Returns: bool - True if transfer was successful, false if not owned by this owner

IsOwnable Trait

Methods available on ownable models that use the IsOwnable trait:

ownerships()

Get all ownership records for this ownable entity.

Returns: \Illuminate\Database\Eloquent\Relations\MorphMany

owners()

Get all owners of this ownable entity.

Returns: \Illuminate\Database\Eloquent\Relations\MorphToMany

currentOwner()

Get the current owner of this ownable entity.

Returns: \Illuminate\Database\Eloquent\Model|null - The current owner or null if no current owner

ownedBy($owner)

Assign ownership of this entity to the given owner.

Parameters: $owner - The owner model
Returns: $this
Throws: \Exception when the model or owner is not saved

isOwnedBy($owner)

Check if this entity is owned by the given owner.

Parameters: $owner - The owner model to check
Returns: bool - True if owned by the given owner, false otherwise

transferOwnershipTo($newOwner)

Transfer ownership of this entity to a new owner.

Parameters: $newOwner - The new owner model
Returns: $this

Owner Facade

Static methods available via the Owner facade:

Owner::give($owner, $ownable)

Give ownership of an ownable entity to an owner.

Parameters: $owner - The owner model, $ownable - The ownable entity
Returns: mixed - The ownable entity with updated ownership
Throws: \InvalidArgumentException when parameters are invalid

Owner::check($owner, $ownable)

Check if an owner owns a specific ownable entity.

Parameters: $owner - The owner model to check, $ownable - The ownable entity
Returns: bool - True if the owner owns the entity, false otherwise
Throws: \InvalidArgumentException when parameters are invalid

Owner::transfer($fromOwner, $toOwner, $ownable)

Transfer ownership of an ownable entity from one owner to another.

Parameters: $fromOwner - The current owner, $toOwner - The new owner, $ownable - The ownable entity
Returns: mixed - The ownable entity with transferred ownership
Throws: \InvalidArgumentException when parameters are invalid

Database Structure

The package creates an ownerships table with the following structure:

Column Type Description
id bigint Primary key
owner_id bigint ID of the owner model
owner_type varchar Class name of the owner model
ownable_id bigint ID of the ownable model
ownable_type varchar Class name of the ownable model
is_current boolean Flag indicating if this is the current ownership
created_at timestamp When ownership was created
updated_at timestamp When ownership was last updated

Practical Examples

Real-world scenarios and use cases

Blog System Example

A complete example of implementing ownership in a blog system where users can own posts and transfer them to other users:

1. Model Setup

// User.php
class User extends Authenticatable implements OwnerContract
{
    use HasOwnables;
    
    protected $fillable = ['name', 'email', 'password'];
}

// Post.php
class Post extends Model implements OwnableContract
{
    use IsOwnable;
    
    protected $fillable = ['title', 'content', 'published_at'];
}

2. Creating and Assigning Posts

// Create a new post and assign ownership
$user = User::find(1);
$post = Post::create([
    'title' => 'My First Blog Post',
    'content' => 'This is the content of my blog post...',
    'published_at' => now()
]);

// Assign ownership
$post->ownedBy($user);

// Or using the owner model
$user->giveOwnershipTo($post);

3. Authorization in Controllers

// PostController.php
public function edit(Post $post)
{
    // Check if current user owns the post
    if (!auth()->user()->owns($post)) {
        abort(403, 'You do not own this post.');
    }
    
    return view('posts.edit', compact('post'));
}

public function transfer(Post $post, User $newOwner)
{
    // Transfer ownership
    if (auth()->user()->owns($post)) {
        $post->transferOwnershipTo($newOwner);
        return redirect()->back()->with('success', 'Post transferred successfully!');
    }
    
    return redirect()->back()->with('error', 'You cannot transfer this post.');
}

E-commerce Store Example

Managing product ownership in a multi-vendor e-commerce platform:

Vendor and Product Models

// Vendor.php
class Vendor extends Model implements OwnerContract
{
    use HasOwnables;
    
    protected $fillable = ['name', 'email', 'store_name'];
    
    public function products()
    {
        return $this->ownables()->where('ownable_type', Product::class);
    }
}

// Product.php
class Product extends Model implements OwnableContract
{
    use IsOwnable;
    
    protected $fillable = ['name', 'description', 'price', 'stock'];
    
    public function vendor()
    {
        return $this->currentOwner();
    }
}

Product Management

// Create product and assign to vendor
$vendor = Vendor::find(1);
$product = Product::create([
    'name' => 'Wireless Headphones',
    'description' => 'High-quality wireless headphones...',
    'price' => 99.99,
    'stock' => 50
]);

$product->ownedBy($vendor);

// Get all products for a vendor
$vendorProducts = $vendor->products()->get();

// Transfer product to another vendor
$newVendor = Vendor::find(2);
$product->transferOwnershipTo($newVendor);

File Management System

Managing file ownership with history tracking:

// File.php
class File extends Model implements OwnableContract
{
    use IsOwnable;
    
    protected $fillable = ['name', 'path', 'size', 'mime_type'];
    
    public function getOwnershipHistory()
    {
        return $this->ownerships()
            ->with('owner')
            ->orderBy('created_at', 'desc')
            ->get();
    }
}

// Usage
$file = File::find(1);
$history = $file->getOwnershipHistory();

foreach ($history as $ownership) {
    echo "Owned by: " . $ownership->owner->name . " from " . $ownership->created_at;
}

Configuration

Optional configuration for advanced usage

Publishing Configuration

You can publish the configuration file to customize the package behavior:

php artisan vendor:publish --provider="Sowailem\Ownable\OwnableServiceProvider" --tag="ownable-config"

Available Configuration Options

owner_model

Default owner model class (default: 'App\Models\User')

ownable_model

Default ownable model class (default: 'App\Models\Model')

table_name

Database table name for ownership records (default: 'ownerships')