Complex classes like MessageTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use MessageTrait, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | trait MessageTrait |
||
24 | { |
||
25 | use MutualTrait; |
||
26 | |||
27 | public $attachmentAttribute = 'attachment'; |
||
28 | public $receivedAtAttribute = 'received_at'; |
||
29 | public $readAtAttribute = 'read_at'; |
||
30 | public static $eventMessageReceived = 'messageReceived'; |
||
31 | public static $eventMessageRead = 'messageRead'; |
||
32 | public $permitChangeContent = false; |
||
33 | public $permitChangeReceivedAt = false; |
||
34 | public $permitChangeReadAt = false; |
||
35 | |||
36 | 4 | public function hasBeenReceived() |
|
37 | { |
||
38 | 4 | return is_string($this->receivedAtAttribute) ? !$this->isInitDatetime($this->getReceivedAt()) : false; |
|
|
|||
39 | } |
||
40 | |||
41 | 4 | public function hasBeenRead() |
|
42 | { |
||
43 | 4 | return is_string($this->readAtAttribute) ? !$this->isInitDatetime($this->getReadAt()) : false; |
|
44 | } |
||
45 | |||
46 | 2 | public function touchReceived() |
|
47 | { |
||
48 | 2 | return $this->setReceivedAt(static::currentDatetime()); |
|
49 | } |
||
50 | |||
51 | 2 | public function touchRead() |
|
52 | { |
||
53 | 2 | return $this->setReadAt(static::currentDatetime()); |
|
54 | } |
||
55 | |||
56 | 4 | public function getReceivedAt() |
|
57 | { |
||
58 | 4 | if (is_string($this->receivedAtAttribute)) { |
|
59 | 4 | $raAttribute = $this->receivedAtAttribute; |
|
60 | 4 | return $this->$raAttribute; |
|
61 | } |
||
62 | return null; |
||
63 | } |
||
64 | |||
65 | 8 | public function setReceivedAt($receivedAt) |
|
66 | { |
||
67 | 8 | if (is_string($this->receivedAtAttribute)) { |
|
68 | 8 | $raAttribute = $this->receivedAtAttribute; |
|
69 | 8 | return $this->$raAttribute = $receivedAt; |
|
70 | } |
||
71 | return null; |
||
72 | } |
||
73 | |||
74 | 4 | public function getReadAt() |
|
75 | { |
||
76 | 4 | if (is_string($this->readAtAttribute)) { |
|
77 | 4 | $raAttribute = $this->readAtAttribute; |
|
78 | 4 | return $this->$raAttribute; |
|
79 | } |
||
80 | return null; |
||
81 | } |
||
82 | |||
83 | 8 | public function setReadAt($readAt) |
|
84 | { |
||
85 | 8 | if (is_string($this->readAtAttribute)) { |
|
86 | 8 | $raAttribute = $this->readAtAttribute; |
|
87 | 8 | return $this->$raAttribute = $readAt; |
|
88 | } |
||
89 | return null; |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * @param \yii\base\ModelEvent $event |
||
94 | */ |
||
95 | 8 | public function onInitReceivedAtAttribute($event) |
|
96 | { |
||
97 | 8 | $sender = $event->sender; |
|
98 | /* @var $sender static */ |
||
99 | 8 | $sender->setReceivedAt(static::getInitDatetime($event)); |
|
100 | 8 | } |
|
101 | |||
102 | /** |
||
103 | * @param \yii\base\ModelEvent $event |
||
104 | */ |
||
105 | 8 | public function onInitReadAtAttribute($event) |
|
106 | { |
||
107 | 8 | $sender = $event->sender; |
|
108 | /* @var $sender static */ |
||
109 | 8 | $sender->setReadAt(static::getInitDatetime($event)); |
|
110 | 8 | } |
|
111 | |||
112 | /** |
||
113 | * We consider you have received the message if you read it. |
||
114 | * @param \yii\base\ModelEvent $event |
||
115 | */ |
||
116 | 4 | public function onReadAtChanged($event) |
|
117 | { |
||
118 | 4 | $sender = $event->sender; |
|
119 | 4 | $raAttribute = $sender->readAtAttribute; |
|
120 | 4 | if (!is_string($raAttribute)) { |
|
121 | return; |
||
122 | } |
||
123 | 4 | $reaAttribute = $sender->receivedAtAttribute; |
|
124 | 4 | if (is_string($reaAttribute) && !$sender->isInitDatetime($sender->$raAttribute) && $sender->isInitDatetime($sender->$reaAttribute)) { |
|
125 | 2 | $sender->$reaAttribute = $sender->currentDatetime(); |
|
126 | 2 | } |
|
127 | 4 | if ($sender->permitChangeReadAt) { |
|
128 | return; |
||
129 | } |
||
130 | 4 | $oldRa = $sender->getOldAttribute($raAttribute); |
|
131 | 4 | if ($oldRa != null && !$sender->isInitDatetime($oldRa) && $sender->$raAttribute != $oldRa) { |
|
132 | $sender->$raAttribute = $oldRa; |
||
133 | return; |
||
134 | } |
||
135 | 4 | } |
|
136 | |||
137 | /** |
||
138 | * You are not allowed to change receive time if you have received it. |
||
139 | * @param \yii\base\ModelEvent $event |
||
140 | */ |
||
141 | 4 | public function onReceivedAtChanged($event) |
|
142 | { |
||
143 | 4 | $sender = $event->sender; |
|
144 | 4 | $raAttribute = $sender->receivedAtAttribute; |
|
145 | 4 | if (!is_string($raAttribute)) { |
|
146 | return; |
||
147 | } |
||
148 | 4 | if ($sender->permitChangeReceivedAt) { |
|
149 | return; |
||
150 | } |
||
151 | 4 | $oldRa = $sender->getOldAttribute($raAttribute); |
|
152 | 4 | if ($oldRa != null && !$sender->isInitDatetime($oldRa) && $sender->$raAttribute != $oldRa) { |
|
153 | $sender->$raAttribute = $oldRa; |
||
154 | return; |
||
155 | } |
||
156 | 4 | } |
|
157 | |||
158 | /** |
||
159 | * You are not allowed to change the content if it is not new message. |
||
160 | * @param \yii\base\ModelEvent $event |
||
161 | */ |
||
162 | 4 | public function onContentChanged($event) |
|
163 | { |
||
164 | 4 | $sender = $event->sender; |
|
165 | 4 | if ($sender->permitChangeContent) { |
|
166 | return; |
||
167 | } |
||
168 | 4 | $cAttribute = $sender->contentAttribute; |
|
169 | 4 | $oldContent = $sender->getOldAttribute($cAttribute); |
|
170 | 4 | if ($oldContent != $sender->$cAttribute) { |
|
171 | 2 | $sender->$cAttribute = $oldContent; |
|
172 | 2 | } |
|
173 | 4 | } |
|
174 | |||
175 | /** |
||
176 | * Trigger message received or read events. |
||
177 | * @param \yii\db\AfterSaveEvent $event |
||
178 | */ |
||
179 | 4 | public function onMessageUpdated($event) |
|
180 | { |
||
181 | 4 | $sender = $event->sender; |
|
182 | 4 | $reaAttribute = $sender->receivedAtAttribute; |
|
183 | 4 | if (isset($event->changedAttributes[$reaAttribute]) && $event->changedAttributes[$reaAttribute] != $sender->$reaAttribute) { |
|
184 | 4 | $sender->trigger(static::$eventMessageReceived); |
|
185 | 4 | } |
|
186 | 4 | $raAttribute = $sender->readAtAttribute; |
|
187 | 4 | if (isset($event->changedAttributes[$raAttribute]) && $event->changedAttributes[$raAttribute] != $sender->$raAttribute) { |
|
188 | 2 | $sender->trigger(static::$eventMessageRead); |
|
189 | 2 | } |
|
190 | 4 | } |
|
191 | |||
192 | 8 | public function initMessageEvents() |
|
193 | { |
||
194 | 8 | $this->on(static::EVENT_BEFORE_INSERT, [$this, 'onInitReceivedAtAttribute']); |
|
195 | 8 | $this->on(static::EVENT_BEFORE_INSERT, [$this, 'onInitReadAtAttribute']); |
|
196 | 8 | $this->on(static::EVENT_BEFORE_UPDATE, [$this, 'onReceivedAtChanged']); |
|
197 | 8 | $this->on(static::EVENT_BEFORE_UPDATE, [$this, 'onReadAtChanged']); |
|
198 | 8 | $this->on(static::EVENT_BEFORE_UPDATE, [$this, 'onContentChanged']); |
|
199 | 8 | $this->on(static::EVENT_AFTER_UPDATE, [$this, 'onMessageUpdated']); |
|
200 | 8 | } |
|
201 | |||
202 | 8 | public function getMessageRules() |
|
203 | { |
||
204 | 8 | $rules = []; |
|
205 | 8 | $rules = array_merge($rules, $this->getMutualRules()); |
|
206 | 8 | if (is_string($this->attachmentAttribute)) { |
|
207 | 8 | $rules[] = [$this->attachmentAttribute, 'safe']; |
|
208 | 8 | } |
|
209 | 8 | if (is_string($this->receivedAtAttribute)) { |
|
210 | 8 | $rules[] = [$this->receivedAtAttribute, 'safe']; |
|
211 | 8 | } |
|
212 | 8 | if (is_string($this->readAtAttribute)) { |
|
213 | 8 | $rules[] = [$this->readAtAttribute, 'safe']; |
|
214 | 8 | } |
|
215 | 8 | return $rules; |
|
216 | } |
||
217 | |||
218 | 8 | public function rules() |
|
222 | |||
223 | 8 | public function enabledFields() |
|
224 | { |
||
225 | 8 | $fields = parent::enabledFields(); |
|
226 | 8 | if (is_string($this->otherGuidAttribute)) { |
|
227 | 8 | $fields[] = $this->otherGuidAttribute; |
|
228 | 8 | } |
|
229 | 8 | if (is_string($this->attachmentAttribute)) { |
|
230 | 8 | $fields[] = $this->attachmentAttribute; |
|
231 | 8 | } |
|
232 | 8 | if (is_string($this->receivedAtAttribute)) { |
|
233 | 8 | $fields[] = $this->receivedAtAttribute; |
|
234 | 8 | } |
|
235 | 8 | if (is_string($this->readAtAttribute)) { |
|
236 | 8 | $fields[] = $this->readAtAttribute; |
|
237 | 8 | } |
|
238 | 8 | return $fields; |
|
240 | } |
||
241 |
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.