belongs-to-through

repository·main·Indexed 22 days ago

https://github.com/staudenmeir/belongs-to-through

An Eloquent extension for Laravel that provides the inverse of the HasManyThrough relationship, allowing models to belong to a distant parent through one or more intermediate models. It supports multi-level traversal, custom foreign and local key mapping, soft-deleted intermediate models, and table aliases for repeating models.

Tokens
2.3K
Snippets
7
Records
10
Agent score
78%

What's inside belongs-to-through

  1. Install BelongsToThrough via Composer

    main

    Install the package using Composer. For standard environments, use the first command. If you are using PowerShell on Windows (e.g., within VS Code), use the second command to avoid issues with character escaping.

    composer require staudenmeir/belongs-to-through:"^2.5"
    
    # For PowerShell on Windows:
    composer require staudenmeir/belongs-to-through:"^^^^2.5"
  2. Use table aliases for repeating models

    main

    If a relationship path includes the same model multiple times, you can use table aliases (Laravel 6+). To do this, pass the model name with an alias string (e.g., Model::class . ' as alias') as an intermediate model.

    Crucially, you must also use the \Znck\Eloquent\Traits\HasTableAlias trait in the models that are being aliased.

    class Comment extends Model
    {
        use \Znck\Eloquent\Traits\HasTableAlias;
    
        public function grandparent(): \Znck\Eloquent\Relations\BelongsToThrough
        {
            return $this->belongsToThrough(
                Comment::class,
                Comment::class . ' as alias',
                foreignKeyLookup: [Comment::class => 'parent_id']
            );
        }
    }
  3. Define BelongsToThrough relationships

    main

    The BelongsToThrough trait allows you to define inverse HasManyThrough relationships with an unlimited number of intermediate models.

    To use it, include the \Znck\Eloquent\Traits\BelongsToThrough trait in your model. The belongsToThrough method accepts the related model as the first argument and an array of intermediate models as the second argument (ordered from the related model to the parent model).

    class Post extends Model
    {
        use \Znck\Eloquent\Traits\BelongsToThrough;
    
        public function country(): \Znck\Eloquent\Relations\BelongsToThrough
        {
            // Post -> User -> Country
            return $this->belongsToThrough(Country::class, User::class);
        }
    }
    
    class Comment extends Model
    {
        use \Znck\Eloquent\Traits\BelongsToThrough;
    
        public function country(): \Znck\Eloquent\Relations\BelongsToThrough
        {
            // Comment -> Post -> User -> Country
            return $this->belongsToThrough(Country::class, [User::class, Post::class]);
        }
    }
  4. Understand the BelongsToThrough relationship

    main

    The BelongsToThrough relationship allows an Eloquent model to belong to a related model through one or more intermediate "through" models. This is useful for traversing deep relationship chains (e.g., User -> Post -> Category where User belongs to Category through Post).

    Key features include:

    • Multi-level traversal: Supports an arbitrary number of intermediate models.
    • Soft Delete support: Automatically handles SoftDeletes on intermediate models by applying global scopes to ensure only non-deleted through-parents are joined.
    • Custom Key Mapping: Allows overriding default foreign and local key lookups for the intermediate models.
    • Eager Loading: Fully supports Eloquent's eager loading mechanism.
  5. Run the development environment via Docker Compose

    main

    The project provides pre-configured Docker services for different PHP versions (8.3, 8.4, and 8.5) to facilitate testing and development. You can use these services to run the application in a controlled environment with Xdebug support. Each service uses the ghcr.io/staudenmeir/php:{version} image and mounts the current directory to /var/www/html.

    services:
      php8.3:
        image: ghcr.io/staudenmeir/php:8.3
        working_dir: /var/www/html
        volumes:
          - .:/var/www/html:delegated
          - .docker/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
      php8.4:
        image: ghcr.io/staudenmeir/php:8.4
        working_dir: /var/www/html
        volumes:
          - .:/var/www/html:delegated
          - .docker/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
      php8.5:
        image: ghcr.io/staudenmeir/php:8.5
        working_dir: /var/www/html
        volumes:
          - .:/var/www/html:delegated
          - .docker/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
  6. Integrate with Laravel IDE Helper

    main

    To ensure that belongs-to-through relationships are correctly recognized by IDE autocomplete and static analysis tools, the package automatically registers a hook with barryvdh/laravel-ide-helper.

    This is handled via the IdeHelperServiceProvider, which injects Staudenmeir\BelongsToThrough\IdeHelper\BelongsToThroughRelationsHook into the ide-helper.model_hooks configuration array. No manual configuration is required if you have laravel-ide-helper installed in your Laravel project.

  7. Configure custom local keys in BelongsToThrough

    main

    To specify custom local keys for the relations, use the localKeyLookup option. This takes an associative array where the keys are the intermediate model classes and the values are the local key names.

    class CustomerAddress extends Model
    {
        use \Znck\Eloquent\Traits\BelongsToThrough;
    
        public function vendorCustomer(): \Znck\Eloquent\Relations\BelongsToThrough
        {
            return $this->belongsToThrough(
                VendorCustomer::class,
                VendorCustomerAddress::class,
                foreignKeyLookup: [VendorCustomerAddress::class => 'id'],
                localKeyLookup: [VendorCustomerAddress::class => 'address_id'],
            );
        }
    }
  8. Configure custom foreign keys in BelongsToThrough

    main

    If your intermediate models use non-standard foreign keys, you can specify them using the foreignKeyLookup option. This option takes an associative array where the keys are the intermediate model classes and the values are the custom foreign key names.

    class Comment extends Model
    {
        use \Znck\Eloquent\Traits\BelongsToThrough;
    
        public function country(): \Znck\Eloquent\Relations\BelongsToThrough
        {
            return $this->belongsToThrough(
                Country::class,
                [User::class, Post::class], 
                foreignKeyLookup: [User::class => 'custom_user_id']
            );
        }
    }
  9. Include soft-deleted intermediate models

    main

    By default, BelongsToThrough excludes soft-deleted intermediate models from the results. To include them, use the withTrashed() method on the relationship, passing the column name used for soft deletes (e.g., 'users.deleted_at').

    class Comment extends Model
    {
        use \Znck\Eloquent\Traits\BelongsToThrough;
    
        public function country(): \Znck\Eloquent\Relations\BelongsToThrough
        {
            return $this->belongsToThrough(Country::class, [User::class, Post::class])
                ->withTrashed('users.deleted_at');
        }
    }
  10. Configure custom foreign and local keys for BelongsToThrough

    main

    When the default naming convention (singular table name + _id) does not match your database schema, you can provide custom mappings for foreign keys and local keys for the intermediate models.

    These mappings are passed to the BelongsToThrough constructor via $foreignKeyLookup and $localKeyLookup arrays, where the keys are the table names and the values are the specific column names.