/** * {@inheritdoc} */ public function count($column = self::DEFAULT_COUNTING_FIELD) { if ($column == self::DEFAULT_COUNTING_FIELD && !empty($this->loader->getPrimaryKey())) { $column = 'DISTINCT(' . $this->loader->getPrimaryKey() . ')'; } return parent::count($column); }
/** * {@inheritdoc} */ public function configureSelector(RecordSelector $selector) { if (empty($this->loaders) && empty($this->joiners)) { //No need to create any column aliases return; } parent::configureSelector($selector); }
/** * Execute query and every related query to compile records data in tree form - every relation * data will be included as sub key. * * Attention, Selector will cache compiled data tree and not query itself to keep data integrity * and to skip data compilation on second query. * * @return array */ public function fetchData() { //Pagination! $this->applyPagination(); //Generating statement $statement = $this->sqlStatement(); if (!empty($this->cacheLifetime)) { $cacheKey = $this->cacheKey ?: md5(serialize([$statement, $this->getParameters()])); if (empty($this->cacheStore)) { $this->cacheStore = $this->orm->container()->get(CacheInterface::class)->store(); } if ($this->cacheStore->has($cacheKey)) { $this->logger()->debug("Selector result were fetched from cache."); //We are going to store parsed result, not queries return $this->cacheStore->get($cacheKey); } } //We are bypassing run() method here to prevent query caching, we will prefer to cache //parsed data rather that database response $result = $this->database->query($statement, $this->getParameters()); //In many cases (too many inloads, too complex queries) parsing can take significant amount //of time, so we better profile it $benchmark = $this->benchmark('parseResult', $statement); //Here we are feeding selected data to our primary loaded to parse it and and create //data tree for our records $this->loader->parseResult($result, $rowsCount); $this->benchmark($benchmark); //Memory freeing $result->close(); //This must force loader to execute all post loaders (including ODM and etc) $this->loader->loadData(); //Now we can request our primary loader for compiled data $data = $this->loader->getResult(); //Memory free! Attention, it will not reset columns aliases but only make possible to run //query again $this->loader->clean(); if (!empty($this->cacheLifetime) && !empty($cacheKey)) { //We are caching full records tree, not queries $this->cacheStore->set($cacheKey, $data, $this->cacheLifetime); } return $data; }
/** * {@inheritdoc} */ public function createSelector() { if (empty($selector = parent::createSelector())) { return null; } if (empty($this->parent)) { //No need for where conditions return $selector; } //Mounting where conditions $this->mountConditions($selector); //Aggregated keys (example: all parent ids) if (empty($aggregatedKeys = $this->parent->aggregatedKeys($this->getReferenceKey()))) { //Nothing to postload, no parents return null; } //Adding condition $selector->where($this->getKey(RecordEntity::OUTER_KEY), 'IN', $aggregatedKeys); return $selector; }
/** * {@inheritdoc} * * We must parse pivot data. */ protected function fetchData(array $row) { $data = parent::fetchData($row); $data[ORM::PIVOT_DATA] = array_combine($this->pivotColumns, array_slice($row, $this->pivotOffset, count($this->pivotColumns))); return $data; }
/** * Parse single result row to generate data tree. Must pass parsing to every nested loader. * * @param array $row * @return bool */ private function parseRow(array $row) { if (!$this->isLoadable()) { //Nothing to parse, we are no waiting for any data return; } //Fetching only required part of resulted row $data = $this->fetchData($row); if (empty($this->parent)) { if ($this->deduplicate($data)) { //Yes, this is reference, i'm using this method to build data tree using nested parsers $this->result[] =& $data; //Registering references to simplify tree compilation for post and inner loaders $this->collectReferences($data); } $this->parseNested($row); return; } if (!($referenceCriteria = $this->fetchCriteria($data))) { //Relation not loaded return; } if ($this->deduplicate($data)) { //Registering references to simplify tree compilation for post and inner loaders $this->collectReferences($data); } //Mounting parsed data into parent under defined container $this->parent->mount($this->container, $this->getReferenceKey(), $referenceCriteria, $data, static::MULTIPLE); $this->parseNested($row); }
/** * Create delete statement based on WHERE statement provided by Selector. * * @param QueryCompiler $compiler * @return string */ protected function deleteStatement(QueryCompiler $compiler = null) { $compiler = !empty($compiler) ? $compiler : $this->compiler->reset(); $this->loader->configureSelector($this, false); return $compiler->delete($this->loader->getTable() . ' AS ' . $this->loader->getAlias(), $this->whereTokens); }