Laravel 5.3 多态关系

Posted

技术标签:

【中文标题】Laravel 5.3 多态关系【英文标题】:Laravel 5.3 polymorphic relationship 【发布时间】:2017-05-13 16:36:46 【问题描述】:

我正试图了解我在 Laravel 5.3 中的第一个多态关系。

我试图实现的关系是“社论”将有许多“元素”,每个元素将是它自己的模型并有一个顺序。

例如,我将有一个“ElementText”、“ElementImage”、“ElementButton”模型,然后一个“Editorial”将分配给它的各种元素。

这就是我认为我需要多态关系的原因。我不希望“社论”模型中的每种元素类型都有多个关系,我希望能够获取所有“元素”而不管其类型如何并按顺序排列。

我认为理想的解决方案是拥有一个带有类似列的“edittorial_element”数据透视表

editorial_id - integer
order - integer
element_id - integer
element_type - string

类似于 laravel 文档中的 morphToManyexample,但在该示例中,您需要指定要变形的模型。

所以我也想知道我是否应该在我的社论模型中使用 morphTo() 方法。

public function elements() 
    return $this->morphTo();

但我认为这意味着我必须将 _type_id 列添加到我的社论表中,这将是向后的(每个社论允许一个元素)

知道我是否可以设置我需要的关系还是我接近它错了?

【问题讨论】:

【参考方案1】:

我认为多态关系在这里不太适合。我可能会这样处理它。

editorials
 - id
 - title
 - etc...

elements
 - editorial_id
 - elements_id
 - elements_type

elements_image
 - id
 - url

elements_text
 - id
 - text

那么你的模型就可以了。

class Editorial extends Model 

    // ...

    public function elements() 
        return $this->hasMany(\Element::class);
    

    // ...



class Element extends Model 

    // ...

    public function editorial() 
        return $this->belongsTo(\Editorial::class);
    

    public function image() 
        return $this->belongsTo(\ElementImage::class, 'elements_id');
    

    public function text() 
        return $this->belongsTo(\ElementText::class, 'elements_id');
    

    public function content() 
        $type = $this->elements_type;
        return $this->$type();
    

    // ...



class ElementImage extends Model 

    // ...

    public function element() 
        return $this->hasOne(\Element::class, 'elements_id');
    

    // ...



class ElementText extends Model 

    // ...

    public function element() 
        return $this->hasOne(\Element::class, 'elements_id');
    

    // ...


也许您可以让所有元素实现一个接口,该接口在所有元素上强制使用render() 方法,然后您可以在视图中执行此操作:

@foreach($editorial->elements as $element)
     $element->content->render() 
@endforeach

render 方法可以负责格式化和输出内容。

这只是理论,我很想看看你是否可以让它工作,因为这也是我之前尝试解决的一个问题。如果您有任何更正,或者您认为我的想法很愚蠢,请告诉我。

【讨论】:

明白,我认为“元素”模型需要手动更新并且不再接受使用这种方法的 attach()、detach() 方法是对的吗? BelongsTo()associate()disassociate() 但我想不出在创建这些关系后你会操纵它们的情况。我唯一能看到改变的是Editorial - hasMany - Element,不幸的是似乎没有sync() 方法,但你可以saveMany() 在提前下班回家之前,我快速尝试了这种方法。我没有使用视图,但做了以下工作 $editorial = \App\Editorials::with( 'elements' )->first(); foreach ( $editorial->elements as &$elements ) $elements->content; 。想知道它如何影响性能.. 你可以在循环之前加载所有相关模型,显然你应该缓存但是我想它不会是世界上性能最好的东西。您还启发了我尝试一下,我得到了一个非常粗糙的版本。我一直试图让它作为一个通用的解决方案工作,可以轻松移植一段时间 如果你愿意的话,请告诉我你过得怎么样 :) 可以通过Skype联系我 vlowe85【参考方案2】:

你应该如下设置你的关系。

在您的 Editorial 模型中,您应该为每个 Element* 模型提供方法。例如

public function textElements() 
    return $this->morphedByMany(ElementText::class, 'element');



public function buttonElements() 
    return $this->morphedByMany(ElementButton::class, 'element');

在您的 Element* 模型中,您应该有以下方法来获取它变形为的所有 Editorials

public function editorials()
    return $this->morphToMany(Editorial::class, 'element');

注意:使用多态关系存在一些值得注意的问题。最重要的是,是的,您的数据库结构得到了一些简化,但同时您失去了外键约束。这意味着如果Element* 被删除,您需要手动删除任何Editorials,而在正常的Many to Many 关系中,您可以设置外键约束,RDBMS 会为您执行此操作。

注2:首先我会尝试看看这些元素表是否可以统一到一个表中。如果他们可以,你的工作会容易得多。但是如果你必须使用许多元素表,我认为这两种方式都可以。每个都有一个优点和一个缺点。它有很多关系表或多态关系,您必须自己处理数据库一致性,无论哪种方式,您都不能对关系进行$editorial->elements()

【讨论】:

所以您认为唯一的解决方案是在社论模型中为每种元素类型建立多个关系?理想情况下,我只想要一种“元素”关系,将它们全部融入其中。 @VinceLowe 编辑了我的答案。阅读注释 2 @FarzinFarzanehnia 嘿,你介意看看我的回答吗?只是想看看你的想法。 @RichardVanbergen 我看到了一些我会尝试指出的问题。第一个问题是您的元素表基本上是多对多多态关系的数据透视表。所以你基本上是在使用这种关系的想法。第二个问题是在编辑中调用 elements() 不会返回 Element* 的集合,因为您需要使用嵌套关系。所以你必须说Editorial::with('elements.text', 'elements.image', ...)。这再次简化了手头的工作。 @RichardVanbergen 很抱歉在这里回答。所以现在不允许我在其他地方发表评论。

以上是关于Laravel 5.3 多态关系的主要内容,如果未能解决你的问题,请参考以下文章

laravel 5.3 关系模型使用非默认键

Laravel 5.3 使用 with() 方法预先加载关系

从 5.1 迁移到 5.3 时急切加载关系的 Laravel 错误

Laravel 5.3 Eloquent 关系 - 用户、角色、页面和权限

在同一张表上定义关系 Laravel 5.3

Laravel 一对多(多态)关系