Passed
Push — master ( a4260e...ab8f86 )
by Pauli
13:01
created

TrackMapper::findAllByCriteria()   B

Complexity

Conditions 9
Paths 144

Size

Total Lines 43
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 25
c 1
b 0
f 0
nc 144
nop 10
dl 0
loc 43
rs 7.6888

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
/**
4
 * ownCloud - Music app
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Morris Jobke <[email protected]>
10
 * @author Pauli Järvinen <[email protected]>
11
 * @copyright Morris Jobke 2013, 2014
12
 * @copyright Pauli Järvinen 2016 - 2024
13
 */
14
15
namespace OCA\Music\Db;
16
17
use OCP\IConfig;
18
use OCP\IDBConnection;
19
20
/**
21
 * @phpstan-extends BaseMapper<Track>
22
 */
23
class TrackMapper extends BaseMapper {
24
	public function __construct(IDBConnection $db, IConfig $config) {
25
		parent::__construct($db, $config, 'music_tracks', Track::class, 'title', 'album_id');
26
	}
27
28
	/**
29
	 * Override the base implementation to include data from multiple tables
30
	 *
31
	 * {@inheritdoc}
32
	 * @see BaseMapper::selectEntities()
33
	 */
34
	protected function selectEntities(string $condition, string $extension=null) : string {
35
		return "SELECT `*PREFIX*music_tracks`.*, `file`.`name` AS `filename`, `file`.`size`, `file`.`mtime` AS `file_mod_time`,
36
						`album`.`name` AS `album_name`, `artist`.`name` AS `artist_name`, `genre`.`name` AS `genre_name`
37
				FROM `*PREFIX*music_tracks`
38
				INNER JOIN `*PREFIX*filecache` `file`
39
				ON `*PREFIX*music_tracks`.`file_id` = `file`.`fileid`
40
				INNER JOIN `*PREFIX*music_albums` `album`
41
				ON `*PREFIX*music_tracks`.`album_id` = `album`.`id`
42
				INNER JOIN `*PREFIX*music_artists` `artist`
43
				ON `*PREFIX*music_tracks`.`artist_id` = `artist`.`id`
44
				LEFT JOIN `*PREFIX*music_genres` `genre`
45
				ON `*PREFIX*music_tracks`.`genre_id` = `genre`.`id`
46
				WHERE $condition $extension";
47
	}
48
49
	/**
50
	 * Overridden from the base implementation to add support for sorting by artist, play_count, and last_played.
51
	 *
52
	 * {@inheritdoc}
53
	 * @see BaseMapper::formatSortingClause()
54
	 */
55
	protected function formatSortingClause(int $sortBy, bool $invertSort = false) : ?string {
56
		switch ($sortBy) {
57
			case SortBy::Parent:
58
				$dir = $invertSort ? 'DESC' : 'ASC';
59
				// Note: the alternative form "LOWER(`artist_name`) wouldn't work on PostgreSQL, see https://github.com/owncloud/music/issues/1046 for a similar case
60
				return "ORDER BY LOWER(`artist`.`name`) $dir, LOWER(`title`) $dir";
61
			case SortBy::PlayCount:
62
				$dir = $invertSort ? 'ASC' : 'DESC';
63
				return "ORDER BY `play_count` $dir";
64
			case SortBy::LastPlayed:
65
				$dir = $invertSort ? 'ASC' : 'DESC';
66
				return "ORDER BY `last_played` $dir";
67
			default:
68
				return parent::formatSortingClause($sortBy, $invertSort);
69
		}
70
	}
71
72
	/**
73
	 * Returns all tracks of the given artist (both album and track artists are considered)
74
	 * @param int[] $artistIds
75
	 * @return Track[]
76
	 */
77
	public function findAllByArtist(array $artistIds, string $userId, ?int $limit=null, ?int $offset=null) : array {
78
		$questionMarks = $this->questionMarks(\count($artistIds));
79
		$sql = $this->selectUserEntities(
80
				"`artist_id` IN $questionMarks OR `album_id` IN (SELECT `id` from `*PREFIX*music_albums` WHERE `album_artist_id` IN $questionMarks)",
81
				'ORDER BY LOWER(`title`)');
82
		$params = \array_merge([$userId], $artistIds, $artistIds);
83
		return $this->findEntities($sql, $params, $limit, $offset);
84
	}
85
86
	/**
87
	 * @param int[] $albumIds
88
	 * @return Track[]
89
	 */
90
	public function findAllByAlbum(array $albumIds, string $userId, ?int $artistId=null, ?int $limit=null, ?int $offset=null) : array {
91
		$condition = '`album_id` IN ' . $this->questionMarks(\count($albumIds));
92
		$params = \array_merge([$userId], $albumIds);
93
94
		if ($artistId !== null) {
95
			$condition .= ' AND `artist_id` = ? ';
96
			$params[] = $artistId;
97
		}
98
99
		$sql = $this->selectUserEntities($condition,
100
				'ORDER BY `*PREFIX*music_tracks`.`disk`, `number`, LOWER(`title`)');
101
		return $this->findEntities($sql, $params, $limit, $offset);
102
	}
103
104
	/**
105
	 * @return Track[]
106
	 */
107
	public function findAllByFolder(int $folderId, string $userId, ?int $limit=null, ?int $offset=null) : array {
108
		$sql = $this->selectUserEntities('`file`.`parent` = ?', 'ORDER BY LOWER(`title`)');
109
		$params = [$userId, $folderId];
110
		return $this->findEntities($sql, $params, $limit, $offset);
111
	}
112
113
	/**
114
	 * @return Track[]
115
	 */
116
	public function findAllByGenre(int $genreId, string $userId, ?int $limit=null, ?int $offset=null) : array {
117
		$sql = $this->selectUserEntities('`genre_id` = ?', 'ORDER BY LOWER(`title`)');
118
		$params = [$userId, $genreId];
119
		return $this->findEntities($sql, $params, $limit, $offset);
120
	}
121
122
	/**
123
	 * @param string $userId
124
	 * @return int[]
125
	 */
126
	public function findAllFileIds(string $userId) : array {
127
		$sql = 'SELECT `file_id` FROM `*PREFIX*music_tracks` WHERE `user_id` = ?';
128
		$result = $this->execute($sql, [$userId]);
129
130
		return \array_map(function ($i) {
131
			return (int)$i['file_id'];
132
		}, $result->fetchAll());
133
	}
134
135
	/**
136
	 * Find a track of user matching a file ID
137
	 * @throws \OCP\AppFramework\Db\DoesNotExistException if not found
138
	 */
139
	public function findByFileId(int $fileId, string $userId) : Track {
140
		$sql = $this->selectUserEntities('`file_id` = ?');
141
		$params = [$userId, $fileId];
142
		return $this->findEntity($sql, $params);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->findEntity($sql, $params) returns the type OCP\AppFramework\Db\Entity which includes types incompatible with the type-hinted return OCA\Music\Db\Track.
Loading history...
143
	}
144
145
	/**
146
	 * Find tracks of user with multiple file IDs
147
	 * @param integer[] $fileIds
148
	 * @param string[] $userIds
149
	 * @return Track[]
150
	 */
151
	public function findByFileIds(array $fileIds, array $userIds) : array {
152
		$sql = $this->selectEntities(
153
				'`*PREFIX*music_tracks`.`user_id` IN ' . $this->questionMarks(\count($userIds)) .
154
				' AND `file_id` IN '. $this->questionMarks(\count($fileIds)));
155
		$params = \array_merge($userIds, $fileIds);
156
		return $this->findEntities($sql, $params);
157
	}
158
159
	/**
160
	 * Finds tracks of all users matching one or multiple file IDs
161
	 * @param integer[] $fileIds
162
	 * @return Track[]
163
	 */
164
	public function findAllByFileIds(array $fileIds) : array {
165
		$sql = $this->selectEntities('`file_id` IN '.
166
				$this->questionMarks(\count($fileIds)));
167
		return $this->findEntities($sql, $fileIds);
168
	}
169
170
	public function countByArtist(int $artistId) : int {
171
		$sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?';
172
		$result = $this->execute($sql, [$artistId]);
173
		$row = $result->fetch();
174
		return (int)$row['count'];
175
	}
176
177
	public function countByAlbum(int $albumId) : int {
178
		$sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?';
179
		$result = $this->execute($sql, [$albumId]);
180
		$row = $result->fetch();
181
		return (int)$row['count'];
182
	}
183
184
	/**
185
	 * @return integer Duration in seconds
186
	 */
187
	public function totalDurationOfAlbum(int $albumId) : int {
188
		$sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?';
189
		$result = $this->execute($sql, [$albumId]);
190
		$row = $result->fetch();
191
		return (int)$row['duration'];
192
	}
193
194
	/**
195
	 * @return integer Duration in seconds
196
	 */
197
	public function totalDurationByArtist(int $artistId) : int {
198
		$sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?';
199
		$result = $this->execute($sql, [$artistId]);
200
		$row = $result->fetch();
201
		return (int)$row['duration'];
202
	}
203
204
	/**
205
	 * Get durations of the given tracks.
206
	 * @param integer[] $trackIds
207
	 * @return array {int => int} where keys are track IDs and values are corresponding durations
208
	 */
209
	public function getDurations(array $trackIds) : array {
210
		$result = [];
211
212
		if (!empty($trackIds)) {
213
			$sql = 'SELECT `id`, `length` FROM `*PREFIX*music_tracks` WHERE `id` IN ' .
214
						$this->questionMarks(\count($trackIds));
215
			$rows = $this->execute($sql, $trackIds)->fetchAll();
216
			foreach ($rows as $row) {
217
				$result[$row['id']] = (int)$row['length'];
218
			}
219
		}
220
		return $result;
221
	}
222
223
	/**
224
	 * @return Track[]
225
	 */
226
	public function findAllByNameRecursive(string $name, string $userId, ?int $limit=null, ?int $offset=null) {
227
		$condition = '( LOWER(`artist`.`name`) LIKE LOWER(?) OR
228
						LOWER(`album`.`name`) LIKE LOWER(?) OR
229
						LOWER(`title`) LIKE LOWER(?) )';
230
		$sql = $this->selectUserEntities($condition, 'ORDER BY LOWER(`title`)');
231
		$name = BaseMapper::prepareSubstringSearchPattern($name);
232
		$params = [$userId, $name, $name, $name];
233
		return $this->findEntities($sql, $params, $limit, $offset);
234
	}
235
236
	/**
237
	 * Returns all tracks specified by name, artist name, and/or album name
238
	 * @param string|null $name the name of the track
239
	 * @param string|null $artistName the name of the artist
240
	 * @param string $userId the name of the user
241
	 * @return Track[] Tracks matching the criteria
242
	 */
243
	public function findAllByNameArtistOrAlbum(?string $name, ?string $artistName, ?string $albumName, string $userId) : array {
244
		$sqlConditions = [];
245
		$params = [$userId];
246
247
		if (!empty($name)) {
248
			$sqlConditions[] = 'LOWER(`title`) = LOWER(?)';
249
			$params[] = $name;
250
		}
251
252
		if (!empty($artistName)) {
253
			$sqlConditions[] = 'LOWER(`artist`.`name`) = LOWER(?)';
254
			$params[] = $artistName;
255
		}
256
257
		if (!empty($albumName)) {
258
			$sqlConditions[] = 'LOWER(`album`.`name`) = LOWER(?)';
259
			$params[] = $albumName;
260
		}
261
262
		// at least one condition has to be given, otherwise return an empty set
263
		if (\count($sqlConditions) > 0) {
264
			$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions));
265
			return $this->findEntities($sql, $params);
266
		} else {
267
			return [];
268
		}
269
	}
270
271
	const FAVORITE_TRACK = 0x1;
272
	const FAVORITE_ALBUM = 0x2;
273
	const FAVORITE_ARTIST = 0x4;
274
275
	/**
276
	 * Returns all tracks specified by various criteria, all of which are optional
277
	 * @param int[] $genres Array of genre IDs
278
	 * @param int[] $artists Array of artist IDs
279
	 * @param int|null $fromYear Earliest release year to include
280
	 * @param int|null $toYear Latest release year to include
281
	 * @param int|null $favorite Bit mask of FAVORITE_TRACK, FAVORITE_ALBUM, FAVORITE_ARTIST (given favorite types are ORed in the query)
282
	 * @param int $sortBy Sorting rule as defined in the class SortBy
283
	 * @param string $userId the name of the user
284
	 * @return Track[] Tracks matching the criteria
285
	 */
286
	public function findAllByCriteria(
287
			array $genres, array $artists, ?int $fromYear, ?int $toYear, ?int $favorite,
288
			int $sortBy, bool $invertSort, string $userId, ?int $limit=null, ?int $offset=null) : array {
289
290
		$sqlConditions = [];
291
		$params = [$userId];
292
293
		if (!empty($genres)) {
294
			$sqlConditions[] = '`genre_id` IN ' . $this->questionMarks(\count($genres));
295
			$params = \array_merge($params, $genres);
296
		}
297
298
		if (!empty($artists)) {
299
			$sqlConditions[] = '`artist_id` IN ' . $this->questionMarks(\count($artists));
300
			$params = \array_merge($params, $artists);
301
		}
302
303
		if (!empty($fromYear)) {
304
			$sqlConditions[] = '`year` >= ?';
305
			$params[] = $fromYear;
306
		}
307
308
		if (!empty($toYear)) {
309
			$sqlConditions[] = '`year` <= ?';
310
			$params[] = $toYear;
311
		}
312
313
		if (!empty($favorite)) {
314
			$favConds = [];
315
			if ($favorite & self::FAVORITE_TRACK) {
316
				$favConds[] = '`*PREFIX*music_tracks`.`starred` IS NOT NULL';
317
			}
318
			if ($favorite & self::FAVORITE_ALBUM) {
319
				$favConds[] = '`album`.`starred` IS NOT NULL';
320
			}
321
			if ($favorite & self::FAVORITE_ARTIST) {
322
				$favConds[] = '`artist`.`starred` IS NOT NULL';
323
			}
324
			$sqlConditions[] = '(' . \implode(' OR ', $favConds) . ')';
325
		} 
326
327
		$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions), $this->formatSortingClause($sortBy, $invertSort));
328
		return $this->findEntities($sql, $params, $limit, $offset);
329
	}
330
331
	/**
332
	 * Find most frequently played tracks
333
	 * @return Track[]
334
	 */
335
	public function findFrequentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
336
		$sql = $this->selectUserEntities('`play_count` > 0', 'ORDER BY `play_count` DESC, LOWER(`title`)');
337
		return $this->findEntities($sql, [$userId], $limit, $offset);
338
	}
339
340
	/**
341
	 * Find most recently played tracks
342
	 * @return Track[]
343
	 */
344
	public function findRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
345
		$sql = $this->selectUserEntities('`last_played` IS NOT NULL', 'ORDER BY `last_played` DESC');
346
		return $this->findEntities($sql, [$userId], $limit, $offset);
347
	}
348
349
	/**
350
	 * Find least recently played tracks
351
	 * @return Track[]
352
	 */
353
	public function findNotRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
354
		$sql = $this->selectUserEntities(null, 'ORDER BY `last_played` ASC');
355
		return $this->findEntities($sql, [$userId], $limit, $offset);
356
	}
357
358
	/**
359
	 * Finds all track IDs of the user along with the parent folder ID of each track
360
	 * @return array where keys are folder IDs and values are arrays of track IDs
361
	 */
362
	public function findTrackAndFolderIds(string $userId) : array {
363
		$sql = 'SELECT `track`.`id` AS id, `file`.`name` AS `filename`, `file`.`parent` AS parent
364
				FROM `*PREFIX*music_tracks` `track`
365
				JOIN `*PREFIX*filecache` `file`
366
				ON `track`.`file_id` = `file`.`fileid`
367
				WHERE `track`.`user_id` = ?';
368
369
		$rows = $this->execute($sql, [$userId])->fetchAll();
370
371
		// Sort the results according the file names. This can't be made using ORDERBY in the
372
		// SQL query because then we couldn't use the "natural order" comparison algorithm
373
		\usort($rows, function ($a, $b) {
374
			return \strnatcasecmp($a['filename'], $b['filename']);
375
		});
376
377
		// group the files to parent folder "buckets"
378
		$result = [];
379
		foreach ($rows as $row) {
380
			$result[(int)$row['parent']][] = (int)$row['id'];
381
		}
382
383
		return $result;
384
	}
385
386
	/**
387
	 * Find names and parents of the file system nodes with given IDs within the given storage
388
	 * @param int[] $nodeIds
389
	 * @param string $storageId
390
	 * @return array where keys are the node IDs and values are associative arrays
391
	 *         like { 'name' => string, 'parent' => int };
392
	 */
393
	public function findNodeNamesAndParents(array $nodeIds, string $storageId) : array {
394
		$result = [];
395
396
		if (!empty($nodeIds)) {
397
			$sql = 'SELECT `fileid`, `name`, `parent` '.
398
					'FROM `*PREFIX*filecache` `filecache` '.
399
					'JOIN `*PREFIX*storages` `storages` '.
400
					'ON `filecache`.`storage` = `storages`.`numeric_id` '.
401
					'WHERE `storages`.`id` = ? '.
402
					'AND `filecache`.`fileid` IN '. $this->questionMarks(\count($nodeIds));
403
404
			$rows = $this->execute($sql, \array_merge([$storageId], $nodeIds))->fetchAll();
405
406
			foreach ($rows as $row) {
407
				$result[$row['fileid']] = [
408
					'name' => $row['name'],
409
					'parent' => (int)$row['parent']
410
				];
411
			}
412
		}
413
414
		return $result;
415
	}
416
417
	/**
418
	 * Returns all genre IDs associated with the given artist
419
	 * @return int[]
420
	 */
421
	public function getGenresByArtistId(int $artistId, string $userId) : array {
422
		$sql = 'SELECT DISTINCT(`genre_id`) FROM `*PREFIX*music_tracks` WHERE
423
				`genre_id` IS NOT NULL AND `user_id` = ? AND `artist_id` = ?';
424
		$rows = $this->execute($sql, [$userId, $artistId]);
425
		return $rows->fetchAll(\PDO::FETCH_COLUMN);
426
	}
427
428
	/**
429
	 * Returns all tracks IDs of the user, organized by the genre_id.
430
	 * @return array where keys are genre IDs and values are arrays of track IDs
431
	 */
432
	public function mapGenreIdsToTrackIds(string $userId) : array {
433
		$sql = 'SELECT `id`, `genre_id` FROM `*PREFIX*music_tracks`
434
				WHERE `genre_id` IS NOT NULL and `user_id` = ?';
435
		$rows = $this->execute($sql, [$userId])->fetchAll();
436
437
		$result = [];
438
		foreach ($rows as $row) {
439
			$result[(int)$row['genre_id']][] = (int)$row['id'];
440
		}
441
442
		return $result;
443
	}
444
445
	/**
446
	 * Returns file IDs of the tracks which do not have genre scanned. This is not the same
447
	 * thing as unknown genre, which means that the genre has been scanned but was not found
448
	 * from the track metadata.
449
	 * @return int[]
450
	 */
451
	public function findFilesWithoutScannedGenre(string $userId) : array {
452
		$sql = 'SELECT `track`.`file_id` FROM `*PREFIX*music_tracks` `track`
453
				INNER JOIN `*PREFIX*filecache` `file`
454
				ON `track`.`file_id` = `file`.`fileid`
455
				WHERE `genre_id` IS NULL and `user_id` = ?';
456
		$rows = $this->execute($sql, [$userId]);
457
		return $rows->fetchAll(\PDO::FETCH_COLUMN);
458
	}
459
460
	/**
461
	 * Update "last played" timestamp and increment the total play count of the track.
462
	 * The DB row is updated *without* updating the `updated` column.
463
	 * @return bool true if the track was found and updated, false otherwise
464
	 */
465
	public function recordTrackPlayed(int $trackId, string $userId, \DateTime $timeOfPlay) : bool {
466
		$sql = 'UPDATE `*PREFIX*music_tracks`
467
				SET `last_played` = ?, `play_count` = `play_count` + 1
468
				WHERE `user_id` = ? AND `id` = ?';
469
		$params = [$timeOfPlay->format(BaseMapper::SQL_DATE_FORMAT), $userId, $trackId];
470
		$result = $this->execute($sql, $params);
471
		return ($result->rowCount() > 0);
472
	}
473
474
	/**
475
	 * Overridden from the base implementation to provide support for table-specific rules
476
	 *
477
	 * {@inheritdoc}
478
	 * @see BaseMapper::advFormatSqlCondition()
479
	 */
480
	protected function advFormatSqlCondition(string $rule, string $sqlOp, string $conv) : string {
481
		// The extra subquery "mysqlhack" seen around some nested queries is needed in order for these to not be insanely slow on MySQL.
482
		switch ($rule) {
483
			case 'anywhere':		return self::formatAdvSearchAnywhereCond($sqlOp, $conv); 
484
			case 'album':			return "$conv(`album`.`name`) $sqlOp $conv(?)";
485
			case 'artist':			return "$conv(`artist`.`name`) $sqlOp $conv(?)";
486
			case 'album_artist':	return "`album_id` IN (SELECT `al`.`id` from `*PREFIX*music_albums` `al` JOIN `*PREFIX*music_artists` `ar` ON `al`.`album_artist_id` = `ar`.`id` WHERE $conv(`ar`.`name`) $sqlOp $conv(?))";
487
			case 'track':			return "`number` $sqlOp ?";
488
			case 'year':			return "`year` $sqlOp ?";
489
			case 'albumrating':		return "`album`.`rating` $sqlOp ?";
490
			case 'artistrating':	return "`artist`.`rating` $sqlOp ?";
491
			case 'favorite_album':	return "$conv(`album`.`name`) $sqlOp $conv(?) AND `album`.`starred` IS NOT NULL";
492
			case 'favorite_artist':	return "$conv(`artist`.`name`) $sqlOp $conv(?) AND `artist`.`starred` IS NOT NULL";
493
			case 'played_times':	return "`play_count` $sqlOp ?";
494
			case 'last_play':		return "`last_played` $sqlOp ?";
495
			case 'played':			// fall through, we give no access to other people's data
496
			case 'myplayed':		return "`last_played` $sqlOp"; // operator "IS NULL" or "IS NOT NULL"
497
			case 'myplayedalbum':	return "`album_id` IN (SELECT * FROM (SELECT `album_id` from `*PREFIX*music_tracks` GROUP BY `album_id` HAVING MAX(`last_played`) $sqlOp) mysqlhack)"; // operator "IS NULL" or "IS NOT NULL"
498
			case 'myplayedartist':	return "`artist_id` IN (SELECT * FROM (SELECT `artist_id` from `*PREFIX*music_tracks` GROUP BY `artist_id` HAVING MAX(`last_played`) $sqlOp) mysqlhack)"; // operator "IS NULL" or "IS NOT NULL"
499
			case 'time':			return "`length` $sqlOp ?";
500
			case 'bitrate':			return "`bitrate` $sqlOp ?";
501
			case 'genre':			// fall through
502
			case 'song_genre':		return "$conv(`genre`.`name`) $sqlOp $conv(?)";
503
			case 'album_genre':		return "`album_id` IN (SELECT * FROM (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_genres` `g` ON `t`.`genre_id` = `g`.`id` GROUP BY `album_id` HAVING $conv(" . $this->sqlGroupConcat('`g`.`name`') . ") $sqlOp $conv(?)) mysqlhack)";
504
			case 'artist_genre':	return "`artist_id` IN (SELECT * FROM (SELECT `artist_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_genres` `g` ON `t`.`genre_id` = `g`.`id` GROUP BY `artist_id` HAVING $conv(" . $this->sqlGroupConcat('`g`.`name`') . ") $sqlOp $conv(?)) mysqlhack)";
505
			case 'no_genre':		return ($sqlOp == 'IS NOT NULL') ? '`genre`.`name` = ""' : '`genre`.`name` != ""';
506
			case 'playlist':		return "$sqlOp EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`id` = ? AND `p`.`track_ids` LIKE " . $this->sqlConcat("'%|'", "`*PREFIX*music_tracks`.`id`", "'|%'") . ')';
507
			case 'playlist_name':	return "EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE $conv(`p`.`name`) $sqlOp $conv(?) AND `p`.`track_ids` LIKE " . $this->sqlConcat("'%|'", "`*PREFIX*music_tracks`.`id`", "'|%'") . ')';
508
			case 'recent_played':	return "`*PREFIX*music_tracks`.`id` IN (SELECT * FROM (SELECT `id` FROM `*PREFIX*music_tracks` WHERE `user_id` = ? ORDER BY `last_played` DESC LIMIT $sqlOp) mysqlhack)";
509
			case 'file':			return "$conv(`file`.`name`) $sqlOp $conv(?)";
510
			case 'mbid_song':		return parent::advFormatSqlCondition('mbid', $sqlOp, $conv); // alias
511
			case 'mbid_album':		return "`album`.`mbid` $sqlOp ?";
512
			case 'mbid_artist':		return "`artist`.`mbid` $sqlOp ?";
513
			default:				return parent::advFormatSqlCondition($rule, $sqlOp, $conv);
514
		}
515
	}
516
517
	private static function formatAdvSearchAnywhereCond(string $sqlOp, string $conv) : string {
518
		$fields = [
519
			"`*PREFIX*music_tracks`.`title`",
520
			"`file`.`name`",
521
			"`artist`.`name`",
522
			"`album`.`name`",
523
			"`genre`.`name`"
524
		];
525
		$parts = \array_map(function(string $field) use ($sqlOp, $conv) {
526
			return "$conv($field) $sqlOp $conv(?)";
527
		}, $fields);
528
529
		$negativeOp = \in_array($sqlOp, ['NOT LIKE', '!=', 'NOT REGEXP']);
530
		$cond = \implode($negativeOp ? ' AND ' : ' OR ', $parts);
531
532
		return "($cond)";
533
	}
534
535
	/**
536
	 * {@inheritdoc}
537
	 * @see \OCA\Music\Db\BaseMapper::findUniqueEntity()
538
	 * @param Track $track
539
	 * @return Track
540
	 */
541
	protected function findUniqueEntity(Entity $track) : Entity {
542
		return $this->findByFileId($track->getFileId(), $track->getUserId());
0 ignored issues
show
Bug introduced by
The method getFileId() does not exist on OCA\Music\Db\Entity. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

542
		return $this->findByFileId($track->/** @scrutinizer ignore-call */ getFileId(), $track->getUserId());
Loading history...
Bug introduced by
It seems like $track->getFileId() can also be of type null; however, parameter $fileId of OCA\Music\Db\TrackMapper::findByFileId() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

542
		return $this->findByFileId(/** @scrutinizer ignore-type */ $track->getFileId(), $track->getUserId());
Loading history...
543
	}
544
}
545