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

Custom Relation Laravel Package

culturegr/custom-relation

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require culturegr/custom-relation
    

    No manual service provider registration required in Laravel 5.5+ (auto-discovered).

  2. First Use Case: Custom Many-to-Many Relation Define a custom relation in your User model:

    use CultureGr\CustomRelation\HasCustomRelations;
    
    class User extends Model
    {
        use HasCustomRelations;
    
        public function permissions()
        {
            return $this->customRelation(
                'permissions', // Relation name
                Permission::class, // Related model
                'user_id', // Local key
                'permission_id', // Foreign key
                'role_user', // Intermediate table
                'user_id', // Intermediate local key
                'role_id' // Intermediate foreign key
            );
        }
    }
    

    Now call it like a native Eloquent relation:

    $user = User::find(1);
    $permissions = $user->permissions; // Returns a Collection of Permissions
    
  3. Where to Look First

    • README.md: Focus on the "Usage" section for syntax and examples.
    • Tests: /tests/ directory for edge cases and validation logic.
    • Service Provider: CustomRelationServiceProvider.php for bootstrapping behavior.

Implementation Patterns

Core Workflows

  1. Defining Custom Relations Use customRelation() for non-standard relationships (e.g., multi-level joins, custom pivot tables):

    // Example: User → Role → Permission (3-table join)
    public function permissions()
    {
        return $this->customRelation(
            'permissions',
            Permission::class,
            'user_id',
            'permission_id',
            'role_user',
            'user_id',
            'role_id',
            'role_permission',
            'role_id',
            'permission_id'
        );
    }
    
  2. Query Scoping Chain Eloquent query methods after defining the relation:

    $user->permissions()->where('name', 'like', '%admin%')->get();
    
  3. Dynamic Relations Useful for polymorphic or context-dependent relations:

    public function dynamicPermissions($context = 'admin')
    {
        return $this->customRelation(
            'permissions_' . $context,
            Permission::class,
            // ... keys
        )->where('context', $context);
    }
    
  4. Integration with Policies Leverage custom relations in authorization logic:

    public function authorize($user, Permission $permission)
    {
        return $user->permissions()->where('id', $permission->id)->exists();
    }
    
  5. Eager Loading Optimize N+1 queries with with():

    User::with('permissions')->get();
    

Advanced Patterns

  • Custom Pivot Data: Extend the package to include pivot attributes by overriding the getPivot() method in the related model.
  • Relation Caching: Cache relation results for performance:
    $user->permissions()->remember(60); // Cache for 60 minutes
    
  • Relation Events: Listen to relation events (e.g., retrieved, saved) via:
    $this->permissions()->listen(function ($query) {
        // Modify query or log access
    });
    

Gotchas and Tips

Pitfalls

  1. Key Mismatches

    • Issue: Passing incorrect keys (e.g., user_id vs id) throws silent RelationNotFoundException.
    • Fix: Validate keys against the database schema. Use dd($this->getTable().' keys') to debug.
  2. Overwriting Native Relations

    • Issue: Naming a custom relation the same as a native one (e.g., belongsTo) will override it.
    • Fix: Prefix custom relations (e.g., custom_permissions).
  3. Performance with Complex Joins

    • Issue: Deep joins (e.g., 4+ tables) can bloat queries.
    • Fix: Use select() to limit columns:
      $this->permissions()->select('permissions.id', 'permissions.name');
      
  4. Circular References

    • Issue: Bidirectional custom relations (e.g., User → Role ← User) cause infinite loops.
    • Fix: Avoid defining inverse relations or use lazy loading:
      $user->permissions()->load('user'); // Explicitly load inverse
      

Debugging

  • Query Logging: Enable Laravel’s query log to inspect generated SQL:
    DB::enableQueryLog();
    $user->permissions()->get();
    dd(DB::getQueryLog());
    
  • Relation Existence: Check if a relation is defined before calling it:
    if (method_exists($user, 'permissions')) {
        $user->permissions;
    }
    

Configuration Quirks

  1. Service Provider Bootstrapping

    • The package registers a macro on the Illuminate\Database\Eloquent\Builder class. If you override this macro elsewhere, conflicts may arise.
    • Fix: Use a unique macro name or namespace the macro.
  2. Model Events

    • Custom relations do not trigger retrieved or saved events by default. Use listeners for custom logic:
      $this->permissions()->listen(function ($query) {
          // Custom logic here
      });
      

Extension Points

  1. Custom Relation Macros Extend the package by adding macros to the HasCustomRelations trait:

    \CultureGr\CustomRelation\HasCustomRelations::macro('scoped', function ($callback) {
        return $this->customRelation(...)->where($callback);
    });
    

    Usage:

    $user->permissions()->scoped(fn($q) => $q->active(true));
    
  2. Relation Caching Override the getCustomRelation() method in your model to add caching:

    public function getCustomRelation($relation)
    {
        return cache()->remember("{$this->id}.{$relation}", 60, function() use ($relation) {
            return parent::getCustomRelation($relation);
        });
    }
    
  3. Validation Add validation to relation keys in a model observer or accessor:

    public function getPermissionsAttribute()
    {
        if (!Schema::hasColumn('role_user', 'user_id')) {
            throw new \RuntimeException('Invalid relation keys');
        }
        return $this->customRelation(...);
    }
    
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope