/** * Retrieves all virtual values of all the entities within the given result-set. * * @param \Cake\Collection\CollectionInterface $entities Set of entities * @param array $args Contains two keys: "options" and "primary" given to the * originating beforeFind(), and "selectedVirtual" a list of virtual columns * selected in the originating find query * @return array Virtual values indexed by entity ID */ protected function _prepareSetValues(CollectionInterface $entities, array $args) { $entityIds = $this->_toolbox->extractEntityIds($entities); if (empty($entityIds)) { return []; } $selectedVirtual = $args['selectedVirtual']; $bundle = $args['options']['bundle']; $validColumns = array_values($selectedVirtual); $validNames = array_intersect($this->_toolbox->getAttributeNames($bundle), $validColumns); $attrsById = []; foreach ($this->_toolbox->attributes($bundle) as $name => $attr) { if (in_array($name, $validNames)) { $attrsById[$attr['id']] = $attr; } } if (empty($attrsById)) { return []; } return TableRegistry::get('Eav.EavValues')->find('all')->bufferResults(false)->where(['EavValues.eav_attribute_id IN' => array_keys($attrsById), 'EavValues.entity_id IN' => $entityIds])->all()->map(function ($value) use($attrsById, $selectedVirtual) { $attrName = $attrsById[$value->get('eav_attribute_id')]->get('name'); $attrType = $attrsById[$value->get('eav_attribute_id')]->get('type'); $alias = array_search($attrName, $selectedVirtual); return ['entity_id' => $value->get('entity_id'), 'property_name' => is_string($alias) ? $alias : $attrName, 'value' => $this->_toolbox->marshal($value->get("value_{$attrType}"), $attrType)]; })->groupBy('entity_id')->toArray(); }
/** * {@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; }
/** * Analyzes the given unary expression and alters it according. * * @param \Cake\Database\Expression\UnaryExpression $expression Unary expression * @param string $bundle Consider attributes only for a specific bundle * @param \Cake\ORM\Query $query The query instance this expression comes from * @return \Cake\Database\Expression\UnaryExpression Scoped expression (or not) */ protected function _inspectUnaryExpression(UnaryExpression $expression, $bundle, Query $query) { $class = new \ReflectionClass($expression); $property = $class->getProperty('_value'); $property->setAccessible(true); $value = $property->getValue($expression); if ($value instanceof IdentifierExpression) { $field = $value->getIdentifier(); $column = is_string($field) ? $this->_toolbox->columnName($field) : ''; if (empty($column) || in_array($column, (array) $this->_table->schema()->columns()) || !in_array($column, $this->_toolbox->getAttributeNames($bundle)) || !$this->_toolbox->isSearchable($column)) { // nothing to alter return $expression; } $pk = $this->_tablePrimaryKey(); $driverClass = $this->_driverClass($query); switch ($driverClass) { case 'sqlite': $concat = implode(' || ', $pk); $field = "({$concat} || '')"; break; case 'mysql': case 'postgres': case 'sqlserver': default: $concat = implode(', ', $pk); $field = "CONCAT({$concat}, '')"; break; } $attr = $this->_toolbox->attributes($bundle)[$column]; $type = $this->_toolbox->getType($column); $subQuery = TableRegistry::get('Eav.EavValues')->find()->select("EavValues.value_{$type}")->where(['EavValues.entity_id' => $field, 'EavValues.eav_attribute_id' => $attr['id']])->sql(); $subQuery = str_replace([':c0', ':c1'], [$field, $attr['id']], $subQuery); $property->setValue($expression, "({$subQuery})"); } return $expression; }