Completed
Push — master ( 2850eb...7f616c )
by Chauncey
10:27
created

ObjectSchedule::getDataDiff()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Charcoal\Object;
4
5
use DateTime;
6
use DateTimeInterface;
7
use Exception;
8
use RuntimeException;
9
use InvalidArgumentException;
10
11
// From 'charcoal-factory'
12
use Charcoal\Factory\FactoryInterface;
13
14
// From 'charcoal-core'
15
use Charcoal\Model\AbstractModel;
16
17
// From 'charcoal-object'
18
use Charcoal\Object\ObjectScheduleInterface;
19
20
/**
21
 * The object schedule class allows object properties to be changed at a scheduled time.
22
 *
23
 * ## Required Services
24
 *
25
 * - "model/factory" — {@see \Charcoal\Model\ModelFactory}
26
 */
27
class ObjectSchedule extends AbstractModel implements ObjectScheduleInterface
0 ignored issues
show
Bug introduced by
There is at least one abstract method in this class. Maybe declare it as abstract, or implement the remaining methods: hasProperty, p, properties, property
Loading history...
28
{
29
    /**
30
     * Store the factory instance for the current class.
31
     *
32
     * @var FactoryInterface
33
     */
34
    private $modelFactory;
35
36
    /**
37
     * The object type of the scheduled object (required).
38
     *
39
     * @var string
40
     */
41
    private $targetType;
42
43
    /**
44
     * The object ID of the scheduled object (required).
45
     *
46
     * @var mixed
47
     */
48
    private $targetId;
49
50
    /**
51
     * When the item should be processed.
52
     *
53
     * The date/time at which this queue item job should be ran.
54
     * If NULL, 0, or a past date/time, then it should be performed immediately.
55
     *
56
     * @var DateTimeInterface $scheduledDate
57
     */
58
    private $scheduledDate;
59
60
    /**
61
     * The property identifier of the scheduled object (required).
62
     *
63
     * @var array
64
     */
65
    private $dataDiff = [];
66
67
    /**
68
     * Whether the item has been processed.
69
     *
70
     * @var boolean $processed
71
     */
72
    private $processed = false;
73
74
    /**
75
     * When the item was processed.
76
     *
77
     * @var DateTimeInterface $processedDate
78
     */
79
    private $processedDate;
80
81
    /**
82
     * Set an object model factory.
83
     *
84
     * @param FactoryInterface $factory The model factory, to create objects.
85
     * @return ObjectScheduleInterface Chainable
86
     */
87
    public function setModelFactory(FactoryInterface $factory)
88
    {
89
        $this->modelFactory = $factory;
90
91
        return $this;
92
    }
93
94
    /**
95
     * Retrieve the object model factory.
96
     *
97
     * @throws RuntimeException If the model factory was not previously set.
98
     * @return FactoryInterface
99
     */
100
    protected function modelFactory()
101
    {
102
        if (!isset($this->modelFactory)) {
103
            throw new RuntimeException(sprintf(
104
                'Model Factory is not defined for "%s"',
105
                get_class($this)
106
            ));
107
        }
108
109
        return $this->modelFactory;
110
    }
111
112
    /**
113
     * Set the scheduled object's type.
114
     *
115
     * @param string $targetType The object type (model).
116
     * @throws InvalidArgumentException If the object type parameter is not a string.
117
     * @return ObjectScheduleInterface Chainable
118
     */
119
    public function setTargetType($targetType)
120
    {
121
        if (!is_string($targetType)) {
122
            throw new InvalidArgumentException(
123
                'Scheduled object type must be a string.'
124
            );
125
        }
126
127
        $this->targetType = $targetType;
128
129
        return $this;
130
    }
131
132
    /**
133
     * Retrieve the scheduled object's type.
134
     *
135
     * @return string
136
     */
137
    public function getTargetType()
138
    {
139
        return $this->targetType;
140
    }
141
142
    /**
143
     * Set the scheduled object's ID.
144
     *
145
     * @param mixed $targetId The object ID.
146
     * @return ObjectScheduleInterface Chainable
147
     */
148
    public function setTargetId($targetId)
149
    {
150
        $this->targetId = $targetId;
151
152
        return $this;
153
    }
154
155
    /**
156
     * Retrieve the scheduled object's ID.
157
     *
158
     * @return mixed
159
     */
160
    public function getTargetId()
161
    {
162
        return $this->targetId;
163
    }
164
165
    /**
166
     * @param array|string $data The data diff.
167
     * @return ObjectRevision
168
     */
169 View Code Duplication
    public function setDataDiff($data)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
    {
171
        if (!is_array($data)) {
172
            $data = json_decode($data, true);
173
        }
174
        if ($data === null) {
175
            $data = [];
176
        }
177
        $this->dataDiff = $data;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data of type * is incompatible with the declared type array of property $dataDiff.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
178
        return $this;
179
    }
180
181
    /**
182
     * @return array
183
     */
184
    public function getDataDiff()
185
    {
186
        return $this->dataDiff;
187
    }
188
189
    /**
190
     * Set the schedule's processed status.
191
     *
192
     * @param boolean $processed Whether the schedule has been processed.
193
     * @return ObjectScheduleInterface Chainable
194
     */
195
    public function setProcessed($processed)
196
    {
197
        $this->processed = !!$processed;
198
199
        return $this;
200
    }
201
202
    /**
203
     * Determine if the schedule has been processed.
204
     *
205
     * @return boolean
206
     */
207
    public function getProcessed()
208
    {
209
        return $this->processed;
210
    }
211
212
    /**
213
     * Set the date/time the item should be processed at.
214
     *
215
     * @param  null|string|DateTimeInterface $ts A date/time string or object.
216
     * @throws InvalidArgumentException If the date/time is invalid.
217
     * @return ObjectScheduleInterface Chainable
218
     */
219 View Code Duplication
    public function setScheduledDate($ts)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
220
    {
221
        if ($ts === null) {
222
            $this->scheduledDate = null;
223
            return $this;
224
        }
225
226
        if (is_string($ts)) {
227
            try {
228
                $ts = new DateTime($ts);
229
            } catch (Exception $e) {
230
                throw new InvalidArgumentException(sprintf(
231
                    '%s (%s)',
232
                    $e->getMessage(),
233
                    $ts
234
                ), 0, $e);
235
            }
236
        }
237
238
        if (!($ts instanceof DateTimeInterface)) {
239
            throw new InvalidArgumentException(
240
                'Invalid "Processing Date" value. Must be a date/time string or a DateTime object.'
241
            );
242
        }
243
244
        $this->scheduledDate = $ts;
245
246
        return $this;
247
    }
248
249
    /**
250
     * Retrieve the date/time the item should be processed at.
251
     *
252
     * @return null|DateTimeInterface
253
     */
254
    public function getScheduledDate()
255
    {
256
        return $this->scheduledDate;
257
    }
258
259
    /**
260
     * Set the date/time the item was processed at.
261
     *
262
     * @param  null|string|DateTimeInterface $ts A date/time string or object.
263
     * @throws InvalidArgumentException If the date/time is invalid.
264
     * @return ObjectScheduleInterface Chainable
265
     */
266 View Code Duplication
    public function setProcessedDate($ts)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267
    {
268
        if ($ts === null) {
269
            $this->processedDate = null;
270
            return $this;
271
        }
272
273
        if (is_string($ts)) {
274
            try {
275
                $ts = new DateTime($ts);
276
            } catch (Exception $e) {
277
                throw new InvalidArgumentException(sprintf(
278
                    '%s (%s)',
279
                    $e->getMessage(),
280
                    $ts
281
                ), 0, $e);
282
            }
283
        }
284
285
        if (!($ts instanceof DateTimeInterface)) {
286
            throw new InvalidArgumentException(
287
                'Invalid "Processed Date" value. Must be a date/time string or a DateTime object.'
288
            );
289
        }
290
291
        $this->processedDate = $ts;
292
293
        return $this;
294
    }
295
296
    /**
297
     * Retrieve the date/time the item was processed at.
298
     *
299
     * @return null|DateTimeInterface
300
     */
301
    public function getProcessedDate()
302
    {
303
        return $this->processedDate;
304
    }
305
306
    /**
307
     * Hook called before saving the item.
308
     *
309
     * Presets the item as _to-be_ processed and queued now.
310
     *
311
     * @return boolean
312
     */
313
    protected function preSave()
314
    {
315
        parent::preSave();
316
317
        $this->setProcessed(false);
318
319
        return true;
320
    }
321
322
    /**
323
     * Process the item.
324
     *
325
     * @param  callable $callback        An optional callback routine executed after the item is processed.
326
     * @param  callable $successCallback An optional callback routine executed when the item is resolved.
327
     * @param  callable $failureCallback An optional callback routine executed when the item is rejected.
328
     * @return boolean|null  Success / Failure, or null in case of a skipped item.
329
     */
330
    public function process(
331
        callable $callback = null,
332
        callable $successCallback = null,
333
        callable $failureCallback = null
334
    ) {
335
336
        if ($this->getProcessed() === true) {
337
            // Do not process twice, ever.
338
            return null;
339
        }
340
341
        if ($this->getTargetType() === null) {
342
            $this->logger->error('Can not process object schedule: no object type defined.');
343
            return false;
344
        }
345
346
        if ($this->getTargetId() === null) {
347
            $this->logger->error(sprintf(
348
                'Can not process object schedule: no object "%s" ID defined.',
349
                $this->getTargetType()
350
            ));
351
            return false;
352
        }
353
354
        if (empty($this->getDataDiff())) {
355
            $this->logger->error('Can not process object schedule: no changes (diff) defined.');
356
            return false;
357
        }
358
359
        $obj = $this->modelFactory()->create($this->getTargetType());
360
        $obj->load($this->getTargetId());
361
        if (!$obj->id()) {
362
            $this->logger->error(sprintf(
363
                'Can not load "%s" object %s',
364
                $this->getTargetType(),
365
                $this->getTargetId()
366
            ));
367
        }
368
        $obj->setData($this->getDataDiff());
369
        $update = $obj->update(array_keys($this->getDataDiff()));
370
371
        if ($update) {
372
            $this->setProcessed(true);
373
            $this->setProcessedDate('now');
374
            $this->update([ 'processed', 'processed_date' ]);
375
376
            if ($successCallback !== null) {
377
                $successCallback($this);
378
            }
379
        } else {
380
            if ($failureCallback !== null) {
381
                $failureCallback($this);
382
            }
383
        }
384
385
        if ($callback !== null) {
386
            $callback($this);
387
        }
388
389
        return $update;
390
    }
391
}
392