Laravel Eloquent 中使用泛型
编程中的泛型是一种特性,允许你使用占位符类型定义函数、类和数据结构。使你能够编写更灵活和可重用的代码。
泛型通常用于 Java、C# 和 C++ 等静态类型语言。Python 等动态语言也支持它。
为什么使用泛型?
使用泛型由多个好处:
- 类型安全:泛型确保关联返回正确类型的模型,从而降低了由于模型类型不正确而导致的运行时错误的风险。
- 更好的 IDE 支持:使用泛型时,IDE 可以提供更好的自动补全和类型检查,使开发更容易,并降低出错的可能性。
- 改善可读性:泛型的使用使代码更加自文档化。从方法签名中可以清楚地看出涉及什么类型的关系和模型。
以下是 Java 中典型的泛型的样子:
public <T> T getFirstElement(List<T> list) {
return list.get(0);
}
本例中,getFirstElement
函数接受 T
类型的 List
并返回 T
类型的元素。T
是一个占位符类型,在调用函数时可以用任何其他类型替换。
在 Laravel 中使用泛型
说到PHP,泛型似乎仍然是一个遥不可及的梦想,但这并没有阻止我们使用它们,这要归功于 PHPStan 或 PSalm 等工具,它们可以让你在 PHP 代码中添加静态类型检查。
我们可以将泛型定义为注释或注解,为 IDE 提供类型提示,PHPStan 等工具可以使用它们进行静态分析。
在最近的 Laravel 版本中,一个社区成员添加了在 Eloquent 模型中使用泛型的支持。泛型在这里的作用是增加指定类或方法将使用的类型的能力。
泛型提供了一种类型提示机制和更好的代码可读性,特别是在处理 Eloquent 模型中的关联时。
参考下例。
class User extends Model
{
/** @return HasOne<Address, $this> */
public function address(): HasOne
{
return $this->hasOne(Address::class);
}
/** @return HasMany<Post, $this> */
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
}
解析
如你所见,我们现在可以指定关联的返回类型,但除此之外,我们还可以指定关联所关联的模型的类型。
HasOne<Address,$this>
:这表示address
方法返回一个HasOne
关联,其中相关模型是Address
,父模型是当前User
实例。HasMany<Post,$this>
:这表示posts
方法返回一个HasMany
关联,其中相关模型是Post
,父模型是当前User
实例。
其他方法也是如此,如 belongsTo
、hasManyThrough
、belongsToMany
等。
现在,如果定义的类型存在不一致,比如 Larastan(Laravel 的 PHPStan 包装器)将抛出一条详细的错误消息,指出问题所在。
以下面为例。
/** @return HasMany<Comment, $this> */
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
本例中,posts
方法指定了一个泛型 HasMany<Comment, $this>
,却返回一个 Post::class
关联。
运行 Larastan (使用命令 ./vendor/bin/phpstan analyse
) 将会生成如下错误信息。
Line app/Models/User.php
------ ----------------------------------------------------------------------------
10 Method App\Models\User::posts() should return
App\Models\Relations\HasMany<App\Models\Comment, App\Models\User>
but returns App\Models\Relations\HasMany<App\Models\Post, App\Models\User>.
------ ----------------------------------------------------------------------------
[ERROR] Found 1 error
这就是泛型如何帮助我们在 Laravel 中编写更健壮和可维护的代码,并使我们避免丑陋的运行时错误。