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 |
||
9 | class ImportRepository extends EntityRepository |
||
10 | { |
||
11 | /** |
||
12 | * Find scheduled imports by feed. |
||
13 | * |
||
14 | * @param Feed $feed |
||
15 | * |
||
16 | * @return Import[] |
||
17 | */ |
||
18 | public function findScheduledByFeed(Feed $feed) |
||
19 | { |
||
20 | $builder = $this->createQueryBuilder('i') |
||
21 | ->where('i.feed = :feed') |
||
22 | ->andWhere('i.datetimeStarted IS NULL') |
||
23 | ->setParameter('feed', $feed) |
||
24 | ; |
||
25 | |||
26 | return $builder->getQuery()->getResult(); |
||
27 | } |
||
28 | |||
29 | /** |
||
30 | * Find latest started import. |
||
31 | * |
||
32 | * @return Import |
||
33 | */ |
||
34 | public function findOneLatestStarted() |
||
35 | { |
||
36 | $builder = $this->createQueryBuilder('i') |
||
37 | ->andWhere('i.datetimeStarted IS NOT NULL') |
||
38 | ->andWhere('SIZE(i.parts) > 0') |
||
39 | ->orderBy('i.datetimeStarted', 'DESC') |
||
40 | ->setMaxResults(1) |
||
41 | ; |
||
42 | |||
43 | return $builder->getQuery()->getOneOrNullResult(); |
||
44 | } |
||
45 | |||
46 | /** |
||
47 | * Find latest started import by feed. |
||
48 | * |
||
49 | * @param Feed $feed |
||
50 | * |
||
51 | * @return Import |
||
52 | */ |
||
53 | View Code Duplication | public function findOneLatestStartedByFeed(Feed $feed) |
|
|
|||
54 | { |
||
55 | $builder = $this->createQueryBuilder('i') |
||
56 | ->where('i.feed = :feed') |
||
57 | ->andWhere('i.datetimeStarted IS NOT NULL') |
||
58 | ->andWhere('SIZE(i.parts) > 0') |
||
59 | ->orderBy('i.datetimeStarted', 'DESC') |
||
60 | ->setMaxResults(1) |
||
61 | ->setParameter('feed', $feed) |
||
62 | ; |
||
63 | |||
64 | return $builder->getQuery()->getOneOrNullResult(); |
||
65 | } |
||
66 | |||
67 | /** |
||
68 | * Find imports by feed, ordered by descending start date. |
||
69 | * |
||
70 | * @param Feed $feed |
||
71 | * |
||
72 | * @return Import[] |
||
73 | */ |
||
74 | View Code Duplication | public function findCompletedByFeed(Feed $feed) |
|
75 | { |
||
76 | $builder = $this->createQueryBuilder('i') |
||
77 | ->where('i.feed = :feed') |
||
78 | ->andWhere('i.datetimeStarted IS NOT NULL') |
||
79 | ->andWhere('i.datetimeEnded IS NOT NULL') |
||
80 | ->orderBy('i.datetimeStarted', 'DESC') |
||
81 | ->setParameter('feed', $feed) |
||
82 | ; |
||
83 | |||
84 | return $builder->getQuery()->getResult(); |
||
85 | } |
||
86 | |||
87 | /** |
||
88 | * Find imports by number of parts. |
||
89 | * |
||
90 | * @param int $number The number of parts |
||
91 | * @param string $operator The operator to use. Possible values are `=`, `<`, `>`, `<=`, and `>=` |
||
92 | * |
||
93 | * @return Import[] |
||
94 | */ |
||
95 | public function findByNumberOfParts($number, $operator = '=') |
||
96 | { |
||
97 | $builder = $this->createQueryBuilder('i') |
||
98 | ->andWhere(sprintf('SIZE(i.parts) %s :parts', $operator)) |
||
99 | ->setParameter('parts', $number) |
||
100 | ; |
||
101 | |||
102 | return $builder->getQuery()->getResult(); |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * Find imports that have started but not yet finished. |
||
107 | * |
||
108 | * @return Import[] |
||
109 | */ |
||
110 | public function findStartedButUnfinished() |
||
111 | { |
||
112 | $builder = $this->createQueryBuilder('i') |
||
113 | ->andWhere('i.datetimeStarted IS NOT NULL') |
||
114 | ->andWhere('i.datetimeEnded IS NULL') |
||
115 | ->orderBy('i.datetimeStarted', 'DESC') |
||
116 | ; |
||
117 | |||
118 | return $builder->getQuery()->getResult(); |
||
119 | } |
||
120 | |||
121 | /** |
||
122 | * @param Import $import |
||
123 | * @param bool $autoFlush |
||
124 | */ |
||
125 | 4 | public function save(Import $import, $autoFlush = true) |
|
126 | { |
||
127 | 4 | $this->getEntityManager()->persist($import); |
|
128 | |||
129 | 4 | if ($autoFlush) { |
|
130 | 4 | $this->getEntityManager()->flush($import); |
|
131 | } |
||
132 | 4 | } |
|
133 | |||
134 | /** |
||
135 | * @param ImportPart $part |
||
136 | * @param bool $autoFlush |
||
137 | */ |
||
138 | 4 | public function savePart(ImportPart $part, $autoFlush = true) |
|
139 | { |
||
140 | 4 | $this->getEntityManager()->persist($part); |
|
141 | |||
142 | 4 | if ($autoFlush) { |
|
143 | 4 | $this->getEntityManager()->flush($part); |
|
144 | } |
||
145 | 4 | } |
|
146 | |||
147 | /** |
||
148 | * @param Import $import |
||
149 | * @param array $transport |
||
150 | * @param \DateTime $scheduleDate |
||
151 | * @param int $position |
||
152 | * |
||
153 | * @return ImportPart |
||
154 | */ |
||
155 | public function createPart(Import $import, array $transport, \DateTime $scheduleDate = null, $position = null) |
||
156 | { |
||
157 | if (is_null($scheduleDate)) { |
||
158 | $scheduleDate = new \DateTime(); |
||
159 | } |
||
160 | |||
161 | if (is_null($position)) { |
||
162 | $position = 0; |
||
163 | |||
164 | /** @var ImportPart $part */ |
||
165 | foreach ($import->getParts() as $part) { |
||
166 | if ($part->getPosition() > $position) { |
||
167 | $position = $part->getPosition(); |
||
168 | } |
||
169 | } |
||
170 | |||
171 | ++$position; |
||
172 | } |
||
173 | |||
174 | $part = new ImportPart(); |
||
175 | $part->setPosition($position); |
||
176 | $part->setTransportConfig($transport); |
||
177 | $part->setDatetimeScheduled($scheduleDate); |
||
178 | $part->setImport($import); |
||
179 | $import->addPart($part); |
||
180 | |||
181 | $this->getEntityManager()->persist($part); |
||
182 | $this->getEntityManager()->flush($part); |
||
183 | |||
184 | return $part; |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * @param Import $import |
||
189 | */ |
||
190 | 4 | public function startImport(Import $import) |
|
195 | |||
196 | /** |
||
197 | * @param Import $import |
||
198 | * |
||
199 | * @throws \RuntimeException |
||
200 | * @throws UnfinishedImportException |
||
201 | * |
||
202 | * @return bool |
||
203 | */ |
||
204 | 4 | public function finishImport(Import $import) |
|
205 | { |
||
206 | 4 | if (!$import->isStarted()) { |
|
207 | throw new \RuntimeException('Import has not yet started'); |
||
208 | } |
||
209 | |||
210 | 4 | if ($this->importHasUnfinishedParts($import)) { |
|
211 | throw UnfinishedImportException::create($import); |
||
212 | } |
||
213 | |||
214 | // set number of errored parts |
||
215 | $erroredParts = $import |
||
216 | 4 | ->getParts() |
|
217 | 4 | ->filter(function (ImportPart $part) { |
|
218 | 4 | return $part->getError(); |
|
219 | 4 | }) |
|
220 | 4 | ->count() |
|
221 | ; |
||
222 | |||
223 | 4 | $import->setErroredParts($erroredParts); |
|
224 | |||
225 | // set end-date/time |
||
226 | 4 | $import->setDatetimeEnded(new \DateTime()); |
|
227 | |||
228 | // flush the end result, and finish the log |
||
229 | 4 | $this->save($import); |
|
230 | 4 | } |
|
231 | |||
232 | /** |
||
233 | * @param ImportPart $part |
||
234 | */ |
||
235 | 4 | public function startImportPart(ImportPart $part) |
|
241 | |||
242 | /** |
||
243 | * @param ImportPart $part |
||
244 | */ |
||
245 | 4 | public function finishImportPart(ImportPart $part) |
|
254 | |||
255 | /** |
||
256 | * Checks if the import has any parts that are unfinished. |
||
257 | * |
||
258 | * @param Import $import The import |
||
259 | * @param bool $refresh Whether to refresh the checked parts first. This |
||
260 | * is useful when time has passed since the import |
||
261 | * start, and you want to avoid race conditions |
||
262 | * |
||
263 | * @return bool |
||
264 | */ |
||
265 | 4 | public function importHasUnfinishedParts(Import $import, $refresh = true) |
|
279 | |||
280 | /** |
||
281 | * @param Import $import |
||
282 | * @param ImportResult $result |
||
283 | */ |
||
284 | 4 | public function addResult(Import $import, ImportResult $result) |
|
305 | } |
||
306 |
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.