Completed
Push — master ( 05d193...e557e0 )
by Filip
04:55
created

EntityManager   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 413
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 11

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 50
lcom 3
cbo 11
dl 0
loc 413
rs 8.6206
c 3
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A bindTracyPanel() 0 4 1
A persist() 0 8 3
A flush() 0 15 3
A __construct() 0 8 2
A remove() 0 8 3
A safePersist() 0 8 2
C create() 0 25 7
A getReflection() 0 4 1
A __call() 0 4 1
A __callStatic() 0 4 1
A extensionMethod() 0 13 3
A __get() 0 4 1
A __set() 0 4 1
A __isset() 0 4 1
A __unset() 0 4 1
A createQueryBuilder() 0 13 4
A clear() 0 8 3
A close() 0 8 2
A newHydrator() 0 10 2
A fetch() 0 9 2
A fetchOne() 0 15 4
A handleQueryException() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like EntityManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EntityManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file is part of the Kdyby (http://www.kdyby.org)
5
 *
6
 * Copyright (c) 2008 Filip Procházka ([email protected])
7
 *
8
 * For the full copyright and license information, please view the file license.txt that was distributed with this source code.
9
 */
10
11
namespace Kdyby\Doctrine;
12
13
use Doctrine;
14
use Doctrine\DBAL\DriverManager;
15
use Doctrine\ORM\AbstractQuery;
16
use Doctrine\ORM\ORMException;
17
use Doctrine\ORM\Query;
18
use Kdyby;
19
use Kdyby\Doctrine\QueryObject;
20
use Kdyby\Doctrine\Tools\NonLockingUniqueInserter;
21
use Kdyby\Persistence;
22
use Nette;
23
use Nette\Utils\ObjectMixin;
24
25
26
27
/**
28
 * @author Filip Procházka <[email protected]>
29
 *
30
 * @method \Kdyby\Doctrine\Connection getConnection()
31
 * @method \Kdyby\Doctrine\Configuration getConfiguration()
32
 * @method \Kdyby\Doctrine\EntityRepository getRepository($entityName)
33
 */
34
class EntityManager extends Doctrine\ORM\EntityManager implements Persistence\QueryExecutor, Persistence\Queryable
0 ignored issues
show
Bug introduced by
There is at least one abstract method in this class. Maybe declare it as abstract, or implement the remaining methods: createNativeQuery, createQuery
Loading history...
35
{
36
37
	/**
38
	 * @var NonLockingUniqueInserter
39
	 */
40
	private $nonLockingUniqueInserter;
41
42
	/**
43
	 * @var Diagnostics\Panel
44
	 */
45
	private $panel;
46
47
48
49
	protected function __construct(Doctrine\DBAL\Connection $conn, Doctrine\ORM\Configuration $config, Doctrine\Common\EventManager $eventManager)
50
	{
51
		parent::__construct($conn, $config, $eventManager);
52
53
		if ($conn instanceof Kdyby\Doctrine\Connection) {
54
			$conn->bindEntityManager($this);
55
		}
56
	}
57
58
59
60
	/**
61
	 * @internal
62
	 * @param Diagnostics\Panel $panel
63
	 */
64
	public function bindTracyPanel(Diagnostics\Panel $panel)
65
	{
66
		$this->panel = $panel;
67
	}
68
69
70
71
	/**
72
	 * @throws NotSupportedException
73
	 * @return \Kdyby\Doctrine\QueryBuilder
74
	 */
75
	public function createQueryBuilder($alias = NULL, $indexBy = NULL)
76
	{
77
		if ($alias !== NULL || $indexBy !== NULL) {
78
			throw new NotSupportedException('Use EntityRepository for $alias and $indexBy arguments to work.');
79
		}
80
81
		if (($config = $this->getConfiguration()) instanceof Configuration) {
82
			$class = $config->getQueryBuilderClassName();
83
			return new $class($this);
84
		}
85
86
		return new QueryBuilder($this);
87
	}
88
89
90
91
	/**
92
	 * {@inheritdoc}
93
	 * @param string|array $entity
0 ignored issues
show
Documentation introduced by
There is no parameter named $entity. Did you maybe mean $entityName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
94
	 * @return EntityManager
95
	 */
96
	public function clear($entityName = null)
97
	{
98
		foreach (is_array($entityName) ? $entityName : (func_get_args() + [0 => NULL]) as $item) {
99
			parent::clear($item);
100
		}
101
102
		return $this;
103
	}
104
105
106
107
	/**
108
	 * {@inheritdoc}
109
	 * @param object|array $entity
110
	 * @return EntityManager
111
	 */
112
	public function remove($entity)
113
	{
114
		foreach (is_array($entity) ? $entity : func_get_args() as $item) {
115
			parent::remove($item);
116
		}
117
118
		return $this;
119
	}
120
121
122
123
	/**
124
	 * {@inheritdoc}
125
	 * @param object|array $entity
126
	 * @return EntityManager
127
	 */
128
	public function persist($entity)
129
	{
130
		foreach (is_array($entity) ? $entity : func_get_args() as $item) {
131
			parent::persist($item);
132
		}
133
134
		return $this;
135
	}
136
137
138
139
	/**
140
	 * {@inheritdoc}
141
	 * @param object|array $entity
142
	 * @return EntityManager
143
	 */
144
	public function flush($entity = null)
145
	{
146
		try {
147
			parent::flush($entity);
148
149
		} catch (\Exception $e) {
150
			if ($this->panel) {
151
				$this->panel->markExceptionOwner($this, $e);
152
			}
153
154
			throw $e;
155
		}
156
157
		return $this;
158
	}
159
160
161
162
	public function close()
163
	{
164
		if ($this->panel) {
165
			$this->panel->snapshotUnitOfWork($this);
166
		}
167
168
		parent::close();
169
	}
170
171
172
173
	/**
174
	 * Tries to persist the given entity and returns FALSE if an unique
175
	 * constaint was violated.
176
	 *
177
	 * Warning: On success you must NOT use the passed entity further
178
	 * in your application. Use the returned one instead!
179
	 *
180
	 * @param $entity
181
	 * @throws \Doctrine\DBAL\DBALException
182
	 * @throws \Exception
183
	 * @return bool|object
184
	 */
185
	public function safePersist($entity)
186
	{
187
		if ($this->nonLockingUniqueInserter === NULL) {
188
			$this->nonLockingUniqueInserter = new NonLockingUniqueInserter($this);
189
		}
190
191
		return $this->nonLockingUniqueInserter->persist($entity);
192
	}
193
194
195
196
	/**
197
	 * @param int $hydrationMode
198
	 * @return Doctrine\ORM\Internal\Hydration\AbstractHydrator
199
	 * @throws \Doctrine\ORM\ORMException
200
	 */
201
	public function newHydrator($hydrationMode)
202
	{
203
		switch ($hydrationMode) {
204
			case Hydration\HashHydrator::NAME:
205
				return new Hydration\HashHydrator($this);
206
207
			default:
208
				return parent::newHydrator($hydrationMode);
209
		}
210
	}
211
212
213
214
	/**
215
	 * Factory method to create EntityManager instances.
216
	 *
217
	 * @param \Doctrine\DBAL\Connection|array $conn
218
	 * @param \Doctrine\ORM\Configuration $config
219
	 * @param \Doctrine\Common\EventManager $eventManager
220
	 * @throws \Doctrine\ORM\ORMException
221
	 * @throws \InvalidArgumentException
222
	 * @throws \Doctrine\ORM\ORMException
223
	 * @return EntityManager
224
	 */
225
	public static function create($conn, Doctrine\ORM\Configuration $config, Doctrine\Common\EventManager $eventManager = NULL)
226
	{
227
		if (!$config->getMetadataDriverImpl()) {
228
			throw ORMException::missingMappingDriverImpl();
229
		}
230
231
		switch (TRUE) {
232
			case (is_array($conn)):
233
				$conn = DriverManager::getConnection(
234
					$conn, $config, ($eventManager ? : new Doctrine\Common\EventManager())
235
				);
236
				break;
237
238
			case ($conn instanceof Doctrine\DBAL\Connection):
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\Connection does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
239
				if ($eventManager !== NULL && $conn->getEventManager() !== $eventManager) {
240
					throw ORMException::mismatchedEventManager();
241
				}
242
				break;
243
244
			default:
245
				throw new \InvalidArgumentException("Invalid connection");
246
		}
247
248
		return new EntityManager($conn, $config, $conn->getEventManager());
249
	}
250
251
252
253
	/****************** Kdyby\Persistence\QueryExecutor *****************/
254
255
256
257
	/**
258
	 * @param \Kdyby\Persistence\Query|\Kdyby\Doctrine\QueryObject $queryObject
259
	 * @param int $hydrationMode
260
	 * @throws QueryException
261
	 * @return array|\Kdyby\Doctrine\ResultSet
262
	 */
263
	public function fetch(Persistence\Query $queryObject, $hydrationMode = AbstractQuery::HYDRATE_OBJECT)
264
	{
265
		try {
266
			return $queryObject->fetch($this, $hydrationMode);
0 ignored issues
show
Unused Code introduced by
The call to Query::fetch() has too many arguments starting with $hydrationMode.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
267
268
		} catch (\Exception $e) {
269
			throw $this->handleQueryException($e, $queryObject);
270
		}
271
	}
272
273
274
275
	/**
276
	 * @param \Kdyby\Persistence\Query|\Kdyby\Doctrine\QueryObject $queryObject
277
	 *
278
	 * @throws InvalidStateException
279
	 * @throws QueryException
280
	 * @return object|NULL
281
	 */
282
	public function fetchOne(Persistence\Query $queryObject)
283
	{
284
		try {
285
			return $queryObject->fetchOne($this);
286
287
		} catch (Doctrine\ORM\NoResultException $e) {
0 ignored issues
show
Bug introduced by
The class Doctrine\ORM\NoResultException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
288
			return NULL;
289
290
		} catch (Doctrine\ORM\NonUniqueResultException $e) { // this should never happen!
0 ignored issues
show
Bug introduced by
The class Doctrine\ORM\NonUniqueResultException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
291
			throw new InvalidStateException("You have to setup your query calling ->setMaxResult(1).", 0, $e);
292
293
		} catch (\Exception $e) {
294
			throw $this->handleQueryException($e, $queryObject);
295
		}
296
	}
297
298
299
300
	/**
301
	 * @param \Exception $e
302
	 * @param \Kdyby\Persistence\Query $queryObject
303
	 *
304
	 * @throws \Exception
305
	 */
306
	private function handleQueryException(\Exception $e, Persistence\Query $queryObject)
307
	{
308
		$lastQuery = $queryObject instanceof QueryObject ? $queryObject->getLastQuery() : NULL;
309
310
		return new QueryException($e, $lastQuery, '['.get_class($queryObject).'] '.$e->getMessage());
311
	}
312
313
314
315
	/*************************** Nette\Object ***************************/
316
317
318
319
	/**
320
	 * Access to reflection.
321
	 *
322
	 * @return \Nette\Reflection\ClassType
323
	 */
324
	public static function getReflection()
325
	{
326
		return new Nette\Reflection\ClassType(get_called_class());
327
	}
328
329
330
331
	/**
332
	 * Call to undefined method.
333
	 *
334
	 * @param string $name
335
	 * @param array $args
336
	 *
337
	 * @throws \Nette\MemberAccessException
338
	 * @return mixed
339
	 */
340
	public function __call($name, $args)
341
	{
342
		return ObjectMixin::call($this, $name, $args);
343
	}
344
345
346
347
	/**
348
	 * Call to undefined static method.
349
	 *
350
	 * @param string $name
351
	 * @param array $args
352
	 *
353
	 * @throws \Nette\MemberAccessException
354
	 * @return mixed
355
	 */
356
	public static function __callStatic($name, $args)
357
	{
358
		return ObjectMixin::callStatic(get_called_class(), $name, $args);
359
	}
360
361
362
363
	/**
364
	 * Adding method to class.
365
	 *
366
	 * @param $name
367
	 * @param null $callback
368
	 *
369
	 * @throws \Nette\MemberAccessException
370
	 * @return callable|null
371
	 */
372
	public static function extensionMethod($name, $callback = NULL)
373
	{
374
		if (strpos($name, '::') === FALSE) {
375
			$class = get_called_class();
376
		} else {
377
			list($class, $name) = explode('::', $name);
378
		}
379
		if ($callback === NULL) {
380
			return ObjectMixin::getExtensionMethod($class, $name);
381
		} else {
382
			ObjectMixin::setExtensionMethod($class, $name, $callback);
383
		}
384
	}
385
386
387
388
	/**
389
	 * Returns property value. Do not call directly.
390
	 *
391
	 * @param string $name
392
	 *
393
	 * @throws \Nette\MemberAccessException
394
	 * @return mixed
395
	 */
396
	public function &__get($name)
397
	{
398
		return ObjectMixin::get($this, $name);
399
	}
400
401
402
403
	/**
404
	 * Sets value of a property. Do not call directly.
405
	 *
406
	 * @param string $name
407
	 * @param mixed $value
408
	 *
409
	 * @throws \Nette\MemberAccessException
410
	 * @return void
411
	 */
412
	public function __set($name, $value)
413
	{
414
		ObjectMixin::set($this, $name, $value);
415
	}
416
417
418
419
	/**
420
	 * Is property defined?
421
	 *
422
	 * @param string $name
423
	 *
424
	 * @return bool
425
	 */
426
	public function __isset($name)
427
	{
428
		return ObjectMixin::has($this, $name);
429
	}
430
431
432
433
	/**
434
	 * Access to undeclared property.
435
	 *
436
	 * @param string $name
437
	 *
438
	 * @throws \Nette\MemberAccessException
439
	 * @return void
440
	 */
441
	public function __unset($name)
442
	{
443
		ObjectMixin::remove($this, $name);
444
	}
445
446
}
447