titonior
titonior

Reputation: 167

Laravel L5.5 No access to pivot table in "Many to many" relationship

Unfortunately, I cannot successfully access a DB column in a pivot table. Besides this, the model is working well.

Model Device

public function tasks()
{
    return $this->belongsToMany('App\Task', 'xx', 'yy', 'zz')
    ->withPivot('id', 'duedate', 'interval', 'reoccurdate', 'completed', 'state')
    ->withTimestamps();
}

Model Task

public function devices()
{
    return $this->belongsToMany('App\Device', 'xx', 'yy', 'zz')
    ->withPivot('id', 'duedate', 'interval', 'reoccurdate', 'completed', 'state')
    ->withTimestamps();
}

I tried with a very simple example but failed:

dd(Task::findOrFail(1)->pivot->created_at);

Error:

"Trying to get property of non-object"

Upvotes: 0

Views: 504

Answers (1)

JJWesterkamp
JJWesterkamp

Reputation: 7916

You can only access the pivot data if the model collection is the result of a call to the relation method that is aware of that pivot data, meaning you must access devices on a Task model to find any pivot data:

$devicesForTask1 = Task::find(1)->devices;
$devicesForTask1->each(function (Device $device) {

    // Here we can use pivot, since the models were found through a
    // relation method that implements the pivot definition. each
    // model now has its own pivot property:
    Log::info($device->pivot->duedate);
    Log::info($device->pivot->interval);
    // etc...
});

This is because of what pivot data represents: additional data that comes with a relationship definition. So created_at represents a statement such as:

Task 1 belongs to - among many - Device x since created_at.

Pivot data does not tell you anything about 1 specific model in of itself.

Problem 1:

It is an impossible to determine request to do Task::find(1)->pivot, since Task might have multiple belongsToMany relations, and the expression above does not explicitly specify which relation to access.

Problem 2: Imagine that Eloquent models would have a cure for problem 1 by implementing a method called pivotFor() that would let you do something like Task::find(1)->pivotFor('devices'). Now there is still information missing, because it is not known which of the related Device models we are trying to access the pivot data for (belongsToMany queries for a Collection of many models)

To fix problem 2 our hypothetical pivotFor method should accept a second argument of type int, whose value matches the id of the target Device. But that is just theory. Instead you can try to compose the behavior you're looking for from the facilities that eloquent models, relationships and collections already provide (it's possible), and create this implementation on a higher level (a Support class / repository, for example).

The Laravel documentation provides much more information about how all this works, and it's very likely you'll find a solution there.

in short: You'll need to determine:

  • Which relation you want to use on Task
  • Which model of the collection of related you want to get back

Since this is a higher order operation - the belongsToMany simply does not return single models - you should come up with your own implementation for what you try to achieve.

P.S.

Task::find(1)->devices returns you exactly Illuminate\Database\Eloquent\Collection|App\Device[].

You tried to access created_at on the pivot object. You should add that to the withPivot call, if that column indeed exists on the pivot table:

public function devices() 
{
    return $this
        ->belongsToMany('App\Device', 'xx', 'yy', 'zz')
        ->withPivot(
            'created_at', // Only if created_at exists on the pivot table!
            'id',
            'duedate',
            'interval',
            'reoccurdate',
            'completed',
            'state'
        )
        ->withTimestamps();
}

If you actually intended to get the created_at value for a Task or Device you should access that on the respective model instances directly:

$task = Task::find(1);

Log::info('Task was created at: ' . $task->created_at);

$task->devices->each(function (Device $device) {
    Log::info(
        'Device '
        . $device->id
        . ' was created at: '
        . $device->created_at
    );
});

Upvotes: 1

Related Questions