Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Ownership Laravel Package

cybercog/laravel-ownership

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require cybercog/laravel-ownership
    

    Publish the config (optional):

    php artisan vendor:publish --provider="Cog\Laravel\Ownership\OwnershipServiceProvider"
    
  2. Configure Auth Model (in config/ownership.php):

    'auth_model' => App\Models\User::class,
    
  3. Apply to a Model (strict ownership):

    use Cog\Laravel\Ownership\Traits\HasOwner;
    
    class Post extends Model
    {
        use HasOwner;
    
        // Optional: Override default foreign key
        // public $ownedBy = 'author_id';
    }
    
  4. First Use Case:

    // Assign ownership
    $post = new Post(['title' => 'Hello']);
    $post->changeOwnerTo(auth()->user());
    
    // Query owned posts
    $posts = Post::ownedBy(auth()->user());
    

Implementation Patterns

Core Workflows

  1. Strict Ownership (Single Type):

    • Use HasOwner for models with one owner type (e.g., User).
    • Example: A Post owned by a User.
    class Post extends Model
    {
        use HasOwner;
    
        public function owner()
        {
            return $this->belongsTo(User::class, 'owned_by_id');
        }
    }
    
  2. Polymorphic Ownership (Multiple Types):

    • Use HasMorphOwner for models with multiple owner types (e.g., User or Team).
    • Example: A Comment owned by either a User or Team.
    class Comment extends Model
    {
        use HasMorphOwner;
    
        public function owner()
        {
            return $this->morphTo();
        }
    }
    
  3. Auto-Assignment on Create:

    • Enable in config/ownership.php:
      'auto_assign_on_create' => true,
      
    • Override resolveDefaultOwner() to customize logic:
      protected function resolveDefaultOwner()
      {
          return auth()->guard('admin')->user() ?? parent::resolveDefaultOwner();
      }
      
  4. Query Scopes:

    • Built-in scopes for filtering:
      $posts = Post::ownedBy($user);
      $posts = Post::withoutOwner();
      $posts = Post::withDefaultOwner();
      
  5. API Methods:

    • Change ownership:
      $post->changeOwnerTo($newOwner);
      
    • Abandon ownership:
      $post->abandonOwner();
      
    • Check ownership:
      $post->hasOwner(); // bool
      $post->isOwnedBy($user); // bool
      

Integration Tips

  • Morph Map: Register custom morph maps if using polymorphic ownership:

    class Comment extends Model
    {
        protected $morphClass = 'commentable';
    
        public static function boot()
        {
            parent::boot();
            self::morphMap([
                'user' => User::class,
                'team' => Team::class,
            ]);
        }
    }
    
  • Observers: Extend OwnableObserver for custom logic:

    class PostObserver extends OwnableObserver
    {
        public function saving(Post $post)
        {
            if ($post->isDirty('title') && $post->hasOwner()) {
                // Custom logic
            }
        }
    }
    
  • Policies: Use Laravel's policies to restrict ownership changes:

    class PostPolicy
    {
        public function update(User $user, Post $post)
        {
            return $user->can('edit', $post->owner);
        }
    }
    

Gotchas and Tips

Pitfalls

  1. Strict Ownership Validation:

    • HasOwner throws InvalidOwnerType if the owner type doesn’t match the configured model. Ensure your owner() relationship returns the correct model class.
    • Fix: Verify the belongsTo relationship in your model matches the config:
      public function owner()
      {
          return $this->belongsTo(User::class); // Must match config
      }
      
  2. Polymorphic Ownership Quirks:

    • HasMorphOwner doesn’t validate owner types by default. Ensure your CanBeOwner implementations are correct.
    • Tip: Use instanceof checks in custom logic:
      if ($comment->owner instanceof User) {
          // Handle user-owned comments
      }
      
  3. Auto-Assignment Conflicts:

    • If auto_assign_on_create is true, ensure resolveDefaultOwner() returns a valid owner. Null owners can cause issues.
    • Debug: Check for NullOwnerException in logs.
  4. Foreign Key Conflicts:

    • Custom foreign keys (e.g., author_id) must be defined in both the trait and the model’s owner() relationship.
    • Fix: Explicitly set $ownedBy in your model:
      use HasOwner;
      
      class Post extends Model
      {
          use HasOwner;
      
          protected $ownedBy = 'author_id';
      
          public function owner()
          {
              return $this->belongsTo(User::class, 'author_id');
          }
      }
      
  5. Observer Overrides:

    • If extending OwnableObserver, ensure you call parent::method() to preserve default behavior unless intentionally overriding.

Debugging Tips

  1. Check Config:

    • Verify config/ownership.php settings, especially auth_model and auto_assign_on_create.
  2. Log Ownership Changes:

    • Add debug logs in resolveDefaultOwner() or observer methods:
      Log::debug('Default owner resolved:', ['owner' => $this->owner]);
      
  3. Test Edge Cases:

    • Test ownership changes with:
      • Null owners.
      • Non-existent owners.
      • Owner type mismatches (for HasOwner).
  4. Database Migrations:

    • Ensure foreign keys are properly indexed:
      $table->foreignId('owned_by_id')->constrained()->onDelete('cascade');
      

Extension Points

  1. Custom Ownership Logic:

    • Override trait methods like changeOwnerTo() or abandonOwner():
      public function changeOwnerTo($owner)
      {
          $this->fireModelEvent('changing_owner', false);
          parent::changeOwnerTo($owner);
          $this->fireModelEvent('changed_owner', false);
      }
      
  2. Dynamic Owner Resolution:

    • Extend resolveDefaultOwner() for context-aware assignment:
      protected function resolveDefaultOwner()
      {
          return request()->has('team_id')
              ? Team::find(request('team_id'))
              : auth()->user();
      }
      
  3. Custom Contracts:

    • Implement Cog\Contracts\Ownership\CanBeOwner for non-standard owner models:
      class Team implements CanBeOwner
      {
          // Implement required methods
      }
      
  4. Query Extensions:

    • Add custom scopes to the trait:
      public function scopeOwnedByTeam($query)
      {
          return $query->whereHas('owner', fn($q) => $q->where('owner_type', Team::class));
      }
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui