Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for relationships #17

Merged
merged 18 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
68929de
extracted reusable stats-parts into a trait
christoph-kluge Feb 20, 2022
a8acac7
moved "getPeriodDateFormat" closer to the stats-query
christoph-kluge Feb 20, 2022
dcfbd39
moved "TYPE_" constants into the DataPoint and deprecate "StatsEvent:…
christoph-kluge Feb 20, 2022
9d8daa4
Added `StatsWriter` with relationship-support (`StatsWriter::for($mod…
christoph-kluge Feb 20, 2022
0b627d2
Extended `StatsQuery` with relationship-support (`StatsQuery::for($mo…
christoph-kluge Feb 20, 2022
2524df9
Added backward-compatibility adjustments for BaseStats usage
christoph-kluge Feb 20, 2022
18e47b3
Added CHANGELOG.md and extended README.md with usage examples
christoph-kluge Feb 20, 2022
491134b
Added tests for additonal attributes
christoph-kluge Feb 20, 2022
4100128
Extended CHANGELOG.md with second argument for StatsQuery
christoph-kluge Feb 20, 2022
101be73
createEvent should return dynamic Eloquent-Model instead of StatsEvent
christoph-kluge Feb 20, 2022
8681ba5
Moved attribute usage as constructor parameters ; while running tests…
christoph-kluge Feb 28, 2022
278ac56
Added BaseStats::writer() to have the same calls as with BaseStats::q…
christoph-kluge Feb 28, 2022
8d17bb3
Moved getAttributes() checks into respective class-based tests
christoph-kluge Mar 1, 2022
de65386
Re-enabled tests for StatsQuery() -> getValue() and moved them from S…
christoph-kluge Mar 1, 2022
f3469c9
Re-enabled tests for "generatePeriod" and made it "protected". Testin…
christoph-kluge Mar 1, 2022
9d93885
nit: forgot to remove the "outdated" test-case
christoph-kluge Mar 1, 2022
d7bd6ba
Merge remote-tracking branch 'refs/remotes/origin/relationships' into…
christoph-kluge Mar 1, 2022
12ab30b
Moved relationship-tests from src/ to tests/
christoph-kluge Mar 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@

All notable changes to `laravel-stats` will be documented in this file

## 2.0.0 - 2022-02-20

### Added

- 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'])`)
- Extended `BaseStats` with direct writer access (`OrderStats::writer()` as addition to `OrderStats::query()`)

### Changed (breaks BC)

- 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

- 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

- Add support for Laravel 9
Expand Down
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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, ['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))
->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(), ['payment_type_column' => 'recurring'])->increment(1)

$stats = StatsQuery::for($tenant->orderStats(), , ['payment_type_column' => 'recurring'])
->start(now()->subMonths(2))
->end(now()->subSecond())
->groupByWeek()
->get();
```

## Testing

``` bash
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
37 changes: 13 additions & 24 deletions src/BaseStats.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,30 @@ 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)
public static function writer(): StatsWriter
{
$number = is_int($number) ? $number : 1;

$stats = new static;

$stats->createEvent(StatsEvent::TYPE_CHANGE, $number, $timestamp);
return StatsWriter::for(StatsEvent::class, [
'name' => (new static)->getName(),
]);
}

public static function decrease(mixed $number = 1, ?DateTimeInterface $timestamp = null)
public static function increase(mixed $number = 1, ?DateTimeInterface $timestamp = null)
{
$number = is_int($number) ? $number : 1;

$stats = new static;

$stats->createEvent(StatsEvent::TYPE_CHANGE, -$number, $timestamp);
static::writer()->increase($number, $timestamp);
}

public static function set(int $value, ?DateTimeInterface $timestamp = null)
public static function decrease(mixed $number = 1, ?DateTimeInterface $timestamp = null)
{
$stats = new static;

$stats->createEvent(StatsEvent::TYPE_SET, $value, $timestamp);
static::writer()->decrease($number, $timestamp);
}

protected function createEvent($type, $value, ?DateTimeInterface $timestamp = null): StatsEvent
public static function set(int $value, ?DateTimeInterface $timestamp = null)
{
return StatsEvent::create([
'name' => $this->getName(),
'type' => $type,
'value' => $value,
'created_at' => $timestamp ?? now(),
]);
static::writer()->set($value, $timestamp);
}
}
4 changes: 4 additions & 0 deletions src/DataPoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
45 changes: 13 additions & 32 deletions src/Models/StatsEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,27 @@

namespace Spatie\Stats\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Spatie\Stats\DataPoint;
use Spatie\Stats\Traits\HasStats;

class StatsEvent extends Model
{
const TYPE_SET = 'set';
const TYPE_CHANGE = 'change';
use HasStats;

/**
* @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',
];

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);
}
}
Loading