Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like PostVote 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 PostVote, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
8 | class PostVote extends Game implements ServiceManagerAwareInterface |
||
9 | { |
||
10 | protected $postvoteMapper; |
||
11 | protected $postvoteformMapper; |
||
12 | protected $postVotePostMapper; |
||
13 | protected $postVoteVoteMapper; |
||
14 | protected $postVotePostElementMapper; |
||
15 | |||
16 | public function getGameEntity() |
||
20 | |||
21 | public function uploadFileToPost($data, $game, $user) |
||
73 | |||
74 | public function deleteFilePosted($data, $game, $user) |
||
99 | |||
100 | /** |
||
101 | * |
||
102 | * @param array $data |
||
103 | * @return \PlaygroundGame\Entity\Game |
||
104 | */ |
||
105 | public function createPost(array $data, $game, $user, $form) |
||
106 | { |
||
107 | $postvotePostMapper = $this->getPostVotePostMapper(); |
||
108 | $postVotePostElementMapper = $this->getPostVotePostElementMapper(); |
||
109 | |||
110 | $entry = $this->findLastActiveEntry($game, $user); |
||
111 | |||
112 | if (!$entry) { |
||
113 | return false; |
||
114 | } |
||
115 | |||
116 | $post = $postvotePostMapper->findOneBy(array('entry' => $entry)); |
||
117 | |||
118 | View Code Duplication | if (! $post) { |
|
119 | $post = new \PlaygroundGame\Entity\PostVotePost(); |
||
120 | $post->setPostvote($game); |
||
121 | $post->setUser($user); |
||
122 | $post->setEntry($entry); |
||
123 | $post = $postvotePostMapper->insert($post); |
||
124 | } |
||
125 | |||
126 | $path = $this->getOptions()->getMediaPath() . DIRECTORY_SEPARATOR . 'game' . $game->getId() . DIRECTORY_SEPARATOR; |
||
127 | if (!is_dir($path)) { |
||
128 | mkdir($path, 0777, true); |
||
129 | } |
||
130 | $path .= 'post'. $post->getId() . DIRECTORY_SEPARATOR; |
||
131 | if (!is_dir($path)) { |
||
132 | mkdir($path, 0777, true); |
||
133 | } |
||
134 | |||
135 | $media_url = $this->getOptions()->getMediaUrl() . '/' . 'game' . $game->getId() . '/' . 'post'. $post->getId() . '/'; |
||
136 | $position=1; |
||
137 | |||
138 | foreach ($data as $name => $value) { |
||
139 | $postElement = $postVotePostElementMapper->findOneBy(array('post' => $post, 'name' => $name)); |
||
140 | if (! $postElement) { |
||
141 | $postElement = new \PlaygroundGame\Entity\PostVotePostElement(); |
||
142 | } |
||
143 | $postElement->setName($name); |
||
144 | $postElement->setPosition($position); |
||
145 | |||
146 | if (is_array($value) && isset($value['tmp_name'])) { |
||
147 | // The file upload has been done in ajax but some weird bugs remain without it |
||
148 | |||
149 | if (! $value['error']) { |
||
150 | ErrorHandler::start(); |
||
151 | $value['name'] = $this->fileNewname($path, $value['name'], true); |
||
152 | move_uploaded_file($value['tmp_name'], $path . $value['name']); |
||
153 | $image = $this->getServiceManager()->get('playgroundcore_image_service'); |
||
154 | $image->setImage($path . $value['name']); |
||
155 | |||
156 | if ($image->canCorrectOrientation()) { |
||
157 | $image->correctOrientation()->save(); |
||
158 | } |
||
159 | $postElement->setValue($media_url . $value['name']); |
||
160 | |||
161 | if (class_exists("Imagick")) { |
||
162 | $ext = pathinfo($value['name'], PATHINFO_EXTENSION); |
||
163 | $img = new \Imagick($path . $value['name']); |
||
164 | $img->cropThumbnailImage(100, 100); |
||
165 | $img->setImageCompression(\Imagick::COMPRESSION_JPEG); |
||
166 | $img->setImageCompressionQuality(75); |
||
167 | // Strip out unneeded meta data |
||
168 | $img->stripImage(); |
||
169 | $img->writeImage($path . str_replace('.'.$ext, '-thumbnail.'.$ext, $value['name'])); |
||
170 | ErrorHandler::stop(true); |
||
171 | } |
||
172 | } |
||
173 | } elseif (is_array($value)) { |
||
174 | $arValues = $form->get($name)->getValueOptions(); |
||
175 | $postElement->setValue($arValues[$value[0]]); |
||
176 | } elseif (!empty($value)) { |
||
177 | $postElement->setValue($value); |
||
178 | } |
||
179 | $postElement->setPost($post); |
||
180 | $postVotePostElementMapper->insert($postElement); |
||
181 | $position++; |
||
182 | } |
||
183 | |||
184 | $postvotePostMapper->update($post); |
||
185 | |||
186 | // If a preview step is not proposed, I confirmPost on this step |
||
187 | $steps = $game->getStepsArray(); |
||
188 | $previewKey = array_search('preview', $steps); |
||
189 | if (!$previewKey) { |
||
190 | $post = $this->confirmPost($game, $user); |
||
191 | } |
||
192 | |||
193 | $this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, array( |
||
194 | 'user' => $user, |
||
195 | 'game' => $game, |
||
196 | 'post' => $post, |
||
197 | 'entry' => $entry |
||
198 | )); |
||
199 | |||
200 | return $post; |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * |
||
205 | * @return \PlaygroundGame\Entity\Game |
||
206 | */ |
||
207 | public function confirmPost($game, $user) |
||
208 | { |
||
209 | $postvotePostMapper = $this->getPostVotePostMapper(); |
||
210 | |||
211 | $entryMapper = $this->getEntryMapper(); |
||
212 | $entry = $this->findLastActiveEntry($game, $user); |
||
213 | |||
214 | if (!$entry) { |
||
215 | return false; |
||
216 | } |
||
217 | |||
218 | $post = $postvotePostMapper->findOneBy(array('entry' => $entry)); |
||
219 | |||
220 | if (! $post) { |
||
221 | return false; |
||
222 | } |
||
223 | |||
224 | // The post is confirmed by user. I update the status and close the associated entry |
||
225 | // Post are validated by default, unless pre-moderation is enable for the game |
||
226 | if ($game->getModerationType()) { |
||
227 | $post->setStatus(1); |
||
228 | } else { |
||
229 | $post->setStatus(2); |
||
230 | } |
||
231 | |||
232 | $postvotePostMapper->update($post); |
||
233 | |||
234 | $entry->setActive(0); |
||
235 | $entryMapper->update($entry); |
||
236 | |||
237 | $this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, array( |
||
238 | 'user' => $user, |
||
239 | 'game' => $game, |
||
240 | 'entry' => $entry, |
||
241 | 'post' => $post |
||
242 | )); |
||
243 | |||
244 | if ($user) { |
||
245 | // send mail for participation |
||
246 | $this->sendGameMail($game, $user, $post, 'postvote'); |
||
247 | } |
||
248 | |||
249 | return $post; |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * |
||
254 | * This service is ready for all types of games |
||
255 | * |
||
256 | * @param array $data |
||
257 | * @return \PlaygroundGame\Entity\Game |
||
258 | */ |
||
259 | View Code Duplication | public function createForm(array $data, $game, $form = null) |
|
260 | { |
||
261 | $title =''; |
||
262 | $description = ''; |
||
263 | |||
264 | if ($data['form_jsonified']) { |
||
265 | $jsonPV = json_decode($data['form_jsonified']); |
||
266 | foreach ($jsonPV as $element) { |
||
267 | if ($element->form_properties) { |
||
268 | $attributes = $element->form_properties[0]; |
||
269 | $title = $attributes->title; |
||
270 | $description = $attributes->description; |
||
271 | |||
272 | break; |
||
273 | } |
||
274 | } |
||
275 | } |
||
276 | if (!$form) { |
||
277 | $form = new \PlaygroundGame\Entity\PostVoteForm(); |
||
278 | } |
||
279 | $form->setPostvote($game); |
||
280 | $form->setTitle($title); |
||
281 | $form->setDescription($description); |
||
282 | $form->setForm($data['form_jsonified']); |
||
283 | $form->setFormTemplate($data['form_template']); |
||
284 | |||
285 | $form = $this->getPostVoteFormMapper()->insert($form); |
||
286 | |||
287 | return $form; |
||
288 | } |
||
289 | |||
290 | public function findArrayOfValidatedPosts($game, $filter, $search = '') |
||
291 | { |
||
292 | $em = $this->getServiceManager()->get('doctrine.entitymanager.orm_default'); |
||
293 | $qb = $em->createQueryBuilder(); |
||
294 | $and = $qb->expr()->andx(); |
||
295 | |||
296 | $and->add($qb->expr()->eq('p.status', 2)); |
||
297 | |||
298 | $and->add($qb->expr()->eq('g.id', ':game')); |
||
299 | $qb->setParameter('game', $game); |
||
300 | |||
301 | if ($search != '') { |
||
302 | $and->add( |
||
303 | $qb->expr()->orX( |
||
304 | $qb->expr()->like('u.username', $qb->expr()->literal('%:search%')), |
||
305 | $qb->expr()->like('u.firstname', $qb->expr()->literal('%:search%')), |
||
306 | $qb->expr()->like('u.lastname', $qb->expr()->literal('%:search%')), |
||
307 | $qb->expr()->like('e.value', $qb->expr()->literal('%:search%')), |
||
308 | $qb->expr()->isNull('g.publicationDate') |
||
309 | ) |
||
310 | ); |
||
311 | $qb->setParameter('search', $search); |
||
312 | } |
||
313 | |||
314 | if ('push' == $filter) { |
||
315 | $and->add( |
||
316 | $qb->expr()->andX( |
||
317 | $qb->expr()->eq('p.pushed', 1) |
||
318 | ) |
||
319 | ); |
||
320 | } |
||
321 | |||
322 | $qb->select('p, COUNT(v) AS votesCount') |
||
323 | ->from('PlaygroundGame\Entity\PostVotePost', 'p') |
||
324 | ->innerJoin('p.postvote', 'g') |
||
325 | ->leftJoin('p.user', 'u') |
||
326 | ->innerJoin('p.postElements', 'e') |
||
327 | ->leftJoin('p.votes', 'v') |
||
328 | ->where($and) |
||
329 | ->groupBy('p.id'); |
||
330 | |||
331 | switch ($filter) { |
||
332 | case 'random': |
||
333 | $qb->orderBy('e.value', 'ASC'); |
||
334 | break; |
||
335 | case 'vote': |
||
336 | $qb->orderBy('votesCount', 'DESC'); |
||
337 | break; |
||
338 | case 'date': |
||
339 | $qb->orderBy('p.createdAt', 'DESC'); |
||
340 | break; |
||
341 | case 'push': |
||
342 | $qb->orderBy('p.createdAt', 'DESC'); |
||
343 | break; |
||
344 | } |
||
345 | |||
346 | $query = $qb->getQuery(); |
||
347 | |||
348 | $posts = $query->getResult(); |
||
349 | $arrayPosts = array(); |
||
350 | $i=0; |
||
351 | foreach ($posts as $postRaw) { |
||
352 | $data = array(); |
||
353 | $post = $postRaw[0]; |
||
354 | if ($post) { |
||
355 | foreach ($post->getPostElements() as $element) { |
||
356 | $data[$element->getPosition()] = $element->getValue(); |
||
357 | } |
||
358 | $arrayPosts[$i]['data'] = $data; |
||
359 | $arrayPosts[$i]['votes'] = count($post->getVotes()); |
||
360 | $arrayPosts[$i]['id'] = $post->getId(); |
||
361 | $arrayPosts[$i]['user'] = $post->getUser(); |
||
362 | $arrayPosts[$i]['createdAt'] = $post->getCreatedAt(); |
||
363 | $i++; |
||
364 | } |
||
365 | } |
||
366 | |||
367 | return $arrayPosts; |
||
368 | } |
||
369 | |||
370 | public function addVote($user, $ipAddress, $post) |
||
398 | |||
399 | public function getEntriesHeader($game) |
||
400 | { |
||
401 | $header = parent::getEntriesHeader($game); |
||
402 | if ($game->getForm()) { |
||
415 | |||
416 | View Code Duplication | public function getEntriesQuery($game) |
|
454 | |||
455 | /** |
||
456 | * getGameEntries : All entries of a game |
||
457 | * |
||
458 | * @return Array of PlaygroundGame\Entity\Game |
||
459 | */ |
||
460 | public function getGameEntries($header, $entries, $game) |
||
489 | |||
490 | public function getPostVoteFormMapper() |
||
498 | |||
499 | public function setPostVoteFormMapper($postvoteformMapper) |
||
505 | |||
506 | public function getPostVotePostElementMapper() |
||
514 | |||
515 | public function setPostVotePostElementMapper($postVotePostElementMapper) |
||
521 | |||
522 | public function getPostVoteVoteMapper() |
||
530 | |||
531 | public function setPostVoteVoteMapper($postVoteVoteMapper) |
||
537 | |||
538 | public function getPostVotePostMapper() |
||
546 | |||
547 | public function setPostVotePostMapper($postVotePostMapper) |
||
553 | |||
554 | public function getPostVoteMapper() |
||
562 | |||
563 | /** |
||
564 | * setQuizQuestionMapper |
||
565 | * |
||
566 | * @return PostVote |
||
567 | */ |
||
568 | public function setPostVoteMapper($postvoteMapper) |
||
574 | } |
||
575 |
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.