adrianb93 / mixable
为 Laravel Macroable 类提供更干净的混入。
Requires
- php: ^7.4|^8.0
- illuminate/contracts: ^8.0|^9.0|^10.0
- spatie/laravel-package-tools: ^1.14.1
Requires (Dev)
- nunomaduro/collision: ^5.0|^6.0
- nunomaduro/larastan: ^1.0|^2.0
- orchestra/testbench: ^6.0|^7.0
- pestphp/pest: ^1.21
- pestphp/pest-plugin-laravel: ^1.1
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^9.4|^9.5
- spatie/laravel-ray: ^1.26
This package is auto-updated.
Last update: 2024-09-09 03:32:44 UTC
README
⚠️ 这仍然处于开发中。 ⚠️
- 完成 Mixable 测试。
- 宏需要支持通过引用传递参数。
- 博客文章。
- 完成那个梗。
Mixable
为 Laravel 中的 Macroable
类提供更优雅的混入。
[ TODO: 链接到解释 Macorable
特性、混入方式以及该包如何不同的博客文章。 ]
安装
您可以通过 composer 安装此包
composer require adrianb93/mixable
用法
此包中有两个特质,Mixin
和 Mixable
。它们使公开方法可用于 Macroable
类。
Mixin
用于普通 PHP 类。您指定将其混入哪些Macroable
类。Mixable
用于Macroable
的子类。它将其混入所扩展的Macroable
类。
注册宏
您可以在 AppServiceProvider
中这样注册混入
// Mixin: You specify which Macroable classes it mixes into. \App\Mixins\CollectionMixin::mix([ \Illuminate\Support\Collection::class, ]); // Mixable: It mixes into the Macroable class it extends. \App\Models\Builders\Builder::mix();
混入
AdrianBrown\Mixable\Mixin
用于普通 PHP 类。它将公开方法宏扩展到指定的 Macroable 类。
示例混入
namespace App\Mixins; use AdrianBrown\Mixable\Mixin; class LoggerMixin { use Mixin; /** * Logs $this then returns $this. * * @param array $context * @return $this */ public function info($context = []) { $message = match (true) { method_exists($this, 'toSql') => $this->toSql(), method_exists($this, 'toArray') => $this->toArray(), default => $this, }; logger()->info($message, $context); return $this; } }
如果混入是 Macroable 的子类,则包将抛出异常。它将指导您使用 Mixable
特性。
混入快速事实
-
Mixin
特性是 Macroable 的装饰器。方法调用和属性获取和设置对私有、受保护和公开可见性都可行。 -
当返回
$this
(混入)时,注册的宏将返回值切换到 Macroable。 -
装饰器大部分感觉像 Macroable,但它在它的作用域之外。如果您需要处于 Macroable 的作用域,可以使用
$this->inScope($callback)
。示例LoggerMixin::mix(Collection::class) class LoggerMixin { use Mixin; public function whoami(): string { static::class; // => "App\Mixins\LoggerMixin" (the mixin) return $this->inScope(function () { return static::class; // => "Illuminate\Support\Collection" (the macroable) }); } }
可混入
AdrianBrown\Mixable\Mixable
用于 Macroable
的子类。它将公开方法宏扩展到父类。
示例可混入
namespace App\Models\Collections; use AdrianBrown\Mixable\Mixable; use Illuminate\Database\Eloquent\Collection as EloquentCollection; class Collection extends EloquentCollection { use Mixable; public function whereBelongsTo($related, $relationshipName = null) { ... return $this; } }
如果可混入不是 Macroable 的子类,则包将抛出异常。它将指导您使用 Mixin
特性。
可混入快速事实
当您调用 "可混入宏" 时
- 子类(可混入)实例化时没有构造函数。
- 父类(Macroable)的状态复制到子类(可混入)。
- 调用宏的子类方法。
- 注册的宏有返回值
- 子类(可混入)的状态复制到父类(Macroable)。
- 如果返回值是
$this
(可混入),则将其切换到父类(Macroable)。
- 注册的宏返回值。
当您在子类实例(可混入)上调用方法时(不是通过 "可混入宏")
Mixable
特性不会做任何事情。您处于一个普通的实例。
调用的方法在任一情况下都限定于子类(可混入)。如果父类(Macroable)的作用域很重要,则可以使用 $this->inScope($callback)
\App\Models\Collections\Collection::mix(); namespace \App\Models\Collections; class Collection extends EloquentCollection { use Mixable; public function whoami() { static::class; // => "App\Models\Collections\Collection" (the mixable) return $this->inScope(fn () => static::class); } }
当方法从父类实例(Macroable)调用时
\Illuminate\Database\Eloquent\Collection::make()->whoami(); // => "Illuminate\Database\Eloquent\Collection"
当方法从子类实例(可混入)调用时
\App\Models\Collections\Collection::make()->whoami(); // => "App\Models\Collections\Collection"
如果没有父类(Macroable),则 inScope($callback)
不会更改回调的作用域。
更多关于注册的信息
注册可混入
可宏扩展的类是Mixable,它将宏注册到这个类上。您可以在AppServiceProvider
中这样注册Mixable
\App\Models\Collections\Collection::mix();
注册混入
在您的AppServiceProvider
中,对使用Mixin
特性的每个类调用mix()
函数。
use App\Mixins\LoggerMixin; public function register() { LoggerMixin::mix(Collection::class); // or LoggerMixin::mix([ Builder::class, Request::class, Collection::class, ]); }
您也可以将宏扩展保留在使用Mixin
特性的类内部。在AppServiceProvider
中,您只需这样做
public function register() { LoggerMixin::mix(); }
...然后混入可以持有它应该注册自己的宏扩展
class LoggerMixin { use Mixin; public $macroable = Collection::class; // or public $macroable = [ Builder::class, Request::class, Collection::class, ]; ... }
故障排除
[Mixable] 我的混入没有返回父类(宏扩展)的实例,而是返回子类/子类(Mixable)的实例。
一个很好的例子是不可变的宏扩展,如Illuminate\Support\Collection
。大多数方法都返回一个新的集合实例。这不是同一个实例。
如果返回值与初始宏扩展实例不是同一个实例,则我们不会将其值复制到宏扩展实例,也不会将返回值交换到宏扩展。
[Mixin] PHP警告:间接修改重载属性没有效果
Mixin
是一个装饰器,意味着它使用魔法方法__get()
和__set()
来与宏扩展的类属性交互。当将属性传递给接受值引用的函数时,您会遇到这个警告,即引用是间接修改。
有几个方法可以解决这个问题
-
使用
$this->inScope($callback)
将您的代码放在回调中。回调内的属性获取和设置是直接在宏扩展上进行的。 -
将属性复制到局部变量中,然后将该局部变量设置为属性。
$items = $this->items; array_walk($items, fn (&$item) => $items = $item * 2); $this->items = $items;
[Mixable] 当子类构造函数中有逻辑没有被触发时。
当从宏/宏扩展调用Mixable时,Mixable会实例化子类/Mixable(没有构造函数)。然后将父类的状态复制到子实例。
如果您在构造函数中有逻辑没有被触发,那么这里有一些解决方案
-
您可以在构造函数中添加一个
bootMixable()
方法来触发相同的设置代码。 -
覆盖
Mixable
实现的“in”和“out”方法,并按您的方式执行。以下是如何使用另一个Eloquent查询构建器实例来创建Eloquent查询构建器实例的示例。protected static function newMixableInstance($parent): self { // IN: Create an instance of the mixable subclass which has the methods // we mixed into the parent class. return (new \App\Models\Builders\Builder($parent->getQuery())) ->setModel($parent->getModel()) ->mergeConstraintsFrom($parent); } public function newMacroableInstance(): BaseBuilder { // OUT: Return the macroable instance which the macro was called from. // You could also return `$this` if you're fine with switching // to an instance of the mixable subclass. return (new \Illuminate\Database\Eloquent\Builder($this->getQuery())) ->setModel($this->getModel()) ->mergeConstraintsFrom($this); }
-
使用一个
Mixin
。对于您要扩展的Macroable
,Mixable
可能不是合适的选择。
[Mixin] [Mixable] static::class
不是我所期望的。
Mixin
是宏扩展的装饰器。它是一个不同的类。Mixable
是宏扩展的子类。它是一个不同的类。
如果您需要static::class
提供宏扩展类,则使用$this->inScope($callback)
。
public function whoami(): string { // Before: return static::class; return $this->inScope(fn () => static::class); }
[Mixin] [Mixable] 我将$this
传递给另一个类,但它没有匹配类型提示。
Mixin
是宏扩展的装饰器。它是一个不同的类。Mixable
是宏扩展的子类。它是一个不同的类。
如果您需要$this
是宏扩展实例,则使用$this->inScope($callback)
。
public function notify(): void { // Before: ExampleNoticiation::notify($this); $this->inScope(fn () => ExampleNoticiation::notify($this)); }
测试
composer test
变更日志
有关最近更改的更多信息,请参阅变更日志。
贡献
有关详细信息,请参阅贡献指南。
安全漏洞
有关如何报告安全漏洞,请参阅我们的安全策略。
鸣谢
我喜欢使用为模型定制的查询构建器和集合。我有一个基础集合和查询构建器,它们具有一些很棒的方法。我在它们上使用了Mixable
特质,并因此创建了此包。您可以在Tim MacDonald的博客上了解更多关于为Eloquent模型定制的集合和查询构建器的信息。
许可证
MIT许可证(MIT)。请参阅许可证文件以获取更多信息。