Passed
Pull Request — master (#1141)
by Pauli
04:07
created

TrackMapper::findAllByCriteria()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 16
c 1
b 0
f 0
nc 16
nop 9
dl 0
loc 29
rs 9.4222

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 and/or artist 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 findAllByNameAndArtistName(?string $name, ?string $artistName, string $userId) : array {
244
		$sqlConditions = [];
245
		$params = [$userId];
246
247
		if (!empty($name)) {
248
			$sqlConditions[] = '`title` = ?';
249
			$params[] = $name;
250
		}
251
252
		if (!empty($artistName)) {
253
			$sqlConditions[] = '`artist`.`name` = ?';
254
			$params[] = $artistName;
255
		}
256
257
		// at least one condition has to be given, otherwise return an empty set
258
		if (\count($sqlConditions) > 0) {
259
			$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions));
260
			return $this->findEntities($sql, $params);
261
		} else {
262
			return [];
263
		}
264
	}
265
266
	/**
267
	 * Returns all tracks specified by various criteria, all of which are optional
268
	 * @param int[] $genres Array of genre IDs
269
	 * @param int[] $artists Array of artist IDs
270
	 * @param int|null $fromYear Earliest release year to include
271
	 * @param int|null $toYear Latest release year to include
272
	 * @param int $sortBy Sorting rule as defined in the class SortBy
273
	 * @param string $userId the name of the user
274
	 * @return Track[] Tracks matching the criteria
275
	 */
276
	public function findAllByCriteria(
277
			array $genres, array $artists, ?int $fromYear, ?int $toYear,
278
			int $sortBy, bool $invertSort, string $userId, ?int $limit=null, ?int $offset=null) : array {
279
280
		$sqlConditions = [];
281
		$params = [$userId];
282
283
		if (!empty($genres)) {
284
			$sqlConditions[] = '`genre_id` IN ' . $this->questionMarks(\count($genres));
285
			$params = \array_merge($params, $genres);
286
		}
287
288
		if (!empty($artists)) {
289
			$sqlConditions[] = '`artist_id` IN ' . $this->questionMarks(\count($artists));
290
			$params = \array_merge($params, $artists);
291
		}
292
293
		if (!empty($fromYear)) {
294
			$sqlConditions[] = '`year` >= ?';
295
			$params[] = $fromYear;
296
		}
297
298
		if (!empty($toYear)) {
299
			$sqlConditions[] = '`year` <= ?';
300
			$params[] = $toYear;
301
		}
302
303
		$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions), $this->formatSortingClause($sortBy, $invertSort));
304
		return $this->findEntities($sql, $params, $limit, $offset);
305
	}
306
307
	/**
308
	 * Find most frequently played tracks
309
	 * @return Track[]
310
	 */
311
	public function findFrequentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
312
		$sql = $this->selectUserEntities('`play_count` > 0', 'ORDER BY `play_count` DESC, LOWER(`title`)');
313
		return $this->findEntities($sql, [$userId], $limit, $offset);
314
	}
315
316
	/**
317
	 * Find most recently played tracks
318
	 * @return Track[]
319
	 */
320
	public function findRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
321
		$sql = $this->selectUserEntities('`last_played` IS NOT NULL', 'ORDER BY `last_played` DESC');
322
		return $this->findEntities($sql, [$userId], $limit, $offset);
323
	}
324
325
	/**
326
	 * Find least recently played tracks
327
	 * @return Track[]
328
	 */
329
	public function findNotRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
330
		$sql = $this->selectUserEntities(null, 'ORDER BY `last_played` ASC');
331
		return $this->findEntities($sql, [$userId], $limit, $offset);
332
	}
333
334
	/**
335
	 * Finds all track IDs of the user along with the parent folder ID of each track
336
	 * @return array where keys are folder IDs and values are arrays of track IDs
337
	 */
338
	public function findTrackAndFolderIds(string $userId) : array {
339
		$sql = 'SELECT `track`.`id` AS id, `file`.`name` AS `filename`, `file`.`parent` AS parent
340
				FROM `*PREFIX*music_tracks` `track`
341
				JOIN `*PREFIX*filecache` `file`
342
				ON `track`.`file_id` = `file`.`fileid`
343
				WHERE `track`.`user_id` = ?';
344
345
		$rows = $this->execute($sql, [$userId])->fetchAll();
346
347
		// Sort the results according the file names. This can't be made using ORDERBY in the
348
		// SQL query because then we couldn't use the "natural order" comparison algorithm
349
		\usort($rows, function ($a, $b) {
350
			return \strnatcasecmp($a['filename'], $b['filename']);
351
		});
352
353
		// group the files to parent folder "buckets"
354
		$result = [];
355
		foreach ($rows as $row) {
356
			$result[(int)$row['parent']][] = (int)$row['id'];
357
		}
358
359
		return $result;
360
	}
361
362
	/**
363
	 * Find names and parents of the file system nodes with given IDs within the given storage
364
	 * @param int[] $nodeIds
365
	 * @param string $storageId
366
	 * @return array where keys are the node IDs and values are associative arrays
367
	 *         like { 'name' => string, 'parent' => int };
368
	 */
369
	public function findNodeNamesAndParents(array $nodeIds, string $storageId) : array {
370
		$result = [];
371
372
		if (!empty($nodeIds)) {
373
			$sql = 'SELECT `fileid`, `name`, `parent` '.
374
					'FROM `*PREFIX*filecache` `filecache` '.
375
					'JOIN `*PREFIX*storages` `storages` '.
376
					'ON `filecache`.`storage` = `storages`.`numeric_id` '.
377
					'WHERE `storages`.`id` = ? '.
378
					'AND `filecache`.`fileid` IN '. $this->questionMarks(\count($nodeIds));
379
380
			$rows = $this->execute($sql, \array_merge([$storageId], $nodeIds))->fetchAll();
381
382
			foreach ($rows as $row) {
383
				$result[$row['fileid']] = [
384
					'name' => $row['name'],
385
					'parent' => (int)$row['parent']
386
				];
387
			}
388
		}
389
390
		return $result;
391
	}
392
393
	/**
394
	 * Returns all genre IDs associated with the given artist
395
	 * @return int[]
396
	 */
397
	public function getGenresByArtistId(int $artistId, string $userId) : array {
398
		$sql = 'SELECT DISTINCT(`genre_id`) FROM `*PREFIX*music_tracks` WHERE
399
				`genre_id` IS NOT NULL AND `user_id` = ? AND `artist_id` = ?';
400
		$rows = $this->execute($sql, [$userId, $artistId]);
401
		return $rows->fetchAll(\PDO::FETCH_COLUMN);
402
	}
403
404
	/**
405
	 * Returns all tracks IDs of the user, organized by the genre_id.
406
	 * @return array where keys are genre IDs and values are arrays of track IDs
407
	 */
408
	public function mapGenreIdsToTrackIds(string $userId) : array {
409
		$sql = 'SELECT `id`, `genre_id` FROM `*PREFIX*music_tracks`
410
				WHERE `genre_id` IS NOT NULL and `user_id` = ?';
411
		$rows = $this->execute($sql, [$userId])->fetchAll();
412
413
		$result = [];
414
		foreach ($rows as $row) {
415
			$result[(int)$row['genre_id']][] = (int)$row['id'];
416
		}
417
418
		return $result;
419
	}
420
421
	/**
422
	 * Returns file IDs of the tracks which do not have genre scanned. This is not the same
423
	 * thing as unknown genre, which means that the genre has been scanned but was not found
424
	 * from the track metadata.
425
	 * @return int[]
426
	 */
427
	public function findFilesWithoutScannedGenre(string $userId) : array {
428
		$sql = 'SELECT `track`.`file_id` FROM `*PREFIX*music_tracks` `track`
429
				INNER JOIN `*PREFIX*filecache` `file`
430
				ON `track`.`file_id` = `file`.`fileid`
431
				WHERE `genre_id` IS NULL and `user_id` = ?';
432
		$rows = $this->execute($sql, [$userId]);
433
		return $rows->fetchAll(\PDO::FETCH_COLUMN);
434
	}
435
436
	/**
437
	 * Update "last played" timestamp and increment the total play count of the track.
438
	 * The DB row is updated *without* updating the `updated` column.
439
	 * @return bool true if the track was found and updated, false otherwise
440
	 */
441
	public function recordTrackPlayed(int $trackId, string $userId, \DateTime $timeOfPlay) : bool {
442
		$sql = 'UPDATE `*PREFIX*music_tracks`
443
				SET `last_played` = ?, `play_count` = `play_count` + 1
444
				WHERE `user_id` = ? AND `id` = ?';
445
		$params = [$timeOfPlay->format(BaseMapper::SQL_DATE_FORMAT), $userId, $trackId];
446
		$result = $this->execute($sql, $params);
447
		return ($result->rowCount() > 0);
448
	}
449
450
	/**
451
	 * Overridden from the base implementation to provide support for table-specific rules
452
	 *
453
	 * {@inheritdoc}
454
	 * @see BaseMapper::advFormatSqlCondition()
455
	 */
456
	protected function advFormatSqlCondition(string $rule, string $sqlOp, string $conv) : string {
457
		// The extra subquery "mysqlhack" seen around some nested queries is needed in order for these to not be insanely slow on MySQL.
458
		switch ($rule) {
459
			case 'anywhere':		return self::formatAdvSearchAnywhereCond($sqlOp, $conv); 
460
			case 'album':			return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE $conv(`al`.`name`) $sqlOp $conv(?))";
461
			case 'artist':			return "$conv(`artist`.`name`) $sqlOp $conv(?)";
462
			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(?))";
463
			case 'track':			return "`number` $sqlOp ?";
464
			case 'year':			return "`year` $sqlOp ?";
465
			case 'albumrating':		return "`album`.`rating` $sqlOp ?";
466
			case 'artistrating':	return "`artist`.`rating` $sqlOp ?";
467
			case 'favorite_album':	return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE $conv(`al`.`name`) $sqlOp $conv(?) AND `al`.`starred` IS NOT NULL)";
468
			case 'favorite_artist':	return "`artist_id` IN (SELECT `id` from `*PREFIX*music_artists` `ar` WHERE $conv(`ar`.`name`) $sqlOp $conv(?) AND `ar`.`starred` IS NOT NULL)";
469
			case 'played_times':	return "`play_count` $sqlOp ?";
470
			case 'last_play':		return "`last_played` $sqlOp ?";
471
			case 'played':			// fall through, we give no access to other people's data
472
			case 'myplayed':		return "`last_played` $sqlOp"; // operator "IS NULL" or "IS NOT NULL"
473
			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"
474
			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"
475
			case 'time':			return "`length` $sqlOp ?";
476
			case 'bitrate':			return "`bitrate` $sqlOp ?";
477
			case 'genre':			// fall through
478
			case 'song_genre':		return "$conv(`genre`.`name`) $sqlOp $conv(?)";
479
			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)";
480
			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)";
481
			case 'no_genre':		return ($sqlOp == 'IS NOT NULL') ? '`genre`.`name` = ""' : '`genre`.`name` != ""';
482
			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`", "'|%'") . ')';
483
			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`", "'|%'") . ')';
484
			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)";
485
			case 'file':			return "$conv(`file`.`name`) $sqlOp $conv(?)";
486
			case 'mbid_song':		return parent::advFormatSqlCondition('mbid', $sqlOp, $conv); // alias
487
			case 'mbid_album':		return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE `al`.`mbid` $sqlOp ?)";
488
			case 'mbid_artist':		return "`artist`.`mbid` $sqlOp ?";
489
			default:				return parent::advFormatSqlCondition($rule, $sqlOp, $conv);
490
		}
491
	}
492
493
	private static function formatAdvSearchAnywhereCond(string $sqlOp, string $conv) : string {
494
		$fields = [
495
			"`*PREFIX*music_tracks`.`title`",
496
			"`file`.`name`",
497
			"`artist`.`name`",
498
			"`album`.`name`",
499
			"`genre`.`name`"
500
		];
501
		$parts = \array_map(function(string $field) use ($sqlOp, $conv) {
502
			return "$conv($field) $sqlOp $conv(?)";
503
		}, $fields);
504
505
		$negativeOp = \in_array($sqlOp, ['NOT LIKE', '!=', 'NOT REGEXP']);
506
		$cond = \implode($negativeOp ? ' AND ' : ' OR ', $parts);
507
508
		return "($cond)";
509
	}
510
511
	/**
512
	 * {@inheritdoc}
513
	 * @see \OCA\Music\Db\BaseMapper::findUniqueEntity()
514
	 * @param Track $track
515
	 * @return Track
516
	 */
517
	protected function findUniqueEntity(Entity $track) : Entity {
518
		return $this->findByFileId($track->getFileId(), $track->getUserId());
0 ignored issues
show
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

518
		return $this->findByFileId(/** @scrutinizer ignore-type */ $track->getFileId(), $track->getUserId());
Loading history...
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

518
		return $this->findByFileId($track->/** @scrutinizer ignore-call */ getFileId(), $track->getUserId());
Loading history...
519
	}
520
}
521