通过口才转换为继承的类

时间:2019-03-27 15:54:01

标签: php laravel eloquent

我想问这个问题,因为Laravel是我遇到过的最优雅的框架,想知道这样做是否有“更漂亮的方式”。

我有一个记录书的系统,这样:

class Chapter extends Model
{
    public function book()
    {
        return $this->belongsTo('\App\Book');
    }
}

在系统中,还有许多其他模型都从“书”扩展而来,例如“小说”,“传记”等。是否有Eloquent提供给我正确信息(正确信息)的对象(例如命名空间类)?目前,我正在使用https://gist.github.com/borzilleri/960035中的函数来获得这本书并进行转换,该函数可以工作,但感觉不太“整洁”。

2 个答案:

答案 0 :(得分:2)

我可以在这里看到一些不同的选项。一种方法是像这样编写您的课程:

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()覆盖到强制类型来创建自己的自定义关系类型。从外部看,这将是非常透明的,并使您仍然可以调用$ chapter-> book()并将其视为一种关系。

答案 1 :(得分:1)

这应该归因于Joshua Dwire,因为他让我踏上了解决之道。他对扩展标准BelongsTo类并使其对我有用的介绍引起了我的兴趣。理想情况下,我希望能够调用自定义关系:

$this->belongsToBook('\App\Book');

然后该函数返回正确投射的对象。

遍历代码,我发现HasRelationship使用的特征Model负责返回关系。通过更改该关系,我们可以更改实现,因此也可以更改返回的对象。

我还想复制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
        );**
    }
}

这只是从belongsTo特征中现有的HasRelationships方法复制而来。这里的关键是,我们将返回一个自定义关系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继承,其中getResults()BelongsTo中定义。我不确定getResults()get()之间的区别是什么,也不知道为什么我必须重写get()而不是getResults()。如果任何人都可以阐明任何想法,将不胜感激。