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:
1 | <?php |
||
17 | class OpportunityRepository extends EntityRepository |
||
18 | { |
||
19 | /** |
||
20 | * @var WorkflowStep[] |
||
21 | */ |
||
22 | protected $workflowStepsByName; |
||
23 | |||
24 | /** |
||
25 | * Get opportunities by state by current quarter |
||
26 | * |
||
27 | * @param $aclHelper AclHelper |
||
28 | * @param array $dateRange |
||
29 | * @return array |
||
30 | */ |
||
31 | public function getOpportunitiesByStatus(AclHelper $aclHelper, $dateRange) |
||
32 | { |
||
33 | $dateEnd = $dateRange['end']; |
||
34 | $dateStart = $dateRange['start']; |
||
35 | |||
36 | return $this->getOpportunitiesDataByStatus($aclHelper, $dateStart, $dateEnd); |
||
37 | } |
||
38 | |||
39 | /** |
||
40 | * @param AclHelper $aclHelper |
||
41 | * @param $dateStart |
||
42 | * @param $dateEnd |
||
43 | * @return array |
||
44 | */ |
||
45 | protected function getOpportunitiesDataByStatus(AclHelper $aclHelper, $dateStart = null, $dateEnd = null) |
||
46 | { |
||
47 | // select statuses |
||
48 | $qb = $this->getEntityManager()->createQueryBuilder(); |
||
49 | $qb->select('status.name, status.label') |
||
50 | ->from('OroCRMSalesBundle:OpportunityStatus', 'status') |
||
51 | ->orderBy('status.name', 'ASC'); |
||
52 | |||
53 | $resultData = array(); |
||
54 | $data = $qb->getQuery()->getArrayResult(); |
||
55 | foreach ($data as $status) { |
||
56 | $name = $status['name']; |
||
57 | $label = $status['label']; |
||
58 | $resultData[$name] = array( |
||
59 | 'name' => $name, |
||
60 | 'label' => $label, |
||
61 | 'budget' => 0, |
||
62 | ); |
||
63 | } |
||
64 | |||
65 | // select opportunity data |
||
66 | $qb = $this->createQueryBuilder('opportunity'); |
||
67 | $qb->select('IDENTITY(opportunity.status) as name, SUM(opportunity.budgetAmount) as budget') |
||
68 | ->groupBy('opportunity.status'); |
||
69 | |||
70 | View Code Duplication | if ($dateStart && $dateEnd) { |
|
|
|||
71 | $qb->where($qb->expr()->between('opportunity.createdAt', ':dateFrom', ':dateTo')) |
||
72 | ->setParameter('dateFrom', $dateStart) |
||
73 | ->setParameter('dateTo', $dateEnd); |
||
74 | } |
||
75 | $groupedData = $aclHelper->apply($qb)->getArrayResult(); |
||
76 | |||
77 | foreach ($groupedData as $statusData) { |
||
78 | $status = $statusData['name']; |
||
79 | $budget = (float)$statusData['budget']; |
||
80 | if ($budget) { |
||
81 | $resultData[$status]['budget'] = $budget; |
||
82 | } |
||
83 | } |
||
84 | |||
85 | return $resultData; |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * @param array $ownerIds |
||
90 | * @param DateTime $date |
||
91 | * @param AclHelper $aclHelper |
||
92 | * |
||
93 | * @return mixed |
||
94 | */ |
||
95 | public function getForecastOfOpporunitiesData($ownerIds, $date, AclHelper $aclHelper) |
||
96 | { |
||
97 | if (!$ownerIds) { |
||
98 | return [ |
||
99 | 'inProgressCount' => 0, |
||
100 | 'budgetAmount' => 0, |
||
101 | 'weightedForecast' => 0, |
||
102 | ]; |
||
103 | } |
||
104 | |||
105 | if ($date === null) { |
||
106 | return $this->getForecastOfOpporunitiesCurrentData($ownerIds, $aclHelper); |
||
107 | } |
||
108 | |||
109 | return $this->getForecastOfOpporunitiesOldData($ownerIds, $date, $aclHelper); |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * @param array $ownerIds |
||
114 | * @param AclHelper $aclHelper |
||
115 | * @return mixed |
||
116 | */ |
||
117 | protected function getForecastOfOpporunitiesCurrentData($ownerIds, AclHelper $aclHelper) |
||
118 | { |
||
119 | $qb = $this->createQueryBuilder('opportunity'); |
||
120 | |||
121 | $select = " |
||
122 | SUM( (CASE WHEN (opportunity.status='in_progress') THEN 1 ELSE 0 END) ) as inProgressCount, |
||
123 | SUM( opportunity.budgetAmount ) as budgetAmount, |
||
124 | SUM( opportunity.budgetAmount * opportunity.probability ) as weightedForecast"; |
||
125 | $qb->select($select); |
||
126 | |||
127 | if (!empty($ownerIds)) { |
||
128 | $qb->join('opportunity.owner', 'owner'); |
||
129 | QueryUtils::applyOptimizedIn($qb, 'owner.id', $ownerIds); |
||
130 | } |
||
131 | |||
132 | $probabilityCondition = $qb->expr()->orX( |
||
133 | $qb->expr()->andX( |
||
134 | 'opportunity.probability <> 0', |
||
135 | 'opportunity.probability <> 1' |
||
136 | ), |
||
137 | 'opportunity.probability is NULL' |
||
138 | ); |
||
139 | |||
140 | $qb->andWhere($probabilityCondition); |
||
141 | |||
142 | return $aclHelper->apply($qb)->getOneOrNullResult(); |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * @param array $ownerIds |
||
147 | * @param \DateTime $date |
||
148 | * @param AclHelper $aclHelper |
||
149 | * @return mixed |
||
150 | */ |
||
151 | protected function getForecastOfOpporunitiesOldData($ownerIds, $date, AclHelper $aclHelper) |
||
194 | |||
195 | /** |
||
196 | * @param mixed $opportunityHistory |
||
197 | * @param string $field |
||
198 | * @return mixed |
||
199 | */ |
||
200 | protected function getHistoryOldValue($opportunityHistory, $field) |
||
213 | |||
214 | /** |
||
215 | * @param array $opportunityHistory |
||
216 | * @param Opportunity $opportunity |
||
217 | * @return bool |
||
218 | */ |
||
219 | protected function isStatusOk($opportunityHistory, $opportunity) |
||
229 | |||
230 | /** |
||
231 | * @param array $ownerIds |
||
232 | * @param array $opportunityHistory |
||
233 | * @param Opportunity $opportunity |
||
234 | * |
||
235 | * @return bool |
||
236 | */ |
||
237 | protected function isOwnerOk($ownerIds, $opportunityHistory, $opportunity) |
||
248 | |||
249 | /** |
||
250 | * @param array $result |
||
251 | * @param array $opportunityHistory |
||
252 | * @param Opportunity $opportunity |
||
253 | * @param mixed $probability |
||
254 | * |
||
255 | * @return array |
||
256 | */ |
||
257 | protected function calculateOpportunityOldValue($result, $opportunityHistory, $opportunity, $probability) |
||
268 | |||
269 | /** |
||
270 | * @param AclHelper $aclHelper |
||
271 | * @param DateTime $start |
||
272 | * @param DateTime $end |
||
273 | * |
||
274 | * @return int |
||
275 | */ |
||
276 | public function getOpportunitiesCount(AclHelper $aclHelper, DateTime $start, DateTime $end) |
||
282 | |||
283 | /** |
||
284 | * @param AclHelper $aclHelper |
||
285 | * @param DateTime $start |
||
286 | * @param DateTime $end |
||
287 | * |
||
288 | * @return int |
||
289 | */ |
||
290 | public function getNewOpportunitiesCount(AclHelper $aclHelper, DateTime $start, DateTime $end) |
||
297 | |||
298 | /** |
||
299 | * @param DateTime $start |
||
300 | * @param DateTime $end |
||
301 | * |
||
302 | * @return QueryBuilder |
||
303 | */ |
||
304 | View Code Duplication | public function createOpportunitiesCountQb(DateTime $start, DateTime $end) |
|
316 | |||
317 | /** |
||
318 | * @param AclHelper $aclHelper |
||
319 | * @param DateTime $start |
||
320 | * @param DateTime $end |
||
321 | * |
||
322 | * @return double |
||
323 | */ |
||
324 | View Code Duplication | public function getTotalServicePipelineAmount(AclHelper $aclHelper, DateTime $start, DateTime $end) |
|
341 | |||
342 | /** |
||
343 | * @param AclHelper $aclHelper |
||
344 | * @param DateTime $start |
||
345 | * @param DateTime $end |
||
346 | * |
||
347 | * @return double |
||
348 | */ |
||
349 | View Code Duplication | public function getTotalServicePipelineAmountInProgress( |
|
368 | |||
369 | /** |
||
370 | * @param AclHelper $aclHelper |
||
371 | * @param DateTime $start |
||
372 | * @param DateTime $end |
||
373 | * |
||
374 | * @return double |
||
375 | */ |
||
376 | View Code Duplication | public function getWeightedPipelineAmount(AclHelper $aclHelper, DateTime $start, DateTime $end) |
|
388 | |||
389 | /** |
||
390 | * @param AclHelper $aclHelper |
||
391 | * @param DateTime $start |
||
392 | * @param DateTime $end |
||
393 | * |
||
394 | * @return double |
||
395 | */ |
||
396 | View Code Duplication | public function getOpenWeightedPipelineAmount(AclHelper $aclHelper, DateTime $start, DateTime $end) |
|
412 | } |
||
413 |
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.