Completed
Push — develop ( 21c1db...84b7db )
by Jimmy
18s queued 10s
created

PurgeCommand::dispatchPurgeEvent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 4
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Spinen\GarbageMan\Commands;
4
5
use Carbon\Carbon;
6
use Illuminate\Console\Command;
7
use Illuminate\Contracts\Events\Dispatcher;
8
use Illuminate\Database\Eloquent\Builder;
9
use Illuminate\Database\Eloquent\Model;
10
use Psr\Log\LoggerInterface as Log;
11
12
/**
13
 * Class PurgeCommand
14
 *
15
 * @package Spinen\GarbageMan\Commands
16
 */
17
class PurgeCommand extends Command
18
{
19
    /**
20
     * The name and signature of the console command.
21
     *
22
     * @var string
23
     */
24
    protected $signature = 'garbageman:purge';
25
26
    /**
27
     * The console command description.
28
     *
29
     * @var string
30
     */
31
    protected $description = 'Purge the soft deleted records based on the age in the configuration file.';
32
33
    /**
34
     * The configured level to log information.
35
     *
36
     * These values are used as the default in case there are not any configured.
37
     *
38
     * @var array
39
     */
40
    protected $logging_level = [
41
        'console' => 6,
42
        'log'     => 6,
43
    ];
44
45
    /**
46
     * Dispatcher instance.
47
     *
48
     * @var Dispatcher
49
     */
50
    protected $dispatcher;
51
52
    /**
53
     * Dispatch events when purging?
54
     *
55
     * This value is used as the default in case there it is not configured.
56
     *
57
     * @var bool
58
     */
59
    protected $dispatch_purge_events = false;
60
61
    /**
62
     * Logging instance.
63
     *
64
     * @var Log
65
     */
66
    protected $log;
67
68
    /**
69
     * Log levels to know the hierarchy.
70
     *
71
     * @var array
72
     */
73
    protected $log_levels = [
74
        'alert'     => 1,
75
        'critical'  => 2,
76
        'debug'     => 7,
77
        'emergency' => 0,
78
        'error'     => 3,
79
        'info'      => 6,
80
        'notice'    => 5,
81
        'warning'   => 4,
82
    ];
83
84
    /**
85
     * Lock in the time that the command was called to make sure that we use that as the point of reference.
86
     *
87
     * @var Carbon
88
     */
89
    protected $now;
90
91
    /**
92
     * Create a new command instance.
93
     *
94
     * @param Carbon $carbon
95
     * @param Dispatcher $dispatcher
96
     * @param Log $log
97
     */
98 9
    public function __construct(Carbon $carbon, Dispatcher $dispatcher, Log $log)
99
    {
100 9
        parent::__construct();
101
102 9
        $this->now = $carbon->now();
103 9
        $this->dispatcher = $dispatcher;
104 9
        $this->log = $log;
105
    }
106
107
    /**
108
     * Dispatch the given event for the record being purged.
109
     *
110
     * @param string $event
111
     * @param string $model_name
112
     * @param Model $model
113
     * @param bool|null $halt
114
     *
115
     * @return mixed
116
     */
117 1
    protected function dispatchPurgeEvent($event, $model_name, Model $model, $halt = true)
118
    {
119 1
        $event = "garbageman.{$event}: " . $model_name;
120
121 1
        $method = $halt ? 'until' : 'dispatch';
122
123 1
        $this->recordMessage(sprintf("Dispatching event [%s] with method [%s]", $event, $method), 'debug');
124
125 1
        return $this->dispatcher->{$method}($event, $model);
126
    }
127
128
    /**
129
     * Execute the console command.
130
     *
131
     * @return void
132
     */
133 8
    public function handle()
134
    {
135 8
        $this->dispatch_purge_events = $this->laravel->make('config')
136 8
                                                     ->get(
137 8
                                                         'garbageman.dispatch_purge_events',
138 8
                                                         $this->dispatch_purge_events
139
                                                     );
140
141 8
        $this->logging_level = $this->laravel->make('config')
142 8
                                             ->get('garbageman.logging_level', $this->logging_level);
143
144 8
        $schedule = $this->laravel->make('config')
145 8
                                  ->get('garbageman.schedule', []);
146
147 8
        foreach ($schedule as $model => $days) {
148 5
            $this->purgeExpiredRecordsForModel($model, $days);
149
        }
150
151 8
        if (count($schedule) < 1) {
152 3
            $this->recordMessage("There were no models configured to purge.", 'notice');
153
        }
154
    }
155
156
    /**
157
     * Purge the expired records.
158
     *
159
     * @param string $model
160
     * @param int $days
161
     *
162
     * @return int|boolean
163
     */
164 5
    protected function purgeExpiredRecordsForModel($model, $days)
165
    {
166 5
        if (!class_exists($model)) {
167 1
            $this->recordMessage(sprintf("The model [%s] was not found.", $model), 'warning');
168
169 1
            return false;
170
        }
171
172 4
        if (!method_exists($model, 'onlyTrashed') || !method_exists($model, 'forceDelete')) {
173 2
            $this->recordMessage(sprintf("The model [%s] does not support soft deleting.", $model), 'error');
174
175 2
            return false;
176
        }
177
178 2
        $expiration = $this->now->copy()
179 2
                                ->subDays($days);
180
181 2
        $query = $this->laravel->make($model)
0 ignored issues
show
Bug introduced by
$model of type object is incompatible with the type string expected by parameter $abstract of Illuminate\Contracts\Container\Container::make(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

181
        $query = $this->laravel->make(/** @scrutinizer ignore-type */ $model)
Loading history...
182 2
                               ->where('deleted_at', '<', $expiration)
183 2
                               ->onlyTrashed();
184
185 2
        $count = $this->purgeRecordsAsConfigured($query, $model);
0 ignored issues
show
Bug introduced by
$model of type object is incompatible with the type string expected by parameter $model_name of Spinen\GarbageMan\Comman...geRecordsAsConfigured(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

185
        $count = $this->purgeRecordsAsConfigured($query, /** @scrutinizer ignore-type */ $model);
Loading history...
186
187 2
        $this->recordMessage(
188 2
            sprintf(
189 2
                "Purged %s record(s) for %s that was deleted before %s days ago.",
190 2
                $count,
191 2
                $model,
0 ignored issues
show
Bug introduced by
$model of type object is incompatible with the type string expected by parameter $args of sprintf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

191
                /** @scrutinizer ignore-type */ $model,
Loading history...
192 2
                $expiration->toIso8601String()
193
            )
194
        );
195
196 2
        return $count;
197
    }
198
199
    /**
200
     * Either purge all the records at once or loop through them one by one.
201
     *
202
     * This is to allow events to get dispatched for each record if needed.
203
     *
204
     * @param Builder $query
205
     * @param string $model_name
206
     *
207
     * @return int
208
     */
209 2
    protected function purgeRecordsAsConfigured(Builder $query, $model_name)
210
    {
211 2
        if ($this->dispatch_purge_events !== true) {
212 1
            $this->recordMessage("Deleting all the records in a single query statement.");
213
214 1
            return $query->forceDelete();
215
        }
216
217 1
        $this->recordMessage("Deleting each record separately and dispatching events.");
218
219 1
        $records = $query->get();
220
221 1
        foreach ($records as $record) {
222 1
            $this->dispatchPurgeEvent('purging', $model_name, $record);
223
224 1
            $record->forceDelete();
225
226 1
            $this->dispatchPurgeEvent('purged', $model_name, $record);
227
        }
228
229 1
        return $records->count();
230
    }
231
232
    /**
233
     * Log the action that was taken on the record.
234
     *
235
     * @param string $message
236
     * @param string|null $level
237
     *
238
     * @return void
239
     */
240 8
    protected function recordMessage($message, $level = null)
241
    {
242 8
        if (is_null($level)) {
243 2
            $level = 'info';
244
        }
245
246
        $console_map = [
247 8
            'alert'     => 'error',
248
            'critical'  => 'error',
249
            'debug'     => 'line',
250
            'emergency' => 'error',
251
            'error'     => 'error',
252
            'info'      => 'info',
253
            'notice'    => 'comment',
254
            'warning'   => 'warn',
255
        ];
256
257 8
        if ($this->supposedToLogAtThisLevel($level, 'log')) {
258 8
            $this->log->{$level}($message);
259
        }
260
261 8
        if ($this->supposedToLogAtThisLevel($level, 'console')) {
262 7
            $this->{$console_map[$level]}($message);
263
        }
264
    }
265
266
    /**
267
     * Decide if the system is supposed to log for the level.
268
     *
269
     * Default to true, if not configured.
270
     *
271
     * @param string $level
272
     * @param string $type
273
     *
274
     * @return bool
275
     */
276 8
    protected function supposedToLogAtThisLevel($level, $type)
277
    {
278 8
        if (!array_key_exists($type, $this->logging_level)) {
279 1
            return true;
280
        }
281
282 7
        return $this->log_levels[$level] <= $this->logging_level[$type];
283
    }
284
}
285