/** * Custom finder for map the ntofications. * * @param \Cake\ORM\Query $query The query finder. * @param array $options The options passed in the query builder. * * @return \Cake\ORM\Query */ public function findMap(Query $query, array $options) { return $query->formatResults(function ($notifications) use($options) { return $notifications->map(function ($notification) use($options) { $notification->data = unserialize($notification->data); switch ($notification->type) { case 'conversation.reply': $username = $notification->data['sender']->username; $conversationTitle = Text::truncate($notification->data['conversation']->title, 50, ['ellipsis' => '...', 'exact' => false]); //Check if the creator of the conversation is the current user. if ($notification->data['conversation']->user_id === $options['session']->read('Auth.User.id')) { $notification->text = __('<strong>{0}</strong> has replied in your conversation <strong>{1}</strong>.', h($username), h($conversationTitle)); } else { $notification->text = __('<strong>{0}</strong> has replied in the conversation <strong>{1}</strong>.', h($username), h($conversationTitle)); } $notification->link = Router::url(['controller' => 'conversations', 'action' => 'go', $notification->data['conversation']->last_message_id, 'prefix' => false]); break; case 'bot': $notification->text = __('Welcome on <strong>{0}</strong>! You can now post your first comment in the blog.', \Cake\Core\Configure::read('Site.name')); $notification->link = Router::url(['controller' => 'blog', 'action' => 'index', 'prefix' => false]); $notification->icon = $notification->data['icon']; break; case 'badge': $notification->text = __('You have unlock the badge "{0}".', $notification->data['badge']->name); $notification->link = Router::url(['_name' => 'users-profile', 'id' => $notification->data['user']->id, 'slug' => $notification->data['user']->username, '#' => 'badges', 'prefix' => false]); break; } return $notification; }); }); }
public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) { if (!array_key_exists('getRelated', $options) || !$options['getRelated']) { //Jen pokud se majà related stahovat return true; } $attachedTables = $this->_InRelatedIndexBehavior->getTablesWithBehaviorNames(); /** @var \Cake\ORM\Table $attachedTable */ foreach ($attachedTables as $tableName) { $modelName = Inflector::camelize($tableName); $query->contain(['Related' . $modelName => []]); } $query->formatResults(function ($results) { return $results->map(function ($row) { $temp = $row->toArray(); $related = []; foreach ($temp as $key => $item) { if (preg_match('/related-.*/', $key)) { foreach ($row->{$key} as $id => $similar) { $table_name = explode('-', $key); $row->{$key}[$id]->table_name = end($table_name); } $related = array_merge($related, $row->{$key}); unset($row->{$key}); } } $row->related = $related; return $row; }); }); return true; }
/** * Decode the fields on after find * * @param \Cake\Event\Event $event * @param \Cake\ORM\Query $query * @return void */ public function beforeFind(Event $event, Query $query) { $query->formatResults(function (ResultSetInterface $results) { return $results->map(function ($row) { $this->processItems($row, 'output'); return $row; }); }); }
/** * beforeFind, starts a timer for a find operation. * * @param \Cake\Event\Event $event The beforeFind event * @param \Cake\ORM\Query $query Query * @return bool true */ public function beforeFind(Event $event, $query) { $alias = $event->subject()->alias(); DebugTimer::start($alias . '_find', $alias . '->find()'); return $query->formatResults(function ($results) use($alias) { DebugTimer::stop($alias . '_find'); return $results; }); }
public function beforeFind(Event $event, Query $query) { $query->formatResults(function ($results) { return $results->map(function ($row) { if (!$row instanceof Entity) { return $row; } $this->transformState($row); return $row; }); }); }
public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) { if (!$query->hydrate()) { return; } $query->formatResults(function ($results) { return $results->map(function ($row) { $type = $row[$this->config('typeField')]; $entityClass = $this->_typeMap[$type]['entityClass']; return new $entityClass($row->forCopy(), ['markNew' => $row->isNew(), 'markClean' => true, 'guard' => false, 'source' => $this->_typeMap[$type]['alias']]); }); }); }
/** * Locks all the rows returned by the query. * This finder requires the `lockingUser` key in options and optionally the * `lockingSession`. * * @param Query $quert The Query to modify * @param array|ArrayObject $options The options containing the `lockingUser` key * @return Query */ public function findAutoLock(Query $query, $options) { $by = Hash::get($options, 'lockingUser'); $session = Hash::get($options, 'lockingSession'); return $query->formatResults(function ($results) use($by, $session) { $results->filter(function ($r) { return $r instanceof LockableInterface; })->each(function ($r) use($by, $session) { $r->lock($by, $session); $this->_table->save($r); }); return $results; }); }
/** * Attaches ContentType information to each content revision. * * @param \Cake\Event\Event $event The event that was triggered * @param \Cake\ORM\Query $query The query object * @param \ArrayObject $options Additional options given as an array * @param bool $primary Whether this find is a primary query or not * @return Query */ public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) { $query->formatResults(function ($results) { return $results->map(function ($revision) { try { if (isset($revision->data->content_type_id)) { $contentType = TableRegistry::get('Content.ContentTypes')->find()->where(['id' => $revision->data->content_type_id])->first(); $revision->data->set('content_type', $contentType); } } catch (\Exception $e) { $revision->data->set('content_type', false); } return $revision; }); }); return $query; }
/** * Attaches comments to each entity on find operation. * * @param \Cake\Event\Event $event The event that was triggered * @param \Cake\ORM\Query $query The query object * @param array $options Additional options as an array * @param bool $primary Whether is find is a primary query or not * @return void */ public function beforeFind(Event $event, $query, $options, $primary) { if ($this->_enabled && $query->count() > 0) { $pk = $this->_table->primaryKey(); $tableAlias = Inflector::underscore($this->_table->alias()); $query->contain(['Comments' => function ($query) { return $query->find('threaded')->contain(['Users'])->order($this->config('order')); }]); if ($this->config('count') || isset($options['comments_count']) && $options['comments_count'] === true) { $query->formatResults(function ($results) use($pk, $tableAlias) { return $results->map(function ($entity) use($pk, $tableAlias) { $entityId = $entity->{$pk}; $count = TableRegistry::get('Comment.Comments')->find()->where(['entity_id' => $entityId, 'table_alias' => $tableAlias])->count(); $entity->set('comments_count', $count); return $entity; }); }); } } }
/** * Custom finder to retrieve decrypted values. * * @param \Cake\ORM\Query $query Query. * @param array $options Options. * @return \Cake\ORM\Query */ public function findDecrypted(Query $query, array $options) { $options += ['fields' => []]; $mapper = function ($row) use($options) { $driver = $this->_table->connection()->driver(); foreach ($this->config('fields') as $field => $type) { if ($options['fields'] && !in_array($field, (array) $options['fields']) || !$row->has($field)) { continue; } $cipher = $row->get($field); $plain = $this->decrypt($cipher); $row->set($field, Type::build($type)->toPHP($plain, $driver)); } return $row; }; $formatter = function ($results) use($mapper) { return $results->map($mapper); }; return $query->formatResults($formatter); }
/** * Decrypt values after retrieving from DB * @param Event $event Event object * @param Query $query Query object * @param ArrayObject $options Query options array * @param type $primary Root/associated query * @return void */ public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) { $fields = $this->config('fields'); $driver = $this->_table->connection()->driver(); $formatter = function (\Cake\Collection\CollectionInterface $results) use($fields, $driver) { return $results->each(function ($entity) use($fields, $driver) { if ($entity instanceof \Cake\Datasource\EntityInterface) { foreach ($fields as $field => $type) { if ($entity->has($field)) { $value = $entity->get($field); $decryptedValue = $this->decrypt($value); // Convert DB values to PHP values after decrypting them $entity->set($field, Type::build($type)->toPHP($decryptedValue, $driver)); $entity->clean(); } } } }); }; $query->formatResults($formatter); }
/** * Adds a formatter function to the passed `$query` if the `$surrogate` query * declares any other formatter. Since the `$surrogate` query correspond to * the associated target table, the resulting formatter will be the result of * applying the surrogate formatters to only the property corresponding to * such table. * * @param \Cake\ORM\Query $query the query that will get the formatter applied to * @param \Cake\ORM\Query $surrogate the query having formatters for the associated * target table. * @param array $options options passed to the method `attachTo` * @return void */ protected function _formatAssociationResults($query, $surrogate, $options) { $formatters = $surrogate->formatResults(); if (!$formatters || empty($options['propertyPath'])) { return; } $property = $options['propertyPath']; $query->formatResults(function ($results) use($formatters, $property) { $extracted = $results->extract($property)->compile(); foreach ($formatters as $callable) { $extracted = new ResultSetDecorator($callable($extracted)); } return $results->insert($property, $extracted); }, Query::PREPEND); }
/** * Adds a formatter function to the passed `$query` if the `$surrogate` query * declares any other formatter. Since the `$surrogate` query correspond to * the associated target table, the resulting formatter will be the result of * applying the surrogate formatters to only the property corresponding to * such table. * * @param \Cake\ORM\Query $query the query that will get the formatter applied to * @param \Cake\ORM\Query $surrogate the query having formatters for the associated * target table. * @param array $options options passed to the method `attachTo` * @return void */ protected function _formatAssociationResults($query, $surrogate, $options) { $formatters = $surrogate->formatResults(); if (!$formatters || empty($options['propertyPath'])) { return; } $property = $options['propertyPath']; $propertyPath = explode('.', $property); $query->formatResults(function ($results) use($formatters, $property, $propertyPath) { $extracted = []; foreach ($results as $result) { foreach ($propertyPath as $propertyPathItem) { if (!isset($result[$propertyPathItem])) { $result = null; break; } $result = $result[$propertyPathItem]; } $extracted[] = $result; } $extracted = new Collection($extracted); foreach ($formatters as $callable) { $extracted = new ResultSetDecorator($callable($extracted)); } return $results->insert($property, $extracted); }, Query::PREPEND); }
/** * Custom finder for hashids field. * * Options: * - hid (required), best to use HashidBehavior::HID constant * - noFirst (optional, to leave the query open for adjustments, no first() called) * * @param \Cake\ORM\Query $query Query. * @param array $options Array of options as described above * @return \Cake\ORM\Query */ public function findHashed(Query $query, array $options) { $field = $this->_config['field']; if (!$field) { return $query; } $idField = $this->_primaryKey; $query->formatResults(function ($results) use($field, $idField) { $newResult = []; $results->each(function ($row, $key) use($field, $idField, &$newResult) { if (!empty($row[$idField])) { $row[$field] = $this->encodeId($row[$idField]); if ($row instanceof Entity) { $row->dirty($field, false); } $newResult[] = $row; } elseif (is_string($row)) { $newResult[$this->encodeId($key)] = $row; } }); return new Collection($newResult); }); if (!empty($options[static::HID])) { $id = $this->decodeHashid($options[static::HID]); $query->where([$idField => $id]); } $first = $this->_config['findFirst'] === true ? 'first' : $this->_config['findFirst']; if (!$first || !empty($options['noFirst'])) { return $query; } return $query->first(); }
/** * Results for this finder will be a nested array, and is appropriate if you want * to use the parent_id field of your model data to build nested results. * * Values belonging to a parent row based on their parent_id value will be * recursively nested inside the parent row values using the `children` property * * You can customize what fields are used for nesting results, by default the * primary key and the `parent_id` fields are used. If you you wish to change * these defaults you need to provide the keys `idField` or `parentField` in * `$options`: * * {{{ * $table->find('threaded', [ * 'idField' => 'id', * 'parentField' => 'ancestor_id' * ]); * }}} * * @param \Cake\ORM\Query $query * @param array $options * @return \Cake\ORM\Query */ public function findThreaded(Query $query, array $options) { $options += ['idField' => $this->primaryKey(), 'parentField' => 'parent_id']; $options = $this->_setFieldMatchers($options, ['idField', 'parentField']); return $query->formatResults(function ($results) use($options) { return $results->nest($options['idField'], $options['parentField']); }); }
/** * Formats entities to set hidden fields * * @param Event $event * @param Query $query * @return Query */ public function beforeFind(Event $event, Query $query) { if (!$this->_buildAuthorization()) { return $query; } /** * Apply field visibility and accessibility */ $notVisible = $this->_getNotVisibleFields(); $query->formatResults(function ($results) use($notVisible) { return $results->map(function ($row) use($notVisible) { if (isset($row) && $row instanceof Entity) { $row->hiddenProperties(array_merge($row->hiddenProperties(), $notVisible)); } return $row; }); }); /** * Apply finders */ $finders = array_diff($this->_getFinders(), $this->_getUnauthorizedFinders()); foreach ($finders as $finder) { $query->find($finder); } return $query; }
/** * Results for this finder will be a nested array, and is appropriate if you want * to use the parent_id field of your model data to build nested results. * * Values belonging to a parent row based on their parent_id value will be * recursively nested inside the parent row values using the `children` property * * You can customize what fields are used for nesting results, by default the * primary key and the `parent_id` fields are used. If you wish to change * these defaults you need to provide the keys `keyField` or `parentField` in * `$options`: * * ``` * $table->find('threaded', [ * 'keyField' => 'id', * 'parentField' => 'ancestor_id' * ]); * ``` * * @param \Cake\ORM\Query $query The query to find with * @param array $options The options to find with * @return \Cake\ORM\Query The query builder */ public function findThreaded(Query $query, array $options) { $options += ['keyField' => $this->primaryKey(), 'parentField' => 'parent_id']; if (isset($options['idField'])) { $options['keyField'] = $options['idField']; unset($options['idField']); trigger_error('Option "idField" is deprecated, use "keyField" instead.', E_USER_WARNING); } $options = $this->_setFieldMatchers($options, ['keyField', 'parentField']); return $query->formatResults(function ($results) use($options) { return $results->nest($options['keyField'], $options['parentField']); }); }
/** * Here we set default values for serializable columns. * * This method triggers the `<TableAlias>.<columnName>.defaultValues` event, for * example "Plugins.settings.defaultValues" for the "settings" columns of the * "Plugins" table. Event listeners should catch this event and provides the * desired values. * * ### Options: * * - flatten: Flattens serialized information into plain entity properties, for * example `settings:some_option` => `value`, where `settings` is the * serialized column and `some_option` a key of the serialized array value. * Valid only for column that stores array values. Example: * * Consider the following entity: * * ```php * object(Cake\Datasource\EntityInterface) { * 'settings' => [ * 'option_1' => 'Lorem ipsum', * 'option_2' => [1, 2, 3, 4], * 'option_3' => object, * ], * } * ``` * * Once `settings` column is flattened the entity will look as follow: * * ```php * object(Cake\Datasource\EntityInterface) { * 'settings' => [ * 'option_1' => 'Lorem ipsum', * 'option_2' => [1, 2, 3, 4], * 'option_3' => object, * ], * 'settings:option_1' => 'Lorem ipsum', * 'settings:option_2' => [1, 2, 3, 4], * 'settings:option_3' => object, * } * ``` * * @param \Cake\Event\Event $event The event that was triggered * @param \Cake\ORM\Query $query Query object * @param \ArrayObject $options Additional options as an array * @param bool $primary Whether is find is a primary query or not * @return void */ public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) { $query->formatResults(function ($results) use($options) { return $results->map(function ($entity) use($options) { if (!$entity instanceof EntityInterface) { return $entity; } foreach ($this->config('columns') as $column) { if ($entity->has($column)) { $eventName = $this->_table->alias() . ".{$column}.defaultValues"; $defaultValue = $this->_table->dispatchEvent($eventName, compact('entity'))->result; $currentValue = $entity->get($column); $newValue = $currentValue; if (is_array($currentValue) && is_array($defaultValue)) { $newValue = Hash::merge($defaultValue, $currentValue); } elseif (is_string($currentValue) && $currentValue === '') { $newValue = $defaultValue; } elseif (empty($currentValue) && !empty($defaultValue)) { $newValue = $defaultValue; } $entity->set($column, $newValue); if (!empty($options['flatten']) && is_array($entity->get($column))) { foreach ($entity->get($column) as $key => $value) { $entity->set("{$column}:{$key}", $value); } } } } return $entity; }); }); }
/** * Custom finder that obfuscates primary keys in returned result set. * * @param \Cake\ORM\Query $query Query. * @param array $options Options. * @return \Cake\ORM\Query */ public function findObfuscate(Query $query, array $options) { $query->applyOptions(['obfuscate' => true]); $query->formatResults(function ($results) { return $results->map(function ($row) { $pk = $this->_table->primaryKey(); $row[$pk] = $this->obfuscate($row[$pk]); return $row; }); }); return $query; }
/** * 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 * @return void */ public function beforeFind(Event $event, Query $query) { $locale = $this->locale(); if ($locale === $this->config('defaultLocale')) { return; } $conditions = function ($q) use($locale) { return $q->select(['id', 'content'])->where([$q->repository()->alias() . '.locale' => $locale]); }; $contain = []; $fields = $this->_config['fields']; $alias = $this->_table->alias(); foreach ($fields as $field) { $contain[$alias . '_' . $field . '_translation'] = $conditions; } $query->contain($contain); $query->formatResults(function ($results) use($locale) { return $this->_rowMapper($results, $locale); }, $query::PREPEND); }
/** * Test fetching results from a qurey with a two custom formatters * * @return void */ public function testQueryWithStackedFormatters() { $table = TableRegistry::get('authors'); $query = new Query($this->connection, $table); $query->select()->formatResults(function ($results) { $this->assertInstanceOf('Cake\\ORM\\ResultSet', $results); return $results->indexBy('id'); }); $query->formatResults(function ($results) { return $results->extract('name'); }); $expected = [1 => 'mariano', 2 => 'nate', 3 => 'larry', 4 => 'garrett']; $this->assertEquals($expected, $query->toArray()); }
/** * Here we set default values for each view mode if they were not defined before. * * @param \Cake\Event\Event $event The event that was triggered * @param \Cake\ORM\Query $query The query object * @param \ArrayObject $options Additional options given as an array * @param bool $primary Whether this find is a primary query or not * @return void */ public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) { $viewModes = $this->viewModes(); $query->formatResults(function ($results) use($viewModes) { return $results->map(function ($instance) use($viewModes) { if (!is_object($instance)) { return $instance; } foreach ($viewModes as $viewMode) { $instanceViewModes = $instance->view_modes; $viewModeDefaults = array_merge(['label_visibility' => 'above', 'shortcodes' => false, 'hidden' => false, 'ordering' => 0], (array) $instance->defaultViewModeSettings($viewMode)); if (!isset($instanceViewModes[$viewMode])) { $instanceViewModes[$viewMode] = []; } $instanceViewModes[$viewMode] = array_merge($viewModeDefaults, $instanceViewModes[$viewMode]); $instance->set('view_modes', $instanceViewModes); } $settingsDefaults = (array) $instance->defaultSettings(); if (!empty($settingsDefaults)) { $instanceSettings = $instance->get('settings'); foreach ($settingsDefaults as $k => $v) { if (!isset($instanceSettings[$k])) { $instanceSettings[$k] = $v; } } $instance->set('settings', $instanceSettings); } return $instance; }); }); }
/** * Formats query as a flat list where the keys are the primary key for the table * and the values are the display field for the table. Values are prefixed to visually * indicate relative depth in the tree. * * ### Options * * - keyPath: A dot separated path to the field that will be the result array key, or a closure to * return the key from the provided row. * - valuePath: A dot separated path to the field that is the array's value, or a closure to * return the value from the provided row. * - spacer: A string to be used as prefix for denoting the depth in the tree for each item. * * @param \Cake\ORM\Query $query The query object to format. * @param array $options Array of options as described above. * @return \Cake\ORM\Query Augmented query. */ public function formatTreeList(Query $query, array $options = []) { return $query->formatResults(function ($results) use($options) { $options += ['keyPath' => $this->_getPrimaryKey(), 'valuePath' => $this->_table->displayField(), 'spacer' => '_']; return $results->listNested()->printer($options['valuePath'], $options['keyPath'], $options['spacer']); }); }
public function findOfficialLanguageList(Query $query) { $query->formatResults(function ($results) { return $results->combine('name', 'official_language.language'); }); return $query->find('withOfficialLanguage'); }