Completed
Push — master ( 7ed290...011fe4 )
by Pauli
07:23
created

AlbumBusinessLayer   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 255
Duplicated Lines 0 %

Test Coverage

Coverage 44.57%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 73
dl 0
loc 255
ccs 41
cts 92
cp 0.4457
rs 9.76
c 1
b 0
f 0
wmc 33

18 Methods

Rating   Name   Duplication   Size   Complexity  
A find() 0 3 1
A __construct() 0 3 1
A findAllByArtist() 0 3 1
A findAllByAlbumArtist() 0 5 1
A findAll() 0 3 1
A findAllByGenre() 0 3 1
A findAllByName() 0 3 1
A countByArtist() 0 2 1
A injectExtraFields() 0 24 5
A removeCovers() 0 2 1
A countByAlbumArtist() 0 2 1
A findCovers() 0 9 3
A findAlbumOwner() 0 7 2
A albumCoverIsOneOfFiles() 0 3 2
A setCover() 0 2 1
A addOrUpdateAlbum() 0 13 1
A updateFolderCover() 0 2 1
B findAllByYearRange() 0 24 8
1
<?php
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 - 2020
13
 */
14
15
namespace OCA\Music\BusinessLayer;
16
17
use \OCA\Music\AppFramework\BusinessLayer\BusinessLayer;
18
use \OCA\Music\AppFramework\BusinessLayer\BusinessLayerException;
19
use \OCA\Music\AppFramework\Core\Logger;
20
21
use \OCA\Music\Db\AlbumMapper;
22
use \OCA\Music\Db\Album;
23
use \OCA\Music\Db\SortBy;
24
25
use \OCA\Music\Utility\Util;
26
27
class AlbumBusinessLayer extends BusinessLayer {
28
	private $logger;
29
30 8
	public function __construct(AlbumMapper $albumMapper, Logger $logger) {
31 8
		parent::__construct($albumMapper);
32 8
		$this->logger = $logger;
33 8
	}
34
35
	/**
36
	 * Return an album
37
	 * @param integer $albumId the id of the album
38
	 * @param string $userId the name of the user
39
	 * @return Album album
40
	 */
41 1
	public function find($albumId, $userId) {
42 1
		$album = parent::find($albumId, $userId);
43 1
		return $this->injectExtraFields([$album], $userId)[0];
44
	}
45
46
	/**
47
	 * Returns all albums
48
	 * @param string $userId the name of the user
49
	 * @param integer $sortBy Sorting order of the result, default to unspecified
50
	 * @param integer|null $limit
51
	 * @param integer|null $offset
52
	 * @return Album[] albums
53
	 */
54 2
	public function findAll($userId, $sortBy=SortBy::None, $limit=null, $offset=null) {
55 2
		$albums = parent::findAll($userId, $sortBy, $limit, $offset);
56 2
		return $this->injectExtraFields($albums, $userId, true);
57
	}
58
59
	/**
60
	 * Returns all albums filtered by artist (both album and track artists are considered)
61
	 * @param string $artistId the id of the artist
62
	 * @return Album[] albums
63
	 */
64 1
	public function findAllByArtist($artistId, $userId) {
65 1
		$albums = $this->mapper->findAllByArtist($artistId, $userId);
0 ignored issues
show
Bug introduced by
The method findAllByArtist() does not exist on OCA\Music\Db\BaseMapper. Did you maybe mean findAll()? ( Ignorable by Annotation )

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

65
		/** @scrutinizer ignore-call */ 
66
  $albums = $this->mapper->findAllByArtist($artistId, $userId);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
66 1
		return $this->injectExtraFields($albums, $userId);
67
	}
68
69
	/**
70
	 * Returns all albums filtered by album artist
71
	 * @param string $artistId the id of the artist
72
	 * @return Album[] albums
73
	 */
74
	public function findAllByAlbumArtist($artistId, $userId) {
75
		$albums = $this->mapper->findAllByAlbumArtist($artistId, $userId);
0 ignored issues
show
Bug introduced by
The method findAllByAlbumArtist() does not exist on OCA\Music\Db\BaseMapper. Did you maybe mean findAll()? ( Ignorable by Annotation )

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

75
		/** @scrutinizer ignore-call */ 
76
  $albums = $this->mapper->findAllByAlbumArtist($artistId, $userId);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
76
		$albums = $this->injectExtraFields($albums, $userId);
77
		\usort($albums, ['\OCA\Music\Db\Album', 'compareYearAndName']);
78
		return $albums;
79
	}
80
81
	/**
82
	 * Returns all albums filtered by genre
83
	 * @param int $genreId the genre to include
84
	 * @param string $userId the name of the user
85
	 * @param int|null $limit
86
	 * @param int|null $offset
87
	 * @return Album[] albums
88
	 */
89
	public function findAllByGenre($genreId, $userId, $limit=null, $offset=null) {
90
		$albums = $this->mapper->findAllByGenre($genreId, $userId, $limit, $offset);
0 ignored issues
show
Bug introduced by
The method findAllByGenre() does not exist on OCA\Music\Db\BaseMapper. Did you maybe mean findAll()? ( Ignorable by Annotation )

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

90
		/** @scrutinizer ignore-call */ 
91
  $albums = $this->mapper->findAllByGenre($genreId, $userId, $limit, $offset);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
91
		return $this->injectExtraFields($albums, $userId);
92
	}
93
94
	/**
95
	 * Returns all albums filtered by release year
96
	 * @param int $fromYear
97
	 * @param int $toYear
98
	 * @param string $userId the name of the user
99
	 * @param int|null $limit
100
	 * @param int|null $offset
101
	 * @return Album[] albums
102
	 */
103
	public function findAllByYearRange($fromYear, $toYear, $userId, $limit=null, $offset=null) {
104
		$reverseOrder = false;
105
		if ($fromYear > $toYear) {
106
			$reverseOrder = true;
107
			Util::swap($fromYear, $toYear);
108
		}
109
110
		// Implement all the custom logic of this function here, without special Mapper function
111
		$albums = \array_filter($this->findAll($userId), function($album) use ($fromYear, $toYear) {
112
			$years = $album->getYears();
113
			return (!empty($years) && \min($years) <= $toYear && \max($years) >= $fromYear);
114
		});
115
116
		\usort($albums, function($album1, $album2) use ($reverseOrder) {
117
			return $reverseOrder
118
				? $album2->yearToAPI() - $album1->yearToAPI()
119
				: $album1->yearToAPI() - $album2->yearToAPI();
120
		});
121
122
		if ($limit || $offset) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $offset of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
123
			$albums = \array_slice($albums, $offset ?: 0, $limit);
124
		}
125
126
		return $albums;
127
	}
128
129
	/**
130
	 * Return all albums with name matching the search criteria
131
	 * @param string $name
132
	 * @param string $userId
133
	 * @param bool $fuzzy
134
	 * @param integer $limit
135
	 * @param integer $offset
136
	 * @return Album[]
137
	 */
138
	public function findAllByName($name, $userId, $fuzzy = false, $limit=null, $offset=null) {
139
		$albums = parent::findAllByName($name, $userId, $fuzzy, $limit, $offset);
140
		return $this->injectExtraFields($albums, $userId);
141
	}
142
143
	/**
144
	 * Add performing artists, release years, genres, and disk counts to the given album objects
145
	 * @param Album[] $albums
146
	 * @param string $userId
147
	 * @param bool $allAlbums Set to true if $albums contains all albums of the user.
148
	 *                        This has now effect on the outcome but helps in optimizing
149
	 *                        the database query.
150
	 * @return Album[]
151
	 */
152 4
	private function injectExtraFields($albums, $userId, $allAlbums = false) {
153 4
		if (\count($albums) > 0) {
154
			// In case we are injecting data to a lot of albums, do not limit the
155
			// SQL SELECTs to only those albums. Very large amount of SQL host parameters
156
			// could cause problems with SQLite (see #239) and probably it would be bad for
157
			// performance also on other DBMSs. For the proper operation of this function,
158
			// it doesn't matter if we fetch data for some extra albums.
159 3
			$albumIds = ($allAlbums || \count($albums) >= 999)
160 3
					? null : Util::extractIds($albums);
161
162 3
			$artists = $this->mapper->getPerformingArtistsByAlbumId($albumIds, $userId);
0 ignored issues
show
Bug introduced by
The method getPerformingArtistsByAlbumId() does not exist on OCA\Music\Db\BaseMapper. It seems like you code against a sub-type of OCA\Music\Db\BaseMapper such as OCA\Music\Db\AlbumMapper. ( Ignorable by Annotation )

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

162
			/** @scrutinizer ignore-call */ 
163
   $artists = $this->mapper->getPerformingArtistsByAlbumId($albumIds, $userId);
Loading history...
163 3
			$years = $this->mapper->getYearsByAlbumId($albumIds, $userId);
0 ignored issues
show
Bug introduced by
The method getYearsByAlbumId() does not exist on OCA\Music\Db\BaseMapper. It seems like you code against a sub-type of OCA\Music\Db\BaseMapper such as OCA\Music\Db\AlbumMapper. ( Ignorable by Annotation )

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

163
			/** @scrutinizer ignore-call */ 
164
   $years = $this->mapper->getYearsByAlbumId($albumIds, $userId);
Loading history...
164 3
			$diskCounts = $this->mapper->getDiscCountByAlbumId($albumIds, $userId);
0 ignored issues
show
Bug introduced by
The method getDiscCountByAlbumId() does not exist on OCA\Music\Db\BaseMapper. It seems like you code against a sub-type of OCA\Music\Db\BaseMapper such as OCA\Music\Db\AlbumMapper. ( Ignorable by Annotation )

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

164
			/** @scrutinizer ignore-call */ 
165
   $diskCounts = $this->mapper->getDiscCountByAlbumId($albumIds, $userId);
Loading history...
165 3
			$genres = $this->mapper->getGenresByAlbumId($albumIds, $userId);
0 ignored issues
show
Bug introduced by
The method getGenresByAlbumId() does not exist on OCA\Music\Db\BaseMapper. It seems like you code against a sub-type of OCA\Music\Db\BaseMapper such as OCA\Music\Db\AlbumMapper. ( Ignorable by Annotation )

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

165
			/** @scrutinizer ignore-call */ 
166
   $genres = $this->mapper->getGenresByAlbumId($albumIds, $userId);
Loading history...
166
167 3
			foreach ($albums as &$album) {
168 3
				$albumId = $album->getId();
169 3
				$album->setArtistIds($artists[$albumId]);
170 3
				$album->setNumberOfDisks($diskCounts[$albumId]);
171 3
				$album->setGenres(Util::arrayGetOrDefault($genres, $albumId));
172 3
				$album->setYears(Util::arrayGetOrDefault($years, $albumId));
173
			}
174
		}
175 4
		return $albums;
176
	}
177
178
	/**
179
	 * Returns the count of albums where the given Artist is featured in
180
	 * @param integer $artistId
181
	 * @return integer
182
	 */
183
	public function countByArtist($artistId) {
184
		return $this->mapper->countByArtist($artistId);
0 ignored issues
show
Bug introduced by
The method countByArtist() does not exist on OCA\Music\Db\BaseMapper. It seems like you code against a sub-type of OCA\Music\Db\BaseMapper such as OCA\Music\Db\TrackMapper or OCA\Music\Db\AlbumMapper. ( Ignorable by Annotation )

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

184
		return $this->mapper->/** @scrutinizer ignore-call */ countByArtist($artistId);
Loading history...
185
	}
186
187
	/**
188
	 * Returns the count of albums where the given artist is the album artist
189
	 * @param integer $artistId
190
	 * @return integer
191
	 */
192
	public function countByAlbumArtist($artistId) {
193
		return $this->mapper->countByAlbumArtist($artistId);
0 ignored issues
show
Bug introduced by
The method countByAlbumArtist() does not exist on OCA\Music\Db\BaseMapper. It seems like you code against a sub-type of OCA\Music\Db\BaseMapper such as OCA\Music\Db\AlbumMapper. ( Ignorable by Annotation )

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

193
		return $this->mapper->/** @scrutinizer ignore-call */ countByAlbumArtist($artistId);
Loading history...
194
	}
195
196
	public function findAlbumOwner($albumId) {
197
		$entities = $this->findById([$albumId]);
198
		if (\count($entities) != 1) {
199
			throw new BusinessLayerException(
200
					'Expected to find one album but got ' . \count($entities));
201
		} else {
202
			return $entities[0]->getUserId();
203
		}
204
	}
205
206
	/**
207
	 * Adds an album if it does not exist already or updates an existing album
208
	 * @param string $name the name of the album
209
	 * @param integer $albumArtistId
210
	 * @param string $userId
211
	 * @return Album The added/updated album
212
	 */
213 1
	public function addOrUpdateAlbum($name, $albumArtistId, $userId) {
214 1
		$album = new Album();
215 1
		$album->setName(Util::truncate($name, 256)); // some DB setups can't truncate automatically to column max size
216 1
		$album->setUserId($userId);
217 1
		$album->setAlbumArtistId($albumArtistId);
218
219
		// Generate hash from the set of fields forming the album identity to prevent duplicates.
220
		// The uniqueness of album name is evaluated in case-insensitive manner.
221 1
		$lowerName = \mb_strtolower($album->getName());
222 1
		$hash = \hash('md5', "$lowerName|$albumArtistId");
223 1
		$album->setHash($hash);
224
225 1
		return $this->mapper->insertOrUpdate($album);
226
	}
227
228
	/**
229
	 * Check if given file is used as cover for the given album
230
	 * @param int $albumId
231
	 * @param int[] $fileIds
232
	 * @return boolean
233
	 */
234
	public function albumCoverIsOneOfFiles($albumId, $fileIds) {
235
		$albums = $this->findById([$albumId]);
236
		return (\count($albums) && \in_array($albums[0]->getCoverFileId(), $fileIds));
237
	}
238
239
	/**
240
	 * updates the cover for albums in the specified folder without cover
241
	 * @param integer $coverFileId the file id of the cover image
242
	 * @param integer $folderId the file id of the folder where the albums are looked from
243
	 * @return true if one or more albums were influenced
244
	 */
245 1
	public function updateFolderCover($coverFileId, $folderId) {
246 1
		return $this->mapper->updateFolderCover($coverFileId, $folderId);
0 ignored issues
show
Bug introduced by
The method updateFolderCover() does not exist on OCA\Music\Db\BaseMapper. Did you maybe mean update()? ( Ignorable by Annotation )

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

246
		return $this->mapper->/** @scrutinizer ignore-call */ updateFolderCover($coverFileId, $folderId);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
247
	}
248
249
	/**
250
	 * set cover file for a specified album
251
	 * @param integer $coverFileId the file id of the cover image
252
	 * @param integer $albumId the id of the album to be modified
253
	 */
254
	public function setCover($coverFileId, $albumId) {
255
		$this->mapper->setCover($coverFileId, $albumId);
0 ignored issues
show
Bug introduced by
The method setCover() does not exist on OCA\Music\Db\BaseMapper. It seems like you code against a sub-type of OCA\Music\Db\BaseMapper such as OCA\Music\Db\AlbumMapper. ( Ignorable by Annotation )

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

255
		$this->mapper->/** @scrutinizer ignore-call */ 
256
                 setCover($coverFileId, $albumId);
Loading history...
256
	}
257
258
	/**
259
	 * removes the cover art from albums, replacement covers will be searched in a background task
260
	 * @param integer[] $coverFileIds the file IDs of the cover images
261
	 * @param string[]|null $userIds the users whose music library is targeted; all users are targeted if omitted
262
	 * @return Album[] albums which got modified, empty array if none
263
	 */
264 1
	public function removeCovers($coverFileIds, $userIds=null) {
265 1
		return $this->mapper->removeCovers($coverFileIds, $userIds);
0 ignored issues
show
Bug introduced by
The method removeCovers() does not exist on OCA\Music\Db\BaseMapper. It seems like you code against a sub-type of OCA\Music\Db\BaseMapper such as OCA\Music\Db\AlbumMapper or OCA\Music\Db\ArtistMapper. ( Ignorable by Annotation )

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

265
		return $this->mapper->/** @scrutinizer ignore-call */ removeCovers($coverFileIds, $userIds);
Loading history...
266
	}
267
268
	/**
269
	 * try to find cover arts for albums without covers
270
	 * @param string|null $userId target user; omit to target all users
271
	 * @return array of users whose collections got modified
272
	 */
273
	public function findCovers($userId = null) {
274
		$affectedUsers = [];
275
		$albums = $this->mapper->getAlbumsWithoutCover($userId);
0 ignored issues
show
Bug introduced by
The method getAlbumsWithoutCover() does not exist on OCA\Music\Db\BaseMapper. It seems like you code against a sub-type of OCA\Music\Db\BaseMapper such as OCA\Music\Db\AlbumMapper. ( Ignorable by Annotation )

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

275
		/** @scrutinizer ignore-call */ 
276
  $albums = $this->mapper->getAlbumsWithoutCover($userId);
Loading history...
276
		foreach ($albums as $album) {
277
			if ($this->mapper->findAlbumCover($album['albumId'], $album['parentFolderId'])) {
0 ignored issues
show
Bug introduced by
The method findAlbumCover() does not exist on OCA\Music\Db\BaseMapper. It seems like you code against a sub-type of OCA\Music\Db\BaseMapper such as OCA\Music\Db\AlbumMapper. ( Ignorable by Annotation )

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

277
			if ($this->mapper->/** @scrutinizer ignore-call */ findAlbumCover($album['albumId'], $album['parentFolderId'])) {
Loading history...
278
				$affectedUsers[$album['userId']] = 1;
279
			}
280
		}
281
		return \array_keys($affectedUsers);
282
	}
283
}
284