I have the following models.
Player
- (
hasMany(Monster::class)
)
- (
Monster
hasOne(MonsterSpecies::class,'id','species_id')
hasOne(MonsterColor::class,'id','color_id')
)
MonsterSpecies
MonsterColor
- (Both pretty much empty, just
public $timestamps = false;
)
- (Both pretty much empty, just
Then, after seeding, in php artisan tinker
I select one player:
$player = Player::all()->first();
It works. Then I check the monsters.
Illuminate\Database\Eloquent\Collection {#3561
all: [
App\Models\Monster {#3569
id: 1,
created_at: "2021-12-09 16:39:29",
updated_at: "2021-12-09 16:39:29",
name: "Alberto Mills",
level: 17,
currHealth: 68,
maxHealth: 76,
strength: 42,
defense: 29,
movement: 13,
species: 28,
color: 34,
player_id: 1,
},
App\Models\Monster {#4505
id: 2,
created_at: "2021-12-09 16:39:29",
updated_at: "2021-12-09 16:39:29",
name: "Darlene Price",
level: 9,
currHealth: 16,
maxHealth: 32,
strength: 44,
defense: 19,
movement: 61,
species: 28,
color: 34,
player_id: 1,
},
],
}
Then $player->monster->get(0)->color;
App\Models\MonsterColor {#4508
id: 34,
name: "Red_Blue",
}
Now I believe I can add getSpeciesAttribute()
and getSpeciesAttribute()
to directly return the name or maybe do something like:
public function getSpeciesAttribute($value)
{
$colors = explode("_",$value->name); // I don't know if this is how I get the name
$out = "Your monster's color is ";
if (count($colors) > 1) {
$out .= "a mesh of " . implode(", ",array_slice($colors, 0, -1)) . " and ";
}
$out .= array_pop($colors);
return $out;
}
But I don't know how to access the name
attribute of MonsterColor
.
Edit:
Here's the Monster, MonsterColor and MonsterSpecies models.
class Monster extends Model
{
use HasFactory;
protected $fillable = [
'name',
'level',
'currHealth',
'maxHealth',
'strength',
'defense',
'movement',
'species',
'color'
];
public function player()
{
return $this->belongsTo(Player::class);
}
public function species()
{
return $this->hasOne(MonsterSpecies::class,'id','species_id');
}
public function color()
{
return $this->hasOne(MonsterColor::class,'id','color_id');
}
public function getSpeciesAttribute()
{
return $this->species->name;
}
public function getColorAttribute()
{
return $this->color->name;
}
}
class MonsterColor extends Model
{
use HasFactory;
public $timestamps = false;
}
class MonsterSpecies extends Model
{
use HasFactory;
public $timestamps = false;
}
CodePudding user response:
The accessor getColorAttribute()
creates a "virtual" color
property on the model class. But you also have a relationship method called color()
. Relationship methods also create virtual properties, which you are trying to access with $this->color
.
So the problem is simply one of colliding names. Easiest solution would be to rename the accessor to something else.
The problem is the same with your other accessor, and also they don't accept parameters; they're accessed as properties so there's no way to pass one.
public function getColorNameAttribute()
{
return $this->color->name;
}
public function getSpeciesDescriptionAttribute()
{
$colors = explode("_", $this->color->name);
return sprintf(
"Your monster's color is %s",
implode(" and ", $colors)
);
}
Now you can access these via $player->monsters[0]->color_name
or $player->monsters[0]->species_description
.
A couple of other notes:
- you shouldn't have
color
andspecies
in$fillable
as they aren't database columns - you don't need to declare column names in relationship methods (e.g.
$this->hasOne(MonsterColor::class,'id','color_id')
) unless they are non-standard – yours are not - if a relationship returns a collection it should be named with a plural (i.e. not
$player->monster
as in your question) - do look into eager loading of relationships.