Laravel Eloquent BelongsToThrough relationships

Overview

CI Code Coverage Scrutinizer Code Quality Latest Stable Version Total Downloads License

Introduction

This inverse version of HasManyThrough allows BelongsToThrough relationships with unlimited intermediate models.

Supports Laravel 5.0+.

Installation

composer require staudenmeir/belongs-to-through:"^2.5"

Use this command if you are in PowerShell on Windows (e.g. in VS Code):

composer require staudenmeir/belongs-to-through:"^^^^2.5"

Usage

Consider this HasManyThrough relationship:
Country → has many → User → has many → Post

class Country extends Model
{
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User');
    }
}

Use the BelongsToThrough trait in your model to define the inverse relationship:
Post → belongs to → User → belongs to → Country

class Post extends Model
{
    use \Znck\Eloquent\Traits\BelongsToThrough;

    public function country()
    {
        return $this->belongsToThrough('App\Country', 'App\User');
    }
}

You can also define deeper relationships:
Comment → belongs to → Post → belongs to → User → belongs to → Country

Supply an array of intermediate models as the second argument, from the related (Country) to the parent model (Comment):

class Comment extends Model
{
    use \Znck\Eloquent\Traits\BelongsToThrough;

    public function country()
    {
        return $this->belongsToThrough('App\Country', ['App\User', 'App\Post']);
    }
}

You can specify custom foreign keys as the fifth argument:

class Comment extends Model
{
    use \Znck\Eloquent\Traits\BelongsToThrough;

    public function country()
    {
        return $this->belongsToThrough(
            'App\Country',
            ['App\User', 'App\Post'], 
            null,
            '',
            ['App\User' => 'custom_user_id']
        );
    }
}

Table Aliases

If your relationship path contains the same model multiple times, you can specify a table alias (Laravel 6+):

class Comment extends Model
{
    use \Znck\Eloquent\Traits\BelongsToThrough;

    public function grandparent()
    {
        return $this->belongsToThrough(
            'App\Comment',
            'App\Comment as alias', 
            null,
            '',
            ['App\Comment' => 'parent_id']
        );
    }
}

Use the HasTableAlias trait in the models you are aliasing:

class Comment extends Model
{
    use \Znck\Eloquent\Traits\HasTableAlias;
}

Soft Deleting

By default, soft-deleted intermediate models will be excluded from the result. Use withTrashed() to include them:

class Comment extends Model
{
    use \Znck\Eloquent\Traits\BelongsToThrough;

    public function country()
    {
        return $this->belongsToThrough('App\Country', ['App\User', 'App\Post'])
            ->withTrashed('users.deleted_at');
    }
}

class User extends Model
{
    use SoftDeletes;
}

Contributing

Please see CONTRIBUTING and CODE OF CONDUCT for details.

Credits

Comments
  • Cannot instal on laravel 6.13.1

    Cannot instal on laravel 6.13.1

    When I try to install this library I get this error:

    Problem 1 - Installation request for staudenmeir/belongs-to-through 2.5 -> satisfiable by staudenmeir/belongs-to-through[v2.5]. - Conclusion: remove laravel/framework v6.13.1 - Conclusion: don't install laravel/framework v6.13.1 - staudenmeir/belongs-to-through v2.5 requires illuminate/database ~5.0 -> satisfiable by illuminate/database[5.0.x-dev, 5.1.x-dev, 5.2.x-dev, 5.3.x-dev, 5.4.x-dev, 5.5.x-dev, 5.6.x-dev, 5.7.x-dev, 5.8.x-dev]. - don't install illuminate/database 5.8.x-dev|don't install laravel/framework v6.13.1 - don't install illuminate/database 5.4.x-dev|don't install laravel/framework v6.13.1 - don't install illuminate/database 5.5.x-dev|don't install laravel/framework v6.13.1 - don't install illuminate/database 5.6.x-dev|don't install laravel/framework v6.13.1 - don't install illuminate/database 5.7.x-dev|don't install laravel/framework v6.13.1 - don't install illuminate/database 5.0.x-dev|don't install laravel/framework v6.13.1 - don't install illuminate/database 5.1.x-dev|don't install laravel/framework v6.13.1 - don't install illuminate/database 5.2.x-dev|don't install laravel/framework v6.13.1 - don't install illuminate/database 5.3.x-dev|don't install laravel/framework v6.13.1 - Installation request for laravel/framework (locked at v6.13.1, required as ^6.2) -> satisfiable by laravel/framework[v6.13.1].

    opened by mjza 11
  • extra bindings bug

    extra bindings bug

    here is what i have

    tables

    • service id

    • service_infos id service_id

    • service_request id service_info_id

    models

    • service
    public function requests()
    {
        return $this->hasManyThrough(ServiceRequest::class, ServiceInfo::class);
    }
    
    • service request
    public function service()
    {
        return $this->belongsToThrough(Service::class, ServiceInfo::class);
    }
    

    test

    DB::connection()->enableQueryLog();
    ServiceRequest::with('service')->first(); // service = null;
    dd(DB::getQueryLog());
    
    "query" => "select `services`.*, `service_infos`.`id` as `laravel_through_key` from `services` inner join `service_infos` on `service_infos`.`service_id` = `services`.`id` where `service_infos`.`id` in (?) ◀"
    "bindings" => array:2 [▼
        0 => "App\Models\Service"
        1 => "144cbcea-2ce1-46a2-b438-50bd3f25ebfb"
    ]
    "time" => 0.0
    

    am not sure if am doing something wrong from my part or its a bug with the package, any help is appreciated.

    opened by ctf0 9
  • LocalKey being ignored

    LocalKey being ignored

    On the Znck\Eloquent\Relations\BelongsToThrough constructor, it allows you to specify a local key, however this appears to be being completely ignored.

    opened by Eyewash01 9
  • BelongsToThrough::associate()

    BelongsToThrough::associate()

    "staudenmeir/belongs-to-through": "2.9", PHP 7.2.26 Laravel Framework 6.18.19 I get this error on update: Call to undefined method Znck\Eloquent\Relations\BelongsToThrough::associate() and this is my relation methode:

     public function language() {
            return $this->belongsToThrough(\ModulMap::model('Language'), [\ModulMap::model('FaqCategory')], null,'', [\ModulMap::model('FaqCategory') => 'category_id'] );
     }
    
    opened by vmihaly2020 8
  • Column not found: 1054

    Column not found: 1054

    Firstly, thanks for the package - it's just what I needed. Secondly, I'm a novice so my apologies in advance if I'm just doing something dumb here but I think this may be an issue.

    I have a Hazard model that relates to the Project model as follows:

    public function project()
        {
            // relies on belongsToThrough package (https://github.com/staudenmeir/belongs-to-through)
            return $this->belongsToThrough(Project::class, Node::class);
        }
    

    To confirm:

    • [x] The nodes table relates to the projects table
    Schema::table('nodes', function (Blueprint $table) {
                $table->unsignedBigInteger('project_id');
                $table->foreign('project_id', 'project_fk_2244664')->references('id')->on('projects');
            });
    
    • [x] The hazards table relates to the nodes table
    Schema::table('hazards', function (Blueprint $table) {
                $table->unsignedBigInteger('node_id');
                $table->foreign('node_id', 'node_fk_2244867')->references('id')->on('nodes');
            });
    

    I have no doubt this works and if I query, for example, Hazard::with(['project']) then I get the expected relationship.

    But there are two places where I have encountered issues:

    1. When I query as below (this is in a filter class):
    return $this->builder->whereHas('project', function($qry) use ($value) {
    info($qry->first());
                $qry->where('projects.id', $value);
                    });
    
    1. When I try to use the relationship in an accessor:
        protected function code(): Attribute
        {
            return new Attribute(
                get: fn () => $this->project->code . "-" . sprintf('%04d', $this->id),
            );
        }
    

    In both instance I think I get the same underlying issue:

    SQLSTATE[42S22]: Column not found: 1054 Unknown column 'hazards.node_id' in 'where clause' (SQL: select * from `projects` inner join `nodes` on `nodes`.`project_id` = `projects`.`id` where `nodes`.`id` = `hazards`.`node_id` and `projects`.`deleted_at` is null and `is_active` = 1 limit 1)
    

    This has me beat because I understand all those columns exist (and can see them there in the database). Could you please take a look?

    opened by rossco555 7
  • Problem Defining a Special Relation

    Problem Defining a Special Relation

    This is probably not the best place for my question but it was the closest to the topic I could think of. So I hope there will be some hints however.

    Is there any possibility to define the following call as a relation?

    CustomerService::where([
        'customer_id' => $this->job->customer_id,
        'service_id' => $this->service_id,
    ])->first();
    

    I have so far not been able to figure it out, because it's some kind of hybrid between belongsToThrough and a belongsTo relation with composite keys. I also use Compoships.

    Thanks to your package I could at least already define the customer relation:

    public function customer()
    {
        return $this->belongsToThrough(
            User::class,
            Job::class,
        );
    }
    

    I would really like to benefit from the built-in query reduction, eager loading etc. that a relation would provide.

    Here the database migrations:

    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
    });
    
    Schema::create('jobs', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('customer_id')->unsigned()->nullable()->index();
        $table->foreign('customer_id')->references('id')->on('users')->onDelete('cascade');
    });
    
    Schema::create('services', function (Blueprint $table) {
        $table->increments('id');
    });
    
    Schema::create('service_jobs', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('job_id')->unsigned()->index();
        $table->foreign('job_id')->references('id')->on('jobs')->onDelete('cascade');
        $table->integer('service_id')->unsigned()->index();
        $table->foreign('service_id')->references('id')->on('services')->onDelete('cascade');
    });
    
    Schema::create('customer_services', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('customer_id')->unsigned()->index();
        $table->foreign('customer_id')->references('id')->on('users')->onDelete('cascade');
        $table->integer('service_id')->unsigned()->index();
        $table->foreign('service_id')->references('id')->on('services')->onDelete('cascade');
        $table->timestamps();
    });
    
    opened by MannikJ 7
  • requires illuminate/database ~5.0 -> found illuminate/database[v5.0.0, ..., 5.8.x-dev] - Laravel 8 error

    requires illuminate/database ~5.0 -> found illuminate/database[v5.0.0, ..., 5.8.x-dev] - Laravel 8 error

    Problem 1 - Root composer.json requires staudenmeir/belongs-to-through 2.5 -> satisfiable by staudenmeir/belongs-to-through[v2.5]. - staudenmeir/belongs-to-through v2.5 requires illuminate/database ~5.0 -> found illuminate/database[v5.0.0, ..., 5.8.x-dev] but these were not loaded, likely because it conflicts with another require.

    opened by WellingtonOstemberg 6
  • BelongsToThrough with custom ForeignKey

    BelongsToThrough with custom ForeignKey

    I have structure table with custom foreignKey name owner_id

    users:
      id: unsigned int, primary
    
    properties:
      id : unsigned int, primary
      name: string
      owner_id: unsigned int, foreign(users)
    
    periods:
      id: unsigned int, primary
      property_id : unsigned int, foreign(properties)
      date: date
    
    activities:
      id : unsigned int, primary
      name : string
      period_id : unsigned int, foreign(periods)
    

    but when i'm using BelongsToThrough relation:

    class Activity {
        public function owner()
        {
            return $this->belongsToThrough('App\User',['App\Property','App\Period']);
        }
    }
    

    there's error because query search for user_id in table Property not owner_id.

    is there any option that i could set custom foreignKey?

    opened by hiddenrebel 6
  • User → belongs to → A or B →  morph to many → C

    User → belongs to → A or B → morph to many → C

    Hi,

    I tried to solve this to recover C model, through a belongsTo relation to A (or B), like this : User → belongs to → A or B → morph to many → C I tried many options but unfornatly unsuccessfully. If someone has an idea, I'll super gratefull. Thanks in advance

    opened by Vicftz 5
  • Missing column on query... scratching head!

    Missing column on query... scratching head!

    Hi!

    Have been trying to get this to work in my project.

    A user can be assigned jobs (pivot table), each job has many areas, which can have many locations, and then once a job in a location is finished a completed model (with location_id) is created.

    Initially I was storing user_id, location_id, area_id and job_id into the Completed table, however this was affecting seeding - then I found your project! Now just storing user_id and location_id

    I've tried to get the amount of completed locations: $user->jobs()->withCount('completed'), however just get this error message:

    SQLSTATE[42S22]: COLUMN NOT FOUND: 1054 unknown COLUMN 'locations.job_id' IN 'where clause' (SQL:
    SELECT     `jobs`.*,
               (
                          SELECT     count(*)
                          FROM       `completeds`
                          INNER JOIN `locations`
                          ON         `locations`.`id` = `completeds`.`location_id`
                          WHERE      `jobs`.`id` = `locations`.`job_id`
                          AND        `completeds`.`deleted_at` IS NULL) AS `completeds_count`,
               `job_subs`.`user_id`                                     AS `pivot_user_id`,
               `job_subs`.`job_id`                                      AS `pivot_job_id`,
               `job_subs`.`created_at`                                  AS `pivot_created_at`,
               `job_subs`.`updated_at`                                  AS `pivot_updated_at`
    FROM       `jobs`
    INNER JOIN `job_subs`
    ON         `jobs`.`id` = `job_subs`.`job_id`
    WHERE      `job_subs`.`user_id` = 1
    AND        `jobs`.`deleted_at` IS NULL)
    

    I have all the belongsToThrough relations in place in the intermediate models, but it still looks for the _id field (which doesn't exist).

    Any ideas? Am I just doing it wrong?

    opened by sparxooo 5
  • BelongToThrough does not respect Model's connection

    BelongToThrough does not respect Model's connection

    BelongToThrough does not respect Model's connection

    Database structure

    Let's assume a database structure with three models in two different database schemas.

    DB: dev_user_db
    
    sellers
    - id
    
    DB: dev_shop_db
    
    categories
    - id
    - seller_id
    
    items
    - id
    - category_id
    

    Database config

    And the following config/database.php which let's us switch between a development and produciton database using the .env file:

    return [
        'user_db' => [
            // Some other things here
            'database' => env('USER_DATABASE')
        ],
        'shop_db' => [
            // Some other things here
            'database' => env('SHOP_DATABASE')
        ],
    ];
    

    Relationships

    The relationships are defined as follows:

    class Seller extends Model
    {
        protected $connection = 'user_db';
        protected $table = 'sellers';
    
        // Some hasMany here ...
    }
    
    class Category extends Model
    {
        protected $connection = 'shop_db';
        protected $table = 'categories';
    
        public function seller()
        {
            return $this->belongsTo(Seller::class);
        }
    }
    
    class Item extends Model
    {
        protected $connection = 'shop_db';
        protected $table = 'items';
    
        public function category()
        {
            return $this->belongsTo(Category::class);
        }
    
        public function seller()
        {
            return $this->belongsToThrough(Seller::class, Category::class);
        }
    }
    

    Issue

    When trying to retrieve an Item with it's Seller:

    public function getItems()
    {
        return Item::with(['seller'])->get();
    }
    

    The following SQL Exception is thrown:

    "SQLSTATE[42S02]: Base table or view not found: 1146 Table 'dev_user_db.categories' doesn't exist 
    (SQL: select `seller`.*, `categories`.`id` as `laravel_through_key` from `seller` inner join `categories` on `categories`.`seller_id` = `seller`.`id` where `categories`.`id` in (1, 2))"
    

    As we can see, the query tries to find dev_user_db.categories even though $connection = 'shop_db' was defined on the Category model.

    A possible suggested solution to this was to also define the schema's name in the table parameter like $table = 'dev_shop_db.categories. Unfortunately this is not possible here, since the schema's name changes depending on the .env file - and expressions are not allowed as field default values.

    So it would be great if the belongsToThrough relationship used the defined $connection attributes to determine the correct database schemas to query.

    opened by tklie 5
  • Unable to get login user data from

    Unable to get login user data from "users" table.

    Login user can create multiple groups and add multiple user to each group. I want to display all unique users added to all groups. So far I tried Belongs to Trhough and Eloquent has many deep but unable to resolve my issue. Anyone help

    User Model

    class User extends Authenticatable implements MustVerifyEmail
    {
     use \Znck\Eloquent\Traits\BelongsToThrough; //extra package added
     public function companies()
        {
            return $this->belongsToThrough(
                User::class,
                [group::class, GroupUser::class],
                null, 
                '', 
                [Group::class => 'assigned_user_id']
            );
        }
    }
    

    Group Model

    class group extends Model
    {
    
    }
    

    GroupUser Model for intermediate table

    class GroupUser extends Model
    {
        protected $table = 'group_user';
    }
    

    Fetch myCompanies

    $user = auth()->user();
    $user->myCompanies;
    

    Exception Illuminate\Database\QueryException SQLSTATE[42S22]: Column not found: 1054 Unknown column 'group_user.id' in 'where clause' (SQL: select users.* from users inner join groups on groups.user_id = users.id inner join group_user on group_user.assigned_user_id = groups.id where group_user.id is null limit 1)

    Users Table Structure user

    Groups Table Structure group

    GroupUser Table Structure group_user

    opened by msayubi76 16
Releases(v2.12.1)
Owner
Jonas Staudenmeir
Backend Developer: PHP, MySQL, Laravel | @laravel Core Contributor
Jonas Staudenmeir
An Eloquent Way To Filter Laravel Models And Their Relationships

Eloquent Filter An Eloquent way to filter Eloquent Models and their relationships Introduction Lets say we want to return a list of users filtered by

Eric Tucker 1.5k Dec 30, 2022
🕵️ Inspect Laravel Eloquent models to collect properties, relationships and more.

??️ Eloquent Inspector Inspect Laravel Eloquent models to collect properties, relationships and more. Install Via Composer composer require cerbero/el

Andrea Marco Sartori 111 Nov 4, 2022
Personal CRM. Remember everything about your friends, family and business relationships.

Personal Relationship Manager Monica is a great open source personal relationship management system. Introduction Purpose Features Who is it for? What

Monica 18.5k Jan 5, 2023
Laravel comments - This package enables to easily associate comments to any Eloquent model in your Laravel application

Laravel comments - This package enables to easily associate comments to any Eloquent model in your Laravel application

Rubik 4 May 12, 2022
This Laravel package merges staudenmeir/eloquent-param-limit-fix and staudenmeir/laravel-adjacency-list to allow them being used in the same model.

This Laravel package merges staudenmeir/eloquent-param-limit-fix and staudenmeir/laravel-adjacency-list to allow them being used in the same model.

Jonas Staudenmeir 5 Jan 6, 2023
Easy creation of slugs for your Eloquent models in Laravel

Eloquent-Sluggable Easy creation of slugs for your Eloquent models in Laravel. NOTE: These instructions are for the latest version of Laravel. If you

Colin Viebrock 3.6k Dec 30, 2022
Automatically validating Eloquent models for Laravel

Validating, a validation trait for Laravel Validating is a trait for Laravel Eloquent models which ensures that models meet their validation criteria

Dwight Watson 955 Dec 25, 2022
Laravel Ban simplify blocking and banning Eloquent models.

Laravel Ban Introduction Laravel Ban simplify management of Eloquent model's ban. Make any model bannable in a minutes! Use case is not limited to Use

cybercog 879 Dec 30, 2022
cybercog 996 Dec 28, 2022
Orbit is a flat-file driver for Laravel Eloquent

Orbit is a flat-file driver for Laravel Eloquent. It allows you to replace your generic database with real files that you can manipulate using the methods you're familiar with.

Ryan Chandler 664 Dec 30, 2022
Collection of the Laravel/Eloquent Model classes that allows you to get data directly from a Magento 2 database.

Laragento LAravel MAgento Micro services Magento 2 has legacy code based on abandoned Zend Framework 1 with really ugly ORM on top of outdated Zend_DB

Egor Shitikov 87 Nov 26, 2022
The missing laravel helper that allows you to inspect your eloquent queries with it's bind parameters

Laravel Query Inspector The missing laravel helper that allows you to ispect your eloquent queries with it's bind parameters Motivations Let's say you

Mouad ZIANI 59 Sep 25, 2022
Laravel Quran is static Eloquent model for Quran.

Laravel Quran بِسْمِ ٱللّٰهِ الرَّحْمٰنِ الرَّحِيْمِ Laravel Quran is static Eloquent model for Quran. The Quran has never changed and never will, bec

Devtical 13 Aug 17, 2022
A package for Laravel One Time Password (OTP) generator and validation without Eloquent Model, since it done by Cache.

Laravel OTP Introduction A package for Laravel One Time Password (OTP) generator and validation without Eloquent Model, since it done by Cache. The ca

Lim Teck Wei 52 Sep 6, 2022
Provides a Eloquent query builder for Laravel or Lumen

This package provides an advanced filter for Laravel or Lumen model based on incoming requets.

M.Fouladgar 484 Jan 4, 2023
Curso Laravel Eloquent ORM

Curso Laravel Eloquent ORM

EspecializaTi 4 Nov 18, 2022
Use eloquent joins in Laravel way, with composite key support.

Eloquent Power Joins with Compoships Support This package is an Eloquent Power Joins extension to support Compoships. You can now use joins in Laravel

Kit Loong 13 Dec 23, 2022
Tag support for Laravel Eloquent models - Taggable Trait

Laravel Taggable Trait This package is not meant to handle javascript or html in any way. This package handles database storage and read/writes only.

Rob 859 Dec 11, 2022
Laravel basic Functions, eloquent cruds, query filters, constants

Emmanuelpcg laravel-basics Description Package with basic starter features for Laravel. Install If Builder Constants Install composer require emmanuel

Emmanuel Pereira Pires 3 Jan 1, 2022