Laravel - 大量使用 Blade 渲染视图的访问器(修改器)
Posted
技术标签:
【中文标题】Laravel - 大量使用 Blade 渲染视图的访问器(修改器)【英文标题】:Laravel - Lot of Accessors (Mutators) for rendering Views with Blade 【发布时间】:2020-08-23 01:28:26 【问题描述】:我有一个 Laravel 7 项目,需要在显示之前从模型到视图进行大量数据转换。
我考虑过使用Laravel Accessors 并直接在我的blade.php 文件中使用它们。 但是当我处理完一个简单的 html 表后,我查看了我的代码,我认为访问器太多了,甚至有些访问器的名称难以阅读。
刀片视图
@foreach($races as $race)
<tr>
<td> $race->display_dates </td>
<td> $race->display_name </td>
<td> $race->type->name </td>
<td> $race->display_price </td>
<td> $race->display_places </td>
<td> $race->online_registration_is_open ? 'Yes' : 'No' </td>
</tr>
@endforeach
控制器
public function show(Group $group)
$races = $group->races;
$races->loadMissing('type'); // Eager loading
return view('races', compact('races'));
型号
// Accessors
public function getOnlineRegistrationIsOpenAttribute()
if (!$this->online_registration_ends_at && !$this->online_registration_starts_at) return false;
if ($this->online_registration_ends_at < now()) return false;
if ($this->online_registration_starts_at > now()) return false;
return true;
public function getNumberOfParticipantsAttribute()
return $this->in_team === true
? $this->teams()->count()
: $this->participants()->count();
// Accessors mainly used for displaying purpose
public function getDisplayPlacesAttribute()
if ($this->online_registration_ends_at < now())
return "Closed registration";
if ($this->online_registration_starts_at > now())
return "Opening date: " . $this->online_registration_starts_at;
return "$this->number_of_participants / $this->max_participants";
public function getDisplayPriceAttribute()
$text = $this->online_registration_price / 100;
$text .= " €";
return $text;
public function getDisplayDatesAttribute()
$text = $this->starts_at->toDateString();
if ($this->ends_at) $text .= " - " . $this->ends_at->toDateString();
return $text;
public function getDisplayNameAttribute()
$text = $this->name;
if ($this->length) $text .= " $this->length m";
if ($this->elevation) $text .= " ($this->elevation m)";
return $text;
这段代码可以工作,但我认为它有很多缺点:可读性,可能会出错,例如,如果关联的数据库表有一个 name
列,而我在这里创建一个 getDisplayNameAttribute
访问器。
这只是一个开始,我想我需要 30-40 更多的访问器来获取其他视图......
另外,我需要多次使用其中的一些,例如getDisplayNameAttribute
可以用于常规页面和管理页面(可能更多)。
我还查看了JsonResource 和ViewComposer,但JsonResource
似乎是针对APIs
而ViewComposer
似乎是特别针对Views
。
我还考虑在访问器前面加上 acc_
之类的前缀,以减少现有 db 列的错误:
public function getAccDisplayNameAttribute() ... ;
但我真的不认为这是一个解决方案,我什至不确定我所做的是对还是错。我还在互联网上搜索了最佳实践,但没有成功。
【问题讨论】:
为什么不直接使用标准的 php getter? 抱歉,我需要说明为什么我应该使用 getter 或其他东西,以及为什么它更好。 虽然 JsonResource 是为 API 设计的,但是这个概念可以起到类似的作用。您只需要像 DTO(数据传输对象)一样拥有它,它可以调解您的数据(模型)和响应。如果访问器很少,则可以使用访问器,但如果这些逻辑专用于其他类来处理,则绝对是可维护的。 【参考方案1】:对于这种情况,我有 2 个解决方案,我在本地机器上进行了测试。虽然不确定他们是否符合最佳实践或设计原则,但必须看到,但我相信他们会以可管理的方式组织代码。
首先,您可以创建一个数组访问器,并在其中包含所有逻辑,这些逻辑将计算并返回数组。这可能适用于 4-5 个属性,您必须更改视图才能访问数组而不是属性。下面给出代码示例1
第二种方法是创建一个单独的类,它将所有不同的计算逻辑作为方法来容纳。假设您创建了一个 ModelAccessor 类,然后您可以像现在一样在您的 Model 类中创建访问器,并从每个访问器内部返回 ModelAccessor->someMethod。这将为您的 Model 类添加一些整洁,您可以轻松地从类方法管理您的计算逻辑。下面的示例代码2可能会更清楚
-
数组作为访问器的示例 1
让我们调用返回的属性
$stats
public function getStatsAttribute()
$stats = [];
if (!$this->online_registration_ends_at && !$this->online_registration_starts_at) $o=false;
if ($this->online_registration_ends_at < now()) $o = false;
if ($this->online_registration_starts_at > now()) $o = false;
$o= true;
$stats['online_registration_is_open'] = $o;
$stats['number_of_participants'] = $this->in_team === true
? $this->teams()->count()
: $this->participants()->count();
return $stats;
您必须更改视图文件以使用 stats
数组
<td> $race->stats['number_of_participants'] </td>
<td> $race->stats["online_registration_is_open"] ? 'Yes' : 'No' </td>
对于大量属性,这可能会变得混乱。为避免这种情况,您可以将多个数组分组相似的东西($stats、$payment_details、$race_registration 等),或者您可以使用单独的类来管理所有这些,如下例所示
-
示例 2,使用设置不同属性的方法分隔类
class ModelAccessors
protected $race;
function __construct(\App\Race $race)
$this->race = $race;
public function displayPrice()
$text = $race->online_registration_price / 100;
$text .= " €";
return $text;
然后在模型内部
public function getDisplayPriceAttribute()
$m = new ModelAccessor($this);
return $m->displayPrice();
使用它您不必更新刀片文件
3。 如果您有 30-40 个访问器,那么我认为使用所有方法维护一个单独的类会简单得多。除此之外,您还可以从类本身创建属性数组并像这样调用它,
class ModelAccessors
protected $race;
protected $attributes;
function __construct(\App\Race $race)
$this->race = $race;
$this->attributes = [];
public function displayPrice()
$text = $race->online_registration_price / 100;
$text .= " €";
$this->attributes['display_price'] = $text;
public function allStats()
$this->displayPrice();
$this->someOtherMethod();
$this->yetAnotherMethod();
// ..
// you can further abstract calling of all the methods either from
// other method or any other way
return $this->attributes;
// From the model class
public function getStatsAccessor()
$m = new ModelAccessor($this);
// Compute the different values, add them to an array and return it as
// $stats[
// "display_price"= "234 €",
// "number_of_participants" = 300;
// ]
return $m->allStats()
【讨论】:
【参考方案2】:您可以创建一个专门处理您的转换的类。然后,您可以在 Model 构造函数中加载此类的实例。 如果你愿意,你也可以让你的访问器类使用 PHP 魔术 getter。
访问器类
class RaceAccessors
// The related model instance
private $model;
// Already generated results to avoid some recalculations
private $attributes = [];
function __construct($model)
$this->model = $model;
// Magic getters
public function __get($key)
// If already called once, just return the result
if (array_key_exists($key, $this->attributes))
return $this->attributes[$key];
// Otherwise, if a method with this attribute name exists
// Execute it and store the result in the attributes array
if (method_exists(self::class, $key))
$this->attributes[$key] = $this->$key($value);
// Then return the attribute value
return $this->attributes[$key];
// An example of accessor
public function price()
$text = $this->model->online_registration_price / 100;
$text .= " €";
return $text;
模型类
class Race
// The Accessors class instance
public $accessors;
function __construct()
$this->accessors = new \App\Accessors\RaceAccessors($this);
查看
@foreach($races as $race)
<tr>
<td> $race->accessors->price </td>
[...]
</tr>
@endforeach
我没有在此处为$model
变量设置类型,因为您可以将相同的accessors class
用于需要以与此访问器类相同的方式转换某些字段的其他模型。
例如,价格访问器可以用于 Race(在您的情况下),但它也可以用于其他模型,这样您就可以想象创建许多模型使用的访问器组,而无需更改太多代码来处理它。
【讨论】:
【参考方案3】:Accessors 和 mutators 是 Laravel 中的常规 getter 和 setter 函数。这只是一种更“优雅”的表达方式。
我可以说,getter 和 setter 函数是为了让您更好地控制类中的变量,但对理解它没有多大帮助。
那么它有什么用呢?为什么要使用它?
我们已经熟悉 DRY 原则(不要重复自己),它指出重复是一种逻辑,应该消除。或厨房用语;不要重复编写相同的代码,因为它会无缘无故地占用您的时间。
那么这有什么帮助呢?
例如,您有一个拼写检查器类,它正在使用服务器上的另一个应用程序 - 例如 hunspell。对于小字符串,您可以直接使用它,当请求到来时,您可以处理请求,通过使用 shell_exec 调用命令,然后获得结果并返回 - 每个人都很高兴。
但是当文本太长时,php 会超过 30 秒(默认)的限制,你不会得到任何结果。在这种情况下,您可以创建一个由守护进程执行的脚本,因此 30 秒的限制不会成为问题。当请求到达时,您正在使用例如 unix 套接字将文本传递给守护进程,然后后台进程为您进行拼写检查并将结果发回。
在这两种情况下,您都必须为拼写检查器类设置某些变量,这可能需要验证或格式化。而且你不想开发它两次,因为它需要时间,弄得一团糟,并且增加了你可能搞砸的地方。
在某些情况下,当您需要使用准备工作来设置或获取数据的地方远不止 2 个时,即使只在 2 个地方也可以帮助避免代码重复。这也有助于防止代码复杂性的增加。
这还不是全部!
有时你编写类,天知道谁会使用它。也许它是一个公共包。然后,当开发人员收到包时,他不想关心电子邮件将如何验证。他不想知道为什么在他从文件中获取变量之前,应该从字符串末尾删除特定变量的\n
。他不想关心整数验证的实现部分。他只是想用这个包,尽快完成工作。
您对 setter 和 getter 所做的事情除了通过这些方法控制类变量来保护它们之外,别无其他。
有时你想在你的类中隐藏你的变量,这样其他类就不会依赖它们。它使您可以灵活地更改实现和变量类型。
你应该一直使用它们吗?
否。 php 中的函数调用很慢。只需检查 Laravel 与原生 PHP 代码相比有多慢。有时编写 getter 和 setter 完全没有意义,而且只是浪费时间。
那我应该什么时候使用呢?
当您需要限制对变量的访问时使用它。为每个字段制作 getter 和 setter 是多余的。这真的取决于情况,所以只在需要使用的地方使用它。
更多关于这个话题
Getter and Setter?
Why use getters and setters/accessors?
https://dev.to/scottshipp/avoid-getters-and-setters-whenever-possible-c8m
【讨论】:
以上是关于Laravel - 大量使用 Blade 渲染视图的访问器(修改器)的主要内容,如果未能解决你的问题,请参考以下文章