1: <?php
2:
3: namespace Charcoal\Object;
4:
5:
6: use \DateTime;
7: use \DateTimeInterface;
8: use \Exception;
9: use \RuntimeException;
10: use \InvalidArgumentException;
11:
12:
13: use \Charcoal\Factory\FactoryInterface;
14:
15:
16: use \Charcoal\Model\AbstractModel;
17:
18:
19: use \Charcoal\Object\ObjectScheduleInterface;
20:
21: 22: 23: 24: 25: 26: 27:
28: class ObjectSchedule extends AbstractModel implements ObjectScheduleInterface
29: {
30: 31: 32: 33: 34:
35: private $modelFactory;
36:
37: 38: 39: 40: 41:
42: private $targetType;
43:
44: 45: 46: 47: 48:
49: private $targetId;
50:
51: 52: 53: 54: 55: 56: 57: 58:
59: private $scheduledDate;
60:
61: 62: 63: 64: 65:
66: private $dataDiff = [];
67:
68: 69: 70: 71: 72:
73: private $processed = false;
74:
75: 76: 77: 78: 79:
80: private $processedDate;
81:
82: 83: 84: 85: 86: 87:
88: public function setModelFactory(FactoryInterface $factory)
89: {
90: $this->modelFactory = $factory;
91:
92: return $this;
93: }
94:
95: 96: 97: 98: 99: 100:
101: protected function modelFactory()
102: {
103: if (!isset($this->modelFactory)) {
104: throw new RuntimeException(
105: sprintf('Model Factory is not defined for "%s"', get_class($this))
106: );
107: }
108:
109: return $this->modelFactory;
110: }
111:
112: 113: 114: 115: 116: 117: 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: 134: 135: 136:
137: public function targetType()
138: {
139: return $this->targetType;
140: }
141:
142: 143: 144: 145: 146: 147:
148: public function setTargetId($targetId)
149: {
150: $this->targetId = $targetId;
151:
152: return $this;
153: }
154:
155: 156: 157: 158: 159:
160: public function targetId()
161: {
162: return $this->targetId;
163: }
164:
165: 166: 167: 168:
169: public function setDataDiff($data)
170: {
171: if (!is_array($data)) {
172: $data = json_decode($data, true);
173: }
174: if ($data === null) {
175: $data = [];
176: }
177: $this->dataDiff = $data;
178: return $this;
179: }
180:
181: 182: 183:
184: public function dataDiff()
185: {
186: return $this->dataDiff;
187: }
188:
189: 190: 191: 192: 193: 194:
195: public function setProcessed($processed)
196: {
197: $this->processed = !!$processed;
198:
199: return $this;
200: }
201:
202: 203: 204: 205: 206:
207: public function processed()
208: {
209: return $this->processed;
210: }
211:
212: 213: 214: 215: 216: 217: 218:
219: public function setScheduledDate($ts)
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(
231: sprintf('%s (%s)', $e->getMessage(), $ts)
232: );
233: }
234: }
235:
236: if (!($ts instanceof DateTimeInterface)) {
237: throw new InvalidArgumentException(
238: 'Invalid "Processing Date" value. Must be a date/time string or a DateTime object.'
239: );
240: }
241:
242: $this->scheduledDate = $ts;
243:
244: return $this;
245: }
246:
247: 248: 249: 250: 251:
252: public function scheduledDate()
253: {
254: return $this->scheduledDate;
255: }
256:
257: 258: 259: 260: 261: 262: 263:
264: public function setProcessedDate($ts)
265: {
266: if ($ts === null) {
267: $this->processedDate = null;
268: return $this;
269: }
270:
271: if (is_string($ts)) {
272: try {
273: $ts = new DateTime($ts);
274: } catch (Exception $e) {
275: throw new InvalidArgumentException(
276: sprintf('%s (%s)', $e->getMessage(), $ts)
277: );
278: }
279: }
280:
281: if (!($ts instanceof DateTimeInterface)) {
282: throw new InvalidArgumentException(
283: 'Invalid "Processed Date" value. Must be a date/time string or a DateTime object.'
284: );
285: }
286:
287: $this->processedDate = $ts;
288:
289: return $this;
290: }
291:
292: 293: 294: 295: 296:
297: public function processedDate()
298: {
299: return $this->processedDate;
300: }
301:
302: 303: 304: 305: 306: 307: 308:
309: public function preSave()
310: {
311: parent::preSave();
312:
313: $this->setProcessed(false);
314:
315: return true;
316: }
317:
318: 319: 320: 321: 322: 323: 324: 325:
326: public function process(
327: callable $callback = null,
328: callable $successCallback = null,
329: callable $failureCallback = null
330: ) {
331:
332: if ($this->processed() === true) {
333:
334: return null;
335: }
336:
337: if ($this->targetType() === null) {
338: $this->logger->error('Can not process object schedule: no object type defined.');
339: return false;
340: }
341:
342: if ($this->targetId() === null) {
343: $this->logger->error(
344: sprintf('Can not process object schedule: no object "%s" ID defined.', $this->targetType())
345: );
346: return false;
347: }
348:
349: if (empty($this->dataDiff())) {
350: $this->logger->error('Can not process object schedule: no changes (diff) defined.');
351: return false;
352: }
353:
354: $obj = $this->modelFactory()->create($this->targetType());
355: $obj->load($this->targetId());
356: if (!$obj->id()) {
357: $this->logger->error(sprintf('Can not load "%s" object %id', $this->targetType(), $this->targetId()));
358: }
359: $obj->setData($this->dataDiff());
360: $update = $obj->update(array_keys($this->dataDiff()));
361:
362: if ($update) {
363: $this->setProcessed(true);
364: $this->setProcessedDate('now');
365: $this->update(['processed', 'processed_date']);
366:
367: if ($successCallback !== null) {
368: $successCallback($this);
369: }
370: } else {
371: if ($failureCallback !== null) {
372: $failureCallback($this);
373: }
374: }
375:
376: if ($callback !== null) {
377: $callback($this);
378: }
379:
380: return $update;
381: }
382: }
383: