Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

kylevorster's avatar

belongsToMany Question

I've got a problem/question regarding belongsToMany.

I've got three tables, the one table "tableone" stores regions for another table, the "tableone" table has a fixed number of records and it might increase or decline but not as much as "tabletwo", my "tabletwo" have multiple sizes and each size can have many regions that it belongs to.

My "tableone" sql looks as follows:

| Field | Type | Null | Key | Default | Extra | ----------- | ----------------- | ----- | ----- | ----------------- | ------------------ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | name | varchar(20) | NO | | NULL | | slug | varchar(10) | NO | MUL | NULL | | available | tinyint(1) | NO | | NULL |

My "tabletwo" sql looks as follows:

+---------------+---------------------+------+-----+---------------------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------------+---------------------+------+-----+---------------------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(20) | NO | | NULL | | | type | varchar(10) | NO | | NULL | | | distribution | varchar(10) | NO | | NULL | | | slug | varchar(10) | NO | MUL | NULL | | | public | tinyint(1) | NO | | NULL | | | min_disk_size | varchar(5) | NO | | NULL | | | created_at | timestamp | NO | | 0000-00-00 00:00:00 | | | updated_at | timestamp | NO | | 0000-00-00 00:00:00 | | +---------------+---------------------+------+-----+---------------------+----------------+

And my mapping table looks as follows:

+---------------+------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------------+------------------+------+-----+---------+-------+ | foreign1_id | int(10) unsigned | NO | MUL | NULL | | | foreign2_id | int(10) unsigned | NO | MUL | NULL | | +---------------+------------------+------+-----+---------+-------+

And my models looks like this:

class TableOne extends Model {

    protected $table = 'tableone';
    public $timestamps = false;

    protected $guarded = ['id'];
    protected $fillable = ['name', 'slug', 'available'];

    public function sizes()
    {
        return $this->belongsToMany('App\TableOne', 'maps', 'foreign1_id', 'foreign2_id');
    }
}
class TableTwo extends Model {

    protected $table = 'tabletwo';
    public $timestamps = true;

    protected $fillable = ['id', 'name', 'distribution', 'slug', 'public', 'created_at', 'min_disk_size'];

    public function regions()
    {
        return $this->belongsToMany('App\TableTwo', 'maps', 'foreign2_id', 'foreign1_id');
    }
}

So I use them as follows to call data,

$TableTwo = TableTwo::find(12950274);

foreach ($TableTwo->regions()->get() as $region)
{
    echo($region->slug) . "\n";
}

Attaching data

$TableOne = TableOne::find(9);
$TableTwo = TableTwo::find(10);

$TableOne->sizes->attach($TableTwo);
$TableOne->save();

Detaching data

$TableOne = TableOne::find(9);
$TableTwo = TableTwo::find(10);

$TableOne->sizes->detach($TableTwo);
$TableOne->save();

So I guess my question would be, am I using the code in the correct format, is there anyway of doing things because at the moment a lot of queries are taking place to attach data.

Any Help or just feedback, questions about what I'm trying to do would be great just to point me in the right direction or to help me to figure out the logic behind all this.

Another thing is I'm using eloquent outside laravel because the package I'm developing for was not written in laravel "which I HATE" but its a paid system, so I can really tell the developers, Hey get with the time.

Please and thank you.

0 likes
4 replies
pmall's avatar

First you should never call get() or first() on a relationship. You should use $TableTwo->regions to get related regions. Because laravel automagically call get() and execute the query only the first time you access it, whereas with your method above the query is executed each time. get() is automagically called on relationship which expect a collection (hasMany, belongsToMany) and first() is called on relationship which expect only one model (belongsTo, hasOne).

For attaching isn't it $TableOne->sizes()->attach($TableTwo); (note the parenthesis).

1 like
kylevorster's avatar
kylevorster
OP
Best Answer
Level 8

Thank you for your reply, please excuse me if I'm having difficulty understanding and repeating answers/questions but I just want to make sure I understand correctly.

So if I wish to get the regions for a specific entry in $TableOne then I'll use the following.

$TableTwo = $TableTwo::find(12950274);

foreach($TableTwo->region as $region)
{
    echo $region->slug . "\n";
}

If I want more than one entry on $TableTwo then I'll use the following.

$TableTwo = $TableTwo::All(); // or TableTwo::where('foo','bar')->get();

foreach($TableTwo as $row)
{
    echo 'Entry: ' . $row->name . "\n";
    
    foreach($row->region as $region)
    {
        echo $region->slug . "\n";
    }
    
    echo "\n";
}

Those seems pretty logic, so I think I'm on track. Another question would then be, if I attach data that's already in tableone then it creates duplicate data. So running the following will create duplicates.

$TableOne = TableOne::find(9);
$TableTwo = TableTwo::find(10);

$TableOne->sizes->attach($TableTwo);
$TableOne->save();

$TableOne = TableOne::find(9);
$TableTwo = TableTwo::find(10);

$TableOne->sizes->attach($TableTwo);
$TableOne->save();

Please and thank you.

pmall's avatar

So if I wish to get the regions for a specific entry in $TableOne then I'll use the following.

Yes. Never use get() or first() on a relationship.

If I want more than one entry on $TableTwo then I'll use the following.

here it will execute a query to get all table two, and a query per table two in order to get their regions. You can eager load the regions so only two queries are executed : $TableTwo = TableTwo::with('regions')->get();.

if I attach data that's already in tableone then it creates duplicate data.

Yes if you attach an object two time the relation is duplicated. What the problem with this ? Just attach it one time if you dont want it to be duplicated. You can also use the sync() method to update the whole list of relationship : $TableOne->sizes()->sync([$size_id1, $size_id2, $size_id3, ...]);.

1 like

Please or to participate in this conversation.