PHPackages                             romanzipp/laravel-projectable-aggregates - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. romanzipp/laravel-projectable-aggregates

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

romanzipp/laravel-projectable-aggregates
========================================

Projectable aggregates for Laravel

1.0.0(1y ago)13404MITPHPPHP ^8.3CI passing

Since Aug 10Pushed 1y ago2 watchersCompare

[ Source](https://github.com/romanzipp/Laravel-Projectable-Aggregates)[ Packagist](https://packagist.org/packages/romanzipp/laravel-projectable-aggregates)[ GitHub Sponsors](https://github.com/romanzipp)[ RSS](/packages/romanzipp-laravel-projectable-aggregates/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (7)Versions (4)Used By (0)

Laravel Projectable Aggregates
==============================

[](#laravel-projectable-aggregates)

[![Latest Stable Version](https://camo.githubusercontent.com/ef8b62dfd821d38aebe910b9cd30993a80eb672a7753f6a496bc45fb661c6346/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f726f6d616e7a6970702f4c61726176656c2d50726f6a65637461626c652d416767726567617465732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/romanzipp/laravel-projectable-aggregates)[![Total Downloads](https://camo.githubusercontent.com/4298dd8e0df2e176f57a8bcae1b13c6997c5e3371dacbec62f0aeaff3afcb6d2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f726f6d616e7a6970702f4c61726176656c2d50726f6a65637461626c652d416767726567617465732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/romanzipp/laravel-projectable-aggregates)[![License](https://camo.githubusercontent.com/aa05f88137f6f939d4471e0ba9e3c011482264d95474bc7eaee11966a21ed6c6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f726f6d616e7a6970702f4c61726176656c2d50726f6a65637461626c652d416767726567617465732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/romanzipp/laravel-projectable-aggregates)[![GitHub Build Status](https://camo.githubusercontent.com/8c5eef921505f5dfe968fd32c68bf6e1cbd912540fdbaaae93399f568f2a91f2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f726f6d616e7a6970702f4c61726176656c2d50726f6a65637461626c652d416767726567617465732f74657374732e796d6c3f6272616e63683d6d61696e267374796c653d666c61742d737175617265)](https://github.com/romanzipp/Laravel-Projectable-Aggregates/actions)

What
----

[](#what)

Laravel Projectable Aggregates is a package that allows you to **easily store 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).
- Option to calculate the aggregate values **periodically in bulk**.

Installation
------------

[](#installation)

```
composer require romanzipp/laravel-projectable-aggregates
```

Terminology
-----------

[](#terminology)

#### 🟢 Consumers

[](#-consumers)

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

#### 🔵 Providers

[](#-providers)

Providing models provide (duh) the aggregate values for the consumer. Think of the provider to exist many times for one consumer.

[![](art/diagram.png)](art/diagram.png)

Usage
-----

[](#usage)

Let's continue with the example of a `Car` model with `Door` models. We want to store the Doors count in the Car's `project_doors_count` field.

### 1. Add a Projection Field to DB

[](#1-add-a-projection-field-to-db)

```
new class() extends Migration
{
    public function up()
    {
        Schema::create('cars', function (Blueprint $table) {
            $table->id();
            $table->unsignedInteger('project_doors_count')->default(0);
        });
    }
}
```

### 2. Update your Models

[](#2-update-your-models)

#### 🟢 Car (Consumer)

[](#-car-consumer)

The consumer model will attach the [`ConsumesProjectableAggregate`](src/Attributes/ConsumesProjectableAggregate.php) attribute to the provider relation.

```
use romanzipp\ProjectableAggregates\Attributes\ConsumesProjectableAggregate;
use romanzipp\ProjectableAggregates\ProjectionAggregateType;

class Car extends Model
{
    #[ConsumesProjectableAggregate(
        projectionAttribute: 'project_doors_count',   // hasMany(Door::class);
    }
}
```

#### 🔵 Door (Provider)

[](#-door-provider)

The provider model will attach the [`ProvidesProjectableAggregate`](src/Attributes/ProvidesProjectableAggregate.php) attribute to the consumer relation.

```
use romanzipp\ProjectableAggregates\Attributes\ProvidesProjectableAggregate;
use romanzipp\ProjectableAggregates\ProjectionAggregateType;

class Door extends Model
{
    #[ProvidesProjectableAggregate(
        projectionAttribute: 'project_doors_count',   // belongsTo(Car::class);
    }
}
```

### 3. Register the Projection Aggregates

[](#3-register-the-projection-aggregates)

In order to listen to model events issued by the provider models, you need to register the consumer models in the `boot` method of your `AppServiceProvider`.

```
use romanzipp\ProjectableAggregates\ProjectableAggregateRegistry;

class AppServiceProvider extends ServiceProvider
{
    public function boot(ProjectableAggregateRegistry $registry)
    {
        $registry->registerConsumers([
            Car::class,
        ]);

        $registry->registerProviders([
            Door::class,
        ]);
    }
}
```

Documentation
-------------

[](#documentation)

Important

Calculating aggregate values (without bulk) **relies on Elouent model events** which are only dispatched when working with Eloquent model itself. Using the `DB` facade will **not trigger** the library to update the aggregate values.

### Aggregate Types

[](#aggregate-types)

There are three types of aggregates that can be calculated:

- `ProjectionAggregateType::TYPE_COUNT`: Counts the number of related models.
- `ProjectionAggregateType::TYPE_SUM`: Sums the related models' values.
- `ProjectionAggregateType::TYPE_AVG`: Averages the related models' values.

Important

In order to use the aggregate types `TYPE_SUM` and `TYPE_AVG`, you need to specify the target attribute of the relationship.

```
#[ProvidesProjectableAggregate(
    projectionAttribute: 'project_price_average',
    projectionType: ProjectionAggregateType::TYPE_AVG,
    targetAttribute: 'price',                          //
