Completed
Pull Request — master (#256)
by Tomáš
02:56
created

QueryObject::toQuery()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
rs 8.6737
cc 6
eloc 13
nc 8
nop 1
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\ORM\AbstractQuery;
15
use Doctrine\ORM\Tools\Pagination\Paginator;
16
use Kdyby;
17
use Kdyby\Persistence\Queryable;
18
use Nette;
19
20
21
22
/**
23
 * Purpose of this class is to be inherited and have implemented doCreateQuery() method,
24
 * which constructs DQL from your constraints and filters.
25
 *
26
 * QueryObject inheritors are great when you're printing a data to the user,
27
 * they may be used in service layer but that's not really suggested.
28
 *
29
 * Don't be afraid to use them in presenters
30
 *
31
 * <code>
32
 * $this->template->articles = $this->articlesRepository->fetch(new ArticlesQuery());
33
 * </code>
34
 *
35
 * or in more complex ways
36
 *
37
 * <code>
38
 * $productsQuery = new ProductsQuery();
39
 * $productsQuery
40
 *    ->setColor('green')
41
 *    ->setMaxDeliveryPrice(100)
42
 *    ->setMaxDeliveryMinutes(75);
43
 *
44
 * $productsQuery->size = 'big';
45
 *
46
 * $this->template->products = $this->productsRepository->fetch($productsQuery);
47
 * </code>
48
 *
49
 * @method onPostFetch(QueryObject $self, Queryable $repository, \Iterator $iterator)
50
 *
51
 * @author Filip Procházka <[email protected]>
52
 */
53
abstract class QueryObject extends Nette\Object implements Kdyby\Persistence\Query
54
{
55
56
	/**
57
	 * @var array
58
	 */
59
	public $onPostFetch = [];
60
61
	/**
62
	 * @var \Doctrine\ORM\Query
63
	 */
64
	private $lastQuery;
65
66
	/**
67
	 * @var \Kdyby\Doctrine\ResultSet
68
	 */
69
	private $lastResult;
70
71
72
73
	/**
74
	 */
75
	public function __construct()
76
	{
77
78
	}
79
80
81
82
	/**
83
	 * @param \Kdyby\Persistence\Queryable $repository
84
	 * @return \Doctrine\ORM\Query|\Doctrine\ORM\QueryBuilder
85
	 */
86
	protected abstract function doCreateQuery(Kdyby\Persistence\Queryable $repository);
87
88
89
90
	/**
91
	 * @param \Kdyby\Persistence\Queryable $repository
92
	 *
93
	 * @throws UnexpectedValueException
94
	 * @return \Doctrine\ORM\Query
95
	 */
96
	protected function getQuery(Queryable $repository)
97
	{
98
		$query = $this->toQuery($this->doCreateQuery($repository));
99
100
		if ($this->lastQuery && $this->lastQuery->getDQL() === $query->getDQL()) {
101
			$query = $this->lastQuery;
102
		}
103
104
		if ($this->lastQuery !== $query) {
105
			$this->lastResult = new ResultSet($query, $this, $repository);
106
		}
107
108
		return $this->lastQuery = $query;
109
	}
110
111
112
113
	/**
114
	 * @param Queryable $repository
115
	 * @return \Doctrine\ORM\Query|\Doctrine\ORM\QueryBuilder
116
	 */
117
	protected function doCreateCountQuery(Queryable $repository)
0 ignored issues
show
Unused Code introduced by
The parameter $repository is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
118
	{
119
120
	}
121
122
123
124
	/**
125
	 * @param \Kdyby\Persistence\Queryable $repository
126
	 * @param ResultSet $resultSet
127
	 * @param \Doctrine\ORM\Tools\Pagination\Paginator $paginatedQuery
128
	 * @return integer
129
	 */
130
	public function count(Queryable $repository, ResultSet $resultSet = NULL, Paginator $paginatedQuery = NULL)
131
	{
132
		if ($query = $this->doCreateCountQuery($repository)) {
133
			return $this->toQuery($query)->getSingleScalarResult();
134
		}
135
136
		if ($this->lastQuery && $this->lastQuery instanceof NativeQueryWrapper) {
137
			$class = get_called_class();
138
			throw new NotSupportedException("You must implement your own count query in $class::doCreateCountQuery(), Paginator from Doctrine doesn't support NativeQueries.");
139
		}
140
141
		if ($paginatedQuery !== NULL) {
142
			return $paginatedQuery->count();
143
		}
144
145
		$query = $this->getQuery($repository)
146
			->setFirstResult(NULL)
147
			->setMaxResults(NULL);
148
149
		$paginatedQuery = new Paginator($query, $resultSet ? $resultSet->getFetchJoinCollection() : TRUE);
150
		$paginatedQuery->setUseOutputWalkers($resultSet ? $resultSet->getUseOutputWalkers() : NULL);
151
152
		return $paginatedQuery->count();
153
	}
154
155
156
157
	/**
158
	 * @param \Kdyby\Persistence\Queryable $repository
159
	 * @param int $hydrationMode
160
	 *
161
	 * @return \Kdyby\Doctrine\ResultSet|array
162
	 */
163
	public function fetch(Queryable $repository, $hydrationMode = AbstractQuery::HYDRATE_OBJECT)
164
	{
165
		$query = $this->getQuery($repository)
166
			->setFirstResult(NULL)
167
			->setMaxResults(NULL);
168
169
		return $hydrationMode !== AbstractQuery::HYDRATE_OBJECT
170
			? $query->execute(NULL, $hydrationMode)
171
			: $this->lastResult;
172
	}
173
174
175
176
	/**
177
	 * If You encounter a problem with the LIMIT 1 here,
178
	 * you should instead of fetching toMany relations just use postFetch.
179
	 *
180
	 * And if you really really need to hack it, just override this method and remove the limit.
181
	 *
182
	 * @param \Kdyby\Persistence\Queryable $repository
183
	 * @return object
184
	 */
185
	public function fetchOne(Queryable $repository)
186
	{
187
		$query = $this->getQuery($repository)
188
			->setFirstResult(NULL)
189
			->setMaxResults(1);
190
191
		return $query->getSingleResult();
192
	}
193
194
195
196
	/**
197
	 * @param \Kdyby\Persistence\Queryable $repository
198
	 * @param \Iterator $iterator
199
	 * @return void
200
	 */
201
	public function postFetch(Queryable $repository, \Iterator $iterator)
202
	{
203
		$this->onPostFetch($this, $repository, $iterator);
204
	}
205
206
207
208
	/**
209
	 * @internal For Debugging purposes only!
210
	 * @return \Doctrine\ORM\Query
211
	 */
212
	public function getLastQuery()
213
	{
214
		return $this->lastQuery;
215
	}
216
217
218
219
	private function toQuery($query)
220
	{
221
		if ($query instanceof Doctrine\ORM\QueryBuilder) {
222
			$query = $query->getQuery();
223
224
		} elseif ($query instanceof Doctrine\ORM\NativeQuery) {
225
			$query = new NativeQueryWrapper($query);
226
227
		} elseif ($query instanceof NativeQueryBuilder) {
228
			$query = $query->getQuery();
229
		}
230
231
		if (!$query instanceof Doctrine\ORM\AbstractQuery) {
232
			throw new UnexpectedValueException(
233
				"Method " . get_called_class() . "::doCreateQuery must return " .
234
				"instanceof Doctrine\\ORM\\Query or Kdyby\\Doctrine\\QueryBuilder, " .
235
				(is_object($query) ? 'instance of ' . get_class($query) : gettype($query)) . " given."
236
			);
237
		}
238
239
		return $query;
240
	}
241
242
}
243