har*_*ryg 5 php laravel eloquent laravel-5
通常情况下,某个雄辩模型的关系未设置(即在书籍表中,author_id
为空),因此调用类似$ model-> relation的东西会返回null.
例如,说Book模型有一个我可能想做的author()(hasOne)关系
$author = Book::find(1)->author->name;
Run Code Online (Sandbox Code Playgroud)
如果第1册没有设置作者,它将抛出"试图获取非对象属性"错误.有没有办法避免这种情况并默认为空白,Author
所以name
无论是否为特定模型设置了关系,我总能调用它?
基本上我想在调用其他方法/属性之前避免条件检查是否$book->author
是实际的Author
.如果未设置关系,它应默认为新的Author实例.
我尝试过类似的东西:
public function getAuthorAttribute($author)
{
return $author ?: new Author;
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用; $author
即使在模型上设置,也会传入null.大概是因为它是一种关系,而不是一本书的直接属性.我需要类似的东西
public function getAuthorAttribute()
{
return $this->author()->first() ?: new Author;
}
Run Code Online (Sandbox Code Playgroud)
这似乎非常优雅,似乎它会覆盖任何急切的负载,导致性能不佳.
从Laravel 5.3.23开始,现在有一种内置的方法可以实现这一目标(至少对于HasOne
关系而言).一种withDefault()
方法被添加到HasOne
关系中.对于Book
/ Author
example,您的代码如下所示:
public function author() {
return $this->hasOne(Author::class)->withDefault();
}
Run Code Online (Sandbox Code Playgroud)
Author
如果在数据库中找不到记录,则此关系现在将返回相当空(键设置)模型.另外,如果你想用一些额外的数据填充你的空模型,你可以传入一个属性数组,或者你可以传入一个Closure,它返回你想要的默认设置(没有)成为一个Author
模特).
直到有一天它进入文档,有关更多信息,您可以查看与更改相关的拉取请求:16198和16382.
在撰写本文时,这只是为了这种HasOne
关系而实施的.它最终可能会迁移到BelongsTo
,MorphOne
和MorphTo
关系,但我不能肯定地说.
我知道没有内置的方法可以做到这一点,但有一些解决方法.
正如您所知,使用访问器的问题在于,$value
传递给访问器的问题始终是null
,因为它是从模型上的属性数组中填充的.此属性数组不包括关系,无论它们是否已加载.
如果您想尝试使用访问器解决此问题,您只需忽略传入的任何值,并自行检查关系.
public function getAuthorAttribute($value)
{
$key = 'author';
/**
* If the relationship is already loaded, get the value. Otherwise, attempt
* to load the value from the relationship method. This will also set the
* key in $this->relations so that subsequent calls will find the key.
*/
if (array_key_exists($key, $this->relations)) {
$value = $this->relations[$key];
} elseif (method_exists($this, $key)) {
$value = $this->getRelationshipFromMethod($key);
}
$value = $value ?: new Author();
/**
* This line is optional. Do you want to set the relationship value to be
* the new Author, or do you want to keep it null? Think of what you'd
* want in your toArray/toJson output...
*/
$this->setRelation($key, $value);
return $value;
}
Run Code Online (Sandbox Code Playgroud)
现在,在访问器中执行此操作的问题是您需要为每个模型上的每个hasOne/belongsTo关系定义一个访问器.
第二个较小的问题是访问者仅在访问属性时使用.因此,例如,如果您急切地加载关系,然后dd()
或toArray
/ 或toJson
模型,它仍然会显示null
相关性,而不是空的作者.
第二个选项,而不是使用属性访问器,将覆盖一些方法Model
.这解决了使用属性访问器的两个问题.
您可以创建自己的基Model
类来扩展Laravel Model
并覆盖这些方法,然后所有其他模型将扩展您的基Model
类,而不是Laravel的Model
类.
要处理急切加载的关系,您需要覆盖该setRelation()
方法.如果使用Laravel> = 5.2.30,这也将处理延迟加载的关系.如果使用Laravel <5.2.30,您还需要覆盖getRelationshipFromMethod()
延迟加载关系的方法.
MyModel.php
class MyModel extends Model
{
/**
* Handle eager loaded relationships. Call chain:
* Model::with() => Builder::with(): sets builder eager loads
* Model::get() => Builder::get() => Builder::eagerLoadRelations() => Builder::loadRelation()
* =>Relation::initRelation() => Model::setRelation()
* =>Relation::match() =>Relation::matchOneOrMany() => Model::setRelation()
*/
public function setRelation($relation, $value)
{
/**
* Relationships to many records will always be a Collection, even when empty.
* Relationships to one record will either be a Model or null. When attempting
* to set to null, override with a new instance of the expected model.
*/
if (is_null($value)) {
// set the value to a new instance of the related model
$value = $this->$relation()->getRelated()->newInstance();
}
$this->relations[$relation] = $value;
return $this;
}
/**
* This override is only needed in Laravel < 5.2.30. In Laravel
* >= 5.2.30, this method calls the setRelation method, which
* is already overridden and contains our logic above.
*
* Handle lazy loaded relationships. Call chain:
* Model::__get() => Model::getAttribute() => Model::getRelationshipFromMethod();
*/
protected function getRelationshipFromMethod($method)
{
$results = parent::getRelationshipFromMethod($method);
/**
* Relationships to many records will always be a Collection, even when empty.
* Relationships to one record will either be a Model or null. When the
* result is null, override with a new instance of the related model.
*/
if (is_null($results)) {
$results = $this->$method()->getRelated()->newInstance();
}
return $this->relations[$method] = $results;
}
}
Run Code Online (Sandbox Code Playgroud)
book.php中
class Book extends MyModel
{
//
}
Run Code Online (Sandbox Code Playgroud)
Ale*_*dis -1
您可以使用模型工厂来实现这一点。
在 ModelFactory.php 中定义一个作者工厂
$factory->define(App\Author::class, function (Faker\Generator $faker) {
return [
'name' => $faker->firstName, //or null
'avatar' => $faker->imageUrl() //or null
];
});
Run Code Online (Sandbox Code Playgroud)
为所有需要的属性添加值我使用的是 Faker 的虚拟值,但你可以使用任何你想要的值。
然后在你的书籍模型中,你可以返回一个 Author 的实例,如下所示:
public function getAuthorAttribute($author)
{
return $author ?: factory(App\Author::class)->make();
}
Run Code Online (Sandbox Code Playgroud)