Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug Report: Incorrect Join Column Used in BelongsToMany Relationship in Laravel 11 #52881

Closed
angelonazzaro opened this issue Sep 22, 2024 · 3 comments

Comments

@angelonazzaro
Copy link

angelonazzaro commented Sep 22, 2024

Laravel Version

11

PHP Version

8.2.0

Database Driver & Version

No response

Description

Bug Report: Incorrect Join Column Used in BelongsToMany Relationship in Laravel 11

Overview

I am experiencing an issue with BelongsToMany relationships in Laravel 11, specifically when using a custom join column that is not the primary key of the related model. The problem arises in a project involving two main tables, participants and giveaways, which have a many-to-many (N:N) relationship through a pivot table.

Table Structure

The (simplified) structure of the tables is as follows:

giveaways 
----------------------
uuid*    name


participant_giveaway
----------------------
id*    giveaway_uuid    fiscal_code    drawn    created_at    updated_at


participants
----------------------
id*    fiscal_code    first_name    last_name

In this schema:

  • The giveaway_uuid column in the participant_giveaway table references the giveaways table's uuid.
  • The fiscal_code column in the participant_giveaway table references the participants table's fiscal_code (not its primary key).

Relationship Definition

I have explicitly defined the BelongsToMany relationship to join the participants table on the fiscal_code column, instead of the default primary key (id). The relationship in the Giveaway model is defined as follows:

class Giveaway extends Model
{
    use HasFactory, InteractsWithMedia, HasUuids;

    protected $primaryKey = 'uuid';
    protected $keyType = 'string';
    public $incrementing = false;

    protected $fillable = ['name'];

    public function participants(): BelongsToMany
    {
        return $this->belongsToMany(Participant::class, 'participant_giveaway', 'giveaway_uuid', 'fiscal_code')
            ->withPivot('drawn', 'created_at')
            ->withTimestamps();
    }
}

Similarly, the relationship is defined in the Participant model as:

class Participant extends Model
{
    use HasFactory;

    protected $primaryKey = 'id';

    protected $fillable = [
        'first_name',
        'fiscal_code', 
    ];

    public function fairs(): BelongsToMany
    {
        return $this->belongsToMany(Fair::class, 'participant_fair', 'fiscal_code', 'fair_uuid')
            ->withPivot('drawn', 'created_at')
            ->withTimestamps();
    }
}

Problem Description

When attempting to retrieve participants for a specific giveaway using Giveaway::find($uuid)->participants, I either receive an empty collection or an incorrect list of participants. The issue persists with eager loading and when attempting to access giveaways from the participant's side.

After debugging the query being generated, I found that Laravel is joining on the id column of the participants table, rather than the fiscal_code column, despite my explicit configuration to use fiscal_code as the foreign key in both models.
The executed query:

select * from participants inner join participant_giveaway on participants.id = participant_giveaway.fiscal_code where participant_giveaway.giveaway_uuid = ?"

As seen above, Laravel is incorrectly joining on participants.id, even though the relationship is configured to join on fiscal_code.

Workaround

The only workaround I found was to change the primary key of the participants table from id to fiscal_code. However, this is not ideal for my application's design.

I have reviewed similar issues on Laravel forums, Reddit, StackOverflow and this repo issues but was unable to find a relevant solution. This issue seems to be specific to Laravel 11, as I haven't encountered it in previous versions.

Expected Behavior

Laravel should respect the custom foreign key (fiscal_code) specified in the relationship definition and not default to the primary key (id) of the related table.

Steps To Reproduce

No steps

Copy link

Thank you for reporting this issue!

As Laravel is an open source project, we rely on the community to help us diagnose and fix issues as it is not possible to research and fix every issue reported to us via GitHub.

If possible, please make a pull request fixing the issue you have described, along with corresponding tests. All pull requests are promptly reviewed by the Laravel team.

Thank you!

@staudenmeir
Copy link
Contributor

Hi @angelonazzaro,

You need to specify that the relationship should join the participants.fiscal_code column by passing a related key:

public function participants(): BelongsToMany
{
    return $this->belongsToMany(Participant::class, 'participant_giveaway', 'giveaway_uuid', 'fiscal_code',
            relatedKey: 'fiscal_code')
        ->withPivot('drawn', 'created_at')
        ->withTimestamps();
}

As seen above, Laravel is incorrectly joining on participants.id, even though the relationship is configured to join on fiscal_code.

The fiscal_code column in your relationship is the related pivot key, i.e. referring to participant_giveaway.fiscal_code. The relationship doesn't customize the joining column on the participants table and so its primary key gets used.

@angelonazzaro
Copy link
Author

Hi @staudenmeir,

It worked perfectly! Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants