From 68929de8bad5e19ff7f96db594113a949d4721f1 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Sun, 20 Feb 2022 19:58:27 +0100 Subject: [PATCH 01/17] extracted reusable stats-parts into a trait --- src/Models/StatsEvent.php | 33 +++------------------------------ src/Traits/HasStats.php | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 30 deletions(-) create mode 100644 src/Traits/HasStats.php diff --git a/src/Models/StatsEvent.php b/src/Models/StatsEvent.php index 0a880aa..5392247 100644 --- a/src/Models/StatsEvent.php +++ b/src/Models/StatsEvent.php @@ -2,11 +2,13 @@ namespace Spatie\Stats\Models; -use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; +use Spatie\Stats\Traits\HasStats; class StatsEvent extends Model { + use HasStats; + const TYPE_SET = 'set'; const TYPE_CHANGE = 'change'; @@ -15,33 +17,4 @@ class StatsEvent extends Model ]; protected $guarded = []; - - public function scopeGroupByPeriod(Builder $query, string $period): void - { - $periodGroupBy = static::getPeriodDateFormat($period); - - $query->groupByRaw($periodGroupBy)->selectRaw("{$periodGroupBy} as period"); - } - - public static function getPeriodDateFormat(string $period): string - { - return match ($period) { - 'year' => "date_format(created_at,'%Y')", - 'month' => "date_format(created_at,'%Y-%m')", - 'week' => "yearweek(created_at, 3)", // see https://stackoverflow.com/questions/15562270/php-datew-vs-mysql-yearweeknow - 'day' => "date_format(created_at,'%Y-%m-%d')", - 'hour' => "date_format(created_at,'%Y-%m-%d %H')", - 'minute' => "date_format(created_at,'%Y-%m-%d %H:%i')", - }; - } - - public function scopeIncrements(Builder $query): void - { - $query->where('value', '>', 0); - } - - public function scopeDecrements(Builder $query): void - { - $query->where('value', '<', 0); - } } diff --git a/src/Traits/HasStats.php b/src/Traits/HasStats.php new file mode 100644 index 0000000..93b91a0 --- /dev/null +++ b/src/Traits/HasStats.php @@ -0,0 +1,37 @@ +groupByRaw($periodGroupBy)->selectRaw("{$periodGroupBy} as period"); + } + + public static function getPeriodDateFormat(string $period): string + { + return match ($period) { + 'year' => "date_format(created_at,'%Y')", + 'month' => "date_format(created_at,'%Y-%m')", + 'week' => "yearweek(created_at, 3)", // see https://stackoverflow.com/questions/15562270/php-datew-vs-mysql-yearweeknow + 'day' => "date_format(created_at,'%Y-%m-%d')", + 'hour' => "date_format(created_at,'%Y-%m-%d %H')", + 'minute' => "date_format(created_at,'%Y-%m-%d %H:%i')", + }; + } + + public function scopeIncrements(Builder $query): void + { + $query->where('value', '>', 0); + } + + public function scopeDecrements(Builder $query): void + { + $query->where('value', '<', 0); + } +} From a8acac74c28ceb0cf609375c0f6ad9f1bf1db312 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Sun, 20 Feb 2022 19:59:56 +0100 Subject: [PATCH 02/17] moved "getPeriodDateFormat" closer to the stats-query --- src/StatsQuery.php | 19 ++++++++++++++++--- src/Traits/HasStats.php | 15 ++------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/StatsQuery.php b/src/StatsQuery.php index b9cbf2c..98aec81 100644 --- a/src/StatsQuery.php +++ b/src/StatsQuery.php @@ -35,6 +35,19 @@ public static function for(string $statistic): self return new self($statistic); } + public static function getPeriodDateFormat(string $period): string + { + return match ($period) { + 'year' => "date_format(created_at,'%Y')", + 'month' => "date_format(created_at,'%Y-%m')", + 'week' => "yearweek(created_at, 3)", // see https://stackoverflow.com/questions/15562270/php-datew-vs-mysql-yearweeknow + 'day' => "date_format(created_at,'%Y-%m-%d')", + 'hour' => "date_format(created_at,'%Y-%m-%d %H')", + 'minute' => "date_format(created_at,'%Y-%m-%d %H:%i')", + }; + } + + public function groupByYear(): self { $this->period = 'year'; @@ -84,7 +97,7 @@ public function end(DateTimeInterface $end): self return $this; } - /** @return \Illuminate\Support\Collection|\Spatie\Stats\DataPoint[] */ + /** @return Collection|DataPoint[] */ public function get(): Collection { $periods = $this->generatePeriods(); @@ -133,7 +146,7 @@ public function get(): Collection * Gets the value at a point in time by using the previous * snapshot and the changes since that snapshot. * - * @param \DateTimeInterface $dateTime + * @param DateTimeInterface $dateTime * * @return int */ @@ -214,7 +227,7 @@ protected function getDifferencesPerPeriod(): EloquentCollection protected function getLatestSetPerPeriod(): EloquentCollection { - $periodDateFormat = StatsEvent::getPeriodDateFormat($this->period); + $periodDateFormat = static::getPeriodDateFormat($this->period); $rankedSets = $this->queryStats() ->selectRaw("ROW_NUMBER() OVER (PARTITION BY {$periodDateFormat} ORDER BY `id` DESC) AS rn, `stats_events`.*, {$periodDateFormat} as period") diff --git a/src/Traits/HasStats.php b/src/Traits/HasStats.php index 93b91a0..f308fe5 100644 --- a/src/Traits/HasStats.php +++ b/src/Traits/HasStats.php @@ -3,28 +3,17 @@ namespace Spatie\Stats\Traits; use Illuminate\Database\Eloquent\Builder; +use Spatie\Stats\StatsQuery; trait HasStats { public function scopeGroupByPeriod(Builder $query, string $period): void { - $periodGroupBy = static::getPeriodDateFormat($period); + $periodGroupBy = StatsQuery::getPeriodDateFormat($period); $query->groupByRaw($periodGroupBy)->selectRaw("{$periodGroupBy} as period"); } - public static function getPeriodDateFormat(string $period): string - { - return match ($period) { - 'year' => "date_format(created_at,'%Y')", - 'month' => "date_format(created_at,'%Y-%m')", - 'week' => "yearweek(created_at, 3)", // see https://stackoverflow.com/questions/15562270/php-datew-vs-mysql-yearweeknow - 'day' => "date_format(created_at,'%Y-%m-%d')", - 'hour' => "date_format(created_at,'%Y-%m-%d %H')", - 'minute' => "date_format(created_at,'%Y-%m-%d %H:%i')", - }; - } - public function scopeIncrements(Builder $query): void { $query->where('value', '>', 0); From dcfbd3972329847e10d414295285fd46a5ac9cfe Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Sun, 20 Feb 2022 20:29:11 +0100 Subject: [PATCH 03/17] moved "TYPE_" constants into the DataPoint and deprecate "StatsEvent::TYPE_" usage --- src/BaseStats.php | 6 +++--- src/DataPoint.php | 4 ++++ src/Models/StatsEvent.php | 12 ++++++++++-- src/StatsQuery.php | 10 +++++----- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/BaseStats.php b/src/BaseStats.php index 82b1c1d..03b92a3 100644 --- a/src/BaseStats.php +++ b/src/BaseStats.php @@ -23,7 +23,7 @@ public static function increase(mixed $number = 1, ?DateTimeInterface $timestamp $stats = new static; - $stats->createEvent(StatsEvent::TYPE_CHANGE, $number, $timestamp); + $stats->createEvent(DataPoint::TYPE_CHANGE, $number, $timestamp); } public static function decrease(mixed $number = 1, ?DateTimeInterface $timestamp = null) @@ -32,14 +32,14 @@ public static function decrease(mixed $number = 1, ?DateTimeInterface $timestamp $stats = new static; - $stats->createEvent(StatsEvent::TYPE_CHANGE, -$number, $timestamp); + $stats->createEvent(DataPoint::TYPE_CHANGE, -$number, $timestamp); } public static function set(int $value, ?DateTimeInterface $timestamp = null) { $stats = new static; - $stats->createEvent(StatsEvent::TYPE_SET, $value, $timestamp); + $stats->createEvent(DataPoint::TYPE_SET, $value, $timestamp); } protected function createEvent($type, $value, ?DateTimeInterface $timestamp = null): StatsEvent diff --git a/src/DataPoint.php b/src/DataPoint.php index d816c0e..99953f9 100644 --- a/src/DataPoint.php +++ b/src/DataPoint.php @@ -7,6 +7,10 @@ class DataPoint implements Arrayable { + + const TYPE_SET = 'set'; + const TYPE_CHANGE = 'change'; + public function __construct( public Carbon $start, public Carbon $end, diff --git a/src/Models/StatsEvent.php b/src/Models/StatsEvent.php index 5392247..2853d33 100644 --- a/src/Models/StatsEvent.php +++ b/src/Models/StatsEvent.php @@ -3,14 +3,22 @@ namespace Spatie\Stats\Models; use Illuminate\Database\Eloquent\Model; +use Spatie\Stats\DataPoint; use Spatie\Stats\Traits\HasStats; class StatsEvent extends Model { use HasStats; - const TYPE_SET = 'set'; - const TYPE_CHANGE = 'change'; + /** + * @deprecated use DataPoint::TYPE_SET + */ + const TYPE_SET = DataPoint::TYPE_SET; + + /** + * @deprecated use DataPoint::TYPE_CHANGE + */ + const TYPE_CHANGE = DataPoint::TYPE_CHANGE; protected $casts = [ 'value' => 'integer', diff --git a/src/StatsQuery.php b/src/StatsQuery.php index 98aec81..7b93da4 100644 --- a/src/StatsQuery.php +++ b/src/StatsQuery.php @@ -103,7 +103,7 @@ public function get(): Collection $periods = $this->generatePeriods(); $changes = $this->queryStats() - ->whereType(StatsEvent::TYPE_CHANGE) + ->whereType(DataPoint::TYPE_CHANGE) ->where('created_at', '>=', $this->start) ->where('created_at', '<', $this->end) ->get(); @@ -153,7 +153,7 @@ public function get(): Collection public function getValue(DateTimeInterface $dateTime): int { $nearestSet = $this->queryStats() - ->where('type', StatsEvent::TYPE_SET) + ->where('type', DataPoint::TYPE_SET) ->where('created_at', '<', $dateTime) ->orderByDesc('created_at') ->first(); @@ -162,7 +162,7 @@ public function getValue(DateTimeInterface $dateTime): int $startValue = optional($nearestSet)->value ?? 0; $differenceSinceSet = $this->queryStats() - ->where('type', StatsEvent::TYPE_CHANGE) + ->where('type', DataPoint::TYPE_CHANGE) ->where('id', '>', $startId) ->where('created_at', '<', $dateTime) ->sum('value'); @@ -214,7 +214,7 @@ protected function queryStats(): Builder protected function getDifferencesPerPeriod(): EloquentCollection { return $this->queryStats() - ->whereType(StatsEvent::TYPE_CHANGE) + ->whereType(DataPoint::TYPE_CHANGE) ->where('created_at', '>=', $this->start) ->where('created_at', '<', $this->end) ->selectRaw('sum(case when value > 0 then value else 0 end) as increments') @@ -231,7 +231,7 @@ protected function getLatestSetPerPeriod(): EloquentCollection $rankedSets = $this->queryStats() ->selectRaw("ROW_NUMBER() OVER (PARTITION BY {$periodDateFormat} ORDER BY `id` DESC) AS rn, `stats_events`.*, {$periodDateFormat} as period") - ->whereType(StatsEvent::TYPE_SET) + ->whereType(DataPoint::TYPE_SET) ->where('created_at', '>=', $this->start) ->where('created_at', '<', $this->end) ->get(); From 9d8daa4d3d7e5bb94b761cd3c4f59d4954f03230 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Sun, 20 Feb 2022 20:50:59 +0100 Subject: [PATCH 04/17] Added `StatsWriter` with relationship-support (`StatsWriter::for($model->relationship())`) --- .../migrations/create_stats_tables.php.stub | 8 +- src/Models/Stat.php | 14 ++ src/StatsWriter.php | 64 +++++++ tests/StatsWriterTest.php | 177 ++++++++++++++++++ 4 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 src/Models/Stat.php create mode 100644 src/StatsWriter.php create mode 100644 tests/StatsWriterTest.php diff --git a/database/migrations/create_stats_tables.php.stub b/database/migrations/create_stats_tables.php.stub index 98ed454..baecac2 100644 --- a/database/migrations/create_stats_tables.php.stub +++ b/database/migrations/create_stats_tables.php.stub @@ -8,10 +8,16 @@ class CreateStatsTables extends Migration { public function up() { + Schema::create('stats', function (Blueprint $table) { + $table->id(); + $table->timestamps(); + }); + Schema::create('stats_events', function (Blueprint $table) { $table->id(); - $table->string('name'); + $table->string('stat_id')->nullable(); + $table->string('name')->nullable(); $table->string('type'); $table->bigInteger('value'); diff --git a/src/Models/Stat.php b/src/Models/Stat.php new file mode 100644 index 0000000..761b174 --- /dev/null +++ b/src/Models/Stat.php @@ -0,0 +1,14 @@ +hasMany(StatsEvent::class); + } +} diff --git a/src/StatsWriter.php b/src/StatsWriter.php new file mode 100644 index 0000000..f1182e9 --- /dev/null +++ b/src/StatsWriter.php @@ -0,0 +1,64 @@ +subject = $subject; + } + + public static function for($subject) + { + return new static($subject); + } + + public function increase(mixed $number = 1, array $attributes = [], ?DateTimeInterface $timestamp = null) + { + $number = is_int($number) ? $number : 1; + + $this->createEvent(DataPoint::TYPE_CHANGE, $number, $attributes, $timestamp); + } + + public function decrease(mixed $number = 1, array $attributes = [], ?DateTimeInterface $timestamp = null) + { + $number = is_int($number) ? $number : 1; + + $this->createEvent(DataPoint::TYPE_CHANGE, -$number, $attributes, $timestamp); + } + + public function set(int $value, array $attributes = [], ?DateTimeInterface $timestamp = null) + { + $this->createEvent(DataPoint::TYPE_SET, $value, $attributes, $timestamp); + } + + protected function createEvent($type, $value, array $attributes = [], ?DateTimeInterface $timestamp = null): StatsEvent + { + if ($this->subject instanceof Relation) { + return $this->subject->create(array_merge($attributes, [ + 'type' => $type, + 'value' => $value, + 'created_at' => $timestamp ?? now(), + ])); + } + + $subject = $this->subject; + if ($subject instanceof Model) { + $subject = get_class($subject); + } + + return $subject::create(array_merge($attributes, [ + 'type' => $type, + 'value' => $value, + 'created_at' => $timestamp ?? now(), + ])); + } +} diff --git a/tests/StatsWriterTest.php b/tests/StatsWriterTest.php new file mode 100644 index 0000000..6f7bd0a --- /dev/null +++ b/tests/StatsWriterTest.php @@ -0,0 +1,177 @@ +increase(); + + $this->assertDatabaseHas('stats_events', [ + 'value' => 1, + 'type' => 'change', + ]); + } + + /** @test */ + public function it_can_create_events_for_model_instances() + { + StatsWriter::for(new StatsEvent())->increase(1); + + $this->assertDatabaseHas('stats_events', [ + 'value' => 1, + 'type' => 'change', + ]); + } + + /** @test */ + public function it_can_create_events_for_has_many_relationships() + { + /** @var Stat $stats */ + $stats = Stat::create(); + + StatsWriter::for($stats->events())->increase(); + + $this->assertDatabaseHas('stats_events', [ + 'stat_id' => $stats->getKey(), + 'value' => 1, + 'type' => 'change', + ]); + } + + /** @test */ + public function it_can_create_events_with_custom_attributes() + { + StatsWriter::for(new StatsEvent())->increase(1, ['name' => 'OrderStats']); + + $this->assertDatabaseHas('stats_events', [ + 'name' => 'OrderStats', + 'value' => 1, + 'type' => 'change', + ]); + } + + /** @test */ + public function it_can_create_events_for_increments_at_a_given_timestamp() + { + StatsWriter::for(StatsEvent::class)->increase(1, [], now()->subWeek()); + + $this->assertDatabaseHas('stats_events', [ + 'value' => 1, + 'type' => 'change', + 'created_at' => now()->subWeek(), + ]); + } + + /** @test */ + public function it_can_create_events_for_increments() + { + StatsWriter::for(StatsEvent::class)->increase(); + + $this->assertDatabaseHas('stats_events', [ + 'value' => 1, + 'type' => 'change', + ]); + } + + /** @test */ + public function it_can_create_events_for_decrements() + { + StatsWriter::for(StatsEvent::class)->decrease(); + + $this->assertDatabaseHas('stats_events', [ + 'value' => -1, + 'type' => 'change', + ]); + } + + /** @test */ + public function it_can_create_events_for_decrements_at_a_given_timestamp() + { + StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subWeek()); + + $this->assertDatabaseHas('stats_events', [ + 'value' => -1, + 'type' => 'change', + 'created_at' => now()->subWeek(), + ]); + } + + /** @test */ + public function it_can_create_events_for_setting_fixed_values() + { + StatsWriter::for(StatsEvent::class)->set(1337); + + $this->assertDatabaseHas('stats_events', [ + 'value' => 1337, + 'type' => 'set', + ]); + } + + /** @test */ + public function it_can_create_events_for_setting_fixed_values_at_a_given_timestamp() + { + StatsWriter::for(StatsEvent::class)->set(1337, [], now()->subWeek()); + + $this->assertDatabaseHas('stats_events', [ + 'value' => 1337, + 'type' => 'set', + 'created_at' => now()->subWeek(), + ]); + } + +// /** @test */ +// public function it_can_get_a_stats_query_object() +// { +// $query = OrderStats::query(); +// +// $this->assertInstanceOf(StatsQuery::class, $query); +// $this->assertInstanceOf(OrderStats::class, $query->getStatistic()); +// } + +// /** @test */ +// public function it_can_get_the_value_at_a_given_time() +// { +// OrderStats::set(3, now()->subDays(19)); +// OrderStats::decrease(1, now()->subDays(4)); +// OrderStats::increase(3, now()->subDays(2)); +// +// $this->assertEquals(0, StatsQuery::for(OrderStats::class)->getValue(now()->subDays(30))); +// $this->assertEquals(3, StatsQuery::for(OrderStats::class)->getValue(now()->subDays(18))); +// $this->assertEquals(5, StatsQuery::for(OrderStats::class)->getValue(now())); +// } +// +// /** @test */ +// public function it_can_generate_and_array_of_periods() +// { +// $periods = StatsQuery::for(OrderStats::class)->start(now()->subYear())->end(now())->generatePeriods(); +// +// $this->assertCount(53, $periods); +// +// $this->assertEquals([ +// Carbon::parse('2018-12-31'), +// Carbon::parse('2019-01-07'), +// '201901', +// ], $periods[0]); +// +// $this->assertEquals([ +// Carbon::parse('2019-12-30'), +// Carbon::parse('2020-01-06'), +// '202001', +// ], $periods[52]); +// } +} From 0b627d29d4a10c664ba884328d3fc1dbd6909f3c Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Sun, 20 Feb 2022 22:12:04 +0100 Subject: [PATCH 05/17] Extended `StatsQuery` with relationship-support (`StatsQuery::for($model->relationship())`) --- src/StatsQuery.php | 72 +++++++++++++----- tests/BaseStatsTest.php | 40 +++++----- tests/StatsQueryTest.php | 154 ++++++++++++++++++++++++++++++--------- 3 files changed, 193 insertions(+), 73 deletions(-) diff --git a/src/StatsQuery.php b/src/StatsQuery.php index 7b93da4..2aeb9ee 100644 --- a/src/StatsQuery.php +++ b/src/StatsQuery.php @@ -5,13 +5,14 @@ use DateTimeInterface; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection as EloquentCollection; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; -use Spatie\Stats\Models\StatsEvent; class StatsQuery { - protected BaseStats $statistic; + private Model|Relation|string $statistic; protected string $period; @@ -19,9 +20,9 @@ class StatsQuery protected DateTimeInterface $end; - public function __construct(string $statistic) + public function __construct(Model|Relation|string $statistic) { - $this->statistic = new $statistic(); + $this->statistic = $statistic; $this->period = 'week'; @@ -30,7 +31,7 @@ public function __construct(string $statistic) $this->end = now(); } - public static function for(string $statistic): self + public static function for(Model|Relation|string $statistic): self { return new self($statistic); } @@ -103,7 +104,7 @@ public function get(): Collection $periods = $this->generatePeriods(); $changes = $this->queryStats() - ->whereType(DataPoint::TYPE_CHANGE) + ->where('type', DataPoint::TYPE_CHANGE) ->where('created_at', '>=', $this->start) ->where('created_at', '<', $this->end) ->get(); @@ -163,7 +164,7 @@ public function getValue(DateTimeInterface $dateTime): int $differenceSinceSet = $this->queryStats() ->where('type', DataPoint::TYPE_CHANGE) - ->where('id', '>', $startId) + ->where($this->getStatsKey(), '>', $startId) ->where('created_at', '<', $dateTime) ->sum('value'); @@ -200,21 +201,25 @@ public function getPeriodTimestampFormat(): string }; } - public function getStatistic(): BaseStats - { - return $this->statistic; - } - protected function queryStats(): Builder { - return StatsEvent::query() - ->where('name', $this->statistic->getName()); + if ($this->statistic instanceof Relation) { + return $this->statistic->getQuery()->clone(); + } + + /** @var Model $subject */ + $subject = $this->statistic; + if (is_string($subject) && class_exists($subject)) { + $subject = new $subject; + } + + return $subject->newQuery(); } protected function getDifferencesPerPeriod(): EloquentCollection { return $this->queryStats() - ->whereType(DataPoint::TYPE_CHANGE) + ->where('type', DataPoint::TYPE_CHANGE) ->where('created_at', '>=', $this->start) ->where('created_at', '<', $this->end) ->selectRaw('sum(case when value > 0 then value else 0 end) as increments') @@ -229,9 +234,12 @@ protected function getLatestSetPerPeriod(): EloquentCollection { $periodDateFormat = static::getPeriodDateFormat($this->period); + $statsTable = $this->getStatsTableName(); + $statsKey = $this->getStatsKey(); + $rankedSets = $this->queryStats() - ->selectRaw("ROW_NUMBER() OVER (PARTITION BY {$periodDateFormat} ORDER BY `id` DESC) AS rn, `stats_events`.*, {$periodDateFormat} as period") - ->whereType(DataPoint::TYPE_SET) + ->selectRaw("ROW_NUMBER() OVER (PARTITION BY {$periodDateFormat} ORDER BY `{$statsKey}` DESC) AS rn, `{$statsTable}`.*, {$periodDateFormat} as period") + ->where('type', DataPoint::TYPE_SET) ->where('created_at', '>=', $this->start) ->where('created_at', '<', $this->end) ->get(); @@ -240,4 +248,34 @@ protected function getLatestSetPerPeriod(): EloquentCollection return $latestSetPerPeriod; } + + protected function getStatsKey(): string + { + if ($this->statistic instanceof Relation) { + return $this->statistic->getRelated()->getKeyName(); + } + + /** @var Model $subject */ + $subject = $this->statistic; + if (is_string($subject) && class_exists($subject)) { + $subject = new $subject; + } + + return $subject->getKeyName(); + } + + protected function getStatsTableName(): string + { + if ($this->statistic instanceof Relation) { + return $this->statistic->getRelated()->getTable(); + } + + /** @var Model $subject */ + $subject = $this->statistic; + if (is_string($subject) && class_exists($subject)) { + $subject = new $subject; + } + + return $subject->getTable(); + } } diff --git a/tests/BaseStatsTest.php b/tests/BaseStatsTest.php index 66c066e..9619f57 100644 --- a/tests/BaseStatsTest.php +++ b/tests/BaseStatsTest.php @@ -102,26 +102,26 @@ public function it_can_create_events_for_setting_fixed_values_at_a_given_timesta ]); } - /** @test */ - public function it_can_get_a_stats_query_object() - { - $query = OrderStats::query(); - - $this->assertInstanceOf(StatsQuery::class, $query); - $this->assertInstanceOf(OrderStats::class, $query->getStatistic()); - } - - /** @test */ - public function it_can_get_the_value_at_a_given_time() - { - OrderStats::set(3, now()->subDays(19)); - OrderStats::decrease(1, now()->subDays(4)); - OrderStats::increase(3, now()->subDays(2)); - - $this->assertEquals(0, StatsQuery::for(OrderStats::class)->getValue(now()->subDays(30))); - $this->assertEquals(3, StatsQuery::for(OrderStats::class)->getValue(now()->subDays(18))); - $this->assertEquals(5, StatsQuery::for(OrderStats::class)->getValue(now())); - } +// /** @test */ +// public function it_can_get_a_stats_query_object() +// { +// $query = OrderStats::query(); +// +// $this->assertInstanceOf(StatsQuery::class, $query); +// $this->assertInstanceOf(OrderStats::class, $query->getStatistic()); +// } + +// /** @test */ +// public function it_can_get_the_value_at_a_given_time() +// { +// OrderStats::set(3, now()->subDays(19)); +// OrderStats::decrease(1, now()->subDays(4)); +// OrderStats::increase(3, now()->subDays(2)); +// +// $this->assertEquals(0, StatsQuery::for(OrderStats::class)->getValue(now()->subDays(30))); +// $this->assertEquals(3, StatsQuery::for(OrderStats::class)->getValue(now()->subDays(18))); +// $this->assertEquals(5, StatsQuery::for(OrderStats::class)->getValue(now())); +// } /** @test */ public function it_can_generate_and_array_of_periods() diff --git a/tests/StatsQueryTest.php b/tests/StatsQueryTest.php index 40219b8..3042e74 100644 --- a/tests/StatsQueryTest.php +++ b/tests/StatsQueryTest.php @@ -3,8 +3,10 @@ namespace Spatie\Stats\Tests; use Carbon\Carbon; +use Spatie\Stats\Models\Stat; +use Spatie\Stats\Models\StatsEvent; use Spatie\Stats\StatsQuery; -use Spatie\Stats\Tests\Stats\OrderStats; +use Spatie\Stats\StatsWriter; class StatsQueryTest extends TestCase { @@ -16,16 +18,96 @@ protected function setUp(): void } /** @test */ - public function it_can_get_stats() + public function it_can_get_stats_for_classname() { - OrderStats::set(3, now()->subMonth()); - OrderStats::decrease(1, now()->subDays(13)); - OrderStats::increase(3, now()->subDays(12)); - OrderStats::set(3, now()->subDays(6)); - OrderStats::decrease(1, now()->subDays(5)); - OrderStats::increase(3, now()->subDays(4)); - - $stats = StatsQuery::for(OrderStats::class) + StatsWriter::for(StatsEvent::class)->set(3, [], now()->subMonth()); + StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(13)); + StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(12)); + StatsWriter::for(StatsEvent::class)->set(3, [], now()->subDays(6)); + StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(5)); + StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(4)); + + $stats = StatsQuery::for(StatsEvent::class) + ->start(now()->subWeeks(2)) + ->end(now()->startOfWeek()) + ->groupByWeek() + ->get(); + + $expected = [ + [ + 'value' => 5, + 'increments' => +3, + 'decrements' => 1, + 'difference' => 2, + 'start' => now()->subWeeks(2)->startOfWeek(), + 'end' => now()->subWeeks(1)->startOfWeek(), + ], + [ + 'value' => 5, + 'increments' => +3, + 'decrements' => 1, + 'difference' => 2, + 'start' => now()->subWeeks(1)->startOfWeek(), + 'end' => now()->startOfWeek(), + ], + ]; + + $this->assertEquals($expected, $stats->toArray()); + } + + + /** @test */ + public function it_can_get_stats_for_object_instance() + { + StatsWriter::for(StatsEvent::class)->set(3, [], now()->subMonth()); + StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(13)); + StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(12)); + StatsWriter::for(StatsEvent::class)->set(3, [], now()->subDays(6)); + StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(5)); + StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(4)); + + $stats = StatsQuery::for(new StatsEvent()) + ->start(now()->subWeeks(2)) + ->end(now()->startOfWeek()) + ->groupByWeek() + ->get(); + + $expected = [ + [ + 'value' => 5, + 'increments' => +3, + 'decrements' => 1, + 'difference' => 2, + 'start' => now()->subWeeks(2)->startOfWeek(), + 'end' => now()->subWeeks(1)->startOfWeek(), + ], + [ + 'value' => 5, + 'increments' => +3, + 'decrements' => 1, + 'difference' => 2, + 'start' => now()->subWeeks(1)->startOfWeek(), + 'end' => now()->startOfWeek(), + ], + ]; + + $this->assertEquals($expected, $stats->toArray()); + } + + /** @test */ + public function it_can_get_stats_for_has_many_relationship() + { + /** @var Stat $stat */ + $stat = Stat::create(); + + StatsWriter::for($stat->events())->set(3, [], now()->subMonth()); + StatsWriter::for($stat->events())->decrease(1, [], now()->subDays(13)); + StatsWriter::for($stat->events())->increase(3, [], now()->subDays(12)); + StatsWriter::for($stat->events())->set(3, [], now()->subDays(6)); + StatsWriter::for($stat->events())->decrease(1, [], now()->subDays(5)); + StatsWriter::for($stat->events())->increase(3, [], now()->subDays(4)); + + $stats = StatsQuery::for($stat->events()) ->start(now()->subWeeks(2)) ->end(now()->startOfWeek()) ->groupByWeek() @@ -56,13 +138,13 @@ public function it_can_get_stats() /** @test */ public function it_can_get_stats_2() { - OrderStats::increase(100, now()->subMonth()); - OrderStats::decrease(1, now()->subDays(13)); - OrderStats::increase(3, now()->subDays(12)); - OrderStats::decrease(1, now()->subDays(5)); - OrderStats::increase(3, now()->subDays(4)); + StatsWriter::for(StatsEvent::class)->increase(100, [], now()->subMonth()); + StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(13)); + StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(12)); + StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(5)); + StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(4)); - $stats = StatsQuery::for(OrderStats::class) + $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subWeeks(2)) ->end(now()->startOfWeek()) ->groupByWeek() @@ -93,9 +175,9 @@ public function it_can_get_stats_2() /** @test */ public function it_can_get_stats_3() { - OrderStats::increase(3, now()->subDays(12)); + StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(12)); - $stats = StatsQuery::for(OrderStats::class) + $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subWeeks(2)) ->end(now()->startOfWeek()) ->groupByWeek() @@ -126,7 +208,7 @@ public function it_can_get_stats_3() /** @test */ public function it_can_get_stats_4() { - $stats = StatsQuery::for(OrderStats::class) + $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subWeeks(2)) ->end(now()->startOfWeek()) ->groupByWeek() @@ -157,11 +239,11 @@ public function it_can_get_stats_4() /** @test */ public function it_can_get_stats_grouped_by_day() { - OrderStats::set(3, now()->subDays(6)); - OrderStats::decrease(1, now()->subDays(2)); - OrderStats::increase(3, now()->subDays(1)); + StatsWriter::for(StatsEvent::class)->set(3, [], now()->subDays(6)); + StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(2)); + StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(1)); - $stats = StatsQuery::for(OrderStats::class) + $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subDays(3)) ->end(now()) ->groupByDay() @@ -200,11 +282,11 @@ public function it_can_get_stats_grouped_by_day() /** @test */ public function it_can_get_stats_grouped_by_hour() { - OrderStats::set(3, now()->subHours(6)); - OrderStats::decrease(1, now()->subHours(2)); - OrderStats::increase(3, now()->subHours(1)); + StatsWriter::for(StatsEvent::class)->set(3, [], now()->subHours(6)); + StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subHours(2)); + StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subHours(1)); - $stats = StatsQuery::for(OrderStats::class) + $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subHours(3)) ->end(now()) ->groupByHour() @@ -243,15 +325,15 @@ public function it_can_get_stats_grouped_by_hour() /** @test */ public function it_can_get_stats_based_on_youngest_sets_in_periods() { - OrderStats::set(1, now()->subHours(49)); - OrderStats::set(2, now()->subHours(37)); - OrderStats::set(3, now()->subHours(25)); // This set will be used for day 1 - OrderStats::decrease(2, now()->subHours(16)); // These decrements and increments will still show up in day 2 - OrderStats::set(4, now()->subHours(13)); - OrderStats::increase(4, now()->subHours(8)); - OrderStats::set(5, now()->subHours(1)); // This set will be used for day 2 - - $stats = StatsQuery::for(OrderStats::class) + StatsWriter::for(StatsEvent::class)->set(1, [], now()->subHours(49)); + StatsWriter::for(StatsEvent::class)->set(2, [], now()->subHours(37)); + StatsWriter::for(StatsEvent::class)->set(3, [], now()->subHours(25)); // This set will be used for day 1 + StatsWriter::for(StatsEvent::class)->decrease(2, [], now()->subHours(16)); // These decrements and increments will still show up in day 2 + StatsWriter::for(StatsEvent::class)->set(4, [], now()->subHours(13)); + StatsWriter::for(StatsEvent::class)->increase(4, [], now()->subHours(8)); + StatsWriter::for(StatsEvent::class)->set(5, [], now()->subHours(1)); // This set will be used for day 2 + + $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subDays(2)) ->end(now()) ->groupByDay() From 2524df9e119d270ceb89b4cf8fb6822d5678fa24 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Sun, 20 Feb 2022 22:53:14 +0100 Subject: [PATCH 06/17] Added backward-compatibility adjustments for BaseStats usage --- src/BaseStats.php | 30 +++++++--------------- src/StatsQuery.php | 39 +++++++++++++++++----------- tests/BaseStatsTest.php | 40 ++++++++++++++--------------- tests/Stats/CustomerStats.php | 9 +++++++ tests/StatsQueryTest.php | 48 +++++++++++++++++++++++++++++++++++ tests/StatsWriterTest.php | 21 +++++++++++++++ 6 files changed, 131 insertions(+), 56 deletions(-) create mode 100644 tests/Stats/CustomerStats.php diff --git a/src/BaseStats.php b/src/BaseStats.php index 03b92a3..ee43c09 100644 --- a/src/BaseStats.php +++ b/src/BaseStats.php @@ -14,41 +14,29 @@ public function getName(): string public static function query(): StatsQuery { - return new StatsQuery(static::class); + return StatsQuery::for(StatsEvent::class, [ + 'name' => (new static)->getName(), + ]); } public static function increase(mixed $number = 1, ?DateTimeInterface $timestamp = null) { - $number = is_int($number) ? $number : 1; - - $stats = new static; + $model = new static(); - $stats->createEvent(DataPoint::TYPE_CHANGE, $number, $timestamp); + StatsWriter::for(StatsEvent::class)->increase($number, ['name' => $model->getName()], $timestamp); } public static function decrease(mixed $number = 1, ?DateTimeInterface $timestamp = null) { - $number = is_int($number) ? $number : 1; + $model = new static(); - $stats = new static; - - $stats->createEvent(DataPoint::TYPE_CHANGE, -$number, $timestamp); + StatsWriter::for(StatsEvent::class)->decrease($number, ['name' => $model->getName()], $timestamp); } public static function set(int $value, ?DateTimeInterface $timestamp = null) { - $stats = new static; + $model = new static(); - $stats->createEvent(DataPoint::TYPE_SET, $value, $timestamp); - } - - protected function createEvent($type, $value, ?DateTimeInterface $timestamp = null): StatsEvent - { - return StatsEvent::create([ - 'name' => $this->getName(), - 'type' => $type, - 'value' => $value, - 'created_at' => $timestamp ?? now(), - ]); + StatsWriter::for(StatsEvent::class)->set($value, ['name' => $model->getName()], $timestamp); } } diff --git a/src/StatsQuery.php b/src/StatsQuery.php index 2aeb9ee..e719c49 100644 --- a/src/StatsQuery.php +++ b/src/StatsQuery.php @@ -12,7 +12,9 @@ class StatsQuery { - private Model|Relation|string $statistic; + private Model|Relation|string $subject; + + private array $attributes = []; protected string $period; @@ -20,9 +22,11 @@ class StatsQuery protected DateTimeInterface $end; - public function __construct(Model|Relation|string $statistic) + public function __construct(Model|Relation|string $subject, array $attributes = []) { - $this->statistic = $statistic; + $this->subject = $subject; + + $this->attributes = $attributes; $this->period = 'week'; @@ -31,9 +35,9 @@ public function __construct(Model|Relation|string $statistic) $this->end = now(); } - public static function for(Model|Relation|string $statistic): self + public static function for(Model|Relation|string $subject, array $attributes = []): self { - return new self($statistic); + return new self($subject, $attributes); } public static function getPeriodDateFormat(string $period): string @@ -201,19 +205,24 @@ public function getPeriodTimestampFormat(): string }; } + public function getAttributes(): array + { + return $this->attributes; + } + protected function queryStats(): Builder { - if ($this->statistic instanceof Relation) { - return $this->statistic->getQuery()->clone(); + if ($this->subject instanceof Relation) { + return $this->subject->getQuery()->clone(); } /** @var Model $subject */ - $subject = $this->statistic; + $subject = $this->subject; if (is_string($subject) && class_exists($subject)) { $subject = new $subject; } - return $subject->newQuery(); + return $subject->newQuery()->where($this->attributes); } protected function getDifferencesPerPeriod(): EloquentCollection @@ -251,12 +260,12 @@ protected function getLatestSetPerPeriod(): EloquentCollection protected function getStatsKey(): string { - if ($this->statistic instanceof Relation) { - return $this->statistic->getRelated()->getKeyName(); + if ($this->subject instanceof Relation) { + return $this->subject->getRelated()->getKeyName(); } /** @var Model $subject */ - $subject = $this->statistic; + $subject = $this->subject; if (is_string($subject) && class_exists($subject)) { $subject = new $subject; } @@ -266,12 +275,12 @@ protected function getStatsKey(): string protected function getStatsTableName(): string { - if ($this->statistic instanceof Relation) { - return $this->statistic->getRelated()->getTable(); + if ($this->subject instanceof Relation) { + return $this->subject->getRelated()->getTable(); } /** @var Model $subject */ - $subject = $this->statistic; + $subject = $this->subject; if (is_string($subject) && class_exists($subject)) { $subject = new $subject; } diff --git a/tests/BaseStatsTest.php b/tests/BaseStatsTest.php index 9619f57..e99d36e 100644 --- a/tests/BaseStatsTest.php +++ b/tests/BaseStatsTest.php @@ -102,26 +102,26 @@ public function it_can_create_events_for_setting_fixed_values_at_a_given_timesta ]); } -// /** @test */ -// public function it_can_get_a_stats_query_object() -// { -// $query = OrderStats::query(); -// -// $this->assertInstanceOf(StatsQuery::class, $query); -// $this->assertInstanceOf(OrderStats::class, $query->getStatistic()); -// } - -// /** @test */ -// public function it_can_get_the_value_at_a_given_time() -// { -// OrderStats::set(3, now()->subDays(19)); -// OrderStats::decrease(1, now()->subDays(4)); -// OrderStats::increase(3, now()->subDays(2)); -// -// $this->assertEquals(0, StatsQuery::for(OrderStats::class)->getValue(now()->subDays(30))); -// $this->assertEquals(3, StatsQuery::for(OrderStats::class)->getValue(now()->subDays(18))); -// $this->assertEquals(5, StatsQuery::for(OrderStats::class)->getValue(now())); -// } + /** @test */ + public function it_can_get_a_stats_query_object() + { + $query = OrderStats::query(); + + $this->assertInstanceOf(StatsQuery::class, $query); + $this->assertSame(['name' => 'OrderStats'], $query->getAttributes()); + } + + /** @test */ + public function it_can_get_the_value_at_a_given_time() + { + OrderStats::set(3, now()->subDays(19)); + OrderStats::decrease(1, now()->subDays(4)); + OrderStats::increase(3, now()->subDays(2)); + + $this->assertEquals(0, OrderStats::query()->getValue(now()->subDays(30))); + $this->assertEquals(3, OrderStats::query()->getValue(now()->subDays(18))); + $this->assertEquals(5, OrderStats::query()->getValue(now())); + } /** @test */ public function it_can_generate_and_array_of_periods() diff --git a/tests/Stats/CustomerStats.php b/tests/Stats/CustomerStats.php new file mode 100644 index 0000000..de9553a --- /dev/null +++ b/tests/Stats/CustomerStats.php @@ -0,0 +1,9 @@ +subMonth()); + CustomerStats::decrease(1, now()->subDays(13)); + CustomerStats::increase(3, now()->subDays(12)); + CustomerStats::set(3, now()->subDays(6)); + CustomerStats::decrease(1, now()->subDays(5)); + CustomerStats::increase(3, now()->subDays(4)); + + OrderStats::set(3, now()->subMonth()); + OrderStats::decrease(1, now()->subDays(13)); + OrderStats::increase(3, now()->subDays(12)); + OrderStats::set(3, now()->subDays(6)); + OrderStats::decrease(1, now()->subDays(5)); + OrderStats::increase(3, now()->subDays(4)); + + $stats = OrderStats::query() + ->start(now()->subWeeks(2)) + ->end(now()->startOfWeek()) + ->groupByWeek() + ->get(); + + $expected = [ + [ + 'value' => 5, + 'increments' => +3, + 'decrements' => 1, + 'difference' => 2, + 'start' => now()->subWeeks(2)->startOfWeek(), + 'end' => now()->subWeeks(1)->startOfWeek(), + ], + [ + 'value' => 5, + 'increments' => +3, + 'decrements' => 1, + 'difference' => 2, + 'start' => now()->subWeeks(1)->startOfWeek(), + 'end' => now()->startOfWeek(), + ], + ]; + + $this->assertEquals($expected, $stats->toArray()); + } + /** @test */ public function it_can_get_stats_for_classname() { diff --git a/tests/StatsWriterTest.php b/tests/StatsWriterTest.php index 6f7bd0a..fcfa52d 100644 --- a/tests/StatsWriterTest.php +++ b/tests/StatsWriterTest.php @@ -6,6 +6,8 @@ use Spatie\Stats\Models\Stat; use Spatie\Stats\Models\StatsEvent; use Spatie\Stats\StatsWriter; +use Spatie\Stats\Tests\Stats\CustomerStats; +use Spatie\Stats\Tests\Stats\OrderStats; class StatsWriterTest extends TestCase { @@ -16,6 +18,25 @@ protected function setUp(): void Carbon::setTestNow('2020-01-01'); } + /** @test */ + public function it_can_write_with_base_stats_extensions() + { + CustomerStats::increase(); + OrderStats::increase(); + + $this->assertDatabaseHas('stats_events', [ + 'name' => 'CustomerStats', + 'value' => 1, + 'type' => 'change', + ]); + + $this->assertDatabaseHas('stats_events', [ + 'name' => 'OrderStats', + 'value' => 1, + 'type' => 'change', + ]); + } + /** @test */ public function it_can_create_events_for_class_names() { From 18e47b3acd16473f9540cbf02d7ed946184bfacd Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Sun, 20 Feb 2022 23:10:31 +0100 Subject: [PATCH 07/17] Added CHANGELOG.md and extended README.md with usage examples --- CHANGELOG.md | 18 ++++++++++++++++++ README.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e1b877..09a1f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ All notable changes to `laravel-stats` will be documented in this file +## 2.0.0 - 2022-02-20 + +### Added + +- Added `StatsWriter` with relationship-support (`StatsWriter::for($model->relationship())`) +- Extended `StatsQuery` with relationship-support (`StatsQuery::for($model->relationship())`) + +### BC breaks + +- Replaced `StatsQuery::for($model)->getStatistic()` with `StatsQuery::for($model)->getAttributes()` +- Removed `BaseStats->createEvent()` + +Migrations: + +- Replace `StatsQuery::for(OrderStats::class)` with `OrderStats::qurey()` +- `StatsEvent::TYPE_SET` use `DataPoint::TYPE_SET` instaed +- `StatsEvent::TYPE_CHANGE` use `DataPoint::TYPE_CHANGE` instead + ## 1.0.1 - 2022-02-02 - Add support for Laravel 9 diff --git a/README.md b/README.md index 04cf1bf..ef9d078 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ grouped by week. ```php use Spatie\Stats\StatsQuery; -$stats = StatsQuery::for(SubscriptionStats::class) +$stats = SubscriptionStats::query() ->start(now()->subMonths(2)) ->end(now()->subSecond()) ->groupByWeek() @@ -171,6 +171,47 @@ This will return an array containing arrayable `Spatie\Stats\DataPoint` objects. ] ``` +## Extended Use-Cases + +### Read and Write from a custom Model + +* Create a new table with `type (string)`, `value (bigInt)`, `created_at`, `updated_at` fields +* Create a model and add `HasStats`-trait + +```php +StatsWriter::for(MyCustomModel::class)->set(123) +StatsWriter::for(MyCustomModel::class)->increment(1, ['additional_column' => '123']) +StatsWriter::for(MyCustomModel::class)->decrement(1, ['additional_column' => '123'], now()->subDay()) + +$stats = StatsQuery::for(MyCustomModel::class) + ->start(now()->subMonths(2)) + ->end(now()->subSecond()) + ->groupByWeek() + ->get(); + +// OR + +$stats = StatsQuery::for(MyCustomModel::class, ['additional_column' => '123']) + ->start(now()->subMonths(2)) + ->end(now()->subSecond()) + ->groupByWeek() + ->get(); +``` + +### Read and Write from a HasMany-Relationship + +```php +$tenant = Tenant::find(1) + +StatsWriter::for($tenant->orderStats())->increment() + +$stats = StatsQuery::for($tenant->orderStats()) + ->start(now()->subMonths(2)) + ->end(now()->subSecond()) + ->groupByWeek() + ->get(); +``` + ## Testing ``` bash From 491134bf13ae6da56ab84400354a838dfa25bdf0 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Sun, 20 Feb 2022 23:41:55 +0100 Subject: [PATCH 08/17] Added tests for additonal attributes --- README.md | 4 +-- src/StatsQuery.php | 2 +- tests/StatsQueryTest.php | 55 +++++++++++++++++++++++++++++++++++++++ tests/StatsWriterTest.php | 16 ++++++++++++ 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ef9d078..9457cbf 100644 --- a/README.md +++ b/README.md @@ -203,9 +203,9 @@ $stats = StatsQuery::for(MyCustomModel::class, ['additional_column' => '123']) ```php $tenant = Tenant::find(1) -StatsWriter::for($tenant->orderStats())->increment() +StatsWriter::for($tenant->orderStats())->increment(1, ['additional_column' => 'recurring']) -$stats = StatsQuery::for($tenant->orderStats()) +$stats = StatsQuery::for($tenant->orderStats(), ['additional_column' => 'recurring']) ->start(now()->subMonths(2)) ->end(now()->subSecond()) ->groupByWeek() diff --git a/src/StatsQuery.php b/src/StatsQuery.php index e719c49..98a8896 100644 --- a/src/StatsQuery.php +++ b/src/StatsQuery.php @@ -213,7 +213,7 @@ public function getAttributes(): array protected function queryStats(): Builder { if ($this->subject instanceof Relation) { - return $this->subject->getQuery()->clone(); + return $this->subject->getQuery()->clone()->where($this->attributes); } /** @var Model $subject */ diff --git a/tests/StatsQueryTest.php b/tests/StatsQueryTest.php index fdbc68c..65e06b8 100644 --- a/tests/StatsQueryTest.php +++ b/tests/StatsQueryTest.php @@ -284,6 +284,61 @@ public function it_can_get_stats_4() $this->assertEquals($expected, $stats->toArray()); } + /** @test */ + public function it_can_get_stats_by_attributes() + { + StatsWriter::for(StatsEvent::class)->increase(1, ['name' => 'one-off'], now()->hour(12)); + StatsWriter::for(StatsEvent::class)->increase(1, ['name' => 'recurring'], now()->hour(12)); + + $stats = StatsQuery::for(StatsEvent::class, ['name' => 'recurring']) + ->start(now()->startOfDay()) + ->end(now()->endOfDay()) + ->groupByDay() + ->get(); + + $expected = [ + [ + 'value' => 1, + 'increments' => 1, + 'decrements' => 0, + 'difference' => 1, + 'start' => now()->startOfDay(), + 'end' => now()->endOfDay()->addMicro(), + ], + ]; + + $this->assertEquals($expected, $stats->toArray()); + } + + /** @test */ + public function it_can_get_stats_by_attributes_for_has_many_relationship() + { + /** @var Stat $stat */ + $stat = Stat::create(); + + StatsWriter::for($stat->events())->increase(1, ['name' => 'one-off'], now()->hour(12)); + StatsWriter::for($stat->events())->increase(1, ['name' => 'recurring'], now()->hour(12)); + + $stats = StatsQuery::for($stat->events(), ['name' => 'recurring']) + ->start(now()->startOfDay()) + ->end(now()->endOfDay()) + ->groupByDay() + ->get(); + + $expected = [ + [ + 'value' => 1, + 'increments' => 1, + 'decrements' => 0, + 'difference' => 1, + 'start' => now()->startOfDay(), + 'end' => now()->endOfDay()->addMicro(), + ], + ]; + + $this->assertEquals($expected, $stats->toArray()); + } + /** @test */ public function it_can_get_stats_grouped_by_day() { diff --git a/tests/StatsWriterTest.php b/tests/StatsWriterTest.php index fcfa52d..36252b8 100644 --- a/tests/StatsWriterTest.php +++ b/tests/StatsWriterTest.php @@ -74,6 +74,22 @@ public function it_can_create_events_for_has_many_relationships() ]); } + /** @test */ + public function it_can_create_events_for_has_many_relationships_with_custom_attributes() + { + /** @var Stat $stats */ + $stats = Stat::create(); + + StatsWriter::for($stats->events())->increase(1, ['name' => 'recurring']); + + $this->assertDatabaseHas('stats_events', [ + 'stat_id' => $stats->getKey(), + 'name' => 'recurring', + 'value' => 1, + 'type' => 'change', + ]); + } + /** @test */ public function it_can_create_events_with_custom_attributes() { From 41001289567caf75ab8e669950d25ddfcc9ecaf0 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Sun, 20 Feb 2022 23:43:43 +0100 Subject: [PATCH 09/17] Extended CHANGELOG.md with second argument for StatsQuery --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09a1f67..07c3898 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to `laravel-stats` will be documented in this file - Added `StatsWriter` with relationship-support (`StatsWriter::for($model->relationship())`) - Extended `StatsQuery` with relationship-support (`StatsQuery::for($model->relationship())`) +- Extended `StatsQuery` with additional attributes (`StatsQuery::for(StatsEvent::class, ['name' => 'OrderStats'])`) ### BC breaks From 101be73405456c160af0894192006c4035555946 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Mon, 21 Feb 2022 00:23:03 +0100 Subject: [PATCH 10/17] createEvent should return dynamic Eloquent-Model instead of StatsEvent --- src/StatsWriter.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/StatsWriter.php b/src/StatsWriter.php index f1182e9..c8f6c44 100644 --- a/src/StatsWriter.php +++ b/src/StatsWriter.php @@ -5,7 +5,6 @@ use DateTimeInterface; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Relation; -use Spatie\Stats\Models\StatsEvent; class StatsWriter { @@ -40,7 +39,7 @@ public function set(int $value, array $attributes = [], ?DateTimeInterface $time $this->createEvent(DataPoint::TYPE_SET, $value, $attributes, $timestamp); } - protected function createEvent($type, $value, array $attributes = [], ?DateTimeInterface $timestamp = null): StatsEvent + protected function createEvent($type, $value, array $attributes = [], ?DateTimeInterface $timestamp = null): Model { if ($this->subject instanceof Relation) { return $this->subject->create(array_merge($attributes, [ From 8681ba52b2f924cd491de698f6f7a4ea544a98ff Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Mon, 28 Feb 2022 15:58:56 +0100 Subject: [PATCH 11/17] Moved attribute usage as constructor parameters ; while running tests in a real-world application it seems more expressive by adding it on constructor-level rather than on method-level --- CHANGELOG.md | 11 ++++-- README.md | 8 ++-- src/BaseStats.php | 6 +-- src/StatsWriter.php | 26 +++++++------ tests/StatsQueryTest.php | 82 +++++++++++++++++++-------------------- tests/StatsWriterTest.php | 10 ++--- 6 files changed, 74 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c3898..2bdb62c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,10 @@ All notable changes to `laravel-stats` will be documented in this file ### Added -- Added `StatsWriter` with relationship-support (`StatsWriter::for($model->relationship())`) +- Added `StatsWriter` with classname support (`StatsWriter::for(MyModel::class)`) +- Added `StatsWriter` with eloquent-model support (`StatsWriter::for($eloquent)`) +- Added `StatsWriter` with "has-many"-relationship support (`StatsWriter::for($model->relationship())`) - other relationships are untested yet +- Added `StatsWriter` with custom-attribute support (`StatsWriter::for(MyModel::class, ['custom_column' => 'orders])`) - Extended `StatsQuery` with relationship-support (`StatsQuery::for($model->relationship())`) - Extended `StatsQuery` with additional attributes (`StatsQuery::for(StatsEvent::class, ['name' => 'OrderStats'])`) @@ -17,9 +20,9 @@ All notable changes to `laravel-stats` will be documented in this file Migrations: -- Replace `StatsQuery::for(OrderStats::class)` with `OrderStats::qurey()` -- `StatsEvent::TYPE_SET` use `DataPoint::TYPE_SET` instaed -- `StatsEvent::TYPE_CHANGE` use `DataPoint::TYPE_CHANGE` instead +- Replace `StatsQuery::for(OrderStats::class)` with `OrderStats::query()` +- Replace `StatsEvent::TYPE_SET` use `DataPoint::TYPE_SET` instead +- Replace `StatsEvent::TYPE_CHANGE` use `DataPoint::TYPE_CHANGE` instead ## 1.0.1 - 2022-02-02 diff --git a/README.md b/README.md index 9457cbf..12ababa 100644 --- a/README.md +++ b/README.md @@ -180,8 +180,8 @@ This will return an array containing arrayable `Spatie\Stats\DataPoint` objects. ```php StatsWriter::for(MyCustomModel::class)->set(123) -StatsWriter::for(MyCustomModel::class)->increment(1, ['additional_column' => '123']) -StatsWriter::for(MyCustomModel::class)->decrement(1, ['additional_column' => '123'], now()->subDay()) +StatsWriter::for(MyCustomModel::class, ['custom_column' => '123'])->increment(1) +StatsWriter::for(MyCustomModel::class, ['another_column' => '234'])->decrement(1, now()->subDay()) $stats = StatsQuery::for(MyCustomModel::class) ->start(now()->subMonths(2)) @@ -203,9 +203,9 @@ $stats = StatsQuery::for(MyCustomModel::class, ['additional_column' => '123']) ```php $tenant = Tenant::find(1) -StatsWriter::for($tenant->orderStats())->increment(1, ['additional_column' => 'recurring']) +StatsWriter::for($tenant->orderStats(), ['payment_type_column' => 'recurring'])->increment(1) -$stats = StatsQuery::for($tenant->orderStats(), ['additional_column' => 'recurring']) +$stats = StatsQuery::for($tenant->orderStats(), , ['payment_type_column' => 'recurring']) ->start(now()->subMonths(2)) ->end(now()->subSecond()) ->groupByWeek() diff --git a/src/BaseStats.php b/src/BaseStats.php index ee43c09..b64933f 100644 --- a/src/BaseStats.php +++ b/src/BaseStats.php @@ -23,20 +23,20 @@ public static function increase(mixed $number = 1, ?DateTimeInterface $timestamp { $model = new static(); - StatsWriter::for(StatsEvent::class)->increase($number, ['name' => $model->getName()], $timestamp); + StatsWriter::for(StatsEvent::class, ['name' => $model->getName()])->increase($number, $timestamp); } public static function decrease(mixed $number = 1, ?DateTimeInterface $timestamp = null) { $model = new static(); - StatsWriter::for(StatsEvent::class)->decrease($number, ['name' => $model->getName()], $timestamp); + StatsWriter::for(StatsEvent::class, ['name' => $model->getName()])->decrease($number, $timestamp); } public static function set(int $value, ?DateTimeInterface $timestamp = null) { $model = new static(); - StatsWriter::for(StatsEvent::class)->set($value, ['name' => $model->getName()], $timestamp); + StatsWriter::for(StatsEvent::class, ['name' => $model->getName()])->set($value, $timestamp); } } diff --git a/src/StatsWriter.php b/src/StatsWriter.php index c8f6c44..4eab70a 100644 --- a/src/StatsWriter.php +++ b/src/StatsWriter.php @@ -9,40 +9,42 @@ class StatsWriter { private Model|Relation|string $subject; + private array $attributes; - public function __construct(Model|Relation|string $subject) + public function __construct(Model|Relation|string $subject, array $attributes = []) { $this->subject = $subject; + $this->attributes = $attributes; } - public static function for($subject) + public static function for(Model|Relation|string $subject, array $attributes = []) { - return new static($subject); + return new static($subject, $attributes); } - public function increase(mixed $number = 1, array $attributes = [], ?DateTimeInterface $timestamp = null) + public function increase(mixed $number = 1, ?DateTimeInterface $timestamp = null) { $number = is_int($number) ? $number : 1; - $this->createEvent(DataPoint::TYPE_CHANGE, $number, $attributes, $timestamp); + $this->createEvent(DataPoint::TYPE_CHANGE, $number, $timestamp); } - public function decrease(mixed $number = 1, array $attributes = [], ?DateTimeInterface $timestamp = null) + public function decrease(mixed $number = 1, ?DateTimeInterface $timestamp = null) { $number = is_int($number) ? $number : 1; - $this->createEvent(DataPoint::TYPE_CHANGE, -$number, $attributes, $timestamp); + $this->createEvent(DataPoint::TYPE_CHANGE, -$number, $timestamp); } - public function set(int $value, array $attributes = [], ?DateTimeInterface $timestamp = null) + public function set(int $value, ?DateTimeInterface $timestamp = null) { - $this->createEvent(DataPoint::TYPE_SET, $value, $attributes, $timestamp); + $this->createEvent(DataPoint::TYPE_SET, $value, $timestamp); } - protected function createEvent($type, $value, array $attributes = [], ?DateTimeInterface $timestamp = null): Model + protected function createEvent($type, $value, ?DateTimeInterface $timestamp = null): Model { if ($this->subject instanceof Relation) { - return $this->subject->create(array_merge($attributes, [ + return $this->subject->create(array_merge($this->attributes, [ 'type' => $type, 'value' => $value, 'created_at' => $timestamp ?? now(), @@ -54,7 +56,7 @@ protected function createEvent($type, $value, array $attributes = [], ?DateTimeI $subject = get_class($subject); } - return $subject::create(array_merge($attributes, [ + return $subject::create(array_merge($this->attributes, [ 'type' => $type, 'value' => $value, 'created_at' => $timestamp ?? now(), diff --git a/tests/StatsQueryTest.php b/tests/StatsQueryTest.php index 65e06b8..3e4addb 100644 --- a/tests/StatsQueryTest.php +++ b/tests/StatsQueryTest.php @@ -68,12 +68,12 @@ public function it_can_get_stats_for_base_stats_class() /** @test */ public function it_can_get_stats_for_classname() { - StatsWriter::for(StatsEvent::class)->set(3, [], now()->subMonth()); - StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(13)); - StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(12)); - StatsWriter::for(StatsEvent::class)->set(3, [], now()->subDays(6)); - StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(5)); - StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(4)); + StatsWriter::for(StatsEvent::class)->set(3, now()->subMonth()); + StatsWriter::for(StatsEvent::class)->decrease(1, now()->subDays(13)); + StatsWriter::for(StatsEvent::class)->increase(3, now()->subDays(12)); + StatsWriter::for(StatsEvent::class)->set(3, now()->subDays(6)); + StatsWriter::for(StatsEvent::class)->decrease(1, now()->subDays(5)); + StatsWriter::for(StatsEvent::class)->increase(3, now()->subDays(4)); $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subWeeks(2)) @@ -107,12 +107,12 @@ public function it_can_get_stats_for_classname() /** @test */ public function it_can_get_stats_for_object_instance() { - StatsWriter::for(StatsEvent::class)->set(3, [], now()->subMonth()); - StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(13)); - StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(12)); - StatsWriter::for(StatsEvent::class)->set(3, [], now()->subDays(6)); - StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(5)); - StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(4)); + StatsWriter::for(StatsEvent::class)->set(3, now()->subMonth()); + StatsWriter::for(StatsEvent::class)->decrease(1, now()->subDays(13)); + StatsWriter::for(StatsEvent::class)->increase(3, now()->subDays(12)); + StatsWriter::for(StatsEvent::class)->set(3, now()->subDays(6)); + StatsWriter::for(StatsEvent::class)->decrease(1, now()->subDays(5)); + StatsWriter::for(StatsEvent::class)->increase(3, now()->subDays(4)); $stats = StatsQuery::for(new StatsEvent()) ->start(now()->subWeeks(2)) @@ -148,12 +148,12 @@ public function it_can_get_stats_for_has_many_relationship() /** @var Stat $stat */ $stat = Stat::create(); - StatsWriter::for($stat->events())->set(3, [], now()->subMonth()); - StatsWriter::for($stat->events())->decrease(1, [], now()->subDays(13)); - StatsWriter::for($stat->events())->increase(3, [], now()->subDays(12)); - StatsWriter::for($stat->events())->set(3, [], now()->subDays(6)); - StatsWriter::for($stat->events())->decrease(1, [], now()->subDays(5)); - StatsWriter::for($stat->events())->increase(3, [], now()->subDays(4)); + StatsWriter::for($stat->events())->set(3, now()->subMonth()); + StatsWriter::for($stat->events())->decrease(1, now()->subDays(13)); + StatsWriter::for($stat->events())->increase(3, now()->subDays(12)); + StatsWriter::for($stat->events())->set(3, now()->subDays(6)); + StatsWriter::for($stat->events())->decrease(1, now()->subDays(5)); + StatsWriter::for($stat->events())->increase(3, now()->subDays(4)); $stats = StatsQuery::for($stat->events()) ->start(now()->subWeeks(2)) @@ -186,11 +186,11 @@ public function it_can_get_stats_for_has_many_relationship() /** @test */ public function it_can_get_stats_2() { - StatsWriter::for(StatsEvent::class)->increase(100, [], now()->subMonth()); - StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(13)); - StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(12)); - StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(5)); - StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(4)); + StatsWriter::for(StatsEvent::class)->increase(100, now()->subMonth()); + StatsWriter::for(StatsEvent::class)->decrease(1, now()->subDays(13)); + StatsWriter::for(StatsEvent::class)->increase(3, now()->subDays(12)); + StatsWriter::for(StatsEvent::class)->decrease(1, now()->subDays(5)); + StatsWriter::for(StatsEvent::class)->increase(3, now()->subDays(4)); $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subWeeks(2)) @@ -223,7 +223,7 @@ public function it_can_get_stats_2() /** @test */ public function it_can_get_stats_3() { - StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(12)); + StatsWriter::for(StatsEvent::class)->increase(3, now()->subDays(12)); $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subWeeks(2)) @@ -287,8 +287,8 @@ public function it_can_get_stats_4() /** @test */ public function it_can_get_stats_by_attributes() { - StatsWriter::for(StatsEvent::class)->increase(1, ['name' => 'one-off'], now()->hour(12)); - StatsWriter::for(StatsEvent::class)->increase(1, ['name' => 'recurring'], now()->hour(12)); + StatsWriter::for(StatsEvent::class, ['name' => 'one-off'])->increase(1, now()->hour(12)); + StatsWriter::for(StatsEvent::class, ['name' => 'recurring'])->increase(1, now()->hour(12)); $stats = StatsQuery::for(StatsEvent::class, ['name' => 'recurring']) ->start(now()->startOfDay()) @@ -316,8 +316,8 @@ public function it_can_get_stats_by_attributes_for_has_many_relationship() /** @var Stat $stat */ $stat = Stat::create(); - StatsWriter::for($stat->events())->increase(1, ['name' => 'one-off'], now()->hour(12)); - StatsWriter::for($stat->events())->increase(1, ['name' => 'recurring'], now()->hour(12)); + StatsWriter::for($stat->events(), ['name' => 'one-off'])->increase(1, now()->hour(12)); + StatsWriter::for($stat->events(), ['name' => 'recurring'])->increase(1, now()->hour(12)); $stats = StatsQuery::for($stat->events(), ['name' => 'recurring']) ->start(now()->startOfDay()) @@ -342,9 +342,9 @@ public function it_can_get_stats_by_attributes_for_has_many_relationship() /** @test */ public function it_can_get_stats_grouped_by_day() { - StatsWriter::for(StatsEvent::class)->set(3, [], now()->subDays(6)); - StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subDays(2)); - StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subDays(1)); + StatsWriter::for(StatsEvent::class)->set(3, now()->subDays(6)); + StatsWriter::for(StatsEvent::class)->decrease(1, now()->subDays(2)); + StatsWriter::for(StatsEvent::class)->increase(3, now()->subDays(1)); $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subDays(3)) @@ -385,9 +385,9 @@ public function it_can_get_stats_grouped_by_day() /** @test */ public function it_can_get_stats_grouped_by_hour() { - StatsWriter::for(StatsEvent::class)->set(3, [], now()->subHours(6)); - StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subHours(2)); - StatsWriter::for(StatsEvent::class)->increase(3, [], now()->subHours(1)); + StatsWriter::for(StatsEvent::class)->set(3, now()->subHours(6)); + StatsWriter::for(StatsEvent::class)->decrease(1, now()->subHours(2)); + StatsWriter::for(StatsEvent::class)->increase(3, now()->subHours(1)); $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subHours(3)) @@ -428,13 +428,13 @@ public function it_can_get_stats_grouped_by_hour() /** @test */ public function it_can_get_stats_based_on_youngest_sets_in_periods() { - StatsWriter::for(StatsEvent::class)->set(1, [], now()->subHours(49)); - StatsWriter::for(StatsEvent::class)->set(2, [], now()->subHours(37)); - StatsWriter::for(StatsEvent::class)->set(3, [], now()->subHours(25)); // This set will be used for day 1 - StatsWriter::for(StatsEvent::class)->decrease(2, [], now()->subHours(16)); // These decrements and increments will still show up in day 2 - StatsWriter::for(StatsEvent::class)->set(4, [], now()->subHours(13)); - StatsWriter::for(StatsEvent::class)->increase(4, [], now()->subHours(8)); - StatsWriter::for(StatsEvent::class)->set(5, [], now()->subHours(1)); // This set will be used for day 2 + StatsWriter::for(StatsEvent::class)->set(1, now()->subHours(49)); + StatsWriter::for(StatsEvent::class)->set(2, now()->subHours(37)); + StatsWriter::for(StatsEvent::class)->set(3, now()->subHours(25)); // This set will be used for day 1 + StatsWriter::for(StatsEvent::class)->decrease(2, now()->subHours(16)); // These decrements and increments will still show up in day 2 + StatsWriter::for(StatsEvent::class)->set(4, now()->subHours(13)); + StatsWriter::for(StatsEvent::class)->increase(4, now()->subHours(8)); + StatsWriter::for(StatsEvent::class)->set(5, now()->subHours(1)); // This set will be used for day 2 $stats = StatsQuery::for(StatsEvent::class) ->start(now()->subDays(2)) diff --git a/tests/StatsWriterTest.php b/tests/StatsWriterTest.php index 36252b8..ed26ea6 100644 --- a/tests/StatsWriterTest.php +++ b/tests/StatsWriterTest.php @@ -80,7 +80,7 @@ public function it_can_create_events_for_has_many_relationships_with_custom_attr /** @var Stat $stats */ $stats = Stat::create(); - StatsWriter::for($stats->events())->increase(1, ['name' => 'recurring']); + StatsWriter::for($stats->events(), ['name' => 'recurring'])->increase(1); $this->assertDatabaseHas('stats_events', [ 'stat_id' => $stats->getKey(), @@ -93,7 +93,7 @@ public function it_can_create_events_for_has_many_relationships_with_custom_attr /** @test */ public function it_can_create_events_with_custom_attributes() { - StatsWriter::for(new StatsEvent())->increase(1, ['name' => 'OrderStats']); + StatsWriter::for(new StatsEvent(), ['name' => 'OrderStats'])->increase(1); $this->assertDatabaseHas('stats_events', [ 'name' => 'OrderStats', @@ -105,7 +105,7 @@ public function it_can_create_events_with_custom_attributes() /** @test */ public function it_can_create_events_for_increments_at_a_given_timestamp() { - StatsWriter::for(StatsEvent::class)->increase(1, [], now()->subWeek()); + StatsWriter::for(StatsEvent::class)->increase(1, now()->subWeek()); $this->assertDatabaseHas('stats_events', [ 'value' => 1, @@ -139,7 +139,7 @@ public function it_can_create_events_for_decrements() /** @test */ public function it_can_create_events_for_decrements_at_a_given_timestamp() { - StatsWriter::for(StatsEvent::class)->decrease(1, [], now()->subWeek()); + StatsWriter::for(StatsEvent::class)->decrease(1, now()->subWeek()); $this->assertDatabaseHas('stats_events', [ 'value' => -1, @@ -162,7 +162,7 @@ public function it_can_create_events_for_setting_fixed_values() /** @test */ public function it_can_create_events_for_setting_fixed_values_at_a_given_timestamp() { - StatsWriter::for(StatsEvent::class)->set(1337, [], now()->subWeek()); + StatsWriter::for(StatsEvent::class)->set(1337, now()->subWeek()); $this->assertDatabaseHas('stats_events', [ 'value' => 1337, From 278ac566ed62adb75d2a12c87cad71e49ff2285b Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Mon, 28 Feb 2022 16:03:37 +0100 Subject: [PATCH 12/17] Added BaseStats::writer() to have the same calls as with BaseStats::queue() --- CHANGELOG.md | 1 + src/BaseStats.php | 19 ++++++++++--------- src/StatsWriter.php | 5 +++++ tests/BaseStatsTest.php | 10 ++++++++++ 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bdb62c..b640a7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to `laravel-stats` will be documented in this file - Added `StatsWriter` with custom-attribute support (`StatsWriter::for(MyModel::class, ['custom_column' => 'orders])`) - Extended `StatsQuery` with relationship-support (`StatsQuery::for($model->relationship())`) - Extended `StatsQuery` with additional attributes (`StatsQuery::for(StatsEvent::class, ['name' => 'OrderStats'])`) +- Extended `BaseStats` with direct writer access (`OrderStats::writer()` as addition to `OrderStats::query()`) ### BC breaks diff --git a/src/BaseStats.php b/src/BaseStats.php index b64933f..bf5bcbc 100644 --- a/src/BaseStats.php +++ b/src/BaseStats.php @@ -19,24 +19,25 @@ public static function query(): StatsQuery ]); } - public static function increase(mixed $number = 1, ?DateTimeInterface $timestamp = null) + public static function writer(): StatsWriter { - $model = new static(); + return StatsWriter::for(StatsEvent::class, [ + 'name' => (new static)->getName(), + ]); + } - StatsWriter::for(StatsEvent::class, ['name' => $model->getName()])->increase($number, $timestamp); + public static function increase(mixed $number = 1, ?DateTimeInterface $timestamp = null) + { + static::writer()->increase($number, $timestamp); } public static function decrease(mixed $number = 1, ?DateTimeInterface $timestamp = null) { - $model = new static(); - - StatsWriter::for(StatsEvent::class, ['name' => $model->getName()])->decrease($number, $timestamp); + static::writer()->decrease($number, $timestamp); } public static function set(int $value, ?DateTimeInterface $timestamp = null) { - $model = new static(); - - StatsWriter::for(StatsEvent::class, ['name' => $model->getName()])->set($value, $timestamp); + static::writer()->set($value, $timestamp); } } diff --git a/src/StatsWriter.php b/src/StatsWriter.php index 4eab70a..4cbab6b 100644 --- a/src/StatsWriter.php +++ b/src/StatsWriter.php @@ -41,6 +41,11 @@ public function set(int $value, ?DateTimeInterface $timestamp = null) $this->createEvent(DataPoint::TYPE_SET, $value, $timestamp); } + public function getAttributes(): array + { + return $this->attributes; + } + protected function createEvent($type, $value, ?DateTimeInterface $timestamp = null): Model { if ($this->subject instanceof Relation) { diff --git a/tests/BaseStatsTest.php b/tests/BaseStatsTest.php index e99d36e..7ffa7df 100644 --- a/tests/BaseStatsTest.php +++ b/tests/BaseStatsTest.php @@ -4,6 +4,7 @@ use Carbon\Carbon; use Spatie\Stats\StatsQuery; +use Spatie\Stats\StatsWriter; use Spatie\Stats\Tests\Stats\OrderStats; class BaseStatsTest extends TestCase @@ -111,6 +112,15 @@ public function it_can_get_a_stats_query_object() $this->assertSame(['name' => 'OrderStats'], $query->getAttributes()); } + /** @test */ + public function it_can_get_a_stats_writer_object() + { + $writer = OrderStats::writer(); + + $this->assertInstanceOf(StatsWriter::class, $writer); + $this->assertSame(['name' => 'OrderStats'], $writer->getAttributes()); + } + /** @test */ public function it_can_get_the_value_at_a_given_time() { From 8d17bb3c908cf7f08e4113d7a17bc15aa2f2292b Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Tue, 1 Mar 2022 16:42:48 +0100 Subject: [PATCH 13/17] Moved getAttributes() checks into respective class-based tests --- tests/BaseStatsTest.php | 18 ------------------ tests/StatsQueryTest.php | 9 +++++++++ tests/StatsWriterTest.php | 16 ++++++++-------- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/tests/BaseStatsTest.php b/tests/BaseStatsTest.php index 7ffa7df..9ece0e2 100644 --- a/tests/BaseStatsTest.php +++ b/tests/BaseStatsTest.php @@ -103,24 +103,6 @@ public function it_can_create_events_for_setting_fixed_values_at_a_given_timesta ]); } - /** @test */ - public function it_can_get_a_stats_query_object() - { - $query = OrderStats::query(); - - $this->assertInstanceOf(StatsQuery::class, $query); - $this->assertSame(['name' => 'OrderStats'], $query->getAttributes()); - } - - /** @test */ - public function it_can_get_a_stats_writer_object() - { - $writer = OrderStats::writer(); - - $this->assertInstanceOf(StatsWriter::class, $writer); - $this->assertSame(['name' => 'OrderStats'], $writer->getAttributes()); - } - /** @test */ public function it_can_get_the_value_at_a_given_time() { diff --git a/tests/StatsQueryTest.php b/tests/StatsQueryTest.php index 3e4addb..17b0c99 100644 --- a/tests/StatsQueryTest.php +++ b/tests/StatsQueryTest.php @@ -19,6 +19,15 @@ protected function setUp(): void Carbon::setTestNow('2020-01-01'); } + /** @test */ + public function it_can_pass_and_receive_attributes() + { + $query = StatsQuery::for(StatsEvent::class, ['custom_attribute' => 'custom_value']); + + $this->assertInstanceOf(StatsQuery::class, $query); + $this->assertSame(['custom_attribute' => 'custom_value'], $query->getAttributes()); + } + /** @test */ public function it_can_get_stats_for_base_stats_class() { diff --git a/tests/StatsWriterTest.php b/tests/StatsWriterTest.php index ed26ea6..1027485 100644 --- a/tests/StatsWriterTest.php +++ b/tests/StatsWriterTest.php @@ -171,14 +171,14 @@ public function it_can_create_events_for_setting_fixed_values_at_a_given_timesta ]); } -// /** @test */ -// public function it_can_get_a_stats_query_object() -// { -// $query = OrderStats::query(); -// -// $this->assertInstanceOf(StatsQuery::class, $query); -// $this->assertInstanceOf(OrderStats::class, $query->getStatistic()); -// } + /** @test */ + public function it_can_pass_and_receive_attributes() + { + $writer = StatsWriter::for(StatsEvent::class, ['customer_attrib' => 'custom_val']); + + $this->assertInstanceOf(StatsWriter::class, $writer); + $this->assertSame(['customer_attrib' => 'custom_val'], $writer->getAttributes()); + } // /** @test */ // public function it_can_get_the_value_at_a_given_time() From de653867e318d6fa75fadfa6bacd8214c03211b8 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Tue, 1 Mar 2022 16:49:03 +0100 Subject: [PATCH 14/17] Re-enabled tests for StatsQuery() -> getValue() and moved them from StatsWriter to StatsQuery --- tests/StatsQueryTest.php | 12 ++++++++++++ tests/StatsWriterTest.php | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/StatsQueryTest.php b/tests/StatsQueryTest.php index 17b0c99..896d268 100644 --- a/tests/StatsQueryTest.php +++ b/tests/StatsQueryTest.php @@ -472,4 +472,16 @@ public function it_can_get_stats_based_on_youngest_sets_in_periods() $this->assertEquals($expected, $stats->toArray()); } + + /** @test */ + public function it_can_get_the_value_at_a_given_time() + { + StatsWriter::for(StatsEvent::class)->set(3, now()->subDays(19)); + StatsWriter::for(StatsEvent::class)->decrease(1, now()->subDays(4)); + StatsWriter::for(StatsEvent::class)->increase(3, now()->subDays(2)); + + $this->assertEquals(0, StatsQuery::for(StatsEvent::class)->getValue(now()->subDays(30))); + $this->assertEquals(3, StatsQuery::for(StatsEvent::class)->getValue(now()->subDays(18))); + $this->assertEquals(5, StatsQuery::for(StatsEvent::class)->getValue(now())); + } } diff --git a/tests/StatsWriterTest.php b/tests/StatsWriterTest.php index 1027485..9fe1089 100644 --- a/tests/StatsWriterTest.php +++ b/tests/StatsWriterTest.php @@ -180,18 +180,6 @@ public function it_can_pass_and_receive_attributes() $this->assertSame(['customer_attrib' => 'custom_val'], $writer->getAttributes()); } -// /** @test */ -// public function it_can_get_the_value_at_a_given_time() -// { -// OrderStats::set(3, now()->subDays(19)); -// OrderStats::decrease(1, now()->subDays(4)); -// OrderStats::increase(3, now()->subDays(2)); -// -// $this->assertEquals(0, StatsQuery::for(OrderStats::class)->getValue(now()->subDays(30))); -// $this->assertEquals(3, StatsQuery::for(OrderStats::class)->getValue(now()->subDays(18))); -// $this->assertEquals(5, StatsQuery::for(OrderStats::class)->getValue(now())); -// } -// // /** @test */ // public function it_can_generate_and_array_of_periods() // { From f3469c9493bc4618cd3fd33f82a6ab4d1948d56c Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Tue, 1 Mar 2022 17:03:33 +0100 Subject: [PATCH 15/17] Re-enabled tests for "generatePeriod" and made it "protected". Testing this behaviour now via public "groupByWeek()" functionality --- CHANGELOG.md | 7 ++++--- src/StatsQuery.php | 2 +- tests/BaseStatsTest.php | 20 -------------------- tests/StatsQueryTest.php | 21 +++++++++++++++++++++ 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b640a7d..4d10065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,12 +14,13 @@ All notable changes to `laravel-stats` will be documented in this file - Extended `StatsQuery` with additional attributes (`StatsQuery::for(StatsEvent::class, ['name' => 'OrderStats'])`) - Extended `BaseStats` with direct writer access (`OrderStats::writer()` as addition to `OrderStats::query()`) -### BC breaks +### Changed (breaks BC) -- Replaced `StatsQuery::for($model)->getStatistic()` with `StatsQuery::for($model)->getAttributes()` +- Changed visibility of `StatsQuery::for($model)->generatePeriods()` from `public` to `protected` +- Replaced `StatsQuery::for($model)->getStatistic()` with `StatsQuery::for($model)->getAttributes()` - Removed `BaseStats->createEvent()` -Migrations: +### Migrations - Replace `StatsQuery::for(OrderStats::class)` with `OrderStats::query()` - Replace `StatsEvent::TYPE_SET` use `DataPoint::TYPE_SET` instead diff --git a/src/StatsQuery.php b/src/StatsQuery.php index 98a8896..ca7b135 100644 --- a/src/StatsQuery.php +++ b/src/StatsQuery.php @@ -175,7 +175,7 @@ public function getValue(DateTimeInterface $dateTime): int return $startValue + $differenceSinceSet; } - public function generatePeriods(): Collection + protected function generatePeriods(): Collection { $data = collect(); $currentDateTime = (new Carbon($this->start))->startOf($this->period); diff --git a/tests/BaseStatsTest.php b/tests/BaseStatsTest.php index 9ece0e2..2460842 100644 --- a/tests/BaseStatsTest.php +++ b/tests/BaseStatsTest.php @@ -114,24 +114,4 @@ public function it_can_get_the_value_at_a_given_time() $this->assertEquals(3, OrderStats::query()->getValue(now()->subDays(18))); $this->assertEquals(5, OrderStats::query()->getValue(now())); } - - /** @test */ - public function it_can_generate_and_array_of_periods() - { - $periods = StatsQuery::for(OrderStats::class)->start(now()->subYear())->end(now())->generatePeriods(); - - $this->assertCount(53, $periods); - - $this->assertEquals([ - Carbon::parse('2018-12-31'), - Carbon::parse('2019-01-07'), - '201901', - ], $periods[0]); - - $this->assertEquals([ - Carbon::parse('2019-12-30'), - Carbon::parse('2020-01-06'), - '202001', - ], $periods[52]); - } } diff --git a/tests/StatsQueryTest.php b/tests/StatsQueryTest.php index 896d268..c7b9493 100644 --- a/tests/StatsQueryTest.php +++ b/tests/StatsQueryTest.php @@ -3,6 +3,7 @@ namespace Spatie\Stats\Tests; use Carbon\Carbon; +use Spatie\Stats\DataPoint; use Spatie\Stats\Models\Stat; use Spatie\Stats\Models\StatsEvent; use Spatie\Stats\StatsQuery; @@ -484,4 +485,24 @@ public function it_can_get_the_value_at_a_given_time() $this->assertEquals(3, StatsQuery::for(StatsEvent::class)->getValue(now()->subDays(18))); $this->assertEquals(5, StatsQuery::for(StatsEvent::class)->getValue(now())); } + + /** @test */ + public function it_will_generate_stats_grouped_by_year() + { + $stats = StatsQuery::for(StatsEvent::class) + ->start(now()->subYear()) + ->end(now()) + ->groupByWeek() + ->get(); + + $this->assertCount(53, $stats); + + $this->assertInstanceOf(DataPoint::class, $stats[0]); + $this->assertSame('2018-12-31 00:00:00', (string)$stats[0]->start); + $this->assertSame('2019-01-07 00:00:00', (string)$stats[0]->end); + + $this->assertInstanceOf(DataPoint::class, $stats[52]); + $this->assertSame('2019-12-30 00:00:00', (string)$stats[52]->start); + $this->assertSame('2020-01-06 00:00:00', (string)$stats[52]->end); + } } From 9d938850529d21cacbd125bc4424bdf2fdd1a234 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Tue, 1 Mar 2022 17:03:33 +0100 Subject: [PATCH 16/17] nit: forgot to remove the "outdated" test-case --- CHANGELOG.md | 7 ++++--- src/StatsQuery.php | 2 +- tests/BaseStatsTest.php | 20 -------------------- tests/StatsQueryTest.php | 21 +++++++++++++++++++++ tests/StatsWriterTest.php | 20 -------------------- 5 files changed, 26 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b640a7d..4d10065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,12 +14,13 @@ All notable changes to `laravel-stats` will be documented in this file - Extended `StatsQuery` with additional attributes (`StatsQuery::for(StatsEvent::class, ['name' => 'OrderStats'])`) - Extended `BaseStats` with direct writer access (`OrderStats::writer()` as addition to `OrderStats::query()`) -### BC breaks +### Changed (breaks BC) -- Replaced `StatsQuery::for($model)->getStatistic()` with `StatsQuery::for($model)->getAttributes()` +- Changed visibility of `StatsQuery::for($model)->generatePeriods()` from `public` to `protected` +- Replaced `StatsQuery::for($model)->getStatistic()` with `StatsQuery::for($model)->getAttributes()` - Removed `BaseStats->createEvent()` -Migrations: +### Migrations - Replace `StatsQuery::for(OrderStats::class)` with `OrderStats::query()` - Replace `StatsEvent::TYPE_SET` use `DataPoint::TYPE_SET` instead diff --git a/src/StatsQuery.php b/src/StatsQuery.php index 98a8896..ca7b135 100644 --- a/src/StatsQuery.php +++ b/src/StatsQuery.php @@ -175,7 +175,7 @@ public function getValue(DateTimeInterface $dateTime): int return $startValue + $differenceSinceSet; } - public function generatePeriods(): Collection + protected function generatePeriods(): Collection { $data = collect(); $currentDateTime = (new Carbon($this->start))->startOf($this->period); diff --git a/tests/BaseStatsTest.php b/tests/BaseStatsTest.php index 9ece0e2..2460842 100644 --- a/tests/BaseStatsTest.php +++ b/tests/BaseStatsTest.php @@ -114,24 +114,4 @@ public function it_can_get_the_value_at_a_given_time() $this->assertEquals(3, OrderStats::query()->getValue(now()->subDays(18))); $this->assertEquals(5, OrderStats::query()->getValue(now())); } - - /** @test */ - public function it_can_generate_and_array_of_periods() - { - $periods = StatsQuery::for(OrderStats::class)->start(now()->subYear())->end(now())->generatePeriods(); - - $this->assertCount(53, $periods); - - $this->assertEquals([ - Carbon::parse('2018-12-31'), - Carbon::parse('2019-01-07'), - '201901', - ], $periods[0]); - - $this->assertEquals([ - Carbon::parse('2019-12-30'), - Carbon::parse('2020-01-06'), - '202001', - ], $periods[52]); - } } diff --git a/tests/StatsQueryTest.php b/tests/StatsQueryTest.php index 896d268..c7b9493 100644 --- a/tests/StatsQueryTest.php +++ b/tests/StatsQueryTest.php @@ -3,6 +3,7 @@ namespace Spatie\Stats\Tests; use Carbon\Carbon; +use Spatie\Stats\DataPoint; use Spatie\Stats\Models\Stat; use Spatie\Stats\Models\StatsEvent; use Spatie\Stats\StatsQuery; @@ -484,4 +485,24 @@ public function it_can_get_the_value_at_a_given_time() $this->assertEquals(3, StatsQuery::for(StatsEvent::class)->getValue(now()->subDays(18))); $this->assertEquals(5, StatsQuery::for(StatsEvent::class)->getValue(now())); } + + /** @test */ + public function it_will_generate_stats_grouped_by_year() + { + $stats = StatsQuery::for(StatsEvent::class) + ->start(now()->subYear()) + ->end(now()) + ->groupByWeek() + ->get(); + + $this->assertCount(53, $stats); + + $this->assertInstanceOf(DataPoint::class, $stats[0]); + $this->assertSame('2018-12-31 00:00:00', (string)$stats[0]->start); + $this->assertSame('2019-01-07 00:00:00', (string)$stats[0]->end); + + $this->assertInstanceOf(DataPoint::class, $stats[52]); + $this->assertSame('2019-12-30 00:00:00', (string)$stats[52]->start); + $this->assertSame('2020-01-06 00:00:00', (string)$stats[52]->end); + } } diff --git a/tests/StatsWriterTest.php b/tests/StatsWriterTest.php index 9fe1089..1d83b3c 100644 --- a/tests/StatsWriterTest.php +++ b/tests/StatsWriterTest.php @@ -179,24 +179,4 @@ public function it_can_pass_and_receive_attributes() $this->assertInstanceOf(StatsWriter::class, $writer); $this->assertSame(['customer_attrib' => 'custom_val'], $writer->getAttributes()); } - -// /** @test */ -// public function it_can_generate_and_array_of_periods() -// { -// $periods = StatsQuery::for(OrderStats::class)->start(now()->subYear())->end(now())->generatePeriods(); -// -// $this->assertCount(53, $periods); -// -// $this->assertEquals([ -// Carbon::parse('2018-12-31'), -// Carbon::parse('2019-01-07'), -// '201901', -// ], $periods[0]); -// -// $this->assertEquals([ -// Carbon::parse('2019-12-30'), -// Carbon::parse('2020-01-06'), -// '202001', -// ], $periods[52]); -// } } From 12ab30b767a86498842b1213eb74b75a38aff863 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 2 Mar 2022 18:50:44 +0100 Subject: [PATCH 17/17] Moved relationship-tests from src/ to tests/ --- composer.json | 1 + database/migrations/create_stats_tables.php.stub | 8 +------- tests/StatsQueryTest.php | 2 +- tests/StatsWriterTest.php | 2 +- tests/TestCase.php | 11 +++++++++++ {src => tests/TestClasses}/Models/Stat.php | 3 ++- 6 files changed, 17 insertions(+), 10 deletions(-) rename {src => tests/TestClasses}/Models/Stat.php (66%) diff --git a/composer.json b/composer.json index 4945eac..16e94ff 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "spatie/laravel-package-tools": "^1.9.2" }, "require-dev": { + "doctrine/dbal": "^3.3", "orchestra/testbench": "^6.23|^7.0", "phpunit/phpunit": "^9.4", "vimeo/psalm": "^4.12" diff --git a/database/migrations/create_stats_tables.php.stub b/database/migrations/create_stats_tables.php.stub index baecac2..98ed454 100644 --- a/database/migrations/create_stats_tables.php.stub +++ b/database/migrations/create_stats_tables.php.stub @@ -8,16 +8,10 @@ class CreateStatsTables extends Migration { public function up() { - Schema::create('stats', function (Blueprint $table) { - $table->id(); - $table->timestamps(); - }); - Schema::create('stats_events', function (Blueprint $table) { $table->id(); - $table->string('stat_id')->nullable(); - $table->string('name')->nullable(); + $table->string('name'); $table->string('type'); $table->bigInteger('value'); diff --git a/tests/StatsQueryTest.php b/tests/StatsQueryTest.php index c7b9493..a0689b6 100644 --- a/tests/StatsQueryTest.php +++ b/tests/StatsQueryTest.php @@ -4,12 +4,12 @@ use Carbon\Carbon; use Spatie\Stats\DataPoint; -use Spatie\Stats\Models\Stat; use Spatie\Stats\Models\StatsEvent; use Spatie\Stats\StatsQuery; use Spatie\Stats\StatsWriter; use Spatie\Stats\Tests\Stats\CustomerStats; use Spatie\Stats\Tests\Stats\OrderStats; +use Spatie\Stats\Tests\TestClasses\Models\Stat; class StatsQueryTest extends TestCase { diff --git a/tests/StatsWriterTest.php b/tests/StatsWriterTest.php index 1d83b3c..24647bc 100644 --- a/tests/StatsWriterTest.php +++ b/tests/StatsWriterTest.php @@ -3,11 +3,11 @@ namespace Spatie\Stats\Tests; use Carbon\Carbon; -use Spatie\Stats\Models\Stat; use Spatie\Stats\Models\StatsEvent; use Spatie\Stats\StatsWriter; use Spatie\Stats\Tests\Stats\CustomerStats; use Spatie\Stats\Tests\Stats\OrderStats; +use Spatie\Stats\Tests\TestClasses\Models\Stat; class StatsWriterTest extends TestCase { diff --git a/tests/TestCase.php b/tests/TestCase.php index 7ba6e84..61e8e7b 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,6 +3,7 @@ namespace Spatie\Stats\Tests; use CreateStatsTables; +use Illuminate\Database\Schema\Blueprint; use Illuminate\Foundation\Testing\DatabaseMigrations; use Orchestra\Testbench\TestCase as Orchestra; use Spatie\Stats\StatsServiceProvider; @@ -29,6 +30,16 @@ public function setupDatabase() { include_once __DIR__.'/../database/migrations/create_stats_tables.php.stub'; + $this->app['db']->connection()->getSchemaBuilder()->create('stats', function (Blueprint $table) { + $table->id(); + $table->timestamps(); + }); + (new CreateStatsTables())->up(); + + $this->app['db']->connection()->getSchemaBuilder()->table('stats_events', function (Blueprint $table) { + $table->string('stat_id')->nullable()->after('id'); + $table->string('name')->nullable()->change(); + }); } } diff --git a/src/Models/Stat.php b/tests/TestClasses/Models/Stat.php similarity index 66% rename from src/Models/Stat.php rename to tests/TestClasses/Models/Stat.php index 761b174..3b54523 100644 --- a/src/Models/Stat.php +++ b/tests/TestClasses/Models/Stat.php @@ -1,8 +1,9 @@