Laravel模型动态属性

6
我想请问如何在模型类上创建动态属性。假设我有一个如下所示的表结构。请看以下代码。
Schema::create('materials', function (Blueprint $table) {
    $table->increments('id');
    $table->string('sp_number');
    $table->string('factory');
    $table->text('dynamic_fields')->comment('All description of the material will saved as json');
    $table->timestamps();
});

我的表结构中有一个名为“dynamic_fields”的列,用于存储字段的JSON字符串。以下是JSON结构的示例:

[  
   {  
      "name":"COLOR WAY",
      "value":"ASDFF12"
   },
   {  
      "name":"DESCRIPTION",
      "value":"agg2sd12"
   },
   {  
      "name":"REF NUM",
      "value":"121312"
   }
]

我想要访问我动态字段中的一个字段,例如"颜色方案"。

在我的模型中,我希望像这样访问动态字段中的"颜色方案"字段。

$material->color_way;

有人能给我展示如何做吗?


1
嗨,凯文。首先,如果您使用较新版本的mysql并将json存储在其中,则可以使用$table->json。不需要是文本。然后,根据您的示例,您正在尝试从json中访问值,而不是键。 Eloquent模型中的访问器函数可以编写为getColorWayAttribute(),这将使您可以使用$material->color_way - nakov
1
你是否总是提前知道动态属性是什么?它们的数量很少吗?如果是这样,您可以为它们定义访问器方法,这将允许您以所需的方式访问它们。如果不是,那么还有另一种方法可供选择。 - Jonathon
3个回答

7
如果您事先知道只会有一些特定的动态字段,那么您可以选择为它们创建访问器方法。例如,您可以将以下内容添加到您的模型中:
// Dynamic fields must be cast as an array to iterate through them as shown below
protected $casts = [
    'dynamic_fields' => 'array'
];

// ...

public function getColorWayAttribute()
{
    foreach ($this->dynamic_fields as $field) {
        if ($field['name'] === 'COLOR WAY') {
            return $field['value'];
        }
    }

    return null;
}

这将使您能够做到以下事情:
$colorWay = $material->color_way;

或者,如果你的dynamic_fields组合不受限制,可能会有大量组合或者希望有更多灵活性来添加更多并使其可访问,则可以覆盖Laravel模型类的getAttribute方法。

// Dynamic fields must be cast as an array to iterate through them as shown below
protected $casts = [
    'dynamic_fields' => 'array'
];

// ...

public function getAttribute($key)
{
    $attribute = parent::getAttribute($key);

    if ($attribute === null && array_key_exists('dynamic_fields', $this->attributes)) {
        foreach ($this->dynamic_fields as $dynamicField) {
            $name = $dynamicField['name'];
            if (str_replace(' ', '_', mb_strtolower($name)) === $key) {
                return $dynamicField['value'];
            }
        }
    }

    return $attribute;
}

这种方法调用了Laravel的getAttribute实现,首先检查是否定义了实际属性或者是否为该属性定义了访问器(如我第一个建议中所述),然后检查基本模型类中是否存在具有该名称的方法,最后尝试加载关系(如果您已经定义了关系)。当每个方法都失败时(返回null),我们将检查模型中是否有一个dynamic_fields属性。如果有,我们循环遍历每个动态字段(假设您的dynamic_fields被转换为array),然后将定义的动态字段名称转换为小写并将空格替换为下划线。然后,我们最终检查我们刚刚派生的名称是否与提供的键匹配,如果匹配,则返回值。如果不匹配,将返回原始的$attribute,其将为null。这样可以使您像在类中定义属性一样获取任何动态字段。
$colorWay = $material->color_way;
$description = $material->description;
$refNum = $material->ref_num;

请注意:我没有测试过这段代码,可能存在一两个问题。试试看是否适用于您的情况。还要注意,这仅适用于获取动态字段,设置它们需要覆盖另一个方法。

0

尝试在您的模型中使用此代码:

protected $casts = [
    'dynamic_fields' => 'array',
];

public function setAttribute($key, $value)
{
    if (!$this->getOriginal($key)) {
        $this->dynamic_fields[$key] = $value;
    }

    parent::setAttribute($key, $value);
}

public function getAttribute($key)
{
    if (!$this->getOriginal($key)) {
        return $this->dynamic_fields[$key]
    }

    parent::getAttribute($key);
}

0
在这个例子中,你可以从动态模型中获取动态列,以及它的模型关系
1)首先,你需要在模型中定义一个表范围。
  private  $dynamicTable='';

  public function scopeDefineTable($query,$tableName)
{
    if( $tableName )
    {
        $this->dynamicTable= $tableName;
    }
    else
    {
        $this->dynamicTable= "deviceLogs_".date('n')."_".date('Y');
    }
    $query->from( $this->dynamicTable );

   $this->table=$this->dynamicTable; # give dynamic table nam to this model.
}


  public function  scopeCustomSelect( $query ,$items=[])
{
    $stu_class_col=['id as stu_class_id','std_id']; // Required else retional model will not retun data. here id and std_id is primary key and foreign key.
    $stu_doc_col=['id as stu_doc_id','std_id'];// Required else retional model will not retun data. here id and std_id is primary key and foreign key.

    foreach ( $items as $col)
    {
        if(  Schema::hasColumn('student_information', $col ))
        {
          $stu_info_col[]= $col ;
        }
        elseif (  Schema::hasColumn('student_class',$col))
        {
            $stu_class_col[]= $col ;
        }
        elseif (  Schema::hasColumn('student_image',$col))
        {
            $stu_doc_col[]= $col ;
        }
    }

   // converting array to string for bind column into with relation...
    $stu_class_col_string =  implode(',',$stu_class_col);
    $stu_doc_col_string =  implode(',',$stu_doc_col); 

    return $colQuery = $query->select($stu_info_col)
                    ->with(["student_class:$stu_class_col_string", "studentImage:$stu_doc_col_string"]);
}

使用这个,你也可以从Rational Model获取数据... 从控制器开始
    $studentInfo =  Student::whereHas("student_class",function($q) use($req){
                                   $q->where("std_session",$req->session_code);
                                   $q ->where("std_class",$req->class_code);
                                   $q ->where("std_section",$req->std_section); })
                           ->customSelect($fields['dataList'])
                           ->get();



here I am not using dynamic Model Scope. only Dynamic SustomSelect scope..

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接