2007-04-13 22:03:44 +04:00
|
|
|
<?php
|
|
|
|
/*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* This software consists of voluntary contributions made by many individuals
|
2012-05-26 16:37:00 +04:00
|
|
|
* and is licensed under the MIT license. For more information, see
|
2009-02-05 20:34:44 +03:00
|
|
|
* <http://www.doctrine-project.org>.
|
2007-04-13 22:03:44 +04:00
|
|
|
*/
|
2008-05-06 17:41:22 +04:00
|
|
|
|
2009-01-22 22:38:10 +03:00
|
|
|
namespace Doctrine\ORM;
|
|
|
|
|
2012-05-28 20:16:42 +04:00
|
|
|
use Doctrine\DBAL\LockMode;
|
|
|
|
use Doctrine\ORM\Query\Parser;
|
|
|
|
use Doctrine\ORM\Query\ParserResult;
|
|
|
|
use Doctrine\ORM\Query\QueryException;
|
2013-06-08 01:01:52 +04:00
|
|
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
|
|
|
use Doctrine\ORM\Query\ParameterTypeInferer;
|
2013-02-14 02:42:13 +04:00
|
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
2008-09-13 16:40:17 +04:00
|
|
|
|
2007-04-13 22:03:44 +04:00
|
|
|
/**
|
2009-03-28 23:59:07 +03:00
|
|
|
* A Query object represents a DQL query.
|
2007-04-13 22:03:44 +04:00
|
|
|
*
|
2010-04-13 02:49:19 +04:00
|
|
|
* @since 1.0
|
|
|
|
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
|
|
|
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
|
|
|
* @author Roman Borschel <roman@code-factory.org>
|
2007-04-13 22:03:44 +04:00
|
|
|
*/
|
2009-05-26 15:30:07 +04:00
|
|
|
final class Query extends AbstractQuery
|
2007-05-16 23:20:55 +04:00
|
|
|
{
|
2007-04-13 22:03:44 +04:00
|
|
|
/**
|
2009-04-12 23:02:12 +04:00
|
|
|
* A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
|
2007-04-13 22:03:44 +04:00
|
|
|
*/
|
2009-04-12 23:02:12 +04:00
|
|
|
const STATE_CLEAN = 1;
|
2012-12-01 20:28:06 +04:00
|
|
|
|
2007-04-13 22:03:44 +04:00
|
|
|
/**
|
2009-04-12 23:02:12 +04:00
|
|
|
* A query object is in state DIRTY when it has DQL parts that have not yet been
|
|
|
|
* parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
|
|
|
|
* is called.
|
2007-04-13 22:03:44 +04:00
|
|
|
*/
|
2009-06-14 21:34:28 +04:00
|
|
|
const STATE_DIRTY = 2;
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2009-07-21 13:25:14 +04:00
|
|
|
/* Query HINTS */
|
2012-12-13 21:56:25 +04:00
|
|
|
|
2009-07-21 13:25:14 +04:00
|
|
|
/**
|
|
|
|
* The refresh hint turns any query into a refresh query with the result that
|
|
|
|
* any local changes in entities are overridden with the fetched values.
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2009-07-21 13:25:14 +04:00
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const HINT_REFRESH = 'doctrine.refresh';
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2013-02-14 02:42:13 +04:00
|
|
|
/**
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const HINT_CACHE_ENABLED = 'doctrine.cache.enabled';
|
|
|
|
|
2013-10-16 01:55:10 +04:00
|
|
|
/**
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const HINT_CACHE_EVICT = 'doctrine.cache.evict';
|
|
|
|
|
2011-07-05 01:19:08 +04:00
|
|
|
/**
|
|
|
|
* Internal hint: is set to the proxy entity that is currently triggered for loading
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2011-07-05 01:19:08 +04:00
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2009-07-21 13:25:14 +04:00
|
|
|
/**
|
|
|
|
* The forcePartialLoad query hint forces a particular query to return
|
2009-10-28 18:32:55 +03:00
|
|
|
* partial objects.
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2009-07-21 13:25:14 +04:00
|
|
|
* @var string
|
2010-04-13 02:49:19 +04:00
|
|
|
* @todo Rename: HINT_OPTIMIZE
|
2009-07-21 13:25:14 +04:00
|
|
|
*/
|
|
|
|
const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
|
2012-12-01 20:28:06 +04:00
|
|
|
|
2009-07-21 13:25:14 +04:00
|
|
|
/**
|
|
|
|
* The includeMetaColumns query hint causes meta columns like foreign keys and
|
|
|
|
* discriminator columns to be selected and returned as part of the query result.
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2009-07-21 13:25:14 +04:00
|
|
|
* This hint does only apply to non-object queries.
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2009-07-21 13:25:14 +04:00
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
|
2010-03-06 12:52:48 +03:00
|
|
|
|
|
|
|
/**
|
2011-12-12 00:52:29 +04:00
|
|
|
* An array of class names that implement \Doctrine\ORM\Query\TreeWalker and
|
2010-03-06 12:52:48 +03:00
|
|
|
* are iterated and executed after the DQL has been parsed into an AST.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
2009-08-13 14:13:06 +04:00
|
|
|
const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
|
2010-03-06 12:52:48 +03:00
|
|
|
|
|
|
|
/**
|
2011-12-12 00:52:29 +04:00
|
|
|
* A string with a class name that implements \Doctrine\ORM\Query\TreeWalker
|
2010-03-06 12:52:48 +03:00
|
|
|
* and is used for generating the target SQL from any DQL AST tree.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';
|
|
|
|
|
2009-07-29 15:57:27 +04:00
|
|
|
//const HINT_READ_ONLY = 'doctrine.readOnly';
|
2007-09-24 22:44:37 +04:00
|
|
|
|
2009-12-08 23:53:01 +03:00
|
|
|
/**
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';
|
|
|
|
|
2010-04-09 00:50:06 +04:00
|
|
|
/**
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const HINT_LOCK_MODE = 'doctrine.lockMode';
|
|
|
|
|
2007-05-19 22:29:29 +04:00
|
|
|
/**
|
2012-12-13 21:56:25 +04:00
|
|
|
* The current state of this query.
|
|
|
|
*
|
|
|
|
* @var integer
|
2007-05-16 23:20:55 +04:00
|
|
|
*/
|
2009-05-26 15:30:07 +04:00
|
|
|
private $_state = self::STATE_CLEAN;
|
2007-05-27 15:38:16 +04:00
|
|
|
|
2015-03-17 19:53:34 +03:00
|
|
|
/**
|
|
|
|
* A snapshot of the parameter types the query was parsed with.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
private $_parsedTypes = array();
|
|
|
|
|
2007-11-20 17:26:42 +03:00
|
|
|
/**
|
2012-12-13 21:56:25 +04:00
|
|
|
* Cached DQL query.
|
|
|
|
*
|
|
|
|
* @var string
|
2007-11-20 17:26:42 +03:00
|
|
|
*/
|
2009-05-26 15:30:07 +04:00
|
|
|
private $_dql = null;
|
2008-05-24 22:18:37 +04:00
|
|
|
|
2009-04-12 23:02:12 +04:00
|
|
|
/**
|
2012-12-13 21:56:25 +04:00
|
|
|
* The parser result that holds DQL => SQL information.
|
|
|
|
*
|
|
|
|
* @var \Doctrine\ORM\Query\ParserResult
|
2009-04-12 23:02:12 +04:00
|
|
|
*/
|
2009-05-26 15:30:07 +04:00
|
|
|
private $_parserResult;
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2009-06-14 21:34:28 +04:00
|
|
|
/**
|
2012-12-13 21:56:25 +04:00
|
|
|
* The first result to return (the "offset").
|
|
|
|
*
|
|
|
|
* @var integer
|
2009-06-14 21:34:28 +04:00
|
|
|
*/
|
|
|
|
private $_firstResult = null;
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2009-06-14 21:34:28 +04:00
|
|
|
/**
|
2012-12-13 21:56:25 +04:00
|
|
|
* The maximum number of results to return (the "limit").
|
|
|
|
*
|
|
|
|
* @var integer
|
2009-06-14 21:34:28 +04:00
|
|
|
*/
|
|
|
|
private $_maxResults = null;
|
2007-04-13 22:03:44 +04:00
|
|
|
|
|
|
|
/**
|
2012-12-13 21:56:25 +04:00
|
|
|
* The cache driver used for caching queries.
|
|
|
|
*
|
|
|
|
* @var \Doctrine\Common\Cache\Cache|null
|
2007-04-13 22:03:44 +04:00
|
|
|
*/
|
2009-05-26 15:30:07 +04:00
|
|
|
private $_queryCache;
|
2008-05-24 22:18:37 +04:00
|
|
|
|
2007-11-18 19:06:37 +03:00
|
|
|
/**
|
2012-12-13 21:56:25 +04:00
|
|
|
* Whether or not expire the query cache.
|
|
|
|
*
|
|
|
|
* @var boolean
|
2007-11-18 19:06:37 +03:00
|
|
|
*/
|
2009-05-26 15:30:07 +04:00
|
|
|
private $_expireQueryCache = false;
|
2007-10-21 10:23:59 +04:00
|
|
|
|
2007-05-24 20:13:50 +04:00
|
|
|
/**
|
2012-12-13 21:56:25 +04:00
|
|
|
* The query cache lifetime.
|
|
|
|
*
|
|
|
|
* @var int
|
2007-05-24 20:13:50 +04:00
|
|
|
*/
|
2009-05-26 15:30:07 +04:00
|
|
|
private $_queryCacheTTL;
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2010-02-25 18:47:20 +03:00
|
|
|
/**
|
2012-12-13 21:56:25 +04:00
|
|
|
* Whether to use a query cache, if available. Defaults to TRUE.
|
|
|
|
*
|
|
|
|
* @var boolean
|
2010-02-25 18:47:20 +03:00
|
|
|
*/
|
|
|
|
private $_useQueryCache = true;
|
2008-05-24 22:18:37 +04:00
|
|
|
|
2007-09-24 22:44:37 +04:00
|
|
|
/**
|
2009-04-12 23:02:12 +04:00
|
|
|
* Gets the SQL query/queries that correspond to this DQL query.
|
2007-11-24 21:11:09 +03:00
|
|
|
*
|
2009-04-12 23:02:12 +04:00
|
|
|
* @return mixed The built sql query or an array of all sql queries.
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
2009-04-12 23:02:12 +04:00
|
|
|
* @override
|
2007-11-24 21:11:09 +03:00
|
|
|
*/
|
2010-03-29 17:20:41 +04:00
|
|
|
public function getSQL()
|
2007-11-24 21:11:09 +03:00
|
|
|
{
|
2010-04-13 02:49:19 +04:00
|
|
|
return $this->_parse()->getSQLExecutor()->getSQLStatements();
|
2007-11-24 21:11:09 +03:00
|
|
|
}
|
2008-05-24 22:18:37 +04:00
|
|
|
|
2010-03-16 22:21:59 +03:00
|
|
|
/**
|
2010-03-29 17:20:41 +04:00
|
|
|
* Returns the corresponding AST for this DQL query.
|
2010-03-16 22:21:59 +03:00
|
|
|
*
|
2011-12-12 00:52:29 +04:00
|
|
|
* @return \Doctrine\ORM\Query\AST\SelectStatement |
|
|
|
|
* \Doctrine\ORM\Query\AST\UpdateStatement |
|
|
|
|
* \Doctrine\ORM\Query\AST\DeleteStatement
|
2010-03-16 22:21:59 +03:00
|
|
|
*/
|
|
|
|
public function getAST()
|
|
|
|
{
|
|
|
|
$parser = new Parser($this);
|
2012-03-15 09:26:06 +04:00
|
|
|
|
2010-03-16 22:21:59 +03:00
|
|
|
return $parser->getAST();
|
|
|
|
}
|
|
|
|
|
2013-02-14 02:42:13 +04:00
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
2013-12-03 01:56:16 +04:00
|
|
|
protected function getResultSetMapping()
|
2013-02-14 02:42:13 +04:00
|
|
|
{
|
|
|
|
// parse query or load from cache
|
|
|
|
if ($this->_resultSetMapping === null) {
|
|
|
|
$this->_resultSetMapping = $this->_parse()->getResultSetMapping();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->_resultSetMapping;
|
|
|
|
}
|
|
|
|
|
2007-11-20 17:26:42 +03:00
|
|
|
/**
|
2009-04-12 23:02:12 +04:00
|
|
|
* Parses the DQL query, if necessary, and stores the parser result.
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2009-04-12 23:02:12 +04:00
|
|
|
* Note: Populates $this->_parserResult as a side-effect.
|
2007-11-20 17:26:42 +03:00
|
|
|
*
|
2011-12-12 00:52:29 +04:00
|
|
|
* @return \Doctrine\ORM\Query\ParserResult
|
2007-11-20 17:26:42 +03:00
|
|
|
*/
|
2009-06-14 21:34:28 +04:00
|
|
|
private function _parse()
|
2009-01-14 00:56:43 +03:00
|
|
|
{
|
2015-03-17 19:53:34 +03:00
|
|
|
$types = array();
|
|
|
|
|
|
|
|
foreach ($this->parameters as $parameter) {
|
|
|
|
/** @var Query\Parameter $parameter */
|
|
|
|
$types[$parameter->getName()] = $parameter->getType();
|
|
|
|
}
|
|
|
|
|
2011-08-16 15:23:53 +04:00
|
|
|
// Return previous parser result if the query and the filter collection are both clean
|
2015-03-17 19:53:34 +03:00
|
|
|
if ($this->_state === self::STATE_CLEAN && $this->_parsedTypes === $types && $this->_em->isFiltersStateClean()) {
|
2010-02-25 18:47:20 +03:00
|
|
|
return $this->_parserResult;
|
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2011-12-01 19:00:26 +04:00
|
|
|
$this->_state = self::STATE_CLEAN;
|
2015-03-17 19:53:34 +03:00
|
|
|
$this->_parsedTypes = $types;
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2010-02-25 18:47:20 +03:00
|
|
|
// Check query cache.
|
2011-12-01 19:00:26 +04:00
|
|
|
if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) {
|
2009-04-12 23:02:12 +04:00
|
|
|
$parser = new Parser($this);
|
2012-03-15 09:26:06 +04:00
|
|
|
|
2009-04-12 23:02:12 +04:00
|
|
|
$this->_parserResult = $parser->parse();
|
2011-12-01 19:00:26 +04:00
|
|
|
|
|
|
|
return $this->_parserResult;
|
2009-04-12 23:02:12 +04:00
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2015-03-24 18:04:20 +03:00
|
|
|
$hash = $this->_getQueryCacheId();
|
2011-12-01 19:00:26 +04:00
|
|
|
$cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
|
|
|
|
|
2012-05-26 21:29:51 +04:00
|
|
|
if ($cached instanceof ParserResult) {
|
2011-12-01 19:00:26 +04:00
|
|
|
// Cache hit.
|
|
|
|
$this->_parserResult = $cached;
|
|
|
|
|
|
|
|
return $this->_parserResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache miss.
|
|
|
|
$parser = new Parser($this);
|
2012-03-15 09:26:06 +04:00
|
|
|
|
2011-12-01 19:00:26 +04:00
|
|
|
$this->_parserResult = $parser->parse();
|
2012-03-15 09:26:06 +04:00
|
|
|
|
2011-12-01 19:00:26 +04:00
|
|
|
$queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2009-04-12 23:02:12 +04:00
|
|
|
return $this->_parserResult;
|
2007-11-20 17:26:42 +03:00
|
|
|
}
|
2008-05-24 22:18:37 +04:00
|
|
|
|
2007-11-20 17:26:42 +03:00
|
|
|
/**
|
2009-06-14 21:34:28 +04:00
|
|
|
* {@inheritdoc}
|
2007-11-20 17:26:42 +03:00
|
|
|
*/
|
2010-03-29 17:20:41 +04:00
|
|
|
protected function _doExecute()
|
2007-11-20 17:26:42 +03:00
|
|
|
{
|
2010-02-25 18:47:20 +03:00
|
|
|
$executor = $this->_parse()->getSqlExecutor();
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2011-10-24 01:28:23 +04:00
|
|
|
if ($this->_queryCacheProfile) {
|
|
|
|
$executor->setQueryCacheProfile($this->_queryCacheProfile);
|
|
|
|
}
|
2007-11-20 17:26:42 +03:00
|
|
|
|
2013-06-08 01:01:52 +04:00
|
|
|
if ($this->_resultSetMapping === null) {
|
|
|
|
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
|
|
|
|
}
|
|
|
|
|
2010-03-29 17:20:41 +04:00
|
|
|
// Prepare parameters
|
2009-06-14 21:34:28 +04:00
|
|
|
$paramMappings = $this->_parserResult->getParameterMappings();
|
2014-07-15 16:17:22 +04:00
|
|
|
$paramCount = count($this->parameters);
|
|
|
|
$mappingCount = count($paramMappings);
|
2009-11-21 15:33:30 +03:00
|
|
|
|
2014-07-15 16:17:22 +04:00
|
|
|
if ($paramCount > $mappingCount) {
|
|
|
|
throw QueryException::tooManyParameters($mappingCount, $paramCount);
|
|
|
|
} elseif ($paramCount < $mappingCount) {
|
|
|
|
throw QueryException::tooFewParameters($mappingCount, $paramCount);
|
2009-11-21 15:33:30 +03:00
|
|
|
}
|
|
|
|
|
2013-10-16 01:55:10 +04:00
|
|
|
// evict all cache for the entity region
|
|
|
|
if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) {
|
|
|
|
$this->evictEntityCacheRegion();
|
|
|
|
}
|
|
|
|
|
2011-09-02 02:11:57 +04:00
|
|
|
list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2011-09-02 02:11:57 +04:00
|
|
|
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
|
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2013-10-16 01:55:10 +04:00
|
|
|
/**
|
|
|
|
* Evict entity cache region
|
|
|
|
*/
|
|
|
|
private function evictEntityCacheRegion()
|
|
|
|
{
|
|
|
|
$AST = $this->getAST();
|
|
|
|
|
|
|
|
if ($AST instanceof \Doctrine\ORM\Query\AST\SelectStatement) {
|
|
|
|
throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.');
|
|
|
|
}
|
|
|
|
|
|
|
|
$className = ($AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement)
|
|
|
|
? $AST->deleteClause->abstractSchemaName
|
|
|
|
: $AST->updateClause->abstractSchemaName;
|
|
|
|
|
|
|
|
$this->_em->getCache()->evictEntityRegion($className);
|
|
|
|
}
|
|
|
|
|
2011-09-02 02:11:57 +04:00
|
|
|
/**
|
2012-12-01 20:28:06 +04:00
|
|
|
* Processes query parameter mappings.
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2011-09-02 02:11:57 +04:00
|
|
|
* @param array $paramMappings
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
2011-09-02 02:11:57 +04:00
|
|
|
* @return array
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
|
|
|
* @throws Query\QueryException
|
2011-09-02 02:11:57 +04:00
|
|
|
*/
|
|
|
|
private function processParameterMappings($paramMappings)
|
|
|
|
{
|
2012-05-28 20:16:42 +04:00
|
|
|
$sqlParams = array();
|
|
|
|
$types = array();
|
|
|
|
|
2012-05-29 23:14:08 +04:00
|
|
|
foreach ($this->parameters as $parameter) {
|
2013-06-08 01:01:52 +04:00
|
|
|
$key = $parameter->getName();
|
|
|
|
$value = $parameter->getValue();
|
2013-10-08 02:53:32 +04:00
|
|
|
$rsm = $this->getResultSetMapping();
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2009-11-21 21:52:02 +03:00
|
|
|
if ( ! isset($paramMappings[$key])) {
|
2009-11-21 20:04:17 +03:00
|
|
|
throw QueryException::unknownParameter($key);
|
2009-11-21 15:33:30 +03:00
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2013-02-14 02:42:13 +04:00
|
|
|
if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) {
|
|
|
|
$value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);
|
2013-06-08 01:01:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
$value = $this->processParameterValue($value);
|
2012-05-28 20:16:42 +04:00
|
|
|
$type = ($parameter->getValue() === $value)
|
|
|
|
? $parameter->getType()
|
2013-06-08 01:01:52 +04:00
|
|
|
: ParameterTypeInferer::inferType($value);
|
2012-05-28 20:16:42 +04:00
|
|
|
|
|
|
|
foreach ($paramMappings[$key] as $position) {
|
|
|
|
$types[$position] = $type;
|
2010-03-29 17:20:41 +04:00
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2011-09-02 02:11:57 +04:00
|
|
|
$sqlPositions = $paramMappings[$key];
|
2012-03-15 09:26:06 +04:00
|
|
|
|
|
|
|
// optimized multi value sql positions away for now,
|
|
|
|
// they are not allowed in DQL anyways.
|
2012-02-18 19:07:55 +04:00
|
|
|
$value = array($value);
|
2011-09-02 02:11:57 +04:00
|
|
|
$countValue = count($value);
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2011-09-02 02:11:57 +04:00
|
|
|
for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
|
|
|
|
$sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
|
2009-06-14 21:34:28 +04:00
|
|
|
}
|
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
|
|
|
if (count($sqlParams) != count($types)) {
|
2013-03-11 04:08:58 +04:00
|
|
|
throw QueryException::parameterTypeMismatch();
|
2011-11-18 20:29:31 +04:00
|
|
|
}
|
|
|
|
|
2010-03-29 17:20:41 +04:00
|
|
|
if ($sqlParams) {
|
|
|
|
ksort($sqlParams);
|
|
|
|
$sqlParams = array_values($sqlParams);
|
2011-11-18 20:29:31 +04:00
|
|
|
|
|
|
|
ksort($types);
|
|
|
|
$types = array_values($types);
|
2010-03-29 17:20:41 +04:00
|
|
|
}
|
|
|
|
|
2011-09-02 02:11:57 +04:00
|
|
|
return array($sqlParams, $types);
|
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2007-04-13 22:03:44 +04:00
|
|
|
/**
|
2009-04-12 23:02:12 +04:00
|
|
|
* Defines a cache driver to be used for caching queries.
|
2007-09-24 22:44:37 +04:00
|
|
|
*
|
2012-12-13 21:56:25 +04:00
|
|
|
* @param \Doctrine\Common\Cache\Cache|null $queryCache Cache driver.
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
2009-06-30 20:00:28 +04:00
|
|
|
* @return Query This query instance.
|
2007-04-13 22:03:44 +04:00
|
|
|
*/
|
2009-05-21 23:18:14 +04:00
|
|
|
public function setQueryCacheDriver($queryCache)
|
2007-04-13 22:03:44 +04:00
|
|
|
{
|
2009-04-12 23:02:12 +04:00
|
|
|
$this->_queryCache = $queryCache;
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2009-04-12 23:02:12 +04:00
|
|
|
return $this;
|
2009-05-21 23:18:14 +04:00
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2010-02-25 18:47:20 +03:00
|
|
|
/**
|
|
|
|
* Defines whether the query should make use of a query cache, if available.
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2010-02-25 18:47:20 +03:00
|
|
|
* @param boolean $bool
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
|
|
|
* @return Query This query instance.
|
2010-02-25 18:47:20 +03:00
|
|
|
*/
|
|
|
|
public function useQueryCache($bool)
|
|
|
|
{
|
|
|
|
$this->_useQueryCache = $bool;
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2010-02-25 18:47:20 +03:00
|
|
|
return $this;
|
|
|
|
}
|
2007-09-24 22:44:37 +04:00
|
|
|
|
2007-07-09 00:18:50 +04:00
|
|
|
/**
|
2009-05-21 23:18:14 +04:00
|
|
|
* Returns the cache driver used for query caching.
|
2007-07-09 00:18:50 +04:00
|
|
|
*
|
2012-12-13 21:56:25 +04:00
|
|
|
* @return \Doctrine\Common\Cache\Cache|null The cache driver used for query caching or NULL, if
|
|
|
|
* this Query does not use query caching.
|
2007-07-09 00:18:50 +04:00
|
|
|
*/
|
2009-05-21 23:18:14 +04:00
|
|
|
public function getQueryCacheDriver()
|
2007-07-09 00:18:50 +04:00
|
|
|
{
|
2009-05-21 23:18:14 +04:00
|
|
|
if ($this->_queryCache) {
|
2009-04-12 23:02:12 +04:00
|
|
|
return $this->_queryCache;
|
|
|
|
}
|
2011-12-01 19:00:26 +04:00
|
|
|
|
|
|
|
return $this->_em->getConfiguration()->getQueryCacheImpl();
|
2009-05-21 23:18:14 +04:00
|
|
|
}
|
2007-10-21 10:23:59 +04:00
|
|
|
|
2007-05-16 23:20:55 +04:00
|
|
|
/**
|
2009-04-12 23:02:12 +04:00
|
|
|
* Defines how long the query cache will be active before expire.
|
2007-05-16 23:20:55 +04:00
|
|
|
*
|
2012-12-01 20:28:06 +04:00
|
|
|
* @param integer $timeToLive How long the cache entry is valid.
|
|
|
|
*
|
2009-06-30 20:00:28 +04:00
|
|
|
* @return Query This query instance.
|
2007-05-16 23:20:55 +04:00
|
|
|
*/
|
2009-05-21 23:18:14 +04:00
|
|
|
public function setQueryCacheLifetime($timeToLive)
|
2007-05-16 23:20:55 +04:00
|
|
|
{
|
2009-04-12 23:02:12 +04:00
|
|
|
if ($timeToLive !== null) {
|
|
|
|
$timeToLive = (int) $timeToLive;
|
2007-11-19 20:55:23 +03:00
|
|
|
}
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2009-04-12 23:02:12 +04:00
|
|
|
$this->_queryCacheTTL = $timeToLive;
|
|
|
|
|
|
|
|
return $this;
|
2009-05-21 23:18:14 +04:00
|
|
|
}
|
2009-04-12 23:02:12 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the lifetime of resultset cache.
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
2009-05-21 23:18:14 +04:00
|
|
|
public function getQueryCacheLifetime()
|
2009-04-12 23:02:12 +04:00
|
|
|
{
|
|
|
|
return $this->_queryCacheTTL;
|
2009-05-21 23:18:14 +04:00
|
|
|
}
|
2009-04-12 23:02:12 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Defines if the query cache is active or not.
|
|
|
|
*
|
|
|
|
* @param boolean $expire Whether or not to force query cache expiration.
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
2009-06-30 20:00:28 +04:00
|
|
|
* @return Query This query instance.
|
2009-04-12 23:02:12 +04:00
|
|
|
*/
|
2009-11-21 16:13:19 +03:00
|
|
|
public function expireQueryCache($expire = true)
|
2009-04-12 23:02:12 +04:00
|
|
|
{
|
2009-05-21 23:18:14 +04:00
|
|
|
$this->_expireQueryCache = $expire;
|
2009-04-12 23:02:12 +04:00
|
|
|
|
|
|
|
return $this;
|
2009-05-21 23:18:14 +04:00
|
|
|
}
|
2009-04-12 23:02:12 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves if the query cache is active or not.
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
2009-05-21 23:18:14 +04:00
|
|
|
public function getExpireQueryCache()
|
2009-04-12 23:02:12 +04:00
|
|
|
{
|
|
|
|
return $this->_expireQueryCache;
|
2009-05-21 23:18:14 +04:00
|
|
|
}
|
2009-04-12 23:02:12 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @override
|
|
|
|
*/
|
|
|
|
public function free()
|
|
|
|
{
|
|
|
|
parent::free();
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2009-04-12 23:02:12 +04:00
|
|
|
$this->_dql = null;
|
|
|
|
$this->_state = self::STATE_CLEAN;
|
2007-09-13 01:42:42 +04:00
|
|
|
}
|
2007-10-21 10:23:59 +04:00
|
|
|
|
2007-09-13 01:42:42 +04:00
|
|
|
/**
|
2009-04-12 23:02:12 +04:00
|
|
|
* Sets a DQL query string.
|
2007-09-13 01:42:42 +04:00
|
|
|
*
|
2012-12-01 20:28:06 +04:00
|
|
|
* @param string $dqlQuery DQL Query.
|
|
|
|
*
|
2011-12-12 00:52:29 +04:00
|
|
|
* @return \Doctrine\ORM\AbstractQuery
|
2007-09-13 01:42:42 +04:00
|
|
|
*/
|
2010-04-13 02:49:19 +04:00
|
|
|
public function setDQL($dqlQuery)
|
2007-09-13 01:42:42 +04:00
|
|
|
{
|
2009-04-12 23:02:12 +04:00
|
|
|
if ($dqlQuery !== null) {
|
|
|
|
$this->_dql = $dqlQuery;
|
|
|
|
$this->_state = self::STATE_DIRTY;
|
2009-04-09 22:12:48 +04:00
|
|
|
}
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2009-11-21 16:13:19 +03:00
|
|
|
return $this;
|
2009-04-12 23:02:12 +04:00
|
|
|
}
|
2009-04-09 22:12:48 +04:00
|
|
|
|
2009-04-12 23:02:12 +04:00
|
|
|
/**
|
|
|
|
* Returns the DQL query that is represented by this query object.
|
|
|
|
*
|
2012-12-01 20:28:06 +04:00
|
|
|
* @return string DQL query.
|
2009-04-12 23:02:12 +04:00
|
|
|
*/
|
2010-04-13 02:49:19 +04:00
|
|
|
public function getDQL()
|
2009-04-12 23:02:12 +04:00
|
|
|
{
|
2009-05-17 23:27:12 +04:00
|
|
|
return $this->_dql;
|
2009-04-12 23:02:12 +04:00
|
|
|
}
|
2007-04-14 20:28:09 +04:00
|
|
|
|
2009-04-12 23:02:12 +04:00
|
|
|
/**
|
|
|
|
* Returns the state of this query object
|
|
|
|
* By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL
|
|
|
|
* part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY.
|
|
|
|
*
|
|
|
|
* @see AbstractQuery::STATE_CLEAN
|
|
|
|
* @see AbstractQuery::STATE_DIRTY
|
|
|
|
*
|
2012-12-01 20:28:06 +04:00
|
|
|
* @return integer The query state.
|
2009-04-12 23:02:12 +04:00
|
|
|
*/
|
|
|
|
public function getState()
|
|
|
|
{
|
|
|
|
return $this->_state;
|
2007-04-14 20:28:09 +04:00
|
|
|
}
|
2007-10-21 10:23:59 +04:00
|
|
|
|
2007-09-24 22:44:37 +04:00
|
|
|
/**
|
2009-05-17 23:27:12 +04:00
|
|
|
* Method to check if an arbitrary piece of DQL exists
|
2009-04-03 15:06:58 +04:00
|
|
|
*
|
2012-12-01 20:28:06 +04:00
|
|
|
* @param string $dql Arbitrary piece of DQL to check for.
|
|
|
|
*
|
2009-04-12 23:02:12 +04:00
|
|
|
* @return boolean
|
2009-04-03 15:06:58 +04:00
|
|
|
*/
|
2009-04-12 23:02:12 +04:00
|
|
|
public function contains($dql)
|
2009-04-03 15:06:58 +04:00
|
|
|
{
|
2010-04-13 02:49:19 +04:00
|
|
|
return stripos($this->getDQL(), $dql) === false ? false : true;
|
2009-06-14 21:34:28 +04:00
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2009-06-14 21:34:28 +04:00
|
|
|
/**
|
|
|
|
* Sets the position of the first result to retrieve (the "offset").
|
|
|
|
*
|
|
|
|
* @param integer $firstResult The first result to return.
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
2009-06-14 21:34:28 +04:00
|
|
|
* @return Query This query object.
|
|
|
|
*/
|
|
|
|
public function setFirstResult($firstResult)
|
|
|
|
{
|
|
|
|
$this->_firstResult = $firstResult;
|
2012-03-15 09:26:06 +04:00
|
|
|
$this->_state = self::STATE_DIRTY;
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2009-06-14 21:34:28 +04:00
|
|
|
return $this;
|
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2009-06-14 21:34:28 +04:00
|
|
|
/**
|
|
|
|
* Gets the position of the first result the query object was set to retrieve (the "offset").
|
|
|
|
* Returns NULL if {@link setFirstResult} was not applied to this query.
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2009-06-14 21:34:28 +04:00
|
|
|
* @return integer The position of the first result.
|
|
|
|
*/
|
|
|
|
public function getFirstResult()
|
|
|
|
{
|
|
|
|
return $this->_firstResult;
|
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2009-06-14 21:34:28 +04:00
|
|
|
/**
|
|
|
|
* Sets the maximum number of results to retrieve (the "limit").
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2009-06-14 21:34:28 +04:00
|
|
|
* @param integer $maxResults
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
2009-06-14 21:34:28 +04:00
|
|
|
* @return Query This query object.
|
|
|
|
*/
|
|
|
|
public function setMaxResults($maxResults)
|
|
|
|
{
|
|
|
|
$this->_maxResults = $maxResults;
|
2012-03-15 09:26:06 +04:00
|
|
|
$this->_state = self::STATE_DIRTY;
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2009-06-14 21:34:28 +04:00
|
|
|
return $this;
|
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2009-06-14 21:34:28 +04:00
|
|
|
/**
|
|
|
|
* Gets the maximum number of results the query object was set to retrieve (the "limit").
|
|
|
|
* Returns NULL if {@link setMaxResults} was not applied to this query.
|
2011-11-18 20:29:31 +04:00
|
|
|
*
|
2009-06-14 21:34:28 +04:00
|
|
|
* @return integer Maximum number of results.
|
|
|
|
*/
|
|
|
|
public function getMaxResults()
|
|
|
|
{
|
|
|
|
return $this->_maxResults;
|
2008-07-04 20:32:19 +04:00
|
|
|
}
|
2009-12-08 23:53:01 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Executes the query and returns an IterableResult that can be used to incrementally
|
|
|
|
* iterated over the result.
|
|
|
|
*
|
2012-12-01 20:28:06 +04:00
|
|
|
* @param ArrayCollection|array|null $parameters The query parameters.
|
|
|
|
* @param integer $hydrationMode The hydration mode to use.
|
|
|
|
*
|
2011-10-29 15:40:01 +04:00
|
|
|
* @return \Doctrine\ORM\Internal\Hydration\IterableResult
|
2009-12-08 23:53:01 +03:00
|
|
|
*/
|
2012-05-29 22:25:54 +04:00
|
|
|
public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT)
|
2009-12-08 23:53:01 +03:00
|
|
|
{
|
|
|
|
$this->setHint(self::HINT_INTERNAL_ITERATION, true);
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2012-05-28 20:16:42 +04:00
|
|
|
return parent::iterate($parameters, $hydrationMode);
|
2009-12-08 23:53:01 +03:00
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2010-02-25 18:47:20 +03:00
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function setHint($name, $value)
|
|
|
|
{
|
|
|
|
$this->_state = self::STATE_DIRTY;
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2010-02-25 18:47:20 +03:00
|
|
|
return parent::setHint($name, $value);
|
|
|
|
}
|
2011-11-18 20:29:31 +04:00
|
|
|
|
2010-02-25 18:47:20 +03:00
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function setHydrationMode($hydrationMode)
|
|
|
|
{
|
|
|
|
$this->_state = self::STATE_DIRTY;
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2010-02-25 18:47:20 +03:00
|
|
|
return parent::setHydrationMode($hydrationMode);
|
|
|
|
}
|
2010-02-10 22:09:25 +03:00
|
|
|
|
2010-04-09 00:50:06 +04:00
|
|
|
/**
|
|
|
|
* Set the lock mode for this Query.
|
|
|
|
*
|
2011-12-12 00:52:29 +04:00
|
|
|
* @see \Doctrine\DBAL\LockMode
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
2010-04-09 00:50:06 +04:00
|
|
|
* @param int $lockMode
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
2010-04-09 00:50:06 +04:00
|
|
|
* @return Query
|
2012-12-01 20:28:06 +04:00
|
|
|
*
|
|
|
|
* @throws TransactionRequiredException
|
2010-04-09 00:50:06 +04:00
|
|
|
*/
|
|
|
|
public function setLockMode($lockMode)
|
|
|
|
{
|
2014-02-01 21:05:47 +04:00
|
|
|
if (in_array($lockMode, array(LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE), true)) {
|
2011-12-01 19:00:26 +04:00
|
|
|
if ( ! $this->_em->getConnection()->isTransactionActive()) {
|
2010-04-09 00:50:06 +04:00
|
|
|
throw TransactionRequiredException::transactionRequired();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->setHint(self::HINT_LOCK_MODE, $lockMode);
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2010-04-09 00:50:06 +04:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current lock mode for this query.
|
|
|
|
*
|
2014-02-01 21:05:47 +04:00
|
|
|
* @return int|null The current lock mode of this query or NULL if no specific lock mode is set.
|
2010-04-09 00:50:06 +04:00
|
|
|
*/
|
|
|
|
public function getLockMode()
|
|
|
|
{
|
|
|
|
$lockMode = $this->getHint(self::HINT_LOCK_MODE);
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2014-02-01 21:05:47 +04:00
|
|
|
if (false === $lockMode) {
|
|
|
|
return null;
|
2010-04-09 00:50:06 +04:00
|
|
|
}
|
2011-12-01 19:00:26 +04:00
|
|
|
|
2010-04-09 00:50:06 +04:00
|
|
|
return $lockMode;
|
|
|
|
}
|
|
|
|
|
2010-02-10 22:09:25 +03:00
|
|
|
/**
|
|
|
|
* Generate a cache id for the query cache - reusing the Result-Cache-Id generator.
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
2015-03-24 18:04:20 +03:00
|
|
|
protected function _getQueryCacheId()
|
2010-02-10 22:09:25 +03:00
|
|
|
{
|
|
|
|
ksort($this->_hints);
|
|
|
|
|
2014-06-27 22:17:43 +04:00
|
|
|
$platform = $this->getEntityManager()
|
|
|
|
->getConnection()
|
|
|
|
->getDatabasePlatform()
|
|
|
|
->getName();
|
|
|
|
|
2010-02-10 22:09:25 +03:00
|
|
|
return md5(
|
2014-06-28 00:36:02 +04:00
|
|
|
$this->getDql() . serialize($this->_hints) .
|
|
|
|
'&platform=' . $platform .
|
2011-09-15 23:34:51 +04:00
|
|
|
($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') .
|
2010-02-14 00:42:09 +03:00
|
|
|
'&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults .
|
2015-03-24 18:04:20 +03:00
|
|
|
'&hydrationMode=' . $this->_hydrationMode . '&types=' . serialize($this->_parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT'
|
2010-02-10 22:09:25 +03:00
|
|
|
);
|
|
|
|
}
|
2010-08-30 22:30:11 +04:00
|
|
|
|
2013-02-14 02:42:13 +04:00
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
protected function getHash()
|
|
|
|
{
|
|
|
|
return sha1(parent::getHash(). '-'. $this->_firstResult . '-' . $this->_maxResults);
|
|
|
|
}
|
|
|
|
|
2010-08-30 22:30:11 +04:00
|
|
|
/**
|
|
|
|
* Cleanup Query resource when clone is called.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function __clone()
|
|
|
|
{
|
|
|
|
parent::__clone();
|
2011-12-20 01:56:19 +04:00
|
|
|
|
2010-08-30 22:30:11 +04:00
|
|
|
$this->_state = self::STATE_DIRTY;
|
|
|
|
}
|
2011-12-12 00:52:29 +04:00
|
|
|
}
|