Completed
Push — master ( f42493...15b459 )
by Joas
41:44
created
lib/public/AppFramework/Db/QBMapper.php 1 patch
Indentation   +359 added lines, -359 removed lines patch added patch discarded remove patch
@@ -22,363 +22,363 @@
 block discarded – undo
22 22
  * @template T of Entity
23 23
  */
24 24
 abstract class QBMapper {
25
-	/** @var string */
26
-	protected $tableName;
27
-
28
-	/** @var string|class-string<T> */
29
-	protected $entityClass;
30
-
31
-	/** @var IDBConnection */
32
-	protected $db;
33
-
34
-	/**
35
-	 * @param IDBConnection $db Instance of the Db abstraction layer
36
-	 * @param string $tableName the name of the table. set this to allow entity
37
-	 * @param class-string<T>|null $entityClass the name of the entity that the sql should be
38
-	 *                                          mapped to queries without using sql
39
-	 * @since 14.0.0
40
-	 */
41
-	public function __construct(IDBConnection $db, string $tableName, ?string $entityClass = null) {
42
-		$this->db = $db;
43
-		$this->tableName = $tableName;
44
-
45
-		// if not given set the entity name to the class without the mapper part
46
-		// cache it here for later use since reflection is slow
47
-		if ($entityClass === null) {
48
-			$this->entityClass = str_replace('Mapper', '', \get_class($this));
49
-		} else {
50
-			$this->entityClass = $entityClass;
51
-		}
52
-	}
53
-
54
-
55
-	/**
56
-	 * @return string the table name
57
-	 * @since 14.0.0
58
-	 */
59
-	public function getTableName(): string {
60
-		return $this->tableName;
61
-	}
62
-
63
-
64
-	/**
65
-	 * Deletes an entity from the table
66
-	 *
67
-	 * @param Entity $entity the entity that should be deleted
68
-	 * @psalm-param T $entity the entity that should be deleted
69
-	 * @return Entity the deleted entity
70
-	 * @psalm-return T the deleted entity
71
-	 * @throws Exception
72
-	 * @since 14.0.0
73
-	 */
74
-	public function delete(Entity $entity): Entity {
75
-		$qb = $this->db->getQueryBuilder();
76
-
77
-		$idType = $this->getParameterTypeForProperty($entity, 'id');
78
-
79
-		$qb->delete($this->tableName)
80
-			->where(
81
-				$qb->expr()->eq('id', $qb->createNamedParameter($entity->getId(), $idType))
82
-			);
83
-		$qb->executeStatement();
84
-		return $entity;
85
-	}
86
-
87
-	/**
88
-	 * Creates a new entry in the db from an entity
89
-	 *
90
-	 * @param Entity $entity the entity that should be created
91
-	 * @psalm-param T $entity the entity that should be created
92
-	 * @return Entity the saved entity with the set id
93
-	 * @psalm-return T the saved entity with the set id
94
-	 * @throws Exception
95
-	 * @since 14.0.0
96
-	 */
97
-	public function insert(Entity $entity): Entity {
98
-		if ($entity instanceof SnowflakeAwareEntity) {
99
-			/** @psalm-suppress DocblockTypeContradiction */
100
-			$entity->generateId();
101
-		}
102
-
103
-		// get updated fields to save, fields have to be set using a setter to
104
-		// be saved
105
-		$properties = $entity->getUpdatedFields();
106
-
107
-		$qb = $this->db->getQueryBuilder();
108
-		$qb->insert($this->tableName);
109
-
110
-		// build the fields
111
-		foreach ($properties as $property => $updated) {
112
-			$column = $entity->propertyToColumn($property);
113
-			$getter = 'get' . ucfirst($property);
114
-			$value = $entity->$getter();
115
-
116
-			if ($property === 'id' && $entity->id === null) {
117
-				continue;
118
-			}
119
-			$type = $this->getParameterTypeForProperty($entity, $property);
120
-			$qb->setValue($column, $qb->createNamedParameter($value, $type));
121
-		}
122
-
123
-		if ($entity->id === null) {
124
-			$qb->executeStatement();
125
-			// When autoincrement is used id is always an int
126
-			$entity->setId($qb->getLastInsertId());
127
-		} else {
128
-			$qb->executeStatement();
129
-		}
130
-		return $entity;
131
-	}
132
-
133
-	/**
134
-	 * Tries to creates a new entry in the db from an entity and
135
-	 * updates an existing entry if duplicate keys are detected
136
-	 * by the database
137
-	 *
138
-	 * @param Entity $entity the entity that should be created/updated
139
-	 * @psalm-param T $entity the entity that should be created/updated
140
-	 * @return Entity the saved entity with the (new) id
141
-	 * @psalm-return T the saved entity with the (new) id
142
-	 * @throws Exception
143
-	 * @throws \InvalidArgumentException if entity has no id
144
-	 * @since 15.0.0
145
-	 */
146
-	public function insertOrUpdate(Entity $entity): Entity {
147
-		try {
148
-			return $this->insert($entity);
149
-		} catch (Exception $ex) {
150
-			if ($ex->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
151
-				return $this->update($entity);
152
-			}
153
-			throw $ex;
154
-		}
155
-	}
156
-
157
-	/**
158
-	 * Updates an entry in the db from an entity
159
-	 *
160
-	 * @param Entity $entity the entity that should be created
161
-	 * @psalm-param T $entity the entity that should be created
162
-	 * @return Entity the saved entity with the set id
163
-	 * @psalm-return T the saved entity with the set id
164
-	 * @throws Exception
165
-	 * @throws \InvalidArgumentException if entity has no id
166
-	 * @since 14.0.0
167
-	 */
168
-	public function update(Entity $entity): Entity {
169
-		// if entity wasn't changed it makes no sense to run a db query
170
-		$properties = $entity->getUpdatedFields();
171
-		if (\count($properties) === 0) {
172
-			return $entity;
173
-		}
174
-
175
-		// entity needs an id
176
-		$id = $entity->getId();
177
-		if ($id === null) {
178
-			throw new \InvalidArgumentException(
179
-				'Entity which should be updated has no id');
180
-		}
181
-
182
-		// get updated fields to save, fields have to be set using a setter to
183
-		// be saved
184
-		// do not update the id field
185
-		unset($properties['id']);
186
-
187
-		$qb = $this->db->getQueryBuilder();
188
-		$qb->update($this->tableName);
189
-
190
-		// build the fields
191
-		foreach ($properties as $property => $updated) {
192
-			$column = $entity->propertyToColumn($property);
193
-			$getter = 'get' . ucfirst($property);
194
-			$value = $entity->$getter();
195
-
196
-			$type = $this->getParameterTypeForProperty($entity, $property);
197
-			$qb->set($column, $qb->createNamedParameter($value, $type));
198
-		}
199
-
200
-		$idType = $this->getParameterTypeForProperty($entity, 'id');
201
-
202
-		$qb->where(
203
-			$qb->expr()->eq('id', $qb->createNamedParameter($id, $idType))
204
-		);
205
-		$qb->executeStatement();
206
-
207
-		return $entity;
208
-	}
209
-
210
-	/**
211
-	 * Returns the type parameter for the QueryBuilder for a specific property
212
-	 * of the $entity
213
-	 *
214
-	 * @param Entity $entity The entity to get the types from
215
-	 * @psalm-param T $entity
216
-	 * @param string $property The property of $entity to get the type for
217
-	 * @return int|string
218
-	 * @since 16.0.0
219
-	 */
220
-	protected function getParameterTypeForProperty(Entity $entity, string $property) {
221
-		$types = $entity->getFieldTypes();
222
-
223
-		if (!isset($types[ $property ])) {
224
-			return IQueryBuilder::PARAM_STR;
225
-		}
226
-
227
-		switch ($types[ $property ]) {
228
-			case 'int':
229
-			case Types::INTEGER:
230
-			case Types::SMALLINT:
231
-				return IQueryBuilder::PARAM_INT;
232
-			case Types::STRING:
233
-				return IQueryBuilder::PARAM_STR;
234
-			case 'bool':
235
-			case Types::BOOLEAN:
236
-				return IQueryBuilder::PARAM_BOOL;
237
-			case Types::BLOB:
238
-				return IQueryBuilder::PARAM_LOB;
239
-			case Types::DATE:
240
-				return IQueryBuilder::PARAM_DATETIME_MUTABLE;
241
-			case Types::DATETIME:
242
-				return IQueryBuilder::PARAM_DATETIME_MUTABLE;
243
-			case Types::DATETIME_TZ:
244
-				return IQueryBuilder::PARAM_DATETIME_TZ_MUTABLE;
245
-			case Types::DATE_IMMUTABLE:
246
-				return IQueryBuilder::PARAM_DATE_IMMUTABLE;
247
-			case Types::DATETIME_IMMUTABLE:
248
-				return IQueryBuilder::PARAM_DATETIME_IMMUTABLE;
249
-			case Types::DATETIME_TZ_IMMUTABLE:
250
-				return IQueryBuilder::PARAM_DATETIME_TZ_IMMUTABLE;
251
-			case Types::TIME:
252
-				return IQueryBuilder::PARAM_TIME_MUTABLE;
253
-			case Types::TIME_IMMUTABLE:
254
-				return IQueryBuilder::PARAM_TIME_IMMUTABLE;
255
-			case Types::JSON:
256
-				return IQueryBuilder::PARAM_JSON;
257
-		}
258
-
259
-		return IQueryBuilder::PARAM_STR;
260
-	}
261
-
262
-	/**
263
-	 * Returns an db result and throws exceptions when there are more or less
264
-	 * results
265
-	 *
266
-	 * @param IQueryBuilder $query
267
-	 * @return array the result as row
268
-	 * @throws Exception
269
-	 * @throws MultipleObjectsReturnedException if more than one item exist
270
-	 * @throws DoesNotExistException if the item does not exist
271
-	 * @see findEntity
272
-	 *
273
-	 * @since 14.0.0
274
-	 */
275
-	protected function findOneQuery(IQueryBuilder $query): array {
276
-		$result = $query->executeQuery();
277
-
278
-		$row = $result->fetch();
279
-		if ($row === false) {
280
-			$result->closeCursor();
281
-			$msg = $this->buildDebugMessage(
282
-				'Did expect one result but found none when executing', $query
283
-			);
284
-			throw new DoesNotExistException($msg);
285
-		}
286
-
287
-		$row2 = $result->fetch();
288
-		$result->closeCursor();
289
-		if ($row2 !== false) {
290
-			$msg = $this->buildDebugMessage(
291
-				'Did not expect more than one result when executing', $query
292
-			);
293
-			throw new MultipleObjectsReturnedException($msg);
294
-		}
295
-
296
-		return $row;
297
-	}
298
-
299
-	/**
300
-	 * @param string $msg
301
-	 * @param IQueryBuilder $sql
302
-	 * @return string
303
-	 * @since 14.0.0
304
-	 */
305
-	private function buildDebugMessage(string $msg, IQueryBuilder $sql): string {
306
-		return $msg
307
-			. ': query "' . $sql->getSQL() . '"; ';
308
-	}
309
-
310
-
311
-	/**
312
-	 * Creates an entity from a row. Automatically determines the entity class
313
-	 * from the current mapper name (MyEntityMapper -> MyEntity)
314
-	 *
315
-	 * @param array $row the row which should be converted to an entity
316
-	 * @return Entity the entity
317
-	 * @psalm-return T the entity
318
-	 * @since 14.0.0
319
-	 */
320
-	protected function mapRowToEntity(array $row): Entity {
321
-		unset($row['DOCTRINE_ROWNUM']); // remove doctrine/dbal helper column
322
-		return \call_user_func($this->entityClass . '::fromRow', $row);
323
-	}
324
-
325
-
326
-	/**
327
-	 * Runs a sql query and returns an array of entities
328
-	 *
329
-	 * @param IQueryBuilder $query
330
-	 * @return list<Entity> all fetched entities
331
-	 * @psalm-return list<T> all fetched entities
332
-	 * @throws Exception
333
-	 * @since 14.0.0
334
-	 */
335
-	protected function findEntities(IQueryBuilder $query): array {
336
-		$result = $query->executeQuery();
337
-		try {
338
-			$entities = [];
339
-			while ($row = $result->fetch()) {
340
-				$entities[] = $this->mapRowToEntity($row);
341
-			}
342
-			return $entities;
343
-		} finally {
344
-			$result->closeCursor();
345
-		}
346
-	}
347
-
348
-	/**
349
-	 * Runs a sql query and yields each resulting entity to obtain database entries in a memory-efficient way
350
-	 *
351
-	 * @param IQueryBuilder $query
352
-	 * @return Generator Generator of fetched entities
353
-	 * @psalm-return Generator<T> Generator of fetched entities
354
-	 * @throws Exception
355
-	 * @since 30.0.0
356
-	 */
357
-	protected function yieldEntities(IQueryBuilder $query): Generator {
358
-		$result = $query->executeQuery();
359
-		try {
360
-			while ($row = $result->fetch()) {
361
-				yield $this->mapRowToEntity($row);
362
-			}
363
-		} finally {
364
-			$result->closeCursor();
365
-		}
366
-	}
367
-
368
-
369
-	/**
370
-	 * Returns an db result and throws exceptions when there are more or less
371
-	 * results
372
-	 *
373
-	 * @param IQueryBuilder $query
374
-	 * @return Entity the entity
375
-	 * @psalm-return T the entity
376
-	 * @throws Exception
377
-	 * @throws MultipleObjectsReturnedException if more than one item exist
378
-	 * @throws DoesNotExistException if the item does not exist
379
-	 * @since 14.0.0
380
-	 */
381
-	protected function findEntity(IQueryBuilder $query): Entity {
382
-		return $this->mapRowToEntity($this->findOneQuery($query));
383
-	}
25
+    /** @var string */
26
+    protected $tableName;
27
+
28
+    /** @var string|class-string<T> */
29
+    protected $entityClass;
30
+
31
+    /** @var IDBConnection */
32
+    protected $db;
33
+
34
+    /**
35
+     * @param IDBConnection $db Instance of the Db abstraction layer
36
+     * @param string $tableName the name of the table. set this to allow entity
37
+     * @param class-string<T>|null $entityClass the name of the entity that the sql should be
38
+     *                                          mapped to queries without using sql
39
+     * @since 14.0.0
40
+     */
41
+    public function __construct(IDBConnection $db, string $tableName, ?string $entityClass = null) {
42
+        $this->db = $db;
43
+        $this->tableName = $tableName;
44
+
45
+        // if not given set the entity name to the class without the mapper part
46
+        // cache it here for later use since reflection is slow
47
+        if ($entityClass === null) {
48
+            $this->entityClass = str_replace('Mapper', '', \get_class($this));
49
+        } else {
50
+            $this->entityClass = $entityClass;
51
+        }
52
+    }
53
+
54
+
55
+    /**
56
+     * @return string the table name
57
+     * @since 14.0.0
58
+     */
59
+    public function getTableName(): string {
60
+        return $this->tableName;
61
+    }
62
+
63
+
64
+    /**
65
+     * Deletes an entity from the table
66
+     *
67
+     * @param Entity $entity the entity that should be deleted
68
+     * @psalm-param T $entity the entity that should be deleted
69
+     * @return Entity the deleted entity
70
+     * @psalm-return T the deleted entity
71
+     * @throws Exception
72
+     * @since 14.0.0
73
+     */
74
+    public function delete(Entity $entity): Entity {
75
+        $qb = $this->db->getQueryBuilder();
76
+
77
+        $idType = $this->getParameterTypeForProperty($entity, 'id');
78
+
79
+        $qb->delete($this->tableName)
80
+            ->where(
81
+                $qb->expr()->eq('id', $qb->createNamedParameter($entity->getId(), $idType))
82
+            );
83
+        $qb->executeStatement();
84
+        return $entity;
85
+    }
86
+
87
+    /**
88
+     * Creates a new entry in the db from an entity
89
+     *
90
+     * @param Entity $entity the entity that should be created
91
+     * @psalm-param T $entity the entity that should be created
92
+     * @return Entity the saved entity with the set id
93
+     * @psalm-return T the saved entity with the set id
94
+     * @throws Exception
95
+     * @since 14.0.0
96
+     */
97
+    public function insert(Entity $entity): Entity {
98
+        if ($entity instanceof SnowflakeAwareEntity) {
99
+            /** @psalm-suppress DocblockTypeContradiction */
100
+            $entity->generateId();
101
+        }
102
+
103
+        // get updated fields to save, fields have to be set using a setter to
104
+        // be saved
105
+        $properties = $entity->getUpdatedFields();
106
+
107
+        $qb = $this->db->getQueryBuilder();
108
+        $qb->insert($this->tableName);
109
+
110
+        // build the fields
111
+        foreach ($properties as $property => $updated) {
112
+            $column = $entity->propertyToColumn($property);
113
+            $getter = 'get' . ucfirst($property);
114
+            $value = $entity->$getter();
115
+
116
+            if ($property === 'id' && $entity->id === null) {
117
+                continue;
118
+            }
119
+            $type = $this->getParameterTypeForProperty($entity, $property);
120
+            $qb->setValue($column, $qb->createNamedParameter($value, $type));
121
+        }
122
+
123
+        if ($entity->id === null) {
124
+            $qb->executeStatement();
125
+            // When autoincrement is used id is always an int
126
+            $entity->setId($qb->getLastInsertId());
127
+        } else {
128
+            $qb->executeStatement();
129
+        }
130
+        return $entity;
131
+    }
132
+
133
+    /**
134
+     * Tries to creates a new entry in the db from an entity and
135
+     * updates an existing entry if duplicate keys are detected
136
+     * by the database
137
+     *
138
+     * @param Entity $entity the entity that should be created/updated
139
+     * @psalm-param T $entity the entity that should be created/updated
140
+     * @return Entity the saved entity with the (new) id
141
+     * @psalm-return T the saved entity with the (new) id
142
+     * @throws Exception
143
+     * @throws \InvalidArgumentException if entity has no id
144
+     * @since 15.0.0
145
+     */
146
+    public function insertOrUpdate(Entity $entity): Entity {
147
+        try {
148
+            return $this->insert($entity);
149
+        } catch (Exception $ex) {
150
+            if ($ex->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
151
+                return $this->update($entity);
152
+            }
153
+            throw $ex;
154
+        }
155
+    }
156
+
157
+    /**
158
+     * Updates an entry in the db from an entity
159
+     *
160
+     * @param Entity $entity the entity that should be created
161
+     * @psalm-param T $entity the entity that should be created
162
+     * @return Entity the saved entity with the set id
163
+     * @psalm-return T the saved entity with the set id
164
+     * @throws Exception
165
+     * @throws \InvalidArgumentException if entity has no id
166
+     * @since 14.0.0
167
+     */
168
+    public function update(Entity $entity): Entity {
169
+        // if entity wasn't changed it makes no sense to run a db query
170
+        $properties = $entity->getUpdatedFields();
171
+        if (\count($properties) === 0) {
172
+            return $entity;
173
+        }
174
+
175
+        // entity needs an id
176
+        $id = $entity->getId();
177
+        if ($id === null) {
178
+            throw new \InvalidArgumentException(
179
+                'Entity which should be updated has no id');
180
+        }
181
+
182
+        // get updated fields to save, fields have to be set using a setter to
183
+        // be saved
184
+        // do not update the id field
185
+        unset($properties['id']);
186
+
187
+        $qb = $this->db->getQueryBuilder();
188
+        $qb->update($this->tableName);
189
+
190
+        // build the fields
191
+        foreach ($properties as $property => $updated) {
192
+            $column = $entity->propertyToColumn($property);
193
+            $getter = 'get' . ucfirst($property);
194
+            $value = $entity->$getter();
195
+
196
+            $type = $this->getParameterTypeForProperty($entity, $property);
197
+            $qb->set($column, $qb->createNamedParameter($value, $type));
198
+        }
199
+
200
+        $idType = $this->getParameterTypeForProperty($entity, 'id');
201
+
202
+        $qb->where(
203
+            $qb->expr()->eq('id', $qb->createNamedParameter($id, $idType))
204
+        );
205
+        $qb->executeStatement();
206
+
207
+        return $entity;
208
+    }
209
+
210
+    /**
211
+     * Returns the type parameter for the QueryBuilder for a specific property
212
+     * of the $entity
213
+     *
214
+     * @param Entity $entity The entity to get the types from
215
+     * @psalm-param T $entity
216
+     * @param string $property The property of $entity to get the type for
217
+     * @return int|string
218
+     * @since 16.0.0
219
+     */
220
+    protected function getParameterTypeForProperty(Entity $entity, string $property) {
221
+        $types = $entity->getFieldTypes();
222
+
223
+        if (!isset($types[ $property ])) {
224
+            return IQueryBuilder::PARAM_STR;
225
+        }
226
+
227
+        switch ($types[ $property ]) {
228
+            case 'int':
229
+            case Types::INTEGER:
230
+            case Types::SMALLINT:
231
+                return IQueryBuilder::PARAM_INT;
232
+            case Types::STRING:
233
+                return IQueryBuilder::PARAM_STR;
234
+            case 'bool':
235
+            case Types::BOOLEAN:
236
+                return IQueryBuilder::PARAM_BOOL;
237
+            case Types::BLOB:
238
+                return IQueryBuilder::PARAM_LOB;
239
+            case Types::DATE:
240
+                return IQueryBuilder::PARAM_DATETIME_MUTABLE;
241
+            case Types::DATETIME:
242
+                return IQueryBuilder::PARAM_DATETIME_MUTABLE;
243
+            case Types::DATETIME_TZ:
244
+                return IQueryBuilder::PARAM_DATETIME_TZ_MUTABLE;
245
+            case Types::DATE_IMMUTABLE:
246
+                return IQueryBuilder::PARAM_DATE_IMMUTABLE;
247
+            case Types::DATETIME_IMMUTABLE:
248
+                return IQueryBuilder::PARAM_DATETIME_IMMUTABLE;
249
+            case Types::DATETIME_TZ_IMMUTABLE:
250
+                return IQueryBuilder::PARAM_DATETIME_TZ_IMMUTABLE;
251
+            case Types::TIME:
252
+                return IQueryBuilder::PARAM_TIME_MUTABLE;
253
+            case Types::TIME_IMMUTABLE:
254
+                return IQueryBuilder::PARAM_TIME_IMMUTABLE;
255
+            case Types::JSON:
256
+                return IQueryBuilder::PARAM_JSON;
257
+        }
258
+
259
+        return IQueryBuilder::PARAM_STR;
260
+    }
261
+
262
+    /**
263
+     * Returns an db result and throws exceptions when there are more or less
264
+     * results
265
+     *
266
+     * @param IQueryBuilder $query
267
+     * @return array the result as row
268
+     * @throws Exception
269
+     * @throws MultipleObjectsReturnedException if more than one item exist
270
+     * @throws DoesNotExistException if the item does not exist
271
+     * @see findEntity
272
+     *
273
+     * @since 14.0.0
274
+     */
275
+    protected function findOneQuery(IQueryBuilder $query): array {
276
+        $result = $query->executeQuery();
277
+
278
+        $row = $result->fetch();
279
+        if ($row === false) {
280
+            $result->closeCursor();
281
+            $msg = $this->buildDebugMessage(
282
+                'Did expect one result but found none when executing', $query
283
+            );
284
+            throw new DoesNotExistException($msg);
285
+        }
286
+
287
+        $row2 = $result->fetch();
288
+        $result->closeCursor();
289
+        if ($row2 !== false) {
290
+            $msg = $this->buildDebugMessage(
291
+                'Did not expect more than one result when executing', $query
292
+            );
293
+            throw new MultipleObjectsReturnedException($msg);
294
+        }
295
+
296
+        return $row;
297
+    }
298
+
299
+    /**
300
+     * @param string $msg
301
+     * @param IQueryBuilder $sql
302
+     * @return string
303
+     * @since 14.0.0
304
+     */
305
+    private function buildDebugMessage(string $msg, IQueryBuilder $sql): string {
306
+        return $msg
307
+            . ': query "' . $sql->getSQL() . '"; ';
308
+    }
309
+
310
+
311
+    /**
312
+     * Creates an entity from a row. Automatically determines the entity class
313
+     * from the current mapper name (MyEntityMapper -> MyEntity)
314
+     *
315
+     * @param array $row the row which should be converted to an entity
316
+     * @return Entity the entity
317
+     * @psalm-return T the entity
318
+     * @since 14.0.0
319
+     */
320
+    protected function mapRowToEntity(array $row): Entity {
321
+        unset($row['DOCTRINE_ROWNUM']); // remove doctrine/dbal helper column
322
+        return \call_user_func($this->entityClass . '::fromRow', $row);
323
+    }
324
+
325
+
326
+    /**
327
+     * Runs a sql query and returns an array of entities
328
+     *
329
+     * @param IQueryBuilder $query
330
+     * @return list<Entity> all fetched entities
331
+     * @psalm-return list<T> all fetched entities
332
+     * @throws Exception
333
+     * @since 14.0.0
334
+     */
335
+    protected function findEntities(IQueryBuilder $query): array {
336
+        $result = $query->executeQuery();
337
+        try {
338
+            $entities = [];
339
+            while ($row = $result->fetch()) {
340
+                $entities[] = $this->mapRowToEntity($row);
341
+            }
342
+            return $entities;
343
+        } finally {
344
+            $result->closeCursor();
345
+        }
346
+    }
347
+
348
+    /**
349
+     * Runs a sql query and yields each resulting entity to obtain database entries in a memory-efficient way
350
+     *
351
+     * @param IQueryBuilder $query
352
+     * @return Generator Generator of fetched entities
353
+     * @psalm-return Generator<T> Generator of fetched entities
354
+     * @throws Exception
355
+     * @since 30.0.0
356
+     */
357
+    protected function yieldEntities(IQueryBuilder $query): Generator {
358
+        $result = $query->executeQuery();
359
+        try {
360
+            while ($row = $result->fetch()) {
361
+                yield $this->mapRowToEntity($row);
362
+            }
363
+        } finally {
364
+            $result->closeCursor();
365
+        }
366
+    }
367
+
368
+
369
+    /**
370
+     * Returns an db result and throws exceptions when there are more or less
371
+     * results
372
+     *
373
+     * @param IQueryBuilder $query
374
+     * @return Entity the entity
375
+     * @psalm-return T the entity
376
+     * @throws Exception
377
+     * @throws MultipleObjectsReturnedException if more than one item exist
378
+     * @throws DoesNotExistException if the item does not exist
379
+     * @since 14.0.0
380
+     */
381
+    protected function findEntity(IQueryBuilder $query): Entity {
382
+        return $this->mapRowToEntity($this->findOneQuery($query));
383
+    }
384 384
 }
Please login to merge, or discard this patch.