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 Extended Relationships Laravel Package

mr-punyapal/laravel-extended-relationships

Adds efficient, custom Eloquent relationships for Laravel via a HasExtendedRelationships trait, reducing query count and duplicate code. Includes helpers like belongsToManyKeys and more, aimed at improving performance in real-world apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:
    composer require mrpunyapal/laravel-extended-relationships
    
  2. Add the trait to your model:
    use MrPunyapal\LaravelExtendedRelationships\HasExtendedRelationships;
    
    class Post extends Model {
        use HasExtendedRelationships;
    }
    

First Use Case: Audit Trails

Define a single belongsToManyKeys relationship to replace multiple belongsTo calls for audit fields:

public function auditors() {
    return $this->belongsToManyKeys(
        related: User::class,
        foreignKey: 'id',
        relations: [
            'created_by' => 'creator',
            'updated_by' => 'updater',
            'deleted_by' => 'deleter',
        ]
    );
}

Usage:

$post = Post::with('auditors')->find(1);
$post->auditors->creator; // Single query for all audit fields

Implementation Patterns

1. Audit Relationships

Pattern: Replace 3+ belongsTo relationships with one belongsToManyKeys call. Workflow:

  • Define in the parent model (e.g., Post).
  • Access via $model->relationship->alias (e.g., $post->auditors->creator).
  • Optimization: Single query for all audit fields, reducing N+1 queries.

Example:

// Before (3 queries)
$post->createdBy->name;
$post->updatedBy->name;
$post->deletedBy->name;

// After (1 query)
$post->auditors->creator->name;
$post->auditors->updater->name;

2. Array Column Relationships

Pattern: Handle JSON/array columns (e.g., users.companies = [1, 2, 3]). Workflow:

  • Use hasManyArrayColumn in the parent model (e.g., User).
  • Use belongsToArrayColumn in the related model (e.g., Company).

Example:

// User model
public function companies() {
    return $this->hasManyArrayColumn(
        related: Company::class,
        foreignKey: 'id',
        localKey: 'company_ids'
    );
}

// Company model (inverse)
public function users() {
    return $this->belongsToArrayColumn(
        related: User::class,
        foreignKey: 'id',
        localKey: 'company_ids',
        isString: true // If IDs are stored as strings
    );
}

3. Inverse Relationships

Pattern: Define bidirectional relationships with minimal code. Workflow:

  • Use hasManyKeys in the parent model (e.g., User for audited posts).
  • Map local keys to relationship aliases (e.g., created_bycreated).

Example:

// User model
public function audited() {
    return $this->hasManyKeys(
        related: Post::class,
        relations: [
            'created_by' => 'created',
            'updated_by' => 'updated',
        ]
    );
}

// Usage
$user->audited->created; // Posts created by the user

4. Lazy Loading

Pattern: Access relationships without eager loading. Workflow:

  • Works seamlessly with lazy loading (e.g., $post->auditors->creator).
  • Under the hood, the package sorts relationships to minimize queries.

Example:

$post = Post::find(1); // No eager loading
$post->auditors->creator; // Single query on access

5. Integration with Laravel Boost

Pattern: Use AI-assisted development for relationship queries. Workflow:

  • Install Laravel Boost (composer require laravel/boost --dev).
  • Run php artisan boost:update --discover to enable the skill.
  • Get context-aware suggestions for relationship queries.

Example:

# Ask Boost for help with a relationship query
php artisan boost:ask "How do I fetch all posts created by a user?"

Gotchas and Tips

Pitfalls

  1. Foreign Key Mismatches:

    • If foreignKey doesn’t match the related model’s primary key, queries fail silently.
    • Fix: Explicitly specify foreignKey (e.g., foreignKey: 'user_id').
  2. Array Column Data Types:

    • hasManyArrayColumn assumes localKey values match the related model’s primary key type.
    • Fix: Use isString: true if IDs are stored as strings (e.g., ["7", "8"]).
  3. Lazy Loading Overhead:

    • While lazy loading works, it can trigger multiple queries if accessed in loops.
    • Fix: Use eager loading (with()) for critical paths.
  4. Boost Skill Not Triggering:

    • If Boost doesn’t recognize the package, run:
      php artisan boost:update --discover
      

Debugging Tips

  1. Query Logging: Enable Laravel’s query log to verify single-query behavior:

    DB::enableQueryLog();
    $post = Post::with('auditors')->find(1);
    dd(DB::getQueryLog()); // Check for 1 query instead of 3+
    
  2. Relationship Sorting: The package sorts relationships alphabetically for consistent lazy-loading behavior.

    • Impact: If you rely on insertion order, this may affect results.
    • Fix: Use eager loading (with()) for predictable ordering.
  3. Type Safety:

    • The package uses PHP generics (since v2.2.0) for better IDE support.
    • Tip: Enable strict typing in php.ini (strict_types=1) for full benefits.

Extension Points

  1. Custom Relationship Logic: Extend the trait to add domain-specific relationships:

    // app/Models/Concerns/ExtendedRelationships.php
    public function customRelationship() {
        return $this->belongsToManyKeys(
            related: CustomModel::class,
            relations: ['field1' => 'alias1', 'field2' => 'alias2']
        );
    }
    
  2. Global Configuration: Override default behavior via a config file (published by the package):

    php artisan vendor:publish --provider="MrPunyapal\LaravelExtendedRelationships\ServiceProvider"
    
    • Customize query scopes, default keys, or relationship naming conventions.
  3. Testing: Use Pest or PHPUnit to test relationships:

    public function test_audit_relationships() {
        $user = User::factory()->create();
        $post = Post::factory()->create(['created_by' => $user->id]);
    
        $this->assertInstanceOf(User::class, $post->auditors->creator);
    }
    

Performance Quirks

  1. Single Query vs. Multiple:

    • belongsToManyKeys and hasManyKeys always use a single query, even with lazy loading.
    • Exception: Array column relationships (hasManyArrayColumn) may require additional joins if the array is large.
  2. Memory Usage:

    • Large array columns (e.g., users.companies = [1, 2, ..., 1000]) can increase memory usage.
    • Fix: Limit results with take() or paginate:
      return $this->hasManyArrayColumn(..., take: 100);
      
  3. Indexing:

    • For belongsToArrayColumn, ensure the localKey column is indexed in the related table:
      ALTER TABLE users ADD INDEX idx_company_ids ON company_ids USING GIN;
      
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle