Passed
Push — master ( 49e88e...27428c )
by Pauli
02:49 queued 10s
created

BaseMapper::deleteAll()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
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 Pauli Järvinen <[email protected]>
10
 * @copyright Pauli Järvinen 2016 - 2020
11
 */
12
13
namespace OCA\Music\Db;
14
15
use \OCP\AppFramework\Db\DoesNotExistException;
16
use \OCP\AppFramework\Db\Entity;
17
use \OCP\AppFramework\Db\Mapper;
18
use \OCP\AppFramework\Db\MultipleObjectsReturnedException;
19
use \OCP\IDBConnection;
20
21
use \Doctrine\DBAL\Exception\UniqueConstraintViolationException;
22
23
/**
24
 * Common base class for data access classes of the Music app
25
 */
26
abstract class BaseMapper extends Mapper {
27
	const SQL_DATE_FORMAT = 'Y-m-d H:i:s.v';
28
29
	protected $nameColumn;
30
31
	public function __construct(IDBConnection $db, string $tableName, string $entityClass, string $nameColumn) {
32
		parent::__construct($db, $tableName, $entityClass);
33
		$this->nameColumn = $nameColumn;
34
	}
35
36
	/**
37
	 * Create an empty object of the entity class bound to this mapper
38
	 */
39
	public function createEntity() : Entity {
40
		return new $this->entityClass();
41
	}
42
43
	/**
44
	 * Find a single entity by id and user_id
45
	 * @throws DoesNotExistException if the entity does not exist
46
	 * @throws MultipleObjectsReturnedException if more than one entity exists
47
	 */
48
	public function find(int $id, string $userId) : Entity {
49
		$sql = $this->selectUserEntities("`{$this->getTableName()}`.`id` = ?");
50
		return $this->findEntity($sql, [$userId, $id]);
51
	}
52
53
	/**
54
	 * Find all entities matching the given IDs. Specifying the owning user is optional.
55
	 * @param integer[] $ids  IDs of the entities to be found
56
	 * @param string|null $userId
57
	 * @return Entity[]
58
	 */
59
	public function findById(array $ids, string $userId=null) : array {
60
		$count = \count($ids);
61
		$condition = "`{$this->getTableName()}`.`id` IN ". $this->questionMarks($count);
62
63
		if (empty($userId)) {
64
			$sql = $this->selectEntities($condition);
65
		} else {
66
			$sql = $this->selectUserEntities($condition);
67
			$ids = \array_merge([$userId], $ids);
68
		}
69
70
		return $this->findEntities($sql, $ids);
71
	}
72
73
	/**
74
	 * Find all user's entities
75
	 * @return Entity[]
76
	 */
77
	public function findAll(string $userId, int $sortBy=SortBy::None, int $limit=null, int $offset=null) : array {
78
		if ($sortBy == SortBy::Name) {
79
			$sorting = "ORDER BY LOWER(`{$this->getTableName()}`.`{$this->nameColumn}`)";
80
		} elseif ($sortBy == SortBy::Newest) {
81
			$sorting = "ORDER BY `{$this->getTableName()}`.`id` DESC"; // abuse the fact that IDs are ever-incrementing values
82
		} else {
83
			$sorting = null;
84
		}
85
		$sql = $this->selectUserEntities('', $sorting);
86
		$params = [$userId];
87
		return $this->findEntities($sql, $params, $limit, $offset);
88
	}
89
90
	/**
91
	 * Find all user's entities matching the given name
92
	 * @return Entity[]
93
	 */
94
	public function findAllByName(
95
			?string $name, string $userId, bool $fuzzy = false, int $limit=null, int $offset=null) : array {
96
		$nameCol = "`{$this->getTableName()}`.`{$this->nameColumn}`";
97
		if ($name === null) {
98
			$condition = "$nameCol IS NULL";
99
			$params = [$userId];
100
		} elseif ($fuzzy) {
101
			$condition = "LOWER($nameCol) LIKE LOWER(?)";
102
			$params = [$userId, "%$name%"];
103
		} else {
104
			$condition = "$nameCol = ?";
105
			$params = [$userId, $name];
106
		}
107
		$sql = $this->selectUserEntities($condition, "ORDER BY LOWER($nameCol)");
108
109
		return $this->findEntities($sql, $params, $limit, $offset);
110
	}
111
112
	/**
113
	 * Find all user's starred entities
114
	 * @return Entity[]
115
	 */
116
	public function findAllStarred(string $userId, int $limit=null, int $offset=null) : array {
117
		$sql = $this->selectUserEntities(
118
				"`{$this->getTableName()}`.`starred` IS NOT NULL",
119
				"ORDER BY LOWER(`{$this->getTableName()}`.`{$this->nameColumn}`)");
120
		return $this->findEntities($sql, [$userId], $limit, $offset);
121
	}
122
123
	/**
124
	 * Delete all entities with given IDs without specifying the user
125
	 * @param integer[] $ids  IDs of the entities to be deleted
126
	 */
127
	public function deleteById(array $ids) : void {
128
		$count = \count($ids);
129
		if ($count === 0) {
130
			return;
131
		}
132
		$sql = "DELETE FROM `{$this->getTableName()}` WHERE `id` IN ". $this->questionMarks($count);
133
		$this->execute($sql, $ids);
134
	}
135
136
	/**
137
	 * Delete all entities of the given user
138
	 */
139
	public function deleteAll(string $userId) : void {
140
		$sql = "DELETE FROM `{$this->getTableName()}` WHERE `user_id` = ?";
141
		$this->execute($sql, [$userId]);
142
	}
143
144
	/**
145
	 * Tests if entity with given ID and user ID exists in the database
146
	 */
147
	public function exists(int $id, string $userId) : bool {
148
		$sql = "SELECT 1 FROM `{$this->getTableName()}` WHERE `id` = ? AND `user_id` = ?";
149
		$result = $this->execute($sql, [$id, $userId]);
150
		return $result->rowCount() > 0;
151
	}
152
153
	/**
154
	 * Count all entities of a user
155
	 */
156
	public function count(string $userId) : int {
157
		$sql = "SELECT COUNT(*) AS count FROM `{$this->getTableName()}` WHERE `user_id` = ?";
158
		$result = $this->execute($sql, [$userId]);
159
		$row = $result->fetch();
160
		return \intval($row['count']);
161
	}
162
163
	/**
164
	 * Insert an entity, or if an entity with the same identity already exists,
165
	 * update the existing entity.
166
	 * @return Entity The inserted or updated entity, containing also the id field
167
	 */
168
	public function insertOrUpdate(Entity $entity) : Entity {
169
		try {
170
			return $this->insert($entity);
171
		} catch (UniqueConstraintViolationException $ex) {
172
			$existingEntity = $this->findUniqueEntity($entity);
173
			$entity->setId($existingEntity->getId());
174
			return $this->update($entity);
175
		}
176
	}
177
178
	/**
179
	 * Set the "starred" column of the given entities
180
	 * @param \DateTime|null $date
181
	 * @param integer[] $ids
182
	 * @param string $userId
183
	 * @return int number of modified entities
184
	 */
185
	public function setStarredDate(?\DateTime $date, array $ids, string $userId) : int {
186
		$count = \count($ids);
187
		if (!empty($date)) {
188
			$date = $date->format(self::SQL_DATE_FORMAT);
189
		}
190
191
		$sql = "UPDATE `{$this->getTableName()}` SET `starred` = ?
192
				WHERE `id` IN {$this->questionMarks($count)} AND `user_id` = ?";
193
		$params = \array_merge([$date], $ids, [$userId]);
194
		return $this->execute($sql, $params)->rowCount();
195
	}
196
197
	/**
198
	 * helper creating a string like '(?,?,?)' with the specified number of elements
199
	 */
200
	protected function questionMarks(int $count) : string {
201
		$questionMarks = [];
202
		for ($i = 0; $i < $count; $i++) {
203
			$questionMarks[] = '?';
204
		}
205
		return '(' . \implode(',', $questionMarks) . ')';
206
	}
207
208
	/**
209
	 * Build a SQL SELECT statement which selects all entities of the given user,
210
	 * and optionally applies other conditions, too.
211
	 * This is built upon `selectEntities` which may be overridden by the derived class.
212
	 * @param string|null $condition Optional extra condition. This will get automatically
213
	 *                               prefixed with ' AND ', so don't include that.
214
	 * @param string|null $extension Any extension (e.g. ORDER BY, LIMIT) to be added after
215
	 *                               the conditions in the SQL statement
216
	 */
217
	protected function selectUserEntities(string $condition=null, string $extension=null) : string {
218
		$allConditions = "`{$this->getTableName()}`.`user_id` = ?";
219
220
		if (!empty($condition)) {
221
			$allConditions .= " AND $condition";
222
		}
223
224
		return $this->selectEntities($allConditions, $extension);
225
	}
226
227
	/**
228
	 * Build a SQL SELECT statement which selects all entities matching the given condition.
229
	 * The derived class may override this if necessary.
230
	 * @param string $condition This will get automatically prefixed with ' WHERE '
231
	 * @param string|null $extension Any extension (e.g. ORDER BY, LIMIT) to be added after
232
	 *                               the conditions in the SQL statement
233
	 */
234
	protected function selectEntities(string $condition, string $extension=null) : string {
235
		return "SELECT * FROM `{$this->getTableName()}` WHERE $condition $extension ";
236
	}
237
238
	/**
239
	 * Find an entity which has the same identity as the supplied entity.
240
	 * How the identity of the entity is defined, depends on the derived concrete class.
241
	 */
242
	abstract protected function findUniqueEntity(Entity $entity) : Entity;
243
}
244