├── .gitignore ├── composer.json ├── readme.md └── src ├── Console ├── MetricsMakeCommand.php ├── MetricsTableCommand.php └── stubs │ ├── metrics.count.stub │ ├── metrics.stub │ └── metrics.time.stub ├── Exceptions └── NotUserClassException.php ├── Metricable.php ├── Metrics.php ├── MetricsServiceProvider.php ├── MetricsStatistics.php ├── Types ├── CountMetrics.php └── TimeMetrics.php └── config └── config.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /node_modules 3 | composer.lock 4 | 5 | 6 | # Laravel 7 | /bootstrap/compiled.php 8 | .env.*.php 9 | .env.php 10 | .env 11 | 12 | # Windows image file caches 13 | Thumbs.db 14 | ehthumbs.db 15 | 16 | # Folder config file 17 | Desktop.ini 18 | 19 | # Recycle Bin used on file shares 20 | $RECYCLE.BIN/ 21 | 22 | # Windows Installer files 23 | *.cab 24 | *.msi 25 | *.msm 26 | *.msp 27 | 28 | # Windows shortcuts 29 | *.lnk 30 | 31 | # Netbeans 32 | nbproject/private/ 33 | build/ 34 | nbbuild/ 35 | dist/ 36 | nbdist/ 37 | nbactions.xml 38 | nb-configuration.xml 39 | .nb-gradle/ 40 | 41 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 42 | 43 | *.iml 44 | 45 | ## Directory-based project format: 46 | .idea/ 47 | # if you remove the above rule, at least ignore the following: 48 | 49 | # User-specific stuff: 50 | # .idea/workspace.xml 51 | # .idea/tasks.xml 52 | # .idea/dictionaries 53 | 54 | # Sensitive or high-churn files: 55 | # .idea/dataSources.ids 56 | # .idea/dataSources.xml 57 | # .idea/sqlDataSources.xml 58 | # .idea/dynamic.xml 59 | # .idea/uiDesigner.xml 60 | 61 | # Gradle: 62 | # .idea/gradle.xml 63 | # .idea/libraries 64 | 65 | # Mongo Explorer plugin: 66 | # .idea/mongoSettings.xml 67 | 68 | ## File-based project format: 69 | *.ipr 70 | *.iws 71 | 72 | ## Plugin-specific files: 73 | 74 | # IntelliJ 75 | /out/ 76 | 77 | # mpeltonen/sbt-idea plugin 78 | .idea_modules/ 79 | 80 | # JIRA plugin 81 | atlassian-ide-plugin.xml 82 | 83 | # Crashlytics plugin (for Android Studio and IntelliJ) 84 | com_crashlytics_export_strings.xml 85 | crashlytics.properties 86 | crashlytics-build.properties 87 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gurmanalexander/laravel-metrics", 3 | "description": "Managable metrics", 4 | "keywords": ["laravel", "metrics", "statistics", "stats"], 5 | "license": "MIT", 6 | "type": "library", 7 | "authors": [ 8 | { 9 | "name": "Alexander Gurman", 10 | "email": "gurmanalexander@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.5.0", 15 | "illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.*" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "GurmanAlexander\\Metrics\\": "src/" 20 | } 21 | }, 22 | "extra": { 23 | "laravel": { 24 | "providers": [ 25 | "GurmanAlexander\\Metrics\\MetricsServiceProvider" 26 | ] 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Laravel Metrics 2 | This package helps you to manage your application metrics (e.g. Time, Count, Money) 3 | 4 | ## Table of Contents 5 | - Installation 6 | - Usage 7 | - Create Metrics 8 | - Metrics usage 9 | - Start Metrics 10 | - Stop Metrics 11 | - Once Metrics 12 | - Statistics 13 | - Methods 14 | - user() 15 | - admin() 16 | - startAt() 17 | - endAt() 18 | - betweenAt() 19 | - period 20 | - getBuilder() 21 | - Results 22 | - count() 23 | - sum() 24 | - avg() 25 | - min() 26 | - max() 27 | 28 | 29 | ## Installation 30 | 31 | Require this package with composer: 32 | 33 | ```shell 34 | composer require gurmanalexander/laravel-metrics:1.* 35 | ``` 36 | 37 | After updating composer, add the ServiceProvider to the providers array in config/app.php 38 | 39 | > Laravel 5.5 uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider 40 | 41 | 42 | ### Laravel 5.x: 43 | 44 | ```php 45 | GurmanAlexander\Metrics\MetricsServiceProvider::class, 46 | ``` 47 | 48 | Copy the package config to your local config with the publish command: 49 | 50 | ```shell 51 | php artisan vendor:publish --provider="GurmanAlexander\Metrics\MetricsServiceProvider" 52 | ``` 53 | 54 | You may use the `metrics:table` command to generate a migration with the proper table schema: 55 | 56 | ```shell 57 | php artisan metrics:table 58 | ``` 59 | 60 | And then migrate: 61 | 62 | ```shell 63 | php artisan migrate 64 | ``` 65 | 66 | ## Usage 67 | ### Create Metrics 68 | You can crete new Metrics (default CountMetrics), but you can change it to TimeMetrics with parameter `--time` 69 | ```shell 70 | php artisan make:metrics PageViewCountMetrics 71 | ``` 72 | 73 | Creating TimeMetrics example: 74 | ```shell 75 | php artisan make:metrics FirstPaymentMetrics 76 | ``` 77 | 78 | This will create new class in `app/Metrics/` folder 79 | ```php 80 | class FirstPaymentMetrics extends CountMetrics 81 | { 82 | 83 | } 84 | ``` 85 | 86 | ### Metrics usage 87 | Now you can start watching your Metrics. You need to add trait `Metricable` to your Model (e.g. User), that you want watching 88 | ```php 89 | use GurmanAlexander\Metrics\Metricable; 90 | class User extends Model 91 | { 92 | use Metricable; 93 | ... 94 | } 95 | ``` 96 | 97 | ### Start metrics 98 | To start Metrics: 99 | > In CountMetrics first parameter - `$user` (The user to which the metrics belongs, default `null`), 100 | > second parameter - `admin` (The user who called the metrics, default `null`), 101 | > third parameter - `$count` (How much to increase the metrics. For example, you can use money metrics. default `1`) 102 | 103 | > In TimeMetrics only two parameters - `$user` and `$admin` 104 | ```php 105 | // For example, when creating user start Metrics 106 | $user = User::create(['email', 'password']); 107 | $user->startMetrics(new FirstPaymentMetrics($user)); 108 | ``` 109 | or 110 | ```php 111 | // when user view some news 112 | $user = auth()->user(); 113 | $news->startMetrics(new PageViewCountMetrics($user)); 114 | ``` 115 | 116 | ### Stop metrics 117 | To stop Metrics: 118 | ```php 119 | // when user make some payment 120 | $user->paySomething(); 121 | $user->closeMetrics(new FirstPaymentMetrics($user)); 122 | ``` 123 | or 124 | ```php 125 | // when user logout 126 | $user = auth()->user(); 127 | $news->closeMetrics(new PageViewCountMetrics($user)); 128 | auth()->logout(); 129 | ``` 130 | 131 | ### Once metrics 132 | To fire once Metrics: 133 | ```php 134 | $user = auth()->user(); 135 | $user->onceMetrics(new SomeOnceMetrics()); 136 | ``` 137 | 138 | ## Statistics 139 | To get statistics you can use `MetricsStatistics` class 140 | 141 | Example: 142 | ```php 143 | // to get total payments 144 | $stats = new MetricsStatistics(new FirstPaymentMetrics()); 145 | ``` 146 | 147 | ### Methods 148 | 149 | #### user 150 | Filter statistics by `$user` (user Model, array of users or Collection of users) 151 | > `$user` - The user to which the metrics belongs. 152 | ```php 153 | // model 154 | $stats->user(auth()->user()); 155 | 156 | // array 157 | $stats->user([auth()->user(), User:first()]); 158 | 159 | // collection 160 | $stats->user(User::where('is_active', 1)->get()); 161 | ``` 162 | 163 | #### admin 164 | Filter by **admin** like by **user** 165 | > `admin` - The user who called the metrics. 166 | 167 | #### startAt 168 | Filter from `$start_at` date 169 | > The metrics stats calculating by `end_at` field (when metrics stops) 170 | ```php 171 | $start_at = Carbon::now()->startOfMonth(); 172 | $stats->startAt($start_at); 173 | ``` 174 | 175 | #### endAt 176 | Filter to `$end_at` date 177 | > The metrics stats calculating by `end_at` field (when metrics stops) 178 | ```php 179 | $end_at = Carbon::now()->endOfMonth(); 180 | $stats->endAt($end_at); 181 | ``` 182 | 183 | #### betweenAt 184 | Filter from `$start_at` to `$end_at` date 185 | > The metrics stats calculating by `end_at` field (when metrics stops) 186 | ```php 187 | $start_at = Carbon::now()->startOfMonth(); 188 | $end_at = Carbon::now()->endOfMonth(); 189 | $stats->betweenAt($start_at, $end_at); 190 | ``` 191 | 192 | #### period 193 | Calculating statistics grouped by periods 194 | ```php 195 | $stats->hourly(); 196 | $stats->daily(); 197 | $stats->weekly(); 198 | $stats->monthly(); 199 | $stats->yearly(); 200 | 201 | // result example 202 | $stats->hourly()->count()->toArray(); 203 | 204 | // [ 205 | // 0 => [ 206 | // "count" => 23, 207 | // "date" => "2017-06-30", 208 | // "period" => "hour", 209 | // "hour" => 9 210 | // ], 211 | // 1 => [ 212 | // "count" => 15, 213 | // "date" => "2017-06-30", 214 | // "period" => "hour", 215 | // "hour" => 8 216 | // ], 217 | // 2 => [ 218 | // "count" => 32, 219 | // "date" => "2017-06-30", 220 | // "period" => "hour", 221 | // "hour" => 7 222 | // ], 223 | // ] 224 | ``` 225 | 226 | #### getBuilder 227 | return Builder to your custom calculating 228 | 229 | ### Results 230 | to get results 231 | 232 | #### count 233 | return **Count** of all filtered Metrics in DB 234 | > return Collection of Metrics 235 | 236 | #### sum 237 | return **Sum** of all filtered Metrics in DB 238 | > if this is TimeMetrics - total seconds for metrics 239 | 240 | #### avg 241 | return **Average** of all filtered Metrics in DB 242 | > if this is TimeMetrics - average seconds for metrics 243 | 244 | #### min 245 | return **Min** of all filtered Metrics in DB 246 | > if this is TimeMetrics - min seconds for metrics 247 | 248 | #### max 249 | return **Max** of all filtered Metrics in DB 250 | > if this is TimeMetrics - max seconds for metrics 251 | 252 | 253 | 254 | ### Example: 255 | ```php 256 | // to get total payments 257 | $stats = new MetricsStatistics(new FirstPaymentMetrics()); 258 | 259 | // to get average time from user registration to first payment (in seconds) 260 | $stats = new MetricsStatistics(new FirstPaymentMetrics())->hourly()->avg()->toArray(); 261 | 262 | // [ 263 | // 0 => [ 264 | // "avg" => 12.13, 265 | // "date" => "2017-06-30", 266 | // "period" => "hour", 267 | // "hour" => 9 268 | // ], 269 | // 1 => [ 270 | // "avg" => 8.00, 271 | // "date" => "2017-06-30", 272 | // "period" => "hour", 273 | // "hour" => 8 274 | // ], 275 | // 2 => [ 276 | // "avg" => 5.34, 277 | // "date" => "2017-06-30", 278 | // "period" => "hour", 279 | // "hour" => 7 280 | // ], 281 | // ] 282 | ``` 283 | 284 | -------------------------------------------------------------------------------- /src/Console/MetricsMakeCommand.php: -------------------------------------------------------------------------------- 1 | option('time')) { 37 | return __DIR__.'/stubs/metrics.time.stub'; 38 | } elseif ($this->option('count')) { 39 | return __DIR__.'/stubs/metrics.count.stub'; 40 | } 41 | 42 | return __DIR__.'/stubs/metrics.count.stub'; 43 | } 44 | 45 | /** 46 | * Get the default namespace for the class. 47 | * 48 | * @param string $rootNamespace 49 | * @return string 50 | */ 51 | protected function getDefaultNamespace($rootNamespace) 52 | { 53 | return $rootNamespace.'\Metrics'; 54 | } 55 | 56 | /** 57 | * Get the console command options. 58 | * 59 | * @return array 60 | */ 61 | protected function getOptions() 62 | { 63 | return [ 64 | ['time', 't', InputOption::VALUE_NONE, 'Generate a Timer metrics class.'], 65 | 66 | ['count', 'c', InputOption::VALUE_NONE, 'Generate a Counter metrics class.'], 67 | ]; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Console/MetricsTableCommand.php: -------------------------------------------------------------------------------- 1 | files = $files; 46 | $this->composer = $composer; 47 | } 48 | 49 | /** 50 | * Execute the console command. 51 | * 52 | * @return void 53 | */ 54 | public function fire() 55 | { 56 | $fullPath = $this->createBaseMigration(); 57 | 58 | $this->files->put($fullPath, $this->files->get(__DIR__.'/stubs/metrics.stub')); 59 | 60 | $this->info('Migration created successfully!'); 61 | 62 | $this->composer->dumpAutoloads(); 63 | } 64 | 65 | /** 66 | * Create a base migration file for the notifications. 67 | * 68 | * @return string 69 | */ 70 | protected function createBaseMigration() 71 | { 72 | $name = 'create_metrics_table'; 73 | 74 | $path = $this->laravel->databasePath().'/migrations'; 75 | 76 | return $this->laravel['migration.creator']->create($name, $path); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Console/stubs/metrics.count.stub: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 19 | $table->boolean('is_failed')->default(false); 20 | 21 | $table->integer('user_id')->unsigned()->nullable(); 22 | $table->foreign('user_id')->references('id')->on(Config::get('metrics.users_table', 'users'))->onDelete('cascade')->onUpdate('cascade'); 23 | 24 | $table->integer('admin_id')->unsigned()->nullable(); 25 | $table->foreign('admin_id')->references('id')->on(Config::get('metrics.users_table', 'users'))->onDelete('set null')->onUpdate('cascade'); 26 | 27 | $table->string('type')->index(); 28 | $table->morphs('metricable'); 29 | 30 | $table->json('data'); 31 | $table->integer('count')->default(0)->index(); 32 | $table->timestamp('start_at')->default(DB::raw('CURRENT_TIMESTAMP'))->index(); 33 | $table->timestamp('end_at')->nullable()->index(); 34 | $table->timestamps(); 35 | }); 36 | } 37 | 38 | /** 39 | * Reverse the migrations. 40 | * 41 | * @return void 42 | */ 43 | public function down() 44 | { 45 | Schema::dropIfExists('metrics'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Console/stubs/metrics.time.stub: -------------------------------------------------------------------------------- 1 | addMetrics($metrics); 15 | } 16 | 17 | /** 18 | * Fire once metrics 19 | * 20 | * @param Metrics $metrics 21 | * @return boolean 22 | */ 23 | public function onceMetrics(Metrics $metrics) 24 | { 25 | return $metrics->onceMetrics($this); 26 | } 27 | 28 | /** 29 | * Add or start metrics to existing metrics 30 | * 31 | * @param Metrics $metrics 32 | * @return boolean 33 | */ 34 | public function addMetrics(Metrics $metrics) 35 | { 36 | return $metrics->addMetrics($this); 37 | } 38 | 39 | /** 40 | * Stop watching metrics 41 | * 42 | * @param Metrics $metrics 43 | * @return boolean 44 | */ 45 | public function closeMetrics(Metrics $metrics) 46 | { 47 | return $metrics->closeMetrics($this); 48 | } 49 | } -------------------------------------------------------------------------------- /src/Metrics.php: -------------------------------------------------------------------------------- 1 | 'array', 36 | 'is_failed' => 'boolean', 37 | 'count' => 'integer', 38 | 'type' => 'string', 39 | ]; 40 | 41 | protected $guarded = []; 42 | 43 | protected $userModel; 44 | protected $adminModel; 45 | 46 | public abstract function addMetrics($metricable); 47 | public abstract function onceMetrics($metricable); 48 | public abstract function closeMetrics($metricable); 49 | 50 | public function __construct(array $attributes = []) 51 | { 52 | $this->id = Uuid::uuid4()->toString(); 53 | parent::__construct($attributes); 54 | } 55 | 56 | public function user() 57 | { 58 | return $this->belongsTo(Config::get('metrics.user', '\App\User'),'user_id'); 59 | } 60 | 61 | public function admin() 62 | { 63 | return $this->belongsTo(Config::get('metrics.user', '\App\User'),'admin_id'); 64 | } 65 | 66 | public function metricable() 67 | { 68 | return $this->morphTo(); 69 | } 70 | 71 | /** 72 | * Find opened metrics if exists 73 | * 74 | * @param $metricable 75 | * @return Metrics|null - child class 76 | */ 77 | protected function findCurrentMetrics($metricable) 78 | { 79 | $metrics = static::where('metricable_id', $metricable->id) 80 | ->where('metricable_type', get_class($metricable)) 81 | ->where('type', static::class) 82 | ->whereNull('end_at') 83 | ; 84 | if (!empty($this->userModel)) { 85 | $metrics->where('user_id', $this->userModel->id); 86 | } 87 | return $metrics->first(); 88 | } 89 | 90 | /** 91 | * Check if $user instance of User Model 92 | * 93 | * @param $user 94 | * @return mixed - User Model 95 | */ 96 | public function checkUserInstance($user) 97 | { 98 | if ($user && get_class($user) !== Config::get('metrics.user')) { 99 | throw new NotUserClassException( 100 | 'Argument passed to ' . static::class . 101 | ' must be an instance of ' . Config::get('metrics.user') . 102 | ', instance of ' . get_class($user) . ' given, called in ' . __FILE__ . 103 | ' on line ' . __LINE__ . ' and defined' 104 | ); 105 | } 106 | return $user; 107 | } 108 | 109 | /** 110 | * Write history of metrics to data 111 | */ 112 | protected function fired() 113 | { 114 | $data = $this->data; 115 | $data['fired_at'][] = Carbon::now()->toDateTimeString(); 116 | $this->data = $data; 117 | } 118 | 119 | /** 120 | * Write user and admin (if exists) to metrics 121 | * Write user activity (history) to data: 122 | * [ 123 | * 'user' => [user_id => [...dates]], 124 | * 'admin' => [admin_id => [...dates]], 125 | * ] 126 | * 127 | * @param Metrics $metrics 128 | */ 129 | protected function fillUsers(Metrics &$metrics) 130 | { 131 | 132 | foreach (['user', 'admin'] as $userType) { 133 | if ($metrics->{$userType . 'Model'}) { 134 | if (!$metrics->{$userType}) { 135 | $metrics->{$userType}()->associate($metrics->{$userType . 'Model'}); 136 | } 137 | } 138 | if (!empty($metrics->{$userType})) { 139 | $data = $this->data; 140 | if (empty($data[$userType])) { 141 | $data[$userType] = []; 142 | } 143 | if (empty($data[$userType][$metrics->{$userType}->id])) { 144 | $data[$userType][$metrics->{$userType}->id] = []; 145 | } 146 | $data[$userType][$metrics->{$userType}->id][] = Carbon::now()->toDateTimeString(); 147 | $this->data = $data; 148 | } 149 | } 150 | 151 | } 152 | 153 | /** 154 | * Build period selectors for SQL query to get stats data for needed period 155 | * For week example: 156 | * 'week' as `period`, 157 | * WEEK(`end_at`, 1) as `week` 158 | * 159 | * @param $period 160 | * @return array 161 | */ 162 | protected static function getPeriodSqlColumns($period) 163 | { 164 | $result = [ 165 | DB::raw('"' . $period . '" as period') 166 | ]; 167 | switch ($period) { 168 | case 'week': { 169 | $result[] = DB::raw(strtoupper($period) . '(end_at, 1) as ' . $period); 170 | }break; 171 | default: { 172 | $result[] = DB::raw(strtoupper($period) . '(end_at) as ' . $period); 173 | } 174 | } 175 | return $result; 176 | } 177 | 178 | /** 179 | * Build complete SQL select 180 | * 181 | * @param Builder $builder 182 | * @param $period 183 | * @param $raw 184 | * @return Builder 185 | */ 186 | protected static function statsBuilder(Builder $builder, $period, $raw) 187 | { 188 | return $builder->select( 189 | array_merge([ 190 | $raw, 191 | DB::raw('DATE_FORMAT(end_at, "%Y-%m-%d") as date'), 192 | ], self::getPeriodSqlColumns($period)) 193 | ); 194 | } 195 | 196 | /** 197 | * Get COUNT stats for Metrics divided into periods 198 | * 199 | * @param Builder $builder 200 | * @param $period 201 | * @return \Illuminate\Database\Eloquent\Collection 202 | */ 203 | public static function statsCount(Builder $builder, $period) 204 | { 205 | return self::statsBuilder($builder, $period, DB::raw('COUNT(*) as count'))->get(); 206 | } 207 | 208 | /** 209 | * Get AVG stats for Metrics divided into periods 210 | * 211 | * @param Builder $builder 212 | * @param $period 213 | * @return \Illuminate\Database\Eloquent\Collection 214 | */ 215 | public static function statsAvg(Builder $builder, $period) 216 | { 217 | return self::statsBuilder($builder, $period, DB::raw('AVG(count) as avg'))->get(); 218 | } 219 | 220 | /** 221 | * Get SUM stats for Metrics divided into periods 222 | * 223 | * @param Builder $builder 224 | * @param $period 225 | * @return \Illuminate\Database\Eloquent\Collection 226 | */ 227 | public static function statsSum(Builder $builder, $period) 228 | { 229 | return self::statsBuilder($builder, $period, DB::raw('SUM(count) as sum'))->get(); 230 | } 231 | 232 | /** 233 | * Get MIN stats for Metrics divided into periods 234 | * 235 | * @param Builder $builder 236 | * @param $period 237 | * @return \Illuminate\Database\Eloquent\Collection 238 | */ 239 | public static function statsMin(Builder $builder, $period) 240 | { 241 | return self::statsBuilder($builder, $period, DB::raw('MIN(count) as min'))->get(); 242 | } 243 | 244 | /** 245 | * Get MAX stats for Metrics divided into periods 246 | * 247 | * @param Builder $builder 248 | * @param $period 249 | * @return \Illuminate\Database\Eloquent\Collection 250 | */ 251 | public static function statsMax(Builder $builder, $period) 252 | { 253 | return self::statsBuilder($builder, $period, DB::raw('MAX(count) as max'))->get(); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/MetricsServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 17 | __DIR__.'/config/config.php' => config_path('metrics.php'), 18 | ]); 19 | } 20 | 21 | /** 22 | * Register the application services. 23 | * 24 | * @return void 25 | */ 26 | public function register() 27 | { 28 | if ($this->app->runningInConsole()) { 29 | 30 | $this->commands([ 31 | MetricsTableCommand::class, 32 | MetricsMakeCommand::class, 33 | ]); 34 | } 35 | 36 | $this->mergeConfigFrom( 37 | __DIR__.'/config/config.php', 'metrics' 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/MetricsStatistics.php: -------------------------------------------------------------------------------- 1 | metricsClass = get_class($metrics); 25 | $this->metricsModel = $metrics; 26 | 27 | $this->metrics = call_user_func(get_class($metrics) . '::where', ['type' => get_class($metrics)]); 28 | $this->metrics 29 | ->where('type', get_class($metrics)) 30 | ->whereNotNull('end_at') 31 | ->orderBy('end_at', 'desc') 32 | ; 33 | 34 | $this->startAt = Carbon::now()->startOfMonth(); 35 | $this->endAt = Carbon::now(); 36 | } 37 | 38 | /** 39 | * Filter by date "start from" calculating statistics 40 | * 41 | * @param Carbon|null $start_at 42 | * @return MetricsStatistics 43 | */ 44 | public function startAt(Carbon $start_at = null) 45 | { 46 | if ($start_at) $this->startAt = $start_at; 47 | 48 | $this->metrics->where('end_at', '>=', $this->startAt); 49 | 50 | return $this; 51 | } 52 | 53 | /** 54 | * Filter by date "end to" calculating statistics 55 | * 56 | * @param Carbon|null $end_at 57 | * @return MetricsStatistics 58 | */ 59 | public function endAt(Carbon $end_at = null) 60 | { 61 | if ($end_at) $this->endAt = $end_at; 62 | 63 | $this->metrics->where('end_at', '<=', $this->endAt); 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * Filter by date "start from" to "end to" calculating statistics 70 | * 71 | * @param Carbon|null $start_at 72 | * @param Carbon|null $end_at 73 | * @return MetricsStatistics 74 | */ 75 | public function betweenAt(Carbon $start_at = null, Carbon $end_at = null) 76 | { 77 | if ($start_at) $this->startAt = $start_at; 78 | if ($end_at) $this->endAt = $end_at; 79 | $this->metrics->whereBetween('end_at', [$this->startAt, $this->endAt]); 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * Filter by user 86 | * 87 | * @param \App\User|array|null $user 88 | * @return MetricsStatistics 89 | */ 90 | public function user($user = null) 91 | { 92 | $this->filterUser($user, 'user_id'); 93 | 94 | return $this; 95 | } 96 | 97 | /** 98 | * Filter by admin 99 | * 100 | * @param \App\User|array|null $user 101 | * @return MetricsStatistics 102 | */ 103 | public function admin($user = null) 104 | { 105 | $this->filterUser($user, 'admin_id'); 106 | 107 | return $this; 108 | } 109 | 110 | protected function period($period) 111 | { 112 | if (in_array($period, [self::HOURLY, self::DAILY, self::WEEKLY, self::MONTHLY, self::YEARLY])) { 113 | $this->period = $period; 114 | } 115 | 116 | 117 | switch ($this->period) { 118 | case self::WEEKLY: { 119 | $groupBy = 'WEEK(end_at, 1)'; 120 | } break; 121 | default: { 122 | $groupBy = strtoupper($this->period) . '(end_at)'; 123 | } 124 | } 125 | 126 | $this->metrics->groupBy(DB::raw($groupBy)); 127 | 128 | return $this; 129 | } 130 | 131 | /** 132 | * Calculating result hourly 133 | * 134 | * @return MetricsStatistics 135 | */ 136 | public function hourly() 137 | { 138 | return $this->period(self::HOURLY); 139 | } 140 | 141 | /** 142 | * Calculating result daily 143 | * 144 | * @return MetricsStatistics 145 | */ 146 | public function daily() 147 | { 148 | return $this->period(self::DAILY); 149 | } 150 | 151 | /** 152 | * Calculating result weekly 153 | * 154 | * @return MetricsStatistics 155 | */ 156 | public function weekly() 157 | { 158 | return $this->period(self::WEEKLY); 159 | } 160 | 161 | /** 162 | * Calculating result monthly 163 | * 164 | * @return MetricsStatistics 165 | */ 166 | public function monthly() 167 | { 168 | return $this->period(self::MONTHLY); 169 | } 170 | 171 | /** 172 | * Calculating result yearly 173 | * 174 | * @return MetricsStatistics 175 | */ 176 | public function yearly() 177 | { 178 | return $this->period(self::YEARLY); 179 | } 180 | 181 | /** 182 | * @param \App\User|array|null $user 183 | * @param string $foreign_key 184 | * @return MetricsStatistics 185 | */ 186 | protected function filterUser($user = null, $foreign_key = 'user_id') 187 | { 188 | if (!$user) return $this; 189 | 190 | if (is_array($user) || $user instanceof \Illuminate\Support\Collection) { 191 | $user_ids = []; 192 | foreach ($user as $item) { 193 | $this->metrics->checkUserInstance($item); 194 | $user_ids[] = $item->id; 195 | } 196 | $this->metrics->whereIn($foreign_key, $user_ids); 197 | return $this; 198 | } 199 | 200 | $this->metricsModel->checkUserInstance($user); 201 | $this->metrics->where($foreign_key, $user->id); 202 | return $this; 203 | } 204 | 205 | /** 206 | * Get statistics Eloquent Builder for custom queries 207 | * 208 | * @return mixed 209 | */ 210 | public function getBuilder() 211 | { 212 | return $this->metrics; 213 | } 214 | 215 | /** 216 | * Call magic methods 217 | * Example: 218 | * - count calls Metrics::statsCount() 219 | * - avg calls Metrics::statsAvg() 220 | * - sum calls Metrics::statsSum() 221 | * - min calls Metrics::statsMin() 222 | * - max calls Metrics::statsMax() 223 | * 224 | * you can add yur own methods in Metrics child classes to get custom stats 225 | * 226 | * @param $name 227 | * @param $arguments 228 | * @return mixed 229 | */ 230 | function __call($name, $arguments) 231 | { 232 | $methodName = ('stats' . strtoupper(substr($name, 0, 1)) . strtolower(substr($name, 1))); 233 | if (method_exists($this->metricsClass, $methodName)) { 234 | return call_user_func_array([$this->metricsClass, $methodName], [$this->metrics, $this->period]); 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/Types/CountMetrics.php: -------------------------------------------------------------------------------- 1 | userModel = $this->checkUserInstance($user); 26 | $this->adminModel = $this->checkUserInstance($admin); 27 | 28 | $this->countMetricsCount = $count; 29 | 30 | parent::__construct([]); 31 | } 32 | 33 | public function addMetrics($metricable) 34 | { 35 | $metrics = $this->findCurrentMetrics($metricable); 36 | 37 | if (!$metrics) $metrics = $this; 38 | 39 | $metrics->type = static::class; 40 | $metrics->count+= $this->countMetricsCount; 41 | 42 | $metrics->fired(); 43 | 44 | $metrics->metricable()->associate($metricable); 45 | 46 | $metrics->fillUsers($this); 47 | 48 | return $metrics->save(); 49 | } 50 | 51 | public function closeMetrics($metricable) 52 | { 53 | $metrics = $this->findCurrentMetrics($metricable); 54 | 55 | if (!$metrics) { 56 | Log::error('trying to close unstarted metrics'); 57 | return false; 58 | } 59 | 60 | $metrics->type = static::class; 61 | $metrics->end_at = Carbon::now(); 62 | $metrics->metricable()->associate($metricable); 63 | 64 | $metrics->fired(); 65 | 66 | $metrics->fillUsers($this); 67 | 68 | return $metrics->save(); 69 | } 70 | 71 | public function onceMetrics($metricable) 72 | { 73 | $this->addMetrics($metricable); 74 | return $this->closeMetrics($metricable); 75 | } 76 | } -------------------------------------------------------------------------------- /src/Types/TimeMetrics.php: -------------------------------------------------------------------------------- 1 | userModel = $this->checkUserInstance($user); 22 | $this->adminModel = $this->checkUserInstance($admin); 23 | 24 | parent::__construct([]); 25 | } 26 | 27 | public function addMetrics($metricable) 28 | { 29 | $metrics = $this->findCurrentMetrics($metricable); 30 | 31 | if ($metrics) return false; 32 | 33 | $metrics = $this; 34 | $metrics->type = static::class; 35 | $metrics->metricable()->associate($metricable); 36 | 37 | $metrics->fired(); 38 | 39 | $metrics->fillUsers($metrics); 40 | 41 | return $metrics->save(); 42 | } 43 | 44 | public function closeMetrics($metricable) 45 | { 46 | $metrics = $this->findCurrentMetrics($metricable); 47 | 48 | if (!$metrics) { 49 | Log::error('trying to close unstarted metrics'); 50 | return false; 51 | } 52 | 53 | $metrics->type = static::class; 54 | $metrics->end_at = Carbon::now(); 55 | $metrics->count = $metrics->end_at->diffInSeconds($metrics->start_at); 56 | $metrics->metricable()->associate($metricable); 57 | 58 | $metrics->fired(); 59 | 60 | $metrics->fillUsers($this); 61 | 62 | return $metrics->save(); 63 | } 64 | 65 | public function onceMetrics($metricable) 66 | { 67 | return false; 68 | } 69 | } -------------------------------------------------------------------------------- /src/config/config.php: -------------------------------------------------------------------------------- 1 | '\App\User', 15 | 16 | /* 17 | |-------------------------------------------------------------------------- 18 | | Application Users Table 19 | |-------------------------------------------------------------------------- 20 | | 21 | | This is the users table used by the application to save users to the 22 | | database. 23 | | 24 | */ 25 | 'users_table' => 'users', 26 | 27 | ]; 28 | --------------------------------------------------------------------------------