在加载时向Laravel / Eloquent模型添加自定义属性?


219

我希望能够在加载Laravel / Eloquent模型时为其添加自定义属性/属性,类似于使用RedBean的 $model->open()方法可以实现的那样

例如,目前,在我的控制器中,我有:

public function index()
{
    $sessions = EventSession::all();
    foreach ($sessions as $i => $session) {
        $sessions[$i]->available = $session->getAvailability();
    }
    return $sessions;
}

能够省略该循环并已设置并填充了“ available”属性将是很好的选择。

我尝试使用文档中描述的一些模型事件在对象加载时附加此属性,但到目前为止还没有成功。

笔记:

  • “可用”不是基础表中的字段。
  • $sessions是作为JSON对象作为API的一部分返回的,因此$session->available()无法在模板中调用类似内容

Answers:


316

问题是由ModeltoArray()方法忽略所有与基础表中的列不直接相关的访问器而导致的。

正如泰勒·奥特威尔(Taylor Otwell)在这里提到的:“这是故意的,并且出于性能考虑。” 但是,有一种简单的方法可以实现此目的:

class EventSession extends Eloquent {

    protected $table = 'sessions';
    protected $appends = array('availability');

    public function getAvailabilityAttribute()
    {
        return $this->calculateAvailability();  
    }
}

如果您添加了适当的访问器,则$ appends属性中列出的所有属性将自动包含在模型的数组或JSON形式中。

旧答案(适用于<4.08的Laravel版本):

我发现的最佳解决方案是重写toArray()方法并显式设置属性:

class Book extends Eloquent {

    protected $table = 'books';

    public function toArray()
    {
        $array = parent::toArray();
        $array['upper'] = $this->upper;
        return $array;
    }

    public function getUpperAttribute()
    {
        return strtoupper($this->title);    
    }

}

或者,如果您有很多自定义访问器,则遍历所有访问器并应用它们:

class Book extends Eloquent {

    protected $table = 'books';

    public function toArray()
    {
        $array = parent::toArray();
        foreach ($this->getMutatedAttributes() as $key)
        {
            if ( ! array_key_exists($key, $array)) {
                $array[$key] = $this->{$key};   
            }
        }
        return $array;
    }

    public function getUpperAttribute()
    {
        return strtoupper($this->title);    
    }

}

今日最佳问答。我正在研究如何实现此目标的不同实现,就在laravel.io上发布问题之前,我发现了这一点!好极了 !!!
Gayan Hewa 2014年

有没有办法在某些情况下不将它们添加到json中?
JordiPuigdellívol,2015年

3
通过关系调用模型时,这些海关属性似乎没有出现。(例如:Models \ Company :: with('people'))。任何想法?
安德鲁(Andrew)

@JordiPuigdellívol在Laravel 5中,您可以使用protected $hidden = []来添加要排除的列/访问器。
junkystu 2015年

124

该Laravel雄辩文档页面上最后一件事是:

protected $appends = array('is_admin');

它可以自动用于向模型中添加新的访问器,而无需进行诸如修改方法之类的任何其他工作::toArray()

只需创建getFooBarAttribute(...)访问器,然后将foo_barto 添加到 $appends数组即可。


4
有趣。自我的问题发布后(github.com/laravel/framework/commit/…),此功能已添加到Laravel中。对于使用v4.08或更高版本的任何人,这都是正确的答案。
coatesap

3
如果您正在使用关系为访问器生成内容,则此功能将不可用。在这种情况下,您可能不得不求助于该toArray方法。
gpmcadam 2015年

2
您所参考的文档似乎已移至此处:https : //laravel.com/docs/5.5/eloquent-serialization
mibbler

40

如果将您的getAvailability()方法重命名为getAvailableAttribute()您的方法成为访问器,则可以->available直接在模型上读取它。

文件:https//laravel.com/docs/5.4/eloquent-mutators#accessors-and-mutators

编辑:由于您的属性是“虚拟”的,因此默认情况下它不包含在对象的JSON表示中。

但我发现了这一点:调用-> toJson()时未处理自定义模型访问器?

为了强制将属性返回到数组中,请将其作为键添加到$ attributes数组中。

class User extends Eloquent {
    protected $attributes = array(
        'ZipCode' => '',
    );

    public function getZipCodeAttribute()
    {
        return ....
    }
}

我没有进行测试,但是对于您在当前设置中尝试来说应该是微不足道的。


它的作用->available与在$session对象上可用的作用相同,但是由于$sessions直接转换为JSON字符串(它是API的一部分),因此没有机会使用它。
coatesap

我不确定我是否了解您的工作原理。如果EventSession::all()从API返回JSON对象,那么您实际上并没有使用Laravel模型,对吗?抱歉,您对模型的实现方式感到困惑。
Alexandre Danault

EventSession是一个标准的雄辩对象(class EventSession extends Eloquent)。::all()仅作为示例。EventSession::find(170071)会是另一个。这些会在整个SessionController(SessionController extends \BaseController)的各个位置被调用,这些位置会通过诸如“ / sessions / 170071”的URL进行调用。
coatesap

我认为关键可能在于Eloquent的内置对象到JSON的转换。即使将自定义属性(例如)添加public $available到模型,转换对象时也不会显示。
coatesap

3
自2013年10月8日Laravel 4.0.8发行以来,此功能就可用。请参见官方文档:laravel.com/docs/eloquent#converting-to-arrays-or-json(查找protected $appends = array('is_admin');
Ronald Hulshof 2013年

23

我有一个类似的东西:我的模型中有一个属性图片,其中包含文件在Storage文件夹中的位置。图片必须以base64编码返回

//Add extra attribute
protected $attributes = ['picture_data'];

//Make it available in the json response
protected $appends = ['picture_data'];

//implement the attribute
public function getPictureDataAttribute()
{
    $file = Storage::get($this->picture);
    $type = Storage::mimeType($this->picture);
    return "data:" . $type . ";base64," . base64_encode($file);
}

1
在$ attributes和$ appends中应该是picture_data而不是pictureData。您也可以跳过$ attributes变量。
Madushan Perera

16

您可以使用setAttribute模型中的函数添加自定义属性


9

假设您的用户表中有2个名为first_name和last_name的列,并且您想检索全名。您可以使用以下代码实现:

class User extends Eloquent {


    public function getFullNameAttribute()
    {
        return $this->first_name.' '.$this->last_name;
    }
}

现在您可以获得全名:

$user = User::find(1);
$user->full_name;

7

步骤1:在$appends
步骤2中定义属性:为该属性定义访问器。
例:

<?php
...

class Movie extends Model{

    protected $appends = ['cover'];

    //define accessor
    public function getCoverAttribute()
    {
        return json_decode($this->InJson)->cover;
    }

3

在我的订阅模型中,我需要知道订阅是否已暂停。这是我做的

public function getIsPausedAttribute() {
    $isPaused = false;
    if (!$this->is_active) {
        $isPaused = true;
    }
}

然后在视图模板中,我可以 $subscription->is_paused用来获取结果。

getIsPausedAttribute是设置自定义属性的格式,

并用于is_paused获取或使用视图中的属性。


2

就我而言,创建一个空列并设置其访问器工作正常。我的访问者从dob列填充用户的年龄。toArray()函数也起作用。

public function getAgeAttribute()
{
  return Carbon::createFromFormat('Y-m-d', $this->attributes['dateofbirth'])->age;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.