1: <?php
2:
3: namespace Charcoal\Object;
4:
5: use \InvalidArgumentException;
6:
7: // From 'charcoal-core'
8: use \Charcoal\Loader\CollectionLoader;
9:
10: // Local Dependencies
11: use \Charcoal\Object\ObjectRevision;
12: use \Charcoal\Object\ObjectRevisionInterface;
13:
14: /**
15: *
16: */
17: trait RevisionableTrait
18: {
19: /**
20: * @var bool $revisionEnabled
21: */
22: protected $revisionEnabled = true;
23:
24: /**
25: * The class name of the object revision model.
26: *
27: * Must be a fully-qualified PHP namespace and an implementation of
28: * {@see \Charcoal\Object\ObjectRevisionInterface}. Used by the model factory.
29: *
30: * @var string
31: */
32: private $objectRevisionClass = ObjectRevision::class;
33:
34: /**
35: * @param boolean $enabled The (revision) enabled flag.
36: * @return RevisionableInterface Chainable
37: */
38: public function setRevisionEnabled($enabled)
39: {
40: $this->revisionEnabled = !!$enabled;
41: return $this;
42: }
43:
44: /**
45: * @return boolean
46: */
47: public function revisionEnabled()
48: {
49: return $this->revisionEnabled;
50: }
51:
52: /**
53: * Create a revision object.
54: *
55: * @return ObjectRevisionInterface
56: */
57: public function createRevisionObject()
58: {
59: $rev = $this->modelFactory()->create($this->objectRevisionClass());
60:
61: return $rev;
62: }
63:
64: /**
65: * Set the class name of the object revision model.
66: *
67: * @param string $className The class name of the object revision model.
68: * @throws InvalidArgumentException If the class name is not a string.
69: * @return AbstractPropertyDisplay Chainable
70: */
71: protected function setObjectRevisionClass($className)
72: {
73: if (!is_string($className)) {
74: throw new InvalidArgumentException(
75: 'Route class name must be a string.'
76: );
77: }
78:
79: $this->objectRevisionClass = $className;
80:
81: return $this;
82: }
83:
84: /**
85: * Retrieve the class name of the object revision model.
86: *
87: * @return string
88: */
89: public function objectRevisionClass()
90: {
91: return $this->objectRevisionClass;
92: }
93:
94: /**
95: * @return ObjectRevision
96: * @see \Charcoal\Object\ObjectRevision::create_fromObject()
97: */
98: public function generateRevision()
99: {
100: $rev = $this->createRevisionObject();
101:
102: $rev->createFromObject($this);
103: if (!empty($rev->dataDiff())) {
104: $rev->save();
105: }
106:
107: return $rev;
108: }
109:
110: /**
111: * @return ObjectRevision
112: * @see \Charcoal\Object\ObejctRevision::lastObjectRevision
113: */
114: public function latestRevision()
115: {
116: $rev = $this->createRevisionObject();
117: $rev = $rev->lastObjectRevision($this);
118:
119: return $rev;
120: }
121:
122: /**
123: * @param integer $revNum The revision number.
124: * @return ObjectRevision
125: * @see \Charcoal\Object\ObejctRevision::objectRevisionNum
126: */
127: public function revisionNum($revNum)
128: {
129: $revNum = (int)$revNum;
130: $rev = $this->createRevisionObject();
131: $rev = $rev->objectRevisionNum($this, $revNum);
132:
133: return $rev;
134: }
135:
136: /**
137: * Retrieves all revisions for the current objet
138: *
139: * @param callable $callback Optional object callback.
140: * @return array
141: */
142: public function allRevisions(callable $callback = null)
143: {
144: $loader = new CollectionLoader([
145: 'logger' => $this->logger,
146: 'factory' => $this->modelFactory()
147: ]);
148: $loader->setModel($this->createRevisionObject());
149: $loader->addFilter('target_type', $this->objType());
150: $loader->addFilter('target_id', $this->id());
151: $loader->addOrder('rev_ts', 'desc');
152: if ($callback !== null) {
153: $loader->setCallback($callback);
154: }
155: $revisions = $loader->load();
156:
157: return $revisions->objects();
158: }
159:
160: /**
161: * @param integer $revNum The revision number to revert to.
162: * @throws InvalidArgumentException If revision number is invalid.
163: * @return boolean Success / Failure.
164: */
165: public function revertToRevision($revNum)
166: {
167: $revNum = (int)$revNum;
168: if (!$revNum) {
169: throw new InvalidArgumentException(
170: 'Invalid revision number'
171: );
172: }
173:
174: $rev = $this->revisionNum($revNum);
175:
176: if (!$rev->id()) {
177: return false;
178: }
179: if (is_callable([$this, 'setLastModifiedBy'])) {
180: $this->setLastModifiedBy($rev->revUser());
181: }
182: $this->setData($rev->dataObj());
183: $this->update();
184:
185: return true;
186: }
187:
188: /**
189: * Retrieve the object model factory.
190: *
191: * @return \Charcoal\Factory\FactoryInterface
192: */
193: abstract public function modelFactory();
194: }
195: