Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using accessors with pivot table fields in Eloquent

I've currently got a few models that have many to many relationships via laravel. here's the structure:

users
    id
    username
    ...

games
    id
    title
    ...

game_user
    game_id
    user_id
    system

Now, my models look a little like this:

<?php

class Game extends Eloquent
{
    /**
     * A game is owned by many users
     *
     * @return mixed
     */
    public function user()
    {
        return $this->belongsToMany('User')->withPivot('system');
    }


<?php

class User extends Eloquent
{
    /**
     * A user has many games.
     *
     * @return mixed
     */
    public function games()
    {
        return $this->belongsToMany('Game')->withPivot('system');
    }

Now, this all works fine. However, I wish to use a mutator upon the system field from the pivot table. I can't find any documentation on this and the following (in both the User and Game models) does not work:

public function getSystemAttribute($val)
{
    return $val.' Testing';
}
like image 503
Euan T Avatar asked Jun 19 '13 16:06

Euan T


4 Answers

I haven't tested but this should work.

public function getSystemAttribute($value)
{
    return $this->pivot->system . ' Testing';
}

...

foreach ($user->games as $game)
{
    echo $game->system;
}

The value getting passed in is being pulled from the models attributes. There is no system attribute so you should get NULL. We can just ignore that and pull the value from the pivot table directly.

like image 68
Collin James Avatar answered Nov 01 '22 00:11

Collin James


You need to define an accessor for the pivot's system property and add the attribute name to the appends property on the model. The whole procedure follows

1) Define an accessor in the parent Model from where we want to access the attribute:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Game extends Model
{
    /**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    public function getSystemAttribute() // the name can be anything e.g getFooAttribute
    {
        return $this->pivot->system;
    }
}

2) (to avoid getting NULL!) Add the attribute name to the appends property on the model (see here).

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    /**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    protected $appends = ['system']; 
}

3) Now you can access that new created system attribute from the parent Model:

$user = App\User::find($id);
foreach ($user->games as $game)
{
    echo $game->system;
}

Of course, like with every accessor, you might also "alter" the value of the pivot table before accessing it, for instance:

public function getSystemAttribute()
{
    return $this->pivot->foo .'Hello word!'; //append the ""Hello word!" string to the end of the `foo` attribute value
}

Finally notice that the name of the accessor can be anything like getFooAttribute, getBarAttribute, getBarBazAttribute. In that case the append would be protected $appends = ['foo']; and you could again access the pivot's foo property via the parent Model like $game->foo

like image 24
ira Avatar answered Oct 31 '22 23:10

ira


I found this question interesting, so I started playing around with the code.

If you need to access a specific entry, here's my solution:

public function getSystemAttribute($game){
    foreach($this->games as $value){
        if($value->id==$game->id){
            $value->pivot->system = 'foo';
            $value->pivot->save();
        }
    }
}

which you can simply call with:

$user->getSystemAttribute($game)

Regarding the accessor, I couldn't find any other way without itarating the collection. There are prettier ways of iterating the collection, but this was not the scope of this question. From the documentation, you can see how any attribute of the pivot table can be accessed.

Hope this helps

like image 2
clod986 Avatar answered Nov 01 '22 01:11

clod986


If you would like to be able to insert your mutated system attribute on your Response's (JSON, etc.), then you should add the following to both Models (game + user).

protected $appends = array('system');
like image 1
Ronald Hulshof Avatar answered Nov 01 '22 00:11

Ronald Hulshof