我想我会问这个问题,因为Laravel是我遇到的最优雅的框架,我想知道是否有“更好的方式”来做到这一点。
我有一个记录账本的系统,这样:
class Chapter extends Model
{
public function book()
{
return $this->belongsTo('\App\Book');
}
}在系统中,有许多其他模型是从"Book“扩展而来的,如”小说“、”传记“等。有没有办法让Eloquent在给定正确信息的情况下为我提供正确转换的对象(即命名空间类)?目前,我正在使用https://gist.github.com/borzilleri/960035中的函数获取这本书并对其进行造型,该函数可以工作,但感觉不是很“整洁”。
发布于 2019-03-28 21:29:05
这应该归功于Joshua Dwire,因为他让我走上了这个解决方案的道路。我对他提到的扩展标准BelongsTo类并让它为我工作很感兴趣。理想情况下,我希望能够调用自定义关系:
$this->belongsToBook('\App\Book');并使该函数返回一个正确转换的对象。
在浏览代码时,我发现负责返回关系的是Model使用的特征HasRelationship。通过改变这种关系,我们可以改变实现,从而改变返回的对象。
我还想复制Laravel使用的相同方法,以便在我自己的应用程序中模仿它。
考虑到所有这些,第一步是创建一个新的特征HasBookRelationship,可以在模型中使用它来处理对$this->belongsToBook('\App\Book')的调用
trait HasBookRelationship
{
public function belongsToBook($related, $foreignKey = null, $ownerKey = null, $relation = null)
{
if (is_null($relation)) {
$relation = $this->guessBelongsToRelation();
}
$instance = $this->newRelatedInstance($related);
if (is_null($foreignKey)) {
$foreignKey = \Str::snake($relation).'_'.$instance->getKeyName();
}
$ownerKey = $ownerKey ?: $instance->getKeyName();
//We change the return relationship here
**return new BelongsToBook(
$instance->newQuery(), $this, $foreignKey, $ownerKey, $relation
);**
}
}这只是从HasRelationships特征中现有的belongsTo方法复制而来。这里的关键是我们将返回一个自定义关系BelongsToBook,并使用它覆盖返回的内容。方法的最后一行被更改为返回我们想要的关系类。
我们使用的类是从BelongsTo扩展而来的,但我们更改了get方法,以便在返回对象之前对其进行强制转换。
class BelongsToBook extends BelongsTo
{
public function __construct(Builder $query, Model $child, $foreignKey, $ownerKey, $relationName)
{
parent::__construct($query, $child, $foreignKey, $ownerKey, $relationName);
}
public function get($columns = ['*'])
{
$objs = $this->query->get($columns);
//iterate over the collated objects...
$objs->transform(function($item)
{
//..and return a cast object with whatever method you want
return castTheCorrectObject($item);
});
return $objs;
}
}castTheCorrectObject可以是您喜欢的任何类型转换函数,可以设置为关系中的帮助器或另一个方法。
一旦这些都设置好了,我们就可以在我们自己的模型中使用它:
class Author extends Model
{
use HasBookRelationship;
public function books()
{
return $this->belongsToBook('\App\Book');
}
}这将返回正确转换的对象的集合,并维护关系。
不过,有一件事确实让我困惑不已。我在BelongsToBook类中覆盖的方法是get(),而不是Joshua建议的getResults()。get()是在Relation中定义的,并由BelongsTo继承,而as getResults()是在BelongsTo中定义的。我不确定getResults()和get()之间的区别是什么,也不知道为什么我必须覆盖get()而不是getResults()。如果有人能提供任何线索,我将不胜感激。
发布于 2019-03-28 00:17:03
我可以在这里看到一些不同的选项。一种是像这样编写你的类:
class Chapter extends Model
{
public function book()
{
return $this->belongsTo('\App\Book');
}
public function biography()
{
return $this->belongsTo('\App\Biography')->where('type', 'biography');
}
public function novel()
{
return $this->belongsTo('\App\Novel')->where('type', 'novel');
}
}然后你需要提前知道它是哪种类型的书。另一种方法是这样做:
class Chapter extends Model
{
protected function parent_book()
{
return $this->belongsTo('\App\Book');
}
public function getBookAttribute()
{
$book = $this->parent_book;
if (!$book) return $book; // No related book.
if ($book->type == 'novel') return (Novel)$book;
if ($book->type == 'biography') return (Biography)$book;
return $book;
}
}你仍然需要自己做所有的转换,但至少所有的转换都在一个地方,并且对应用程序的其余部分是透明的,因为它仍然可以在第二个解决方案中引用$chapter->book,如果你设置了$chapter->book = new Book(),你还需要确保创建一个setBookAttribute()函数。
一种更复杂的可能性是创建您自己的自定义关系类型,方法是扩展BelongsTo类,并在返回结果之前将getResults()覆盖到转换。从外部看,这是非常透明的,并且允许您仍然调用$章节->book()并将其视为一种关系。
https://stackoverflow.com/questions/55381463
复制相似问题