Laravel 中自定义模型中间表(请使用合适的方法来解决问题)
几天前在 tweet
看到这个:
我的第一反应是,「一定有」。这种类型的事情,恰恰我觉许多人都是写利害评论,而不是简单的说说,经常掩盖甚至没有意识到。 Laravel
是「顽固的」,但通常意味着有很多默认设置将你推向一种特定风格。多年以来我发现很少的情况下,你不能轻松的覆盖这些;往往,当你实例化时,它只是添加了另一个函数调用。
通常,当我想到我的代码出现 Mark
的问题时, 从文档中搜索和发现更直接的答案,在我看来,这是一个很好的例子。
首先,用正常的工作方式检查一下中间表。在这个例子中我们有一个多对多 Users
与 Subjects
之间的关系。因此,一个典型的 update
语句看起来像是:
User::find($id)->subjects()->updateExistingPivot($subjectId, ['status' => 'enrolled']);
如果你想要在这个语句下调度一个事件或是有其他任务,你可以写在这条语句下面。如果你希望在你的整个项目中使用,你预先把这条语句写成一个服务类函数,这样就可以在任意一个地方 update
。
这样运行,没有什么内在「错误」。这样 做 感觉有点程序化,不过,难道不是吗? 让你感觉也许有些你可以做的更好?假如,这段代码是全局要使用,但是我们 没有在服务类中创建它?如果在每个不同的操作中,有很多中间表的字段要被更新?
这样编写程序变得「困难」。不是编写代码,而是设计代码。先考虑如何使用这个类,并作出明智的选择,而不是仅仅放在你学习到的技术。因为它很简单,你可以从其他地方复制/粘贴,没有记住它是如何使用的(经验之谈,不要感到是攻击)。
例如,如果这个中间表包含的信息是有用的,本身独立于 users
和特定的 subjects
? 这样的情况下,你可能选择建立自己的实体,完整的控制器,模型,验证规则。这个想法在过去一年越来越强烈了;你可能会喜欢听Full Stack Radio插曲 ,DHH讨论如何建立一个 BaseCamp
时的中心思想。 UserSubject
也许是事情本身,你创建,更新和报告 。重要的是意识到中间表类在 Laravel
是默认选择的,而不是可选的。
第二个选项---可能是最常见的---就是按照我们上面描述的方法来创建一个UserSubjectService
类。任何时候你想要更新链接数据库,你都要经过服务类的update()
函数。这使您可以完全控制流程的每个步骤,同时使所有内容都完全透明。当然,这是完全可以测试的。其中一些缺点可能是不断声明新类的所有开销,将用户,主题和更新数据的实例传递给它,并可能在需要单独更新函数的任何地方调用相同的事件函数。服务类更常用于管理其他几个类,所以如果我们在用户和主题之间做了很多的工作,或者增加了额外的功能,这将是一个不错的选择。对于像我们以后那样简单的事情,这可能太多了。
我会告诉你的最后一个选项就是覆盖Laravel默认值的一个例子。我们已经知道,每个Eloquent模型都有Observers - 内置在模型中的许多常见动作的侦听器钩子中,比如completed
,saving
...和update
。既然我们想在每次数据透视表更新的时候触发一个事件,我们只需要进入这个模型。
雄辩使用了一种叫做「等待」的特殊模式---一个在Illuminate\Database\Eloquent\Relations\Pivot
中找到的枢轴,是一个模型的继承类,它具有处理多对多的功能,很多关系。为了定制这些函数中的任何一个,我们首先扩展Pivot,例如class UserSubject extends Pivot
来添加我们的代码,然后调用我们的模型关系函数来调用它:
class Subject extends Model
{
public function users()
{
return $this->belongsToMany('App\User')
->using('App\UserSubject');
}
}
现在每当关系被调用的时候,任何你添加到你自己的类中的东西都就像继承的模型一样会被访问。 在这个特定的情况下,我们将注册一个UserSubjectObserver
类,并将代码添加到updating()
或updated()
方法中。
三种方法都可以实现同样的事情 -- 但并没有说这所罗列的就是所有的解决方式了。 本文的重点,除了介绍Pivot
类和using()
函数之外,还让你意识到用Laravel方法
来编程的优势,但是最终设计代码还要靠你自己!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。