/**
  * 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);
         }
     }
 }
 /**
  * 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';
 }
Example #3
0
 /**
  * Loads a list of belongs to many from ids.
  *
  * @param \Cake\ORM\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([$target, 'aliasField'], $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 #4
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;
 }
Example #5
0
 /**
  * Create a new sub-marshaller and marshal the associated data.
  *
  * @param \Cake\ORM\Association $assoc The association to marshall
  * @param array $value The data to hydrate
  * @param array $options List of options.
  * @return mixed
  */
 protected function _marshalAssociation($assoc, $value, $options)
 {
     if (!is_array($value)) {
         return null;
     }
     $targetTable = $assoc->target();
     $marshaller = $targetTable->marshaller();
     $types = [Association::ONE_TO_ONE, Association::MANY_TO_ONE];
     if (in_array($assoc->type(), $types)) {
         return $marshaller->one($value, (array) $options);
     }
     if ($assoc->type() === Association::ONE_TO_MANY || $assoc->type() === Association::MANY_TO_MANY) {
         $hasIds = array_key_exists('_ids', $value);
         $onlyIds = array_key_exists('onlyIds', $options) && $options['onlyIds'];
         if ($hasIds && is_array($value['_ids'])) {
             return $this->_loadAssociatedByIds($assoc, $value['_ids']);
         }
         if ($hasIds || $onlyIds) {
             return [];
         }
     }
     if ($assoc->type() === Association::MANY_TO_MANY) {
         return $marshaller->_belongsToMany($assoc, $value, (array) $options);
     }
     return $marshaller->many($value, (array) $options);
 }
Example #6
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;
     $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();
 }