1 | <?php |
||
14 | trait Approvable |
||
15 | { |
||
16 | |||
17 | /** @var array */ |
||
18 | public $approveOf = array(); |
||
19 | |||
20 | /** @var array */ |
||
21 | public $dontApproveOf = array(); |
||
22 | |||
23 | /** @var bool */ |
||
24 | protected $withoutApproval = false; |
||
25 | |||
26 | /** |
||
27 | * Create the event listeners for the saving event |
||
28 | * This lets us save approvals whenever a save is made, no matter the |
||
29 | * http method |
||
30 | */ |
||
31 | public static function bootApprovable(): void |
||
37 | |||
38 | /** |
||
39 | * @return MorphMany |
||
40 | */ |
||
41 | 3 | public function approvals(): MorphMany |
|
45 | |||
46 | /** |
||
47 | * Check if this model has pending changes, |
||
48 | * If an attribute is provided, check if the attribute has pending changes. |
||
49 | * |
||
50 | * @param null $attribute |
||
51 | * @return bool |
||
52 | */ |
||
53 | 2 | public function isPendingApproval($attribute = null): bool |
|
62 | |||
63 | /** |
||
64 | * List all the attributes, that currently have pending changes. |
||
65 | * |
||
66 | * @return \Illuminate\Support\Collection |
||
67 | */ |
||
68 | 1 | public function getPendingApprovalAttributes(): Collection |
|
75 | |||
76 | public function withoutApproval(bool $withoutApproval = true): void |
||
77 | { |
||
78 | $this->withoutApproval = $withoutApproval; |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Invoked before a model is saved. Return false to abort the operation. |
||
83 | 1 | * |
|
84 | * @return bool |
||
85 | 1 | */ |
|
86 | 1 | protected function preSave(): bool |
|
87 | { |
||
88 | if ($this->withoutApproval) { |
||
89 | return true; |
||
90 | } |
||
91 | |||
92 | if ($this->currentUserCanApprove()) { |
||
93 | // If the user is able to approve edits, do nothing. |
||
94 | 8 | return true; |
|
95 | } |
||
96 | 8 | ||
97 | if (!$this->exists) { |
||
98 | 2 | // There is currently no way (implemented) to enable this for new models. |
|
99 | return true; |
||
100 | } |
||
101 | 6 | ||
102 | $changes_to_record = $this->changedApprovableFields(); |
||
103 | 6 | ||
104 | $approvals = array(); |
||
105 | foreach ($changes_to_record as $key => $change) { |
||
106 | 5 | $approvals[] = array( |
|
107 | 'approvable_type' => $this->getMorphClass(), |
||
108 | 5 | 'approvable_id' => $this->getKey(), |
|
109 | 5 | 'key' => $key, |
|
110 | 5 | 'value' => $change, |
|
111 | 5 | 'user_id' => $this->getSystemUserId(), |
|
112 | 5 | 'created_at' => new \DateTime(), |
|
113 | 5 | 'updated_at' => new \DateTime(), |
|
114 | 5 | ); |
|
115 | 5 | } |
|
116 | 5 | ||
117 | 5 | if (count($approvals) > 0) { |
|
118 | $approval = new Approval(); |
||
119 | DB::table($approval->getTable())->insert($approvals); |
||
120 | } |
||
121 | 5 | ||
122 | 5 | return true; |
|
123 | 5 | } |
|
124 | |||
125 | /** |
||
126 | 5 | * Get all of the changes that have been made, that are also supposed |
|
127 | * to be approved. |
||
128 | * |
||
129 | * @return array fields with new data, that should be recorded |
||
130 | */ |
||
131 | private function changedApprovableFields(): array |
||
153 | |||
154 | private function isApprovable(string $key): bool |
||
155 | 5 | { |
|
165 | |||
166 | protected function getSystemUserId(): ?int |
||
170 | |||
171 | 5 | protected function currentUserCanApprove(): bool |
|
175 | } |
||
176 |
This check looks for methods that are used by a trait but not required by it.
To illustrate, let’s look at the following code example
The trait
Idable
provides a methodequalsId
that in turn relies on the methodgetId()
. If this method does not exist on a class mixing in this trait, the method will fail.Adding the
getId()
as an abstract method to the trait will make sure it is available.