如何在不获取模型内容的情况下从相关模型追加Eloquent属性?


How can I append an Eloquent property from a related model without getting the model's contents?

我有两个雄辩的模型:

EloquentUser

SharedEvents

它们都由user_id

关联

我正在尝试在SharedEvents模型中设置和附加属性,该模型将附加与事件共享的用户的full_name。

为了可读性,我只包括了类

的追加组件
class SharedEvents extends Model {
protected $appends      = ['fullName'];
/**
 * @return BelongsTo
 */
public function user() : BelongsTo {
    return $this->belongsTo('Path'To'EloquentUser', 'shared_with_id', 'user_id');
}
    /**
 * @return mixed
 */
public function getFullNameAttribute(){
    return $this->user->user_full_name;
}

不幸的是,当我运行这个时,当我只想要全名时,我得到了全名和整个用户模型。

是否有办法避免附加用户模型的内容?

感觉就像您试图在SharedEvent模型中从EloquentUser模型中获得一级公民的列。你离成功越来越近了,但是考虑一下……

当处理关系时,这是显式的好方法:

假设user_full_nameUser模型上的访问器:

// EloquentUser.php
// This will automatically add your accessor to every query 
// as long as you select the columns the accessor is made 
// up of
protected $appends = ['user_full_name'];
/**
 * User Full Name Accessor
 * @return string
 */
public function getUserFullNameAttribute()
{
    return $this->first_name . ' ' . $this->last_name;
}
// SharedEvent.php
/**
 * A SharedEvent belongs to an Eloquent User
 * @return BelongsTo
 */
public function user() : BelongsTo 
{
    return $this->belongsTo('Path'To'EloquentUser', 'shared_with_id', 'user_id');
}
// Somewhere in your controller or wherever you want to access the data
$sharedEvents = SharedEvent::with(['user' => function ($query) {
    $query->select('user_id', 'first_name', 'last_name');
}])->where(...)->get(['shared_with_id', ...other columns]); // you must select the related column here

这应该让你最接近你想要的,但有几件事你应该知道:

  1. 如果user_full_name是一个访问器,您需要选择组成该访问器的所有列(如上所述)
  2. 必须选择相关键(EloquentUser中的user_id, SharedEvent中的shared_with_id)
  3. $appendsEloquentUser中是必要的,因为你不能直接在闭包中为子查询添加访问器。

试着习惯使用闭包作为关系中的第二个参数。这是在加载关系时精确选择哪些列的最好方法——Eloquent让你很容易偷懒,只需要做:

SharedEvent::with('user')->get();

,正如你所看到的,它只会对SharedEventuser的关系执行select *

在处理使用关系的复杂查询时,我注意到的另一件事是,你可以很快达到一个点,感觉你在与框架作斗争。这通常是考虑简化而不是仅仅使用原始SQL的信号。雄辩是强大的,但只是编程工具带中的另一个工具。请记住,您还可以使用其他工具。

我遇到了同样的问题,我的第一个想法是在获取SharedEvent时显式地隐藏(整个)关联模型。

在您的例子中,它看起来像:

class SharedEvents extends Model {
   protected $appends  = ['fullName'];
   protected $hidden   = ['user'];
   ...
}

我实际上决定不这样做,因为我要返回很多其他附加的模型,所以我决定附加整个用户,但我测试了它,它工作。

仅供参考,我不确定是否差异是由于旧版本的Laravel,但Eloquent实际上期望$appends属性是蛇形的(https://laravel.com/docs/5.6/eloquent-serialization#appending-values-to-json):

)

请注意,属性名通常以"蛇形大小写"引用,即使访问器是使用"驼形大小写"定义的

你能试试吗?

public function user() : BelongsTo {
    return $this->belongsTo('Path'To'EloquentUser', 'shared_with_id', 'user_id')->select('fullName','user_id');
}

在做了一些研究和实验之后,看起来并没有办法在Eloquent中做到这一点。不幸的是,我最终采用的解决方案是运行一个额外的查询。

/**
 * This is inefficient, but I could not find a way to get the full name
 * attribute without including all of user.
 *
 * @return string
 */
public function getFullNameAttribute(){
    return EloquentUser::select('user_full_name')
                        ->where('user_id', $this->shared_with_id)
                        ->first()
                        ->user_full_name;
}