Skip to content

Commit

Permalink
Add command + job
Browse files Browse the repository at this point in the history
  • Loading branch information
romanzipp committed Aug 10, 2024
1 parent b3f68e7 commit e7e56ec
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 2 deletions.
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

## What

Laravel Projectable Aggregates is a package that allows you to **easily storage aggregate values like counts, sums, averages**, etc. in your models eliminating the need to **calculate these values on the fly** (with `withCount`, `withStum`, `withAvg`, etc.).
Laravel Projectable Aggregates is a package that allows you to **easily storage aggregate values like counts, sums, averages**, etc. in your models eliminating the need to **calculate these values on the fly** (with `withCount`, `withSum`, `withAvg`, etc.).

- **Speed up database queries** by storing aggregate values in the database.
- **Automatically updates** aggregate values with [Model Events](https://laravel.com/docs/events).
Expand All @@ -23,7 +23,7 @@ composer require romanzipp/laravel-projectable-aggregates

#### 🟢 Consumers

Consumers hold the projectable aggregate database field. This is the model which otherwise would calculate the relationship fields via `withCount`, `withStum`, `withAvg`, etc.
Consumers hold the projectable aggregate database field. This is the model which otherwise would calculate the relationship fields via `withCount`, `withSum`, `withAvg`, etc.

#### 🔵 Providers

Expand Down Expand Up @@ -138,6 +138,20 @@ There are three types of aggregates that can be calculated:
> )]
> ```
### Triggers
You can decide if you would only like to rely on **models events** or if you want to calculate the aggregate values **periodically in bulk**.
#### Rely on Model Events
This will automatically work if your've attached the [`ProvidesProjectableAggregate`](src/Attributes/ProvidesProjectableAggregate.php) attribute to your provider relations. Once a provider model has been created/deleted the according consumer aggregate attribute will be incremented/decremented.
#### Calculate periodically in Bulk
```bash
php artisan aggregates:bulk-aggregate {--queued} {--queue=}
```
## Testing

This repository contains a [Lando](https://lando.dev) configuration file that can be used to run the tests on your local machine.
Expand Down
39 changes: 39 additions & 0 deletions src/Command/CalculateBulkAggregatesCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace romanzipp\ProjectableAggregates\Command;

use Illuminate\Console\Command;
use romanzipp\ProjectableAggregates\Jobs\CalculateBulkAggregatesJob;

final class CalculateBulkAggregatesCommand extends Command
{
protected $signature = 'aggregates:bulk-aggregate
{--queued : Push a job to the queue}
{--queue= : The queue to push the job to}
{--class=}';

public function handle(): int
{
$classes = [];

$job = new CalculateBulkAggregatesJob($classes);

if ($this->option('queued')) {
$this->info('Pushing aggregation to queue...');

$pendingDispatch = dispatch($job);

if ($this->hasOption('queue')) {
$pendingDispatch->onQueue(
$this->option('queue')
);
}

return 0;
}

$job->handle();

return 0;
}
}
27 changes: 27 additions & 0 deletions src/Jobs/CalculateBulkAggregatesJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace romanzipp\ProjectableAggregates\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use romanzipp\ProjectableAggregates\ProjectableAggregateRegistry;

final class CalculateBulkAggregatesJob
{
use SerializesModels;
use Queueable;

/**
* @param array<class-string> $consumerClasses
*/
public function __construct(
public array $consumerClasses = []
) {
}

public function handle(): void
{
$registry = app(ProjectableAggregateRegistry::class);
$registry->bulkAggregate();
}
}
7 changes: 7 additions & 0 deletions src/Providers/ProjectableAggregatesProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
use romanzipp\ProjectableAggregates\Command\CalculateBulkAggregatesCommand;
use romanzipp\ProjectableAggregates\ProjectableAggregateRegistry;

class ProjectableAggregatesProvider extends ServiceProvider
Expand All @@ -13,6 +14,12 @@ public function boot(): void
$this->publishes([
dirname(__DIR__) . '/../config/projectable-aggregates.php' => config_path('projectable-aggregates.php'),
], 'config');

if ($this->app->runningInConsole()) {
$this->commands([
CalculateBulkAggregatesCommand::class,
]);
}
}

public function register(): void
Expand Down
60 changes: 60 additions & 0 deletions tests/BulkCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace romanzipp\ProjectableAggregates\Tests;

use Illuminate\Support\Facades\Bus;
use romanzipp\ProjectableAggregates\Jobs\CalculateBulkAggregatesJob;
use romanzipp\ProjectableAggregates\ProjectableAggregateRegistry;
use romanzipp\ProjectableAggregates\Tests\Support\PivotConsumer;
use romanzipp\ProjectableAggregates\Tests\Support\PivotProvider;
use romanzipp\ProjectableAggregates\Tests\Support\PivotProviderConsumerPivot;

class BulkCommandTest extends TestCase
{
public function testCommand()
{
$registry = app(ProjectableAggregateRegistry::class);
$registry->registerConsumers([PivotConsumer::class]);
$registry->registerProviders([PivotProvider::class]);

PivotProviderConsumerPivot::query()->create([
'consumer_id' => ($consumer = PivotConsumer::query()->create()->refresh())->id,
'provider_id' => ($provider = PivotProvider::query()->create())->id,
]);

self::assertSame(1, $consumer->providers()->count());
self::assertSame(0, $consumer->projection_providers_count);

// Command -----------------------------------------------------
$this->artisan('aggregates:bulk-aggregate')->assertSuccessful();
// -------------------------------------------------------------

$consumer->refresh();

self::assertSame(1, $consumer->providers()->count());
self::assertSame(1, $consumer->projection_providers_count);
}

public function testQueued()
{
$bus = Bus::fake();

$this->artisan('aggregates:bulk-aggregate --queued');

$bus->assertDispatched(
CalculateBulkAggregatesJob::class
);
}

public function testQueuedOnOtherQueue()
{
$bus = Bus::fake();

$this->artisan('aggregates:bulk-aggregate --queued --queue=foobar');

$bus->assertDispatched(
CalculateBulkAggregatesJob::class,
fn (CalculateBulkAggregatesJob $job) => 'foobar' === $job->queue
);
}
}
8 changes: 8 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Foundation\Application;
use Orchestra\Testbench\TestCase as BaseTestCase;
use romanzipp\ProjectableAggregates\Providers\ProjectableAggregatesProvider;

class TestCase extends BaseTestCase
{
protected function getPackageProviders($app): array
{
return [
ProjectableAggregatesProvider::class,
];
}

public function setUp(): void
{
parent::setUp();
Expand Down

0 comments on commit e7e56ec

Please sign in to comment.