public function beforeFind(Event $event, Query $query, $options, $primary) { if ($query->clause('limit') == 1) { return $query; } foreach ($this->orderBy() as $field => $ord) { $f = $this->aliasField($field); $query->order([$this->aliasField($field) => $ord]); } if (!is_array($this->primaryKey())) { return $query; } $query->sql(); // force evaluation of internal state/objects foreach ($query->clause('join') as $join) { if (!$this->association($join['table'])) { continue; } $table = TableRegistry::get($join['table']); $table->alias($join['alias']); foreach ($table->orderBy() as $field => $ord) { $query->order([$table->aliasField($field) => $ord]); } } return $query; }
public function beforeFind(Event $event, Query $query, \ArrayObject $options, $primary) { $order = $query->clause('order'); if ($order === null || !count($order)) { $query->order(['Viewers.name']); } }
public function beforeFind(Event $event, Query $query, \ArrayObject $options, $primary) { $order = $query->clause('order'); if ($order === null || !count($order)) { $query->order(['Films.released DESC', 'Films.title']); } }
/** * "Random" find method * @param Query $query Query object * @param array $options Options * @return Query Query object */ public function findRandom(Query $query, array $options) { $query->order('rand()'); if (!$query->clause('limit')) { $query->limit(1); } return $query; }
public function findCached(Query $query, array $options) { if ($conditions = $query->clause('where')) { $query->cache(function ($q) use($conditions) { return $this->table() . '-' . md5(serialize($conditions)); }); } return $query; }
/** * Add default order clause to query as necessary. * * @param \Cake\Event\Event $event Event * @param \Cake\ORM\Query $query Query * @param \ArrayObject $options Options * @param bool $primary Boolean indicating whether it's primary query. * @return void */ public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) { $orders = $this->_config['orders']; $args = [$query, $options, $primary]; foreach ($orders as $config) { if (!empty($config['callback']) && call_user_func_array($config['callback'], $args) || !$query->clause('order')) { $query->order($config['order']); break; } } }
/** * {@inheritDoc} * * Look for virtual columns in query's WHERE clause. * * @param \Cake\ORM\Query $query The query to scope * @param string|null $bundle Consider attributes only for a specific bundle * @return \Cake\ORM\Query The modified query object */ public function scope(Query $query, $bundle = null) { $whereClause = $query->clause('where'); if (!$whereClause) { return $query; } $whereClause->traverse(function (&$expression) use($bundle, $query) { if ($expression instanceof ExpressionInterface) { $expression = $this->_inspectExpression($expression, $bundle, $query); } }); return $query; }
public function beforeFind(Event $event, Query $query, $options, $primary) { $config = $this->config(); $tableAlias = $query->repository()->alias(); $founded = false; if ($where = $query->clause('where')) { $where->iterateParts(function ($w) use($config, $tableAlias, &$founded) { $field = is_object($w) ? $w->getField() : $w; if ($field == $tableAlias . '.' . $config['field'] || $field == $config['field']) { $founded = true; } return $w; }); } if (!$founded) { $query->where(['deleted' => 0]); } }
/** * {@inheritDoc} * * Look for virtual columns in query's WHERE clause. * * @param \Cake\ORM\Query $query The query to scope * @param string|null $bundle Consider attributes only for a specific bundle * @return \Cake\ORM\Query The modified query object */ public function scope(Query $query, $bundle = null) { $orderClause = $query->clause('order'); if (!$orderClause) { return $query; } $class = new \ReflectionClass($orderClause); $property = $class->getProperty('_conditions'); $property->setAccessible(true); $conditions = $property->getValue($orderClause); foreach ($conditions as $column => $direction) { if (empty($column) || in_array($column, (array) $this->_table->schema()->columns()) || !in_array($column, $this->_toolbox->getAttributeNames())) { continue; } $conditions['(' . $this->_subQuery($column, $bundle) . ')'] = $direction; unset($conditions[$column]); } $property->setValue($orderClause, $conditions); return $query; }
/** * Creates a map of row keys out of the query select clause that can be * used to hydrate nested result sets more quickly. * * @return void */ protected function _calculateColumnMap() { $map = []; foreach ($this->_query->clause('select') as $key => $field) { $key = trim($key, '"`[]'); if (strpos($key, '__') > 0) { $parts = explode('__', $key, 2); $map[$parts[0]][$key] = $parts[1]; } else { $map[$this->_defaultAlias][$key] = $key; } } foreach ($this->_matchingMap as $alias => $assoc) { if (!isset($map[$alias])) { continue; } $this->_matchingMapColumns[$alias] = $map[$alias]; unset($map[$alias]); } $this->_map = $map; }
/** * Traverse over a clause to alias fields * * The objective here is to transparently prevent ambiguous field errors by * prefixing fields with the appropriate table alias. This method currently * expects to receive a where clause only. * * @param \Cake\ORM\Query $query the query to check * @param string $name The clause name * @param array $config the config to use for adding fields * @return bool Whether a join to the translation table is required */ protected function _traverseClause(Query $query, $name = '', $config = []) { $clause = $query->clause($name); if (!$clause || !$clause->count()) { return false; } $alias = $config['hasOneAlias']; $fields = $this->_translationFields(); $mainTableAlias = $config['mainTableAlias']; $mainTableFields = $this->_mainFields(); $alias = $config['referenceName']; $joinRequired = false; $clause->traverse(function ($expression) use($fields, $alias, $mainTableAlias, $mainTableFields, &$joinRequired) { if (!$expression instanceof FieldInterface) { return; } $field = $expression->getField(); if (!$field || strpos($field, '.')) { return; } if (in_array($field, $fields)) { $joinRequired = true; $expression->setField("{$alias}.{$field}"); return; } if (in_array($field, $mainTableFields)) { $expression->setField("{$mainTableAlias}.{$field}"); } }); return $joinRequired; }
/** * Adds order value if not already set in query. * * @param \Cake\Event\Event $event The beforeFind event that was fired. * @param \Cake\ORM\Query $query The query object. * @param \ArrayObject $options The options passed to the find method. * * @return void */ public function beforeFind(Event $event, Query $query, ArrayObject $options) { if (!$query->clause('order')) { $query->order([$this->_table->alias() . '.' . $this->_config['order'] => 'ASC']); } }
/** * Tests that default fields for associations are added to the select clause when * none is specified * * @return void */ public function testContainToFieldsDefault() { $contains = ['clients' => ['orders']]; $query = new Query($this->connection, $this->table); $query->select()->contain($contains)->sql(); $select = $query->clause('select'); $expected = ['foo__id' => 'foo.id', 'clients__name' => 'clients.name', 'clients__id' => 'clients.id', 'clients__phone' => 'clients.phone', 'orders__id' => 'orders.id', 'orders__total' => 'orders.total', 'orders__placed' => 'orders.placed']; $expected = $this->_quoteArray($expected); $this->assertEquals($expected, $select); $contains['clients']['fields'] = ['name']; $query = new Query($this->connection, $this->table); $query->select('foo.id')->contain($contains)->sql(); $select = $query->clause('select'); $expected = ['foo__id' => 'foo.id', 'clients__name' => 'clients.name']; $expected = $this->_quoteArray($expected); $this->assertEquals($expected, $select); $contains['clients']['fields'] = []; $contains['clients']['orders']['fields'] = false; $query = new Query($this->connection, $this->table); $query->select()->contain($contains)->sql(); $select = $query->clause('select'); $expected = ['foo__id' => 'foo.id', 'clients__id' => 'clients.id', 'clients__name' => 'clients.name', 'clients__phone' => 'clients.phone']; $expected = $this->_quoteArray($expected); $this->assertEquals($expected, $select); }
/** * Gets a list of all virtual columns present in given $query's SELECT clause. * * This method will alter the given Query object removing any virtual column * present in its SELECT clause in order to avoid incorrect SQL statements. * Selected virtual columns should be fetched after query is executed using * mapReduce or similar. * * @param \Cake\ORM\Query $query The query object to be scoped * @param string|null $bundle Consider attributes only for a specific bundle * @return array List of virtual columns names */ public function getVirtualColumns(Query $query, $bundle = null) { static $selectedVirtual = []; $cacheKey = md5($query->sql()) . '_' . $bundle; if (isset($selectedVirtual[$cacheKey])) { return $selectedVirtual[$cacheKey]; } $selectClause = (array) $query->clause('select'); if (empty($selectClause)) { $selectedVirtual[$cacheKey] = array_keys($this->_toolbox->attributes($bundle)); return $selectedVirtual[$cacheKey]; } $selectedVirtual[$cacheKey] = []; $virtualColumns = array_keys($this->_toolbox->attributes($bundle)); foreach ($selectClause as $index => $column) { list($table, $column) = pluginSplit($column); if ((empty($table) || $table == $this->_table->alias()) && in_array($column, $virtualColumns)) { $selectedVirtual[$cacheKey][$index] = $column; unset($selectClause[$index]); } } if (empty($selectClause) && !empty($selectedVirtual[$cacheKey])) { $selectClause[] = $this->_table->primaryKey(); } $query->select($selectClause, true); return $selectedVirtual[$cacheKey]; }
/** * Helper function used to conditionally append fields to the select clause of * a query from the fields found in another query object. * * @param \Cake\ORM\Query $query the query that will get the fields appended to * @param \Cake\ORM\Query $surrogate the query having the fields to be copied from * @param array $options options passed to the method `attachTo` * @return void */ protected function _appendFields($query, $surrogate, $options) { $fields = $surrogate->clause('select') ?: $options['fields']; $target = $this->_targetTable; $autoFields = $surrogate->autoFields(); if ($query->eagerLoader()->autoFields() === false) { return; } if (empty($fields) && !$autoFields) { if ($options['includeFields'] && ($fields === null || $fields !== false)) { $fields = $target->schema()->columns(); } } if ($autoFields === true) { $fields = array_merge((array) $fields, $target->schema()->columns()); } if (!empty($fields)) { $query->select($query->aliasFields($fields, $target->alias())); } }
/** * Calculate the fields that need to participate in a subquery. * * Normally this includes the binding key columns. If there is a an ORDER BY, * those columns are also included as the fields may be calculated or constant values, * that need to be present to ensure the correct association data is loaded. * * @param \Cake\ORM\Query $query The query to get fields from. * @return array The list of fields for the subquery. */ protected function _subqueryFields($query) { $keys = (array) $this->bindingKey(); if ($this->type() === $this::MANY_TO_ONE) { $keys = (array) $this->foreignKey(); } $fields = $query->aliasFields($keys, $this->source()->alias()); $group = $fields = array_values($fields); $order = $query->clause('order'); if ($order) { $columns = $query->clause('select'); $order->iterateParts(function ($direction, $field) use(&$fields, $columns) { if (isset($columns[$field])) { $fields[$field] = $columns[$field]; } }); } return ['select' => $fields, 'group' => $group]; }
/** * Assertion method for select clause contents. * * @param array $expected Array of expected fields. * @param \Cake\ORM\Query $query The query to check. * @return void */ protected function assertSelectClause($expected, $query) { if ($this->autoQuote) { $connection = $query->connection(); foreach ($expected as $key => $value) { $expected[$connection->quoteIdentifier($key)] = $connection->quoteIdentifier($value); unset($expected[$key]); } } $this->assertEquals($expected, $query->clause('select')); }
/** * Helper function used to conditionally append fields to the select clause of * a query from the fields found in another query object. * * @param \Cake\ORM\Query $query the query that will get the fields appended to * @param \Cake\ORM\Query $surrogate the query having the fields to be copied from * @param array $options options passed to the method `attachTo` * @return void */ protected function _appendFields($query, $surrogate, $options) { $options['fields'] = $surrogate->clause('select') ?: $options['fields']; $target = $this->_targetTable; if (empty($options['fields'])) { $f = isset($options['fields']) ? $options['fields'] : null; if ($options['includeFields'] && ($f === null || $f !== false)) { $options['fields'] = $target->schema()->columns(); } } if (!empty($options['fields'])) { $query->select($query->aliasFields($options['fields'], $target->alias())); } }
/** * Sets the default ordering as 2.x shim. * * If you don't want that, don't call parent when overwriting it in extending classes. * * @param \Cake\Event\Event $event * @param \Cake\ORM\Query $query * @param array $options * @param bool $primary * @return \Cake\ORM\Query */ public function beforeFind(Event $event, Query $query, $options, $primary) { $order = $query->clause('order'); if (($order === null || !count($order)) && !empty($this->order)) { $query->order($this->order); } return $query; }
/** * Before Find * Transforma o valor de BRL para o formato SQL antes de executar uma query * com conditions. * * @param Event $event Evento reportado * @param Query $query Consulta a ser feita * @param array $options Opções da consulta * @return void * @access public */ public function beforeFind(Event $event, Query $query, $options = []) { $query->clause("where")->traverse([$this, "traverseClause"]); }
/** * @param Query $query * @return void */ public function encodeBitmaskConditions(Query $query) { $field = $this->_config['field']; if (!($mappedField = $this->_config['mappedField'])) { $mappedField = $field; } $where = $query->clause('where'); if (!$where) { return; } $callable = function ($foo) use($field, $mappedField) { if (!$foo instanceof \Cake\Database\Expression\Comparison) { return $foo; } $key = $foo->getField(); if ($key === $mappedField || $key === $this->_table->alias() . '.' . $mappedField) { $foo->setValue($this->encodeBitmask($foo->getValue())); } if ($field !== $mappedField) { $foo->setField($field); } return $foo; }; $where->iterateParts($callable); }
/** * Get the children nodes of the current model * * Available options are: * * - for: The id of the record to read. * - direct: Boolean, whether to return only the direct (true), or all (false) children, * defaults to false (all children). * * If the direct option is set to true, only the direct children are returned (based upon the parent_id field) * * @param \Cake\ORM\Query $query Query. * @param array $options Array of options as described above * @return \Cake\ORM\Query * @throws \InvalidArgumentException When the 'for' key is not passed in $options */ public function findChildren(Query $query, array $options) { $config = $this->config(); $options += ['for' => null, 'direct' => false]; list($parent, $left, $right) = array_map(function ($field) { return $this->_table->aliasField($field); }, [$config['parent'], $config['left'], $config['right']]); list($for, $direct) = [$options['for'], $options['direct']]; if (empty($for)) { throw new InvalidArgumentException("The 'for' key is required for find('children')"); } if ($query->clause('order') === null) { $query->order([$left => 'ASC']); } if ($direct) { return $this->_scope($query)->where([$parent => $for]); } $node = $this->_getNode($for); return $this->_scope($query)->where(["{$right} <" => $node->get($config['right']), "{$left} >" => $node->get($config['left'])]); }
/** * Callback method that listens to the `beforeFind` event in the bound * table. It modifies the passed query by eager loading the translated fields * and adding a formatter to copy the values into the main table records. * * @param \Cake\Event\Event $event The beforeFind event that was fired. * @param \Cake\ORM\Query $query Query * @param \ArrayObject $options The options for the query * @return void */ public function beforeFind(Event $event, Query $query, $options) { $locale = $this->locale(); if ($locale === $this->config('defaultLocale')) { return; } $conditions = function ($field, $locale, $query, $select) { return function ($q) use($field, $locale, $query, $select) { $q->where([$q->repository()->aliasField('locale') => $locale]); if ($query->autoFields() || in_array($field, $select, true) || in_array($this->_table->aliasField($field), $select, true)) { $q->select(['id', 'content']); } return $q; }; }; $contain = []; $fields = $this->_config['fields']; $alias = $this->_table->alias(); $select = $query->clause('select'); $changeFilter = isset($options['filterByCurrentLocale']) && $options['filterByCurrentLocale'] !== $this->_config['onlyTranslated']; foreach ($fields as $field) { $name = $alias . '_' . $field . '_translation'; $contain[$name]['queryBuilder'] = $conditions($field, $locale, $query, $select); if ($changeFilter) { $filter = $options['filterByCurrentLocale'] ? 'INNER' : 'LEFT'; $contain[$name]['joinType'] = $filter; } } $query->contain($contain); $query->formatResults(function ($results) use($locale) { return $this->_rowMapper($results, $locale); }, $query::PREPEND); }
/** * Appends any conditions required to load the relevant set of records in the * target table query given a filter key and some filtering values when the * filtering needs to be done using a subquery. * * @param \Cake\ORM\Query $query Target table's query * @param string $key the fields that should be used for filtering * @param \Cake\ORM\Query $subquery The Subquery to use for filtering * @return \Cake\ORM\Query */ public function _addFilteringJoin($query, $key, $subquery) { $filter = []; $aliasedTable = $this->source()->alias(); foreach ($subquery->clause('select') as $aliasedField => $field) { $filter[] = new IdentifierExpression($field); } $subquery->select($filter, true); if (is_array($key)) { $conditions = $this->_createTupleCondition($query, $key, $filter, '='); } else { $filter = current($filter); } $conditions = isset($conditions) ? $conditions : $query->newExpr([$key => $filter]); return $query->innerJoin([$aliasedTable => $subquery], $conditions); }
/** * Tests that first can be called again on an already executed query * * @return void */ public function testFirstCleanQuery() { $table = TableRegistry::get('articles', ['table' => 'articles']); $query = new Query($this->connection, $table); $query->select(['id'])->toArray(); $first = $query->hydrate(false)->first(); $this->assertEquals(['id' => 1], $first); $this->assertEquals(1, $query->clause('limit')); }
/** * Sets up a query object so results appear as an indexed array, useful for any * place where you would want a list such as for populating input select boxes. * * When calling this finder, the fields passed are used to determine what should * be used as the array key, value and optionally what to group the results by. * By default the primary key for the model is used for the key, and the display * field as value. * * The results of this finder will be in the following form: * * ``` * [ * 1 => 'value for id 1', * 2 => 'value for id 2', * 4 => 'value for id 4' * ] * ``` * * You can specify which property will be used as the key and which as value * by using the `$options` array, when not specified, it will use the results * of calling `primaryKey` and `displayField` respectively in this table: * * ``` * $table->find('list', [ * 'keyField' => 'name', * 'valueField' => 'age' * ]); * ``` * * Results can be put together in bigger groups when they share a property, you * can customize the property to use for grouping by setting `groupField`: * * ``` * $table->find('list', [ * 'groupField' => 'category_id', * ]); * ``` * * When using a `groupField` results will be returned in this format: * * ``` * [ * 'group_1' => [ * 1 => 'value for id 1', * 2 => 'value for id 2', * ] * 'group_2' => [ * 4 => 'value for id 4' * ] * ] * ``` * * @param \Cake\ORM\Query $query The query to find with * @param array $options The options for the find * @return \Cake\ORM\Query The query builder */ public function findList(Query $query, array $options) { $options += ['keyField' => $this->primaryKey(), 'valueField' => $this->displayField(), 'groupField' => null]; if (isset($options['idField'])) { $options['keyField'] = $options['idField']; unset($options['idField']); trigger_error('Option "idField" is deprecated, use "keyField" instead.', E_USER_WARNING); } if (!$query->clause('select') && !is_object($options['keyField']) && !is_object($options['valueField']) && !is_object($options['groupField'])) { $fields = array_merge((array) $options['keyField'], (array) $options['valueField'], (array) $options['groupField']); $columns = $this->schema()->columns(); if (count($fields) === count(array_intersect($fields, $columns))) { $query->select($fields); } } $options = $this->_setFieldMatchers($options, ['keyField', 'valueField', 'groupField']); return $query->formatResults(function ($results) use($options) { return $results->combine($options['keyField'], $options['valueField'], $options['groupField']); }); }