Laravel 通过 hasOne 获取属性

Posted

技术标签:

【中文标题】Laravel 通过 hasOne 获取属性【英文标题】:Laravel going through hasOne to get attributes 【发布时间】:2022-01-14 14:31:48 【问题描述】:

我有以下型号。

Player (hasMany(Monster::class)) Monster hasOne(MonsterSpecies::class,'id','species_id') hasOne(MonsterColor::class,'id','color_id')) MonsterSpecies MonsterColor (两者几乎都是空的,只是public $timestamps = false;

然后,播种后,在php artisan tinker 中选择一名玩家:

$player = Player::all()->first();

它有效。然后我检查怪物。

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,
       ,
     ],
   

然后$player->monster->get(0)->color;

 App\Models\MonsterColor #4508
     id: 34,
     name: "Red_Blue",
   

现在我相信我可以添加 getSpeciesAttribute()getSpeciesAttribute() 来直接返回名称,或者执行以下操作:

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;

但我不知道如何访问MonsterColorname 属性。

编辑:

这是 Monster、MonsterColor 和 MonsterSpecies 模型。

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;

【问题讨论】:

getSpeciesAttribute 是什么型号的? @Andy on Monster,用 Monster 类更新了 OP。 看起来您在编辑中的内容应该可以工作..您是否遇到某种错误或意外行为?如果我不得不猜测,我认为您会遇到具有相同名称的关系和属性的问题。 可能对他有用,他想要我认为加入 laravel 的所有属性我猜Monster::with("player")->with("species")->with("color")->get(); Player::with("monster",function($e) $e->with("species")->with("color")->get(); ); 【参考方案1】:

访问器getColorAttribute() 在模型类上创建一个“虚拟”color 属性。但是您也有一个名为color() 的关系方法。关系方法还会创建虚拟属性,您尝试使用 $this->color 访问这些属性。

所以问题只是名称冲突之一。最简单的解决方案是将访问器重命名为其他名称。

问题与您的其他访问器相同,并且它们不接受参数;它们作为属性访问,因此无法传递一个。

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)
    );

现在您可以通过$player->monsters[0]->color_name$player->monsters[0]->species_description 访问这些内容。


其他几点说明:

$fillable 中不应包含 colorspecies,因为它们不是数据库列 你不需要在关系方法中声明列名(例如$this->hasOne(MonsterColor::class,'id','color_id')),除非它们是非标准的——你的不是 如果一个关系返回一个集合,它应该用复数命名(即不是$player->monster,如您的问题) 请查看eager loading 的关系。

【讨论】:

创建怪物时玩家可以选择颜色和物种,它们是数据库列。如果我从关系方法中删除列名,我会得到 Column not found: 1054 Unknown column 'monster_colors.monster_id' in 'where clause' (SQL: select * from monster_colors` 其中 monster_colors.monster_id = 1 和 monster_colors.monster_id 不是空限制 1. I renamed to getSpeciesNameAttribute` 和 getColorNameAttribute 和我可以通过$player->monsters->get(0)->colorName$player->monsters->get(0)->color_name 访问它们。 问题,将关系方法MonsterSpeciesMonsterColor 以及访问器getSpeciesAttributegetColorAttribute 设置为更简洁/更可取,从那时起$monster->MonsterSpecies 实际上返回一个MonsterSpecies model 和 $monster->species 只返回名称?? 那个错误意味着你的种族和肤色关系应该是belongsTo,而不是hasOne。见laravel.com/docs/8.x/eloquent-relationships#one-to-many。将它们命名为对您最有意义的名称,没有硬性规定。但是有一些约定,例如访问属性时,通常使用snake case ($monster->monster_species) 为了清楚起见,我还将在MonsterSpeciesMonsterColor 模型上添加public function monsters() return $this->hasMany(Monster::class);`。即使从未使用过,它也可以是一个很好的健全性检查。在关系的一侧看到hasMany,就知道另一侧应该是belongsTo

以上是关于Laravel 通过 hasOne 获取属性的主要内容,如果未能解决你的问题,请参考以下文章

Laravel hasOne 通过数据透视表

Laravel一对一关系显示非对象属性

Laravel 中 BelongsTo 和 HasOne 有啥区别

理解 Laravel 中的 hasOne() 和 belongsTo() 函数

我应该在 Laravel 中使用 belongsTo 还是 hasOne?

Laravel 关系:hasOne vs belongsToMany