Completed
Push — some-scrutinizing ( b2f84c...29b6ab )
by Maxence
23:41
created

CirclesMapper   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 355
Duplicated Lines 9.86 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 32
lcom 1
cbo 5
dl 35
loc 355
rs 9.6
c 3
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B findCirclesByUser() 0 69 6
A buildWithCircleId() 0 8 2
A buildWithMemberLevel() 0 8 2
A generateTypeEntryForCirclePersonal() 0 15 2
A generateTypeEntryForCircleHidden() 0 21 2
A generateTypeEntryForCirclePrivate() 11 11 2
A generateTypeEntryForCirclePublic() 11 11 2
A getDetailsFromCircle() 0 16 3
A create() 0 21 2
A destroy() 12 12 1
B isCircleUnique() 0 27 4
A isPersonalCircleUnique() 0 15 3

How to fix   Duplicated Code   

Duplicated Code

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
2
/**
3
 * Circles - Bring cloud-users closer together.
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @copyright 2017
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\Circles\Db;
28
29
use OCA\Circles\Exceptions\CircleAlreadyExistsException;
30
use OCA\Circles\Exceptions\CircleDoesNotExistException;
31
use OCA\Circles\Exceptions\ConfigNoCircleAvailable;
32
use OCA\Circles\Model\Circle;
33
use OCA\Circles\Model\Member;
34
35
use OCP\DB\QueryBuilder\IQueryBuilder;
36
use OCP\IDBConnection;
37
use OCP\AppFramework\Db\Mapper;
38
39
class CirclesMapper extends Mapper {
40
41
	const TABLENAME = 'circles_circles';
42
43
	private $miscService;
44
45
	public function __construct(IDBConnection $db, $miscService) {
46
		parent::__construct($db, self::TABLENAME, 'OCA\Circles\Db\Circles');
47
		$this->miscService = $miscService;
48
49
	}
50
51
52
	/**
53
	 * Returns all circle from a user point-of-view
54
	 *
55
	 * @param $userId
56
	 * @param $type
57
	 * @param string $name
58
	 * @param int $level
59
	 * @param int $circleId
60
	 *
61
	 * @return Circle[]
62
	 * @throws ConfigNoCircleAvailable
63
	 */
64
	public function findCirclesByUser($userId, $type, $name = '', $level = 0, $circleId = -1) {
65
66
		$type = (int)$type;
67
		$level = (int)$level;
68
		$circleId = (int)$circleId;
69
		$qb = $this->db->getQueryBuilder();
70
		$expr = $qb->expr();
71
72
		$qb->select(
73
			'c.id', 'c.name', 'c.description', 'c.type', 'c.creation',
74
			'u.joined', 'u.level', 'u.status'
75
		)
76
		   ->selectAlias('o.user_id', 'owner')
77
		   ->from(self::TABLENAME, 'c')
78
		   ->from(MembersMapper::TABLENAME, 'o')
79
		   ->where(
80
			   $expr->eq('c.id', 'o.circle_id'),
81
			   $expr->eq('o.level', $qb->createNamedParameter(Member::LEVEL_OWNER))
82
		   );
83
84
		$this->buildWithMemberLevel($qb, 'u.level', $level);
85
		$this->buildWithCircleId($qb, 'c.id', $circleId);
86
87
		$qb->leftJoin(
88
			'c', MembersMapper::TABLENAME, 'u',
89
			$expr->andX(
90
				$expr->eq('c.id', 'u.circle_id'),
91
				$expr->eq('u.user_id', $qb->createNamedParameter($userId))
92
			)
93
		);
94
95
		$orTypesArray = [];
96
		array_push($orTypesArray, $this->generateTypeEntryForCirclePersonal($qb, $type, $userId));
97
		array_push(
98
			$orTypesArray, $this->generateTypeEntryForCircleHidden($qb, $type, $circleId, $name)
99
		);
100
		array_push($orTypesArray, $this->generateTypeEntryForCirclePrivate($qb, $type));
101
		array_push($orTypesArray, $this->generateTypeEntryForCirclePublic($qb, $type));
102
103
		$orTypesArray = array_filter($orTypesArray);
104
105
		if (sizeof($orTypesArray) === 0) {
106
			throw new ConfigNoCircleAvailable();
107
		}
108
109
		$orXTypes = $expr->orX();
110
111
		foreach ($orTypesArray as $orTypes) {
112
			$orXTypes->add($orTypes);
113
		}
114
115
		$qb->andWhere($orXTypes);
116
117
		$qb->groupBy('c.id');
118
		$qb->orderBy('c.name', 'ASC');
119
120
		$cursor = $qb->execute();
121
122
		$result = [];
123
		while ($data = $cursor->fetch()) {
124
			if ($name === '' || stripos($data['name'], $name) !== false) {
125
				$circle = new Circle();
126
				$result[] = $circle->fromArray($data);
127
			}
128
		}
129
		$cursor->closeCursor();
130
131
		return $result;
132
	}
133
134
135
	/**
136
	 * @param IQueryBuilder $qb
137
	 * @param string $field
138
	 * @param int $circleId
139
	 */
140
	private function buildWithCircleId(IQueryBuilder & $qb, string $field, int $circleId) {
141
		if ($circleId > 0) {
142
			$qb->andWhere(
143
				$qb->expr()
144
				   ->eq($field, $qb->createNamedParameter($circleId))
145
			);
146
		}
147
	}
148
149
150
	/**
151
	 * @param IQueryBuilder $qb
152
	 * @param string $field
153
	 * @param int $level
154
	 */
155
	private function buildWithMemberLevel(IQueryBuilder & $qb, string $field, int $level) {
156
		if ($level > 0) {
157
			$qb->andWhere(
158
				$qb->expr()
159
				   ->gte($field, $qb->createNamedParameter($level))
160
			);
161
		}
162
	}
163
164
165
	/**
166
	 * @param IQueryBuilder $qb
167
	 * @param int $type
168
	 * @param int|string $userId
169
	 *
170
	 * @return \OCP\DB\QueryBuilder\ICompositeExpression
171
	 */
172
	private function generateTypeEntryForCirclePersonal(IQueryBuilder $qb, int $type, string $userId
173
	) {
174
		if (Circle::CIRCLES_PERSONAL & (int)$type) {
175
			return $qb->expr()
176
					  ->andX(
177
						  $qb->expr()
178
							 ->eq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL)),
179
						  $qb->expr()
180
							 ->eq('o.user_id', $qb->createNamedParameter($userId))
181
182
					  );
183
		}
184
185
		return null;
186
	}
187
188
	/**
189
	 * @param IQueryBuilder $qb
190
	 * @param int $type
191
	 * @param int $circleId
192
	 * @param string $name
193
	 *
194
	 * @return string
195
	 */
196
	private function generateTypeEntryForCircleHidden(
197
		IQueryBuilder $qb, int $type, int $circleId, string $name
198
	) {
199
		if (!(Circle::CIRCLES_HIDDEN & (int)$type)) {
200
			return null;
201
		}
202
203
		$expr = $qb->expr();
204
		$sqb = $expr->andX(
205
			$expr->eq('c.type', $qb->createNamedParameter(Circle::CIRCLES_HIDDEN)),
206
			$expr->orX(
207
				$expr->gte(
208
					'u.level', $qb->createNamedParameter(Member::LEVEL_MEMBER)
209
				),
210
				$expr->eq('c.id', $qb->createNamedParameter($circleId)),
211
				$expr->eq('c.name', $qb->createNamedParameter($name))
212
			)
213
		);
214
215
		return $sqb;
216
	}
217
218
219
	/**
220
	 * @param IQueryBuilder $qb
221
	 * @param int $type
222
	 *
223
	 * @return string
224
	 */
225 View Code Duplication
	private function generateTypeEntryForCirclePrivate(IQueryBuilder $qb, int $type) {
226
		if (Circle::CIRCLES_PRIVATE & (int)$type) {
227
			return $qb->expr()
228
					  ->eq(
229
						  'c.type',
230
						  $qb->createNamedParameter(Circle::CIRCLES_PRIVATE)
231
					  );
232
		}
233
234
		return null;
235
	}
236
237
238
	/**
239
	 * @param IQueryBuilder $qb
240
	 * @param int $type
241
	 *
242
	 * @return string
243
	 */
244 View Code Duplication
	private function generateTypeEntryForCirclePublic(IQueryBuilder $qb, int $type) {
245
		if (Circle::CIRCLES_PUBLIC & (int)$type) {
246
			return $qb->expr()
247
					  ->eq(
248
						  'c.type',
249
						  $qb->createNamedParameter(Circle::CIRCLES_PUBLIC)
250
					  );
251
		}
252
253
		return null;
254
	}
255
256
	/**
257
	 * Returns details about a circle.
258
	 *
259
	 * @param string $userId
260
	 * @param int $circleId
261
	 *
262
	 * @return Circle
263
	 * @throws CircleDoesNotExistException
264
	 * @throws ConfigNoCircleAvailable
265
	 */
266
	public function getDetailsFromCircle($userId, $circleId) {
267
268
		try {
269
			$result = $this->findCirclesByUser($userId, Circle::CIRCLES_ALL, '', 0, $circleId);
270
		} catch (ConfigNoCircleAvailable $e) {
271
			throw $e;
272
		}
273
274
		if (sizeof($result) !== 1) {
275
			throw new CircleDoesNotExistException(
276
				"The circle does not exist or is hidden to the user"
277
			);
278
		}
279
280
		return $result[0];
281
	}
282
283
284
	/**
285
	 * @param Circle $circle
286
	 * @param Member $owner
287
	 *
288
	 * @return bool
289
	 * @throws CircleAlreadyExistsException
290
	 */
291
	public function create(Circle & $circle, Member & $owner) {
292
293
		if (!$this->isCircleUnique($circle, $owner)) {
294
			throw new CircleAlreadyExistsException();
295
		}
296
297
		$qb = $this->db->getQueryBuilder();
298
		$qb->insert(self::TABLENAME)
299
		   ->setValue('name', $qb->createNamedParameter($circle->getName()))
300
		   ->setValue('description', $qb->createNamedParameter($circle->getDescription()))
301
		   ->setValue('type', $qb->createNamedParameter($circle->getType()));
302
		$qb->execute();
303
		$circleId = $qb->getLastInsertId();
304
305
		$circle->setId($circleId);
306
		$owner->setLevel(Member::LEVEL_OWNER)
307
			  ->setStatus(Member::STATUS_MEMBER)
308
			  ->setCircleId($circleId);
309
310
		return true;
311
	}
312
313
314
	/**
315
	 * remove a circle
316
	 *
317
	 * @param Circle $circle
318
	 */
319 View Code Duplication
	public function destroy(Circle $circle) {
320
		$qb = $this->db->getQueryBuilder();
321
		$qb->delete(self::TABLENAME)
322
		   ->where(
323
			   $qb->expr()
324
				  ->eq(
325
					  'id', $qb->createNamedParameter($circle->getId())
326
				  )
327
		   );
328
329
		$qb->execute();
330
	}
331
332
333
	/**
334
	 * returns if the circle is already in database
335
	 *
336
	 * @param Circle $circle
337
	 * @param Member $owner
338
	 *
339
	 * @return bool
340
	 */
341
	public function isCircleUnique(Circle $circle, Member $owner) {
342
343
		if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
344
			return $this->isPersonalCircleUnique($circle, $owner);
345
		}
346
347
		$qb = $this->db->getQueryBuilder();
348
		$qb->select(
349
			'c.id', 'c.name', 'c.type'
350
		)
351
		   ->from(self::TABLENAME, 'c')
352
		   ->where(
353
			   $qb->expr()
354
				  ->neq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL))
355
		   );
356
357
		$cursor = $qb->execute();
358
359
		while ($data = $cursor->fetch()) {
360
			if (strtolower($data['name']) === strtolower($circle->getName())) {
361
				return false;
362
			}
363
		}
364
		$cursor->closeCursor();
365
366
		return true;
367
	}
368
369
370
	/**
371
	 * return if the personal circle is unique
372
	 *
373
	 * @param Circle $circle
374
	 * @param Member $owner
375
	 *
376
	 * @return bool
377
	 */
378
	private function isPersonalCircleUnique(Circle $circle, Member $owner) {
379
380
		$list = $this->findCirclesByUser(
381
			$owner->getUserId(), Circle::CIRCLES_PERSONAL, $circle->getName(),
382
			Member::LEVEL_OWNER
383
		);
384
385
		foreach ($list as $test) {
386
			if ($test->getName() === $circle->getName()) {
387
				return false;
388
			}
389
		}
390
391
		return true;
392
	}
393
}
394
395