/**
  * Updates counter cache for a single association
  *
  * @param \Cake\Event\Event $event Event instance.
  * @param \Cake\ORM\Entity $entity Entity
  * @param Association $assoc The association object
  * @param array $settings The settings for for counter cache for this association
  * @return void
  */
 protected function _processAssociation(Event $event, Entity $entity, Association $assoc, array $settings)
 {
     $foreignKeys = (array) $assoc->foreignKey();
     $primaryKeys = (array) $assoc->target()->primaryKey();
     $countConditions = $entity->extract($foreignKeys);
     $updateConditions = array_combine($primaryKeys, $countConditions);
     $countOriginalConditions = $entity->extractOriginal($foreignKeys);
     if ($countOriginalConditions !== []) {
         $updateOriginalConditions = array_combine($primaryKeys, $countOriginalConditions);
     }
     foreach ($settings as $field => $config) {
         if (is_int($field)) {
             $field = $config;
             $config = [];
         }
         if (!is_string($config) && is_callable($config)) {
             $count = $config($event, $entity, $this->_table, false);
         } else {
             $count = $this->_getCount($config, $countConditions);
         }
         $assoc->target()->updateAll([$field => $count], $updateConditions);
         if (isset($updateOriginalConditions)) {
             if (!is_string($config) && is_callable($config)) {
                 $count = $config($event, $entity, $this->_table, true);
             } else {
                 $count = $this->_getCount($config, $countOriginalConditions);
             }
             $assoc->target()->updateAll([$field => $count], $updateOriginalConditions);
         }
     }
 }
Example #2
0
 /**
  * Converts an entity class type to its DocBlock hint type counterpart.
  *
  * @param string $type The entity class type (a fully qualified class name).
  * @param \Cake\ORM\Association $association The association related to the entity class.
  * @return string The DocBlock type
  */
 public function associatedEntityTypeToHintType($type, Association $association)
 {
     if ($association->type() === Association::MANY_TO_MANY || $association->type() === Association::ONE_TO_MANY) {
         return $type . '[]';
     }
     return $type;
 }
 /**
  * Get finder to use for provided association.
  *
  * @param \Cake\ORM\Association $association Association instance
  * @return string
  */
 public function finder(Association $association)
 {
     if ($association->target()->behaviors()->has('Tree')) {
         return 'treeList';
     }
     return 'list';
 }
 /**
  * Method that fetches and deletes document-file manyToMany association record Entity.
  *
  * @param  string $id file id
  * @return bool
  */
 protected function _deleteFileAssociationRecord($id)
 {
     $query = $this->_fileStorageAssociation->find('all', ['conditions' => [$this->_fileStorageForeignKey => $id]]);
     $entity = $query->first();
     if (is_null($entity)) {
         return false;
     }
     return $this->_fileStorageAssociation->delete($entity);
 }
Example #5
0
 /**
  * Sets the name of the field representing the foreign key to the target table.
  * If no parameters are passed current field is returned
  *
  * @param string|null $key the key to be used to link both tables together
  * @return string
  */
 public function foreignKey($key = null)
 {
     if ($key === null) {
         if ($this->_foreignKey === null) {
             $this->_foreignKey = $this->_modelKey($this->target()->alias());
         }
         return $this->_foreignKey;
     }
     return parent::foreignKey($key);
 }
Example #6
0
 /**
  * Sets the property name that should be filled with data from the target table
  * in the source table record.
  * If no arguments are passed, currently configured type is returned.
  *
  * @param string|null $name The name of the property. Pass null to read the current value.
  * @return string
  */
 public function property($name = null)
 {
     if ($name !== null) {
         return parent::property($name);
     }
     if ($name === null && !$this->_propertyName) {
         list(, $name) = pluginSplit($this->_name);
         $this->_propertyName = Inflector::underscore(Inflector::singularize($name));
     }
     return $this->_propertyName;
 }
Example #7
0
 /**
  * Marshals data for belongsToMany associations.
  *
  * Builds the related entities and handles the special casing
  * for junction table entities.
  *
  * @param \Cake\ORM\Association $assoc The association to marshal.
  * @param array $data The data to convert into entities.
  * @param array $options List of options.
  * @return array An array of built entities.
  */
 protected function _belongsToMany(Association $assoc, array $data, $options = [])
 {
     $associated = isset($options['associated']) ? $options['associated'] : [];
     $forceNew = isset($options['forceNew']) ? $options['forceNew'] : false;
     $data = array_values($data);
     $target = $assoc->target();
     $primaryKey = array_flip((array) $target->primaryKey());
     $records = $conditions = [];
     $primaryCount = count($primaryKey);
     $conditions = [];
     foreach ($data as $i => $row) {
         if (!is_array($row)) {
             continue;
         }
         if (array_intersect_key($primaryKey, $row) === $primaryKey) {
             $keys = array_intersect_key($row, $primaryKey);
             if (count($keys) === $primaryCount) {
                 $rowConditions = [];
                 foreach ($keys as $key => $value) {
                     $rowConditions[][$target->aliasfield($key)] = $value;
                 }
                 if ($forceNew && !$target->exists($rowConditions)) {
                     $records[$i] = $this->one($row, $options);
                 }
                 $conditions = array_merge($conditions, $rowConditions);
             }
         } else {
             $records[$i] = $this->one($row, $options);
         }
     }
     if (!empty($conditions)) {
         $query = $target->find();
         $query->andWhere(function ($exp) use($conditions) {
             return $exp->or_($conditions);
         });
         $keyFields = array_keys($primaryKey);
         $existing = [];
         foreach ($query as $row) {
             $k = implode(';', $row->extract($keyFields));
             $existing[$k] = $row;
         }
         foreach ($data as $i => $row) {
             $key = [];
             foreach ($keyFields as $k) {
                 if (isset($row[$k])) {
                     $key[] = $row[$k];
                 }
             }
             $key = implode(';', $key);
             // Update existing record and child associations
             if (isset($existing[$key])) {
                 $records[$i] = $this->merge($existing[$key], $data[$i], $options);
             }
         }
     }
     $jointMarshaller = $assoc->junction()->marshaller();
     $nested = [];
     if (isset($associated['_joinData'])) {
         $nested = (array) $associated['_joinData'];
     }
     foreach ($records as $i => $record) {
         // Update junction table data in _joinData.
         if (isset($data[$i]['_joinData'])) {
             $joinData = $jointMarshaller->one($data[$i]['_joinData'], $nested);
             $record->set('_joinData', $joinData);
         }
     }
     return $records;
 }
 /**
  * Merge the special _joinData property into the entity set.
  *
  * @param \Cake\Datasource\EntityInterface $original The original entity
  * @param \Cake\ORM\Association $assoc The association to marshall
  * @param array $value The data to hydrate
  * @param array $options List of options.
  * @return array An array of entities
  */
 protected function _mergeJoinData($original, $assoc, $value, $options)
 {
     $associated = isset($options['associated']) ? $options['associated'] : [];
     $extra = [];
     foreach ($original as $entity) {
         // Mark joinData as accessible so we can marshal it properly.
         $entity->accessible('_joinData', true);
         $joinData = $entity->get('_joinData');
         if ($joinData && $joinData instanceof EntityInterface) {
             $extra[spl_object_hash($entity)] = $joinData;
         }
     }
     $joint = $assoc->junction();
     $marshaller = $joint->marshaller();
     $nested = [];
     if (isset($associated['_joinData'])) {
         $nested = (array) $associated['_joinData'];
     }
     $records = $this->mergeMany($original, $value, $options);
     foreach ($records as $record) {
         $hash = spl_object_hash($record);
         $value = $record->get('_joinData');
         if (!is_array($value)) {
             $record->unsetProperty('_joinData');
             continue;
         }
         if (isset($extra[$hash])) {
             $record->set('_joinData', $marshaller->merge($extra[$hash], $value, $nested));
         } else {
             $joinData = $marshaller->one($value, $nested);
             $record->set('_joinData', $joinData);
         }
     }
     return $records;
 }
Example #9
0
 /**
  * Loads a list of belongs to many from ids.
  *
  * @param Association $assoc The association class for the belongsToMany association.
  * @param array $ids The list of ids to load.
  * @return array An array of entities.
  */
 protected function _loadAssociatedByIds($assoc, $ids)
 {
     if (empty($ids)) {
         return [];
     }
     $target = $assoc->target();
     $primaryKey = (array) $target->primaryKey();
     $multi = count($primaryKey) > 1;
     $primaryKey = array_map(function ($key) use($target) {
         return $target->alias() . '.' . $key;
     }, $primaryKey);
     if ($multi) {
         if (count(current($ids)) !== count($primaryKey)) {
             return [];
         }
         $filter = new TupleComparison($primaryKey, $ids, [], 'IN');
     } else {
         $filter = [$primaryKey[0] . ' IN' => $ids];
     }
     return $target->find()->where($filter)->toArray();
 }
Example #10
0
 /**
  * Proxies the finding operation to the target table's find method
  * and modifies the query accordingly based of this association
  * configuration.
  *
  * If your association includes conditions, the junction table will be
  * included in the query's contained associations.
  *
  * @param string|array $type the type of query to perform, if an array is passed,
  *   it will be interpreted as the `$options` parameter
  * @param array $options The options to for the find
  * @see \Cake\ORM\Table::find()
  * @return \Cake\ORM\Query
  */
 public function find($type = null, array $options = [])
 {
     $query = parent::find($type, $options);
     if (!$this->conditions()) {
         return $query;
     }
     $belongsTo = $this->junction()->association($this->target()->alias());
     $conditions = $belongsTo->_joinCondition(['foreignKey' => $this->foreignKey()]);
     return $this->_appendJunctionJoin($query, $conditions);
 }
Example #11
0
 /**
  * Registers a table alias, typically loaded as a join in a query, as belonging to
  * an association. This helps hydrators know what to do with the columns coming
  * from such joined table.
  *
  * @param string $alias The table alias as it appears in the query.
  * @param \Cake\ORM\Association $assoc The association object the alias represents;
  * will be normalized
  * @param bool $asMatching Whether or not this join results should be treated as a
  * 'matching' association.
  * @param string $targetProperty The property name where the results of the join should be nested at.
  * If not passed, the default property for the association will be used.
  * @return void
  */
 public function addToJoinsMap($alias, Association $assoc, $asMatching = false, $targetProperty = null)
 {
     $this->_joinsMap[$alias] = new EagerLoadable($alias, ['aliasPath' => $alias, 'instance' => $assoc, 'canBeJoined' => true, 'forMatching' => $asMatching, 'targetProperty' => $targetProperty ?: $assoc->property()]);
 }
 /**
  * Method that retrieves one to many associated records
  * @param  \Cake\ORM\Table       $table       Table object
  * @param  \Cake\ORM\Association $association Association object
  * @return array                              associated records
  */
 protected function _oneToManyAssociatedRecords(\Cake\ORM\Table $table, \Cake\ORM\Association $association)
 {
     $assocName = $association->name();
     $assocTableName = $association->table();
     $assocForeignKey = $association->foreignKey();
     $recordId = $this->request->params['pass'][0];
     // get associated index View csv fields
     $fields = $this->_getTableFields($association);
     $query = $table->{$assocName}->find('all', ['conditions' => [$assocForeignKey => $recordId], 'fields' => $fields]);
     $records = $query->all();
     // store associated table records
     $result['records'] = $records;
     // store associated table fields
     $result['fields'] = $fields;
     // store associated table name
     $result['table_name'] = $assocTableName;
     return $result;
 }
Example #13
0
 /**
  * Alters a Query object to include the associated target table data in the final
  * result
  *
  * The options array accept the following keys:
  *
  * - includeFields: Whether to include target model fields in the result or not
  * - foreignKey: The name of the field to use as foreign key, if false none
  *   will be used
  * - conditions: array with a list of conditions to filter the join with
  * - fields: a list of fields in the target table to include in the result
  * - type: The type of join to be used (e.g. INNER)
  *
  * @param \Cake\ORM\Query $query the query to be altered to include the target table data
  * @param array $options Any extra options or overrides to be taken in account
  * @return void
  */
 public function attachTo(Query $query, array $options = [])
 {
     if (!empty($options['negateMatch'])) {
         $this->_appendNotMatching($query, $options);
         return;
     }
     $junction = $this->junction();
     $belongsTo = $junction->association($this->source()->alias());
     $cond = $belongsTo->_joinCondition(['foreignKey' => $belongsTo->foreignKey()]);
     $cond += $this->junctionConditions();
     if (isset($options['includeFields'])) {
         $includeFields = $options['includeFields'];
     }
     // Attach the junction table as well we need it to populate _joinData.
     $assoc = $this->_targetTable->association($junction->alias());
     $newOptions = array_intersect_key($options, ['joinType' => 1, 'fields' => 1]);
     $newOptions += ['conditions' => $cond, 'includeFields' => $includeFields, 'foreignKey' => false];
     $assoc->attachTo($query, $newOptions);
     $query->eagerLoader()->addToJoinsMap($junction->alias(), $assoc, true);
     parent::attachTo($query, $options);
     $foreignKey = $this->targetForeignKey();
     $thisJoin = $query->clause('join')[$this->name()];
     $thisJoin['conditions']->add($assoc->_joinCondition(['foreignKey' => $foreignKey]));
 }
Example #14
0
 /**
  * Loads a list of belongs to many from ids.
  *
  * @param Association $assoc The association class for the belongsToMany association.
  * @param array $ids The list of ids to load.
  * @return array An array of entities.
  */
 protected function _loadBelongsToMany($assoc, $ids)
 {
     $target = $assoc->target();
     $primaryKey = (array) $target->primaryKey();
     $multi = count($primaryKey) > 1;
     if ($multi) {
         if (count(current($ids)) !== count($primaryKey)) {
             return [];
         }
         $filter = new TupleComparison($primaryKey, $ids, [], 'IN');
     } else {
         $filter = [$primaryKey[0] . ' IN' => $ids];
     }
     return $assoc->find()->where($filter)->toArray();
 }
Example #15
0
 /**
  * Merge the special _joinData property into the entity set.
  *
  * @param \Cake\Datasource\EntityInterface $original The original entity
  * @param \Cake\ORM\Association $assoc The association to marshall
  * @param array $value The data to hydrate
  * @param array $options List of options.
  * @return array An array of entities
  */
 protected function _mergeJoinData($original, $assoc, $value, $options)
 {
     $associated = isset($options['associated']) ? $options['associated'] : [];
     $extra = [];
     foreach ($original as $entity) {
         // Mark joinData as accessible so we can marshal it properly.
         $entity->accessible('_joinData', true);
         $joinData = $entity->get('_joinData');
         if ($joinData && $joinData instanceof EntityInterface) {
             $extra[spl_object_hash($entity)] = $joinData;
         }
     }
     $joint = $assoc->junction();
     $marshaller = $joint->marshaller();
     $nested = [];
     if (isset($associated['_joinData'])) {
         $nested = (array) $associated['_joinData'];
     }
     $options['accessibleFields'] = ['_joinData' => true];
     $records = $this->mergeMany($original, $value, $options);
     foreach ($records as $record) {
         $hash = spl_object_hash($record);
         $value = $record->get('_joinData');
         // Already an entity, no further marshalling required.
         if ($value instanceof EntityInterface) {
             continue;
         }
         // Scalar data can't be handled
         if (!is_array($value)) {
             $record->unsetProperty('_joinData');
             continue;
         }
         // Marshal data into the old object, or make a new joinData object.
         if (isset($extra[$hash])) {
             $record->set('_joinData', $marshaller->merge($extra[$hash], $value, $nested));
         } elseif (is_array($value)) {
             $joinData = $marshaller->one($value, $nested);
             $record->set('_joinData', $joinData);
         }
     }
     return $records;
 }
Example #16
0
 /**
  * Helper method for saving an association's data.
  *
  * @param Association $association The association object to save with.
  * @param Entity $entity The entity to save
  * @param array $nested Options for deeper associations
  * @param array $options Original options
  * @return bool Success
  */
 protected function _save($association, $entity, $nested, $options)
 {
     if (!$entity->dirty($association->property())) {
         return true;
     }
     if (!empty($nested)) {
         $options = (array) $nested + $options;
     }
     return (bool) $association->save($entity, $options);
 }
Example #17
0
 /**
  * {@inheritDoc}
  */
 public function transformRow($row, $nestKey, $joined)
 {
     $alias = $this->junction()->alias();
     if ($joined) {
         $row[$this->target()->alias()][$this->_junctionProperty] = $row[$alias];
         unset($row[$alias]);
     }
     return parent::transformRow($row, $nestKey, $joined);
 }
 /**
  * Get association CSV fields
  * @param Cake\ORM\Associations $association ORM association
  * @param object $action action passed
  * @return array
  */
 protected function _getAssociationCsvFields(Association $association, $action)
 {
     list($plugin, $controller) = pluginSplit($association->className());
     $fields = $this->_getCsvFields($controller, $action);
     return $fields;
 }