You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Apr 1, 2024. It is now read-only.
This package enables a Laravel application to maintain statistics of aggregated database records. It serves as a companion package to (and relies upon) [triggers](https://github.com/mattkingshott/triggers).
14
+
This package enables a Laravel application to maintain aggregated statistics for database tables. It serves as a companion package to (and relies upon) [triggers](https://github.com/mattkingshott/triggers).
15
15
16
16
## Who is this for?
17
17
@@ -53,30 +53,6 @@ $stats = Statistic::query()
53
53
| Projects |`{ "count" : 3 }`|
54
54
| Tasks |`{ "count" : 2 }`|
55
55
56
-
This becomes even more powerful when using joins for individual rows:
57
-
58
-
```php
59
-
$users = User::query()
60
-
->join('statistics', function ($join) {
61
-
$join->on('users.id', '=', 'statistics.id')
62
-
->where('statistics.table', 'users');
63
-
})
64
-
->orderByRaw('`values`->"$.post_count" DESC')
65
-
->get(['users.name', DB::raw('`values`->"$.post_count" AS `posts`')])
66
-
```
67
-
68
-
| Name | Posts |
69
-
| ----- | ----- |
70
-
| John | 6 |
71
-
| Fred | 4 |
72
-
| Dave | 1 |
73
-
74
-
## How does it work?
75
-
76
-
The package will automatically register and migrate a `statistics` table to your database. This table then serves as a repository for aggregated values. You can then easily join records to this table to get the associated metrics you need.
77
-
78
-
The aggregated values are maintained using database triggers, which will automatically fire after a record is inserted, updated or deleted.
79
-
80
56
## Installation
81
57
82
58
Pull in the package using Composer:
@@ -95,54 +71,34 @@ php artisan vendor:publish
95
71
96
72
## Usage
97
73
98
-
The package allows you to:
74
+
The package will automatically register and migrate a `statistics` table to your database. This table then serves as a repository for aggregated values. The values are maintained using database triggers, which will automatically fire after a record is inserted, updated or deleted.
99
75
100
-
1. Maintain statistics for all the records in a table.
101
-
2. Maintain statistics for individual rows in a table.
102
-
3. Maintain statistics for individual AND all records in a table.
103
-
104
-
Before proceeding further, it is important to remember that database triggers (which the package relies on) can only be added to a table after it has been created. Therefore, you should ensure that any referenced tables have first been added via `Schema::create` within your migrations e.g.
105
-
106
-
```php
107
-
// At this point, adding statistics that rely on the 'users' table will
108
-
// throw a SQL error as the table has not yet been added to the database.
// At this point, adding statistics that rely on the 'users' table will not
115
-
// throw a SQL error as the table has been added to the database.
116
-
```
117
-
118
-
### Configuring models
76
+
> Before proceeding further, it is important to remember that database triggers (which the package relies on) can only be added to a table after it has been created. In other words, don't try to create statistics for a table before it has been created by `Schema::create` (this will become clearer in the examples below).
119
77
120
78
To begin, add the `InteractsWithStatistics` trait to any `Model` class that you want to maintain statistics for e.g.
121
79
122
80
```php
123
81
namespace App\Models;
124
82
125
83
use Illuminate\Database\Eloquent\Model;
126
-
use Statistics\Traits\InteractsWithStatistics;
84
+
use Statistics\InteractsWithStatistics;
127
85
128
86
class Article extends Model
129
87
{
130
88
use InteractsWithStatistics;
131
89
}
132
90
```
133
91
134
-
### Tracking all records
135
-
136
-
To maintain statistics across the entire table (e.g. how many rows there are), call the static `trackAll` method on the `Model`.
92
+
Next, call the static `track` method on the `Model`.
137
93
138
94
```php
139
-
Article::trackAll();
95
+
Article::track();
140
96
```
141
97
142
98
Next, call one or more of the available aggregation methods:
143
99
144
100
```php
145
-
Article::trackAll()
101
+
Article::track()
146
102
->count() // Count all records
147
103
->sum('likes') // Get the sum of all records using the 'likes' column
148
104
->average('likes') // Get the average value from the 'likes' column
@@ -153,7 +109,7 @@ Article::trackAll()
153
109
You can call an aggregation method more than once if you need to maintain statistics on multiple columns. Simply supply a custom name to differentiate them:
154
110
155
111
```php
156
-
Article::trackAll()
112
+
Article::track()
157
113
->count()
158
114
->sum('likes', 'sum_likes')
159
115
->sum('views', 'sum_views');
@@ -162,7 +118,7 @@ Article::trackAll()
162
118
Finally, call the `create` method to install the triggers.
163
119
164
120
```php
165
-
Article::trackAll()
121
+
Article::track()
166
122
->count()
167
123
->create();
168
124
```
@@ -181,116 +137,13 @@ class CreateArticlesTable extends Migration
181
137
$table->string('title');
182
138
});
183
139
184
-
Article::trackAll()
140
+
Article::track()
185
141
->count()
186
142
->create();
187
143
}
188
144
}
189
145
```
190
146
191
-
### Tracking individual rows
192
-
193
-
In addition to maintaining statistics on a whole table, you can also maintain statistics for individual rows. This can be useful when tracking related records e.g. the count of 'articles', 'projects' and 'tasks' belonging to a 'user'.
194
-
195
-
To begin, call the static `track` method on the `Model`:
196
-
197
-
```php
198
-
User::track();
199
-
```
200
-
201
-
Next, we need to add a watcher for each of the related tables. We do this by calling the `watch` method and supplying the related `Model`:
202
-
203
-
```php
204
-
User::track()
205
-
->watch(Task::class);
206
-
```
207
-
208
-
The package will guess the foreign key on `Task` by combining the main model (`User`) and `_id`. However, you can override this by supplying your own foreign key:
209
-
210
-
```php
211
-
User::track()
212
-
->watch(Task::class, 'author_id');
213
-
```
214
-
215
-
Next, we need to provide an array of SQL statements that the trigger should execute in order to maintain one or more statistics:
216
-
217
-
```php
218
-
User::track()
219
-
->watch(Task::class, [
220
-
'task_count' => "(SELECT COUNT(*) FROM `tasks` WHERE `user_id` = {ROW}.user_id)",
221
-
]);
222
-
223
-
// Or when supplying a custom foreign key...
224
-
225
-
User::track()
226
-
->watch(Task::class, 'author_id', [
227
-
'task_count' => "(SELECT COUNT(*) FROM `tasks` WHERE `author_id` = {ROW}.author_id)",
228
-
]);
229
-
```
230
-
231
-
Notice the use of `{ROW}` in the SQL statement. You can use this placeholder to access the current row within the trigger. `{ROW}` will be automatically replaced with `OLD` or `NEW` depending on the event type.
232
-
233
-
You are free to maintain more than one statistic for a related table if required e.g.
234
-
235
-
```php
236
-
User::track()
237
-
->watch(Task::class, [
238
-
'task_count' => "(SELECT COUNT(*) FROM `tasks` WHERE `user_id` = {ROW}.user_id)",
239
-
'task_max_priority' => "(SELECT MAX(`priority`) FROM `tasks` WHERE `user_id` = {ROW}.user_id)",
240
-
]);
241
-
```
242
-
243
-
Next, repeat the process for any further watchers that you need e.g.
244
-
245
-
```php
246
-
User::track()
247
-
->watch(Task::class, [
248
-
'task_count' => "(SELECT COUNT(*) FROM `tasks` WHERE `user_id` = {ROW}.user_id)",
249
-
'task_max_priority' => "(SELECT MAX(`priority`) FROM `tasks` WHERE `user_id` = {ROW}.user_id)",
250
-
])
251
-
->watch(Project::class, [
252
-
'project_count' => "(SELECT COUNT(*) FROM `projects` WHERE `user_id` = {ROW}.user_id)",
253
-
])
254
-
->watch(Article::class, [
255
-
'article_count' => "(SELECT COUNT(*) FROM `articles` WHERE `user_id` = {ROW}.user_id)",
256
-
]);
257
-
```
258
-
259
-
Finally, call the `create` method to install the triggers.
260
-
261
-
```php
262
-
User::track()
263
-
->watch(Task::class, [
264
-
'task_count' => "(SELECT COUNT(*) FROM `tasks` WHERE `user_id` = {ROW}.user_id)",
265
-
])
266
-
->create();
267
-
```
268
-
269
-
#### Example
270
-
271
-
Here's a simple example within a database migration:
272
-
273
-
```php
274
-
return new class extends Migration
275
-
{
276
-
public function up() : void
277
-
{
278
-
User::track()
279
-
->watch(Task::class, [
280
-
'task_count' => "(SELECT COUNT(*) FROM `tasks` WHERE `user_id` = {ROW}.user_id)",
281
-
'task_max_priority' => "(SELECT MAX(`priority`) FROM `tasks` WHERE `user_id` = {ROW}.user_id)",
282
-
])
283
-
->watch(Project::class, [
284
-
'project_count' => "(SELECT COUNT(*) FROM `projects` WHERE `user_id` = {ROW}.user_id)",
285
-
])
286
-
->watch(Article::class, [
287
-
'article_count' => "(SELECT COUNT(*) FROM `articles` WHERE `user_id` = {ROW}.user_id)",
288
-
])
289
-
->create();
290
-
}
291
-
};
292
-
```
293
-
294
147
## Contributing
295
148
296
149
Thank you for considering a contribution to the package. You are welcome to submit a PR containing improvements, however if they are substantial in nature, please also be sure to include a test or tests.
0 commit comments