From b0f29b2a7ce30e78a87c3a212e80bb1c46e3c954 Mon Sep 17 00:00:00 2001 From: Drsdre Date: Fri, 14 Mar 2025 07:03:00 +0100 Subject: [PATCH 1/3] Explicitly use parameter binding to prevent generating mangled FQN projection names because of backslash escaping i.e. prevent App\Models\Class to AppModelsClass. --- src/Models/Projection.php | 2 +- src/Models/Scopes/ProjectionScope.php | 2 +- src/Models/Traits/Projectable.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Models/Projection.php b/src/Models/Projection.php index 0b1a990..5b9c728 100644 --- a/src/Models/Projection.php +++ b/src/Models/Projection.php @@ -87,7 +87,7 @@ public function scopeName(Builder $query, string $projectorName): Builder { $this->projectionName = $projectorName; - return $query->where('projection_name', $projectorName); + return $query->whereRaw('projection_name = ?', [$projectorName]); } /** diff --git a/src/Models/Scopes/ProjectionScope.php b/src/Models/Scopes/ProjectionScope.php index 27a6e76..a416f64 100644 --- a/src/Models/Scopes/ProjectionScope.php +++ b/src/Models/Scopes/ProjectionScope.php @@ -15,7 +15,7 @@ class ProjectionScope implements Scope public function apply(Builder $builder, Model $model): void { if (! $this->isAbstractProjection($model)) { - $builder->where('projection_name', $model::class); + $builder->whereRaw('projection_name = ?', [$model::class]); } } diff --git a/src/Models/Traits/Projectable.php b/src/Models/Traits/Projectable.php index 7bf1e3e..4de1beb 100644 --- a/src/Models/Traits/Projectable.php +++ b/src/Models/Traits/Projectable.php @@ -52,7 +52,7 @@ public function projections( $query = $this->morphToMany(Projection::class, 'projectable', 'time_series_projectables'); if (isset($projectionName)) { - $query->where('projection_name', $projectionName); + $query->whereRaw('projection_name = ?', [$projectionName]); } if (isset($periods) && is_string($periods)) { From 7d48abdc7cc7939c0f9c564ac7bcbd02cd6828ef Mon Sep 17 00:00:00 2001 From: AFS Date: Mon, 24 Mar 2025 14:53:35 +0100 Subject: [PATCH 2/3] Apply whereRaw and updated_at Retrieving the projection also requires a whereRaw to handle the backslashes in the projection_name. The updated_at field should be used to determine the start_date as not the created_at. --- src/Projector.php | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Projector.php b/src/Projector.php index 51bfc5d..8314c0c 100644 --- a/src/Projector.php +++ b/src/Projector.php @@ -87,12 +87,13 @@ private function parsePeriod(string $period): void */ private function findGlobalProjection(): Projection|null { - return Projection::firstWhere([ - ['projection_name', $this->projectionName], - ['key', $this->hasKey() ? $this->key() : null], - ['period', '*'], - ['start_date', null], - ]); + return Projection::whereRaw('projection_name = ?', [$this->projectionName]) + ->where([ + ['key', $this->hasKey() ? $this->key() : null], + ['period', '*'], + ['start_date', null], + ]) + ->first(); } /** @@ -100,12 +101,14 @@ private function findGlobalProjection(): Projection|null */ private function findProjection(string $period): Projection|null { - return Projection::firstWhere([ - ['projection_name', $this->projectionName], - ['key', $this->hasKey() ? $this->key() : null], - ['period', $period], - ['start_date', app(TimeSeries::class)->resolveFloorDate($this->projectedModel->created_at, $period)], - ]); + return Projection::whereRaw('projection_name = ?', [$this->projectionName]) + ->where([ + ['key', $this->hasKey() ? $this->key() : null], + ['period', $period], + ['start_date', app(TimeSeries::class)->resolveFloorDate($this->projectedModel->updated_at, $period), + ], + ]) + ->first(); } /** @@ -117,7 +120,7 @@ private function createProjection(string $period): void 'projection_name' => $this->projectionName, 'key' => $this->hasKey() ? $this->key() : null, 'period' => $period, - 'start_date' => app(TimeSeries::class)->resolveFloorDate($this->projectedModel->created_at, $period), + 'start_date' => app(TimeSeries::class)->resolveFloorDate($this->projectedModel->updated_at, $period), 'content' => $this->mergeProjectedContent((new $this->projectionName())->defaultContent(), $period), ]); } From 0ff8a877d4931d949cd6b7146443685816c30921 Mon Sep 17 00:00:00 2001 From: Drsdre Date: Mon, 24 Mar 2025 14:55:52 +0100 Subject: [PATCH 3/3] Set updated_at as this determines the time series start_date period. --- tests/Collections/ProjectionCollectionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Collections/ProjectionCollectionTest.php b/tests/Collections/ProjectionCollectionTest.php index 42046de..b320541 100644 --- a/tests/Collections/ProjectionCollectionTest.php +++ b/tests/Collections/ProjectionCollectionTest.php @@ -223,7 +223,7 @@ function (Projection $lastProjection) { /** @test */ public function it_is_formatted_to_a_time_series() { - Log::factory()->create(['created_at' => today()]); + Log::factory()->create(['created_at' => today(), 'updated_at' => today()]); /** @var ProjectionCollection $collection */ $collection = Projection::all();