romanzipp / laravel-projectable-aggregates
Laravel 项目聚合
Requires
- php: ^8.3
- illuminate/support: ^10.0|^11.0
Requires (Dev)
- doctrine/dbal: ^3.0
- friendsofphp/php-cs-fixer: ^3.0
- orchestra/testbench: ^8.0|^9.0
- phpstan/phpstan: ^1.0
- phpunit/phpunit: ^9.0|^10.0|^11.0
- romanzipp/php-cs-fixer-config: ^3.0
README
是什么
Laravel Projectable Aggregates 是一个包,允许您轻松地在模型中存储聚合值,如计数、总和、平均值等,从而消除在运行时计算这些值的需要(使用 withCount
、withSum
、withAvg
等)。
- 通过在数据库中存储聚合值来 加速数据库查询。
- 使用 模型事件 自动更新 聚合值。
- 可以选择 定期批量 计算聚合值。
安装
composer require romanzipp/laravel-projectable-aggregates
术语
🟢 消费者
消费者持有可投影聚合数据库字段。这是否则会通过 withCount
、withSum
、withAvg
等计算关系字段的模式。
🔵 提供者
提供模型提供(当然)给消费者的聚合值。想象一下,对于单个消费者,提供者可以存在多次。
用法
让我们继续以一个具有 Door
模型的 Car
模型为例。我们希望将门数存储在汽车的 project_doors_count
字段中。
1. 在数据库中添加投影字段
new class() extends Migration { public function up() { Schema::create('cars', function (Blueprint $table) { $table->id(); $table->unsignedInteger('project_doors_count')->default(0); }); } }
2. 更新您的模型
🟢 汽车(消费者)
消费者模型将向提供者关系附加 ConsumesProjectableAggregate
属性。
use romanzipp\ProjectableAggregates\Attributes\ConsumesProjectableAggregate; use romanzipp\ProjectableAggregates\ProjectionAggregateType; class Car extends Model { #[ConsumesProjectableAggregate( projectionAttribute: 'project_doors_count', // <- Name of the projection field in the database projectionType: ProjectionAggregateType::TYPE_COUNT )] public function doors(): HasMany { return $this->hasMany(Door::class); } }
🔵 门(提供者)
提供者模型将向消费者关系附加 ProvidesProjectableAggregate
属性。
use romanzipp\ProjectableAggregates\Attributes\ProvidesProjectableAggregate; use romanzipp\ProjectableAggregates\ProjectionAggregateType; class Door extends Model { #[ProvidesProjectableAggregate( projectionAttribute: 'project_doors_count', // <- Name of the FOREIGN projection field in the database projectionType: ProjectionAggregateType::TYPE_COUNT )] public function car(): BelongsTo { return $this->belongsTo(Car::class); } }
3. 注册投影聚合
为了监听提供者模型发出的模型事件,您需要在您的 AppServiceProvider
的 boot
方法中注册消费者模型。
use romanzipp\ProjectableAggregates\ProjectableAggregateRegistry; class AppServiceProvider extends ServiceProvider { public function boot(ProjectableAggregateRegistry $registry) { $registry->registerConsumers([ Car::class, ]); $registry->registerProviders([ Door::class, ]); } }
文档
重要
计算聚合值(无批量)依赖于 Elouent 模型事件,这些事件仅在直接使用 Eloquent 模型本身时才会发出。使用 DB
门面将 不会触发 库更新聚合值。
聚合类型
可以计算三种类型的聚合
ProjectionAggregateType::TYPE_COUNT
:计算相关模型的数量。ProjectionAggregateType::TYPE_SUM
:计算相关模型的值的总和。ProjectionAggregateType::TYPE_AVG
:计算相关模型的值的平均值。
重要
为了使用聚合类型 TYPE_SUM
和 TYPE_AVG
,您需要指定关系的目标属性。
#[ProvidesProjectableAggregate( projectionAttribute: 'project_price_average', projectionType: ProjectionAggregateType::TYPE_AVG, targetAttribute: 'price', // <- Attribute of the related model to average/sum up )]
触发器
您可以选择只依赖于 模型事件,或者如果您想定期批量计算聚合值。
依赖于模型事件
如果您的提供者关系附加了 ProvidesProjectableAggregate
属性,这将自动工作。一旦创建或删除提供者模型,相应的消费者聚合属性将递增或递减。
定期批量计算
如果您不想或不能依赖于模型事件,您可以使用 bulk-aggregate
命令定期批量计算聚合值。
php artisan aggregates:bulk-aggregate {--queued} {--queue=} {--class=}
--queued
:向工作队列发送作业。--queue=
:指定运行命令的队列。--class=
:将批量计算限制为特定的消费者类。
关系
以下关系得到了支持和测试
Provider::belongsTo()
<-> Consumer::hasMany()
- ✅ 模型事件
- ✅ 批量聚合
Provider::hasOneThrough()
<-> Pivot
<-> Consumer::hasManyThrough()
⚠️ 进行中
- ❌ 模型事件
- ✅ 批量聚合
Provider::morphTo()
<-> Consumer::morphMany()
- ✅ 模型事件
- ✅ 批量聚合
测试
此存储库包含一个可用于在本地机器上运行测试的 Lando 配置文件。
lando start
lando phpunit
许可
MIT 许可证 (MIT)。有关更多信息,请参阅 许可文件。