Passed
Pull Request — master (#1078)
by Pauli
05:42 queued 02:56
created

TrackMapper::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

514
		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

514
		return $this->findByFileId($track->/** @scrutinizer ignore-call */ getFileId(), $track->getUserId());
Loading history...
515
	}
516
}
517