Passed
Push — feature/909_Ampache_API_improv... ( 2004a5...b752cc )
by Pauli
12:33
created

BusinessLayer::findAllIdsAndNames()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
c 0
b 0
f 0
nc 4
nop 11
dl 0
loc 16
rs 9.9332

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php declare(strict_types=1);
2
/**
3
 * ownCloud
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Alessandro Cosentino <[email protected]>
9
 * @author Bernhard Posselt <[email protected]>
10
 * @author Pauli Järvinen <[email protected]>
11
 * @copyright Alessandro Cosentino 2012
12
 * @copyright Bernhard Posselt 2012, 2014
13
 * @copyright Pauli Järvinen 2017 - 2023
14
 */
15
16
namespace OCA\Music\AppFramework\BusinessLayer;
17
18
use OCA\Music\Db\BaseMapper;
19
use OCA\Music\Db\Entity;
20
use OCA\Music\Db\MatchMode;
21
use OCA\Music\Db\SortBy;
22
use OCA\Music\Utility\Util;
23
24
use OCP\AppFramework\Db\DoesNotExistException;
25
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
26
use OCP\IL10N;
27
28
/**
29
 * @phpstan-template EntityType of Entity
30
 */
31
abstract class BusinessLayer {
32
	protected $mapper;
33
34
	// Some SQLite installations can't handle more than 999 query args. Remember that `user_id` takes one slot in most queries.
35
	public const MAX_SQL_ARGS = 999;
36
37
	/**
38
	 * @phpstan-param BaseMapper<EntityType> $mapper
39
	 */
40
	public function __construct(BaseMapper $mapper) {
41
		$this->mapper = $mapper;
42
	}
43
44
	/**
45
	 * Update an entity in the database
46
	 * @phpstan-param EntityType $entity
47
	 * @phpstan-return EntityType
48
	 */
49
	public function update(Entity $entity) : Entity {
50
		return $this->mapper->update($entity);
51
	}
52
53
	/**
54
	 * Delete an entity
55
	 * @param int $id the id of the entity
56
	 * @param string $userId the name of the user for security reasons
57
	 * @throws BusinessLayerException if the entity does not exist or more than one entity exists
58
	 * @phpstan-return EntityType
59
	 */
60
	public function delete(int $id, string $userId) : Entity {
61
		$entity = $this->find($id, $userId);
62
		return $this->mapper->delete($entity);
63
	}
64
65
	/**
66
	 * Deletes entities without specifying the owning user.
67
	 * This should never be called directly from the HTML API, but only in case
68
	 * we can actually trust the passed IDs (e.g. file deleted hook).
69
	 * @param array $ids the ids of the entities which should be deleted
70
	 */
71
	public function deleteById(array $ids) : void {
72
		if (\count($ids) > 0) {
73
			$this->mapper->deleteById($ids);
74
		}
75
	}
76
77
	/**
78
	 * Delete all entities of the given user
79
	 */
80
	public function deleteAll(string $userId) : void {
81
		$this->mapper->deleteAll($userId);
82
	}
83
84
	/**
85
	 * Finds an entity by id
86
	 * @param int $id the id of the entity
87
	 * @param string $userId the name of the user for security reasons
88
	 * @throws BusinessLayerException if the entity does not exist or more than one entity exists
89
	 * @phpstan-return EntityType
90
	 */
91
	public function find(int $id, string $userId) : Entity {
92
		try {
93
			return $this->mapper->find($id, $userId);
94
		} catch (DoesNotExistException $ex) {
95
			throw new BusinessLayerException($ex->getMessage());
96
		} catch (MultipleObjectsReturnedException $ex) {
97
			throw new BusinessLayerException($ex->getMessage());
98
		}
99
	}
100
101
	/**
102
	 * Finds an entity by id, or returns an empty entity instance if the requested one is not found
103
	 * @param int $id the id of the entity
104
	 * @param string $userId the name of the user for security reasons
105
	 * @phpstan-return EntityType
106
	 */
107
	public function findOrDefault(int $id, string $userId) : Entity {
108
		try {
109
			return $this->find($id, $userId);
110
		} catch (BusinessLayerException $ex) {
111
			return $this->mapper->createEntity();
112
		}
113
	}
114
115
	/**
116
	 * Find all entities matching the given IDs.
117
	 * Specifying the user is optional; if omitted, the caller should make sure that
118
	 * user's data is not leaked to unauthorized users.
119
	 * @param integer[] $ids  IDs of the entities to be found
120
	 * @param string|null $userId
121
	 * @param bool $preserveOrder If true, then the result will be in the same order as @a $ids
122
	 * @return Entity[]
123
	 * @phpstan-return EntityType[]
124
	 */
125
	public function findById(array $ids, string $userId=null, bool $preserveOrder=false) : array {
126
		$entities = [];
127
		if (\count($ids) > 0) {
128
			// don't use more than 999 SQL args in one query since that may be a problem for SQLite
129
			$idChunks = \array_chunk($ids, 998);
130
			foreach ($idChunks as $idChunk) {
131
				$entities = \array_merge($entities, $this->mapper->findById($idChunk, $userId));
132
			}
133
		}
134
135
		if ($preserveOrder) {
136
			$lut = Util::createIdLookupTable($entities);
137
			$result = [];
138
			foreach ($ids as $id) {
139
				$result[] = $lut[$id];
140
			}
141
		} else {
142
			$result = $entities;
143
		}
144
145
		return $result;
146
	}
147
148
	/**
149
	 * Finds all entities
150
	 * @param string $userId the name of the user
151
	 * @param integer $sortBy sort order of the result set
152
	 * @param integer|null $limit
153
	 * @param integer|null $offset
154
	 * @param string|null $createdMin Optional minimum `created` timestamp.
155
	 * @param string|null $createdMax Optional maximum `created` timestamp.
156
	 * @param string|null $updatedMin Optional minimum `updated` timestamp.
157
	 * @param string|null $updatedMax Optional maximum `updated` timestamp.
158
	 * @return Entity[]
159
	 * @phpstan-return EntityType[]
160
	 */
161
	public function findAll(
162
			string $userId, int $sortBy=SortBy::None, ?int $limit=null, ?int $offset=null,
163
			?string $createdMin=null, ?string $createdMax=null, ?string $updatedMin=null, ?string $updatedMax=null) : array {
164
		return $this->mapper->findAll($userId, $sortBy, $limit, $offset, $createdMin, $createdMax, $updatedMin, $updatedMax);
165
	}
166
167
	/**
168
	 * Return all entities with name matching the search criteria
169
	 * @param string|null $createdMin Optional minimum `created` timestamp.
170
	 * @param string|null $createdMax Optional maximum `created` timestamp.
171
	 * @param string|null $updatedMin Optional minimum `updated` timestamp.
172
	 * @param string|null $updatedMax Optional maximum `updated` timestamp.
173
	 * @return Entity[]
174
	 * @phpstan-return EntityType[]
175
	 */
176
	public function findAllByName(
177
			?string $name, string $userId, int $matchMode=MatchMode::Exact, ?int $limit=null, ?int $offset=null,
178
			?string $createdMin=null, ?string $createdMax=null, ?string $updatedMin=null, ?string $updatedMax=null) : array {
179
		if ($name !== null) {
180
			$name = \trim($name);
181
		}
182
		return $this->mapper->findAllByName($name, $userId, $matchMode, $limit, $offset, $createdMin, $createdMax, $updatedMin, $updatedMax);
183
	}
184
185
	/**
186
	 * Find all starred entities
187
	 * @return Entity[]
188
	 * @phpstan-return EntityType[]
189
	 */
190
	public function findAllStarred(string $userId, ?int $limit=null, ?int $offset=null) : array {
191
		return $this->mapper->findAllStarred($userId, $limit, $offset);
192
	}
193
194
	/**
195
	 * Find all entities with user-given rating 1-5
196
	 * @return Entity[]
197
	 * @phpstan-return EntityType[]
198
	 */
199
	public function findAllRated(string $userId, ?int $limit=null, ?int $offset=null) : array {
200
		return $this->mapper->findAllRated($userId, $limit, $offset);
201
	}
202
203
	/**
204
	 * Find all entities matching multiple criteria, as needed for the Ampache API method `advanced_search`
205
	 * @param string $conjunction Operator to use between the rules, either 'and' or 'or'
206
	 * @param array $rules Array of arrays: [['rule' => string, 'operator' => string, 'input' => string], ...]
207
	 * 				Here, 'rule' has dozens of possible values depending on the business layer in question,
208
	 * 				(see https://ampache.org/api/api-advanced-search#available-search-rules, alias names not supported here),
209
	 * 				'operator' is one of 
210
	 * 				['contain', 'notcontain', 'start', 'end', 'is', 'isnot', '>=', '<=', '=', '!=', '>', '<', 'true', 'false', 'equal', 'ne', 'limit'],
211
	 * 				'input' is the right side value of the 'operator' (disregarded for the operators 'true' and 'false')
212
	 * @return Entity[]
213
	 * @phpstan-return EntityType[]
214
	 */
215
	public function findAllAdvanced(string $conjunction, array $rules, string $userId, ?int $limit=null, ?int $offset=null) : array {
216
		if ($conjunction !== 'and' && $conjunction !== 'or') {
217
			throw new BusinessLayerException("Bad conjunction '$conjunction'");
218
		}
219
		try {
220
			return $this->mapper->findAllAdvanced($conjunction, $rules, $userId, $limit, $offset);
221
		} catch (\Exception $e) {
222
			// catch everything as many kinds of DB exceptions are possible on various cloud versions
223
			throw new BusinessLayerException($e->getMessage());
224
		}
225
	}
226
227
	/**
228
	 * Find IDs of all user's entities of this kind.
229
	 * Optionally, limit to given IDs which may be used to check the validity of those IDs.
230
	 * @return int[]
231
	 */
232
	public function findAllIds(string $userId, ?array $ids = null) : array {
233
		if ($ids === null || \count($ids) > 0) {
234
			return $this->mapper->findAllIds($userId, $ids);
235
		} else {
236
			return [];
237
		}
238
	}
239
240
	/**
241
	 * Find all IDs and names of user's entities of this kind.
242
	 * Optionally, limit results based on a parent entity (not applicable for all entity types) or update/insert times or name
243
	 * @param bool $excludeChildless Exclude entities having no child-entities if applicable for this business layer (eg. artists without albums)
244
	 * @return array of arrays like ['id' => string, 'name' => string]
245
	 */
246
	public function findAllIdsAndNames(string $userId, IL10N $l10n, ?int $parentId=null, ?int $limit=null, ?int $offset=null,
247
			?string $createdMin=null, ?string $createdMax=null, ?string $updatedMin=null, ?string $updatedMax=null,
248
			bool $excludeChidless=false, ?string $name=null) : array {
249
		try {
250
			$idsAndNames = $this->mapper->findAllIdsAndNames(
251
				$userId, $parentId, $limit, $offset, $createdMin, $createdMax, $updatedMin, $updatedMax, $excludeChidless, $name);
252
		} catch (\DomainException $ex) {
253
			throw new BusinessLayerException($ex->getMessage());
254
		}
255
		foreach ($idsAndNames as &$idAndName) {
256
			if (empty($idAndName['name'])) {
257
				$emptyEntity = $this->mapper->createEntity($idAndName);
0 ignored issues
show
Unused Code introduced by
The call to OCA\Music\Db\BaseMapper::createEntity() has too many arguments starting with $idAndName. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

257
				/** @scrutinizer ignore-call */ 
258
    $emptyEntity = $this->mapper->createEntity($idAndName);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
258
				$idAndName['name'] = $emptyEntity->getNameString($l10n);
259
			}
260
		}
261
		return $idsAndNames;
262
	}
263
264
	/**
265
	 * Find IDs of all users owning any entities of this business layer
266
	 * @return string[]
267
	 */
268
	public function findAllUsers() : array {
269
		return $this->mapper->findAllUsers();
270
	}
271
272
	/**
273
	 * Set the given entities as "starred" on this date
274
	 * @param int[] $ids
275
	 * @param string $userId
276
	 * @return int number of modified entities
277
	 */
278
	public function setStarred(array $ids, string $userId) : int {
279
		if (\count($ids) > 0) {
280
			return $this->mapper->setStarredDate(new \DateTime(), $ids, $userId);
281
		} else {
282
			return 0;
283
		}
284
	}
285
286
	/**
287
	 * Remove the "starred" status of the given entities
288
	 * @param integer[] $ids
289
	 * @param string $userId
290
	 * @return int number of modified entities
291
	 */
292
	public function unsetStarred(array $ids, string $userId) : int {
293
		if (\count($ids) > 0) {
294
			return $this->mapper->setStarredDate(null, $ids, $userId);
295
		} else {
296
			return 0;
297
		}
298
	}
299
300
	/**
301
	 * Tests if entity with given ID and user ID exists in the database
302
	 * @param int $id
303
	 * @param string $userId
304
	 * @return bool
305
	 */
306
	public function exists(int $id, string $userId) : bool {
307
		return $this->mapper->exists($id, $userId);
308
	}
309
310
	/**
311
	 * Get the number of entities
312
	 * @param string $userId
313
	 */
314
	public function count(string $userId) : int {
315
		return $this->mapper->count($userId);
316
	}
317
318
	/**
319
	 * Get the timestamp of the latest insert operation on the entity type in question
320
	 */
321
	public function latestInsertTime(string $userId) : \DateTime {
322
		return $this->mapper->latestInsertTime($userId) ?? new \DateTime('1970-01-01');
323
	}
324
325
	/**
326
	 * Get the timestamp of the latest update operation on the entity type in question
327
	 */
328
	public function latestUpdateTime(string $userId) : \DateTime {
329
		return $this->mapper->latestUpdateTime($userId) ?? new \DateTime('1970-01-01');
330
	}
331
}
332