/** * Return readable fields * * @param Model $model Model using this behavior * @return void */ private function __prepare(Model $model) { $this->UserAttribute = ClassRegistry::init('UserAttributes.UserAttribute'); $this->UserAttributesRole = ClassRegistry::init('UserRoles.UserAttributesRole'); if (!isset($this->__readableFields)) { $results = $this->UserAttributesRole->find('list', array('recursive' => -1, 'fields' => array('user_attribute_key', 'user_attribute_key'), 'conditions' => array('role_key' => AuthComponent::user('role_key'), 'other_readable' => true))); $this->__readableFields = array('id'); foreach ($results as $key => $field) { //Fieldのチェック if ($model->hasField($field)) { $this->__readableFields[$key] = $model->escapeField($field); } if ($model->UsersLanguage->hasField($field)) { $this->__readableFields[$key] = $model->UsersLanguage->escapeField($field); } //Field(is_xxxx_public)のチェック $fieldKey = sprintf(UserAttribute::PUBLIC_FIELD_FORMAT, $field); if ($model->hasField($fieldKey)) { $this->__readableFields[$fieldKey] = $model->escapeField($fieldKey); } //Field(xxxx_file_id)のチェック $fieldKey = sprintf(UserAttribute::FILE_FIELD_FORMAT, $field); if ($model->hasField($fieldKey)) { $this->__readableFields[$fieldKey] = $model->escapeField($fieldKey); } } } }
/** * Parses `joins` key and replaces it with parsed one which the datasource can work with * * @param Model $Model * @param array $query * @return array|bool */ public function beforeFind(Model $Model, $query) { $this->_query = $query; if (!empty($query['joins'])) { $joins = $query['joins']; $this->_query['fields'] = isset($query['fields']) ? (array) $query['fields'] : array($Model->escapeField('*')); unset($this->_query['joins']); $this->_parseJoins($Model, $joins); } return $this->_query; }
/** * Filter records based on period * * @return array Options passed to Model::find() */ public function beforeFind(Model $model, $query = array()) { $settings = $this->settings[$model->alias]; if (!$model->Behaviors->enabled('Publishable')) { return $query; } if ($settings['admin'] === false) { if (AuthComponent::user('role_id') == 1) { return $query; } } if (!$model->hasField($settings['fields']['publish_start']) || !$model->hasField($settings['fields']['publish_end'])) { return $query; } $date = isset($query['date']) ? $query['date'] : date('Y-m-d H:i:s'); $start = $model->escapeField($settings['fields']['publish_start']); $end = $model->escapeField($settings['fields']['publish_end']); if (is_string($query['conditions'])) { $query['conditions'] = (array) $query['conditions']; } $query['conditions'][] = array('OR' => array($start => null, array($start . ' <> ' => null, $start . ' <=' => $date))); $query['conditions'][] = array('OR' => array($end => null, array($end . ' <> ' => null, $end . ' >=' => $date))); return $query; }
/** * Hash condition when it contains a field specified in setting */ public function beforeFind(Model $Model, $queryData) { $setting = $this->settings[$Model->name]; $conditions =& $queryData['conditions']; foreach ((array) $setting['fields'] as $field) { $escapeField = $Model->escapeField($field); if (array_key_exists($field, (array) $conditions)) { $queryField = $field; } elseif (array_key_exists($escapeField, (array) $conditions)) { $queryField = $escapeField; } if (isset($queryField)) { $data = $conditions[$queryField]; $conditions[$queryField] = Security::hash($data, null, true); } } return $queryData; }
/** * Generic delete action * * Triggers the following callbacks * - beforeFind * - recordNotFound * - beforeDelete * - afterDelete * * @param string $id * @return void */ protected function _deleteAction($id = null) { if (empty($id)) { $id = $this->getIdFromRequest(); } $this->_validateId($id); if (!$this->_request->is('delete') && !($this->_request->is('post') && false === $this->config('secureDelete'))) { $subject = $this->_getSubject(compact('id')); $this->_setFlash('invalid_http_request.error'); return $this->_redirect($subject, $this->_controller->referer(array('action' => 'index'))); } $query = array(); $query['conditions'] = array($this->_model->escapeField() => $id); $findMethod = $this->_getFindMethod(null, 'count'); $subject = $this->trigger('beforeFind', compact('id', 'query', 'findMethod')); $query = $subject->query; $count = $this->_model->find($subject->findMethod, $query); if (empty($count)) { $subject = $this->trigger('recordNotFound', compact('id')); $this->_setFlash('find.error'); return $this->_redirect($subject, $this->_controller->referer(array('action' => 'index'))); } $subject = $this->trigger('beforeDelete', compact('id')); if ($subject->stopped) { $this->_setFlash('delete.error'); return $this->_redirect($subject, $this->_controller->referer(array('action' => 'index'))); } if ($this->_model->delete($id)) { $this->_setFlash('delete.success'); $subject = $this->trigger('afterDelete', array('id' => $id, 'success' => true)); } else { $this->_setFlash('delete.error'); $subject = $this->trigger('afterDelete', array('id' => $id, 'success' => false)); } return $this->_redirect($subject, $this->_controller->referer(array('action' => 'index'))); }
/** * Quotes and prepares fields and values for an SQL UPDATE statement * * @param Model $Model The model to prepare fields for. * @param array $fields The fields to update. * @param bool $quoteValues If values should be quoted, or treated as SQL snippets * @param bool $alias Include the model alias in the field name * @return array Fields and values, quoted and prepared */ protected function _prepareUpdateFields(Model $Model, $fields, $quoteValues = true, $alias = false) { $quotedAlias = $this->startQuote . $Model->alias . $this->endQuote; $schema = $Model->schema(); $updates = array(); foreach ($fields as $field => $value) { if ($alias && strpos($field, '.') === false) { $quoted = $Model->escapeField($field); } elseif (!$alias && strpos($field, '.') !== false) { $quoted = $this->name(str_replace($quotedAlias . '.', '', str_replace($Model->alias . '.', '', $field))); } else { $quoted = $this->name($field); } if ($value === null) { $updates[] = $quoted . ' = NULL'; continue; } $update = $quoted . ' = '; if ($quoteValues) { $update .= $this->value($value, $Model->getColumnType($field), isset($schema[$field]) ? $schema[$field]['null'] : true); } elseif ($Model->getColumnType($field) === 'boolean' && (is_int($value) || is_bool($value))) { $update .= $this->boolean($value, true); } elseif (!$alias) { $update .= str_replace($quotedAlias . '.', '', str_replace($Model->alias . '.', '', $value)); } else { $update .= $value; } $updates[] = $update; } return $updates; }
/** * get the minimum index value in the table. * * @param Model $Model * @param string $scope * @param string $left * @param integer $recursive * @return integer */ protected function _getMin(Model $Model, $scope, $left, $recursive = -1) { $db = ConnectionManager::getDataSource($Model->useDbConfig); $name = $Model->escapeField($left); list($edge) = array_values($Model->find('first', array('conditions' => $scope, 'fields' => $db->calculate($Model, 'min', array($name, $left)), 'recursive' => $recursive, 'callbacks' => false))); return empty($edge[$left]) ? 0 : $edge[$left]; }
/** * Updates the computed cache of belongsTo associations after a save or delete operation. * * @param Model $Model * @param array $keys Optional foreign key data, defaults to the information `$this->data`. * @param boolean $created True if a new record was created, otherwise only associations with * 'computedScope' defined get updated * @return void */ public function updateComputedCache(Model $Model, $keys = array(), $created = false) { $keys = empty($keys) ? $Model->data[$Model->alias] : $keys; $keys['old'] = isset($keys['old']) ? $keys['old'] : array(); foreach ($Model->belongsTo as $parent => $assoc) { if (empty($assoc['computedCache'])) { continue; } if (!is_array($assoc['computedCache'])) { if (isset($assoc['computedScope'])) { $assoc['computedCache'] = array($assoc['computedCache'] => $assoc['computedScope']); } else { $assoc['computedCache'] = array($assoc['computedCache'] => array()); } } $foreignKey = $assoc['foreignKey']; $fkQuoted = $Model->escapeField($assoc['foreignKey']); foreach ($assoc['computedCache'] as $field => $conditions) { if (!is_string($field)) { $field = sprintf('%s_%s_computed', Inflector::underscore($Model->alias), $this->_methods[strtolower($this->settings[$Model->alias]['method'])]); } if (!$Model->{$parent}->hasField($field)) { continue; } if ($conditions === true) { $conditions = array(); } else { $conditions = (array) $conditions; } if (!array_key_exists($foreignKey, $keys)) { $keys[$foreignKey] = $Model->field($foreignKey); } $recursive = empty($conditions) ? -1 : 0; $computeProp = sprintf('last%sComputation', Inflector::classify($field)); if (isset($keys['old'][$foreignKey])) { if ($keys['old'][$foreignKey] != $keys[$foreignKey]) { $conditions[$fkQuoted] = $keys['old'][$foreignKey]; $Model->{$computeProp} = array_pop(array_pop(array_pop($Model->find('computed', compact('conditions', 'recursive'))))); $Model->{$parent}->updateAll(array($field => $Model->{$computeProp}), array($Model->{$parent}->escapeField() => $keys['old'][$foreignKey])); } } $conditions[$fkQuoted] = $keys[$foreignKey]; if ($recursive === 0) { $conditions = array_merge($conditions, (array) $conditions); } $Model->{$computeProp} = current(current(current($Model->find('computed', compact('conditions', 'recursive'))))); $Model->{$parent}->updateAll(array($field => $Model->{$computeProp}), array($Model->{$parent}->escapeField() => $keys[$foreignKey])); } } }
/** * Quotes and prepares fields and values for an SQL UPDATE statement * * @param Model $model * @param array $fields * @param boolean $quoteValues If values should be quoted, or treated as SQL snippets * @param boolean $alias Include the model alias in the field name * @return array Fields and values, quoted and preparted * @access protected */ function _prepareUpdateFields(&$model, $fields, $quoteValues = true, $alias = false) { $quotedAlias = $this->startQuote . $model->alias . $this->endQuote; $updates = array(); foreach ($fields as $field => $value) { if ($alias && strpos($field, '.') === false) { $quoted = $model->escapeField($field); } elseif (!$alias && strpos($field, '.') !== false) { $quoted = $this->name(str_replace($quotedAlias . '.', '', str_replace($model->alias . '.', '', $field))); } else { $quoted = $this->name($field); } if ($value === null) { $updates[] = $quoted . ' = NULL'; continue; } $update = $quoted . ' = '; if ($quoteValues) { $update .= $this->value($value, $model->getColumnType($field), false); } elseif (!$alias) { $update .= str_replace($quotedAlias . '.', '', str_replace($model->alias . '.', '', $value)); } else { $update .= $value; } $updates[] = $update; } return $updates; }
/** * Generates and executes an SQL UPDATE statement for given model, fields, and values. * * @param Model $model * @param array $fields * @param array $values * @param mixed $conditions * @return array */ function update(&$model, $fields = array(), $values = null, $conditions = null) { $updates = array(); if ($values == null) { $combined = $fields; } else { $combined = array_combine($fields, $values); } foreach ($combined as $field => $value) { if ($value === null) { $updates[] = $model->escapeField($field) . ' = NULL'; } else { $update = $model->escapeField($field) . ' = '; if ($conditions == null) { $update .= $this->value($value, $model->getColumnType($field)); } else { $update .= $value; } $updates[] = $update; } } $conditions = $this->defaultConditions($model, $conditions); if ($conditions === false) { return false; } $fields = join(', ', $updates); $table = $this->fullTableName($model); $conditions = $this->conditions($conditions); $alias = $this->name($model->alias); $joins = implode(' ', $this->_getJoins($model)); if (!$this->execute($this->renderStatement('update', compact('table', 'alias', 'joins', 'fields', 'conditions')))) { $model->onError(); return false; } return true; }
/** * Bulk Delete * * @param Model $model Model object * @param array $ids Array of IDs * @return boolean True on success, false on failure */ public function bulkDelete(Model $model, $ids) { return $model->deleteAll(array($model->escapeField() => $ids), true, true); }
/** * Cascades model deletes through associated hasMany and hasOne child records. * * @see CakePHP 2.1.3 lib/Cake/Model/Model.php * @param string $id ID of record that was deleted * @param boolean $cascade Set to true to delete records that depend on this record * @return void */ protected function _deleteDependent(Model $model, $id, $cascade) { if (!empty($model->__backAssociation)) { $savedAssociatons = $model->__backAssociation; $model->__backAssociation = array(); } if ($cascade === true) { foreach (array_merge($model->hasMany, $model->hasOne) as $assoc => $data) { if ($data['dependent'] === true) { $dependentModel = $model->{$assoc}; if ($data['foreignKey'] === false && $data['conditions'] && in_array($model->name, $dependentModel->getAssociated('belongsTo'))) { $dependentModel->recursive = 0; $conditions = array($model->escapeField(null, $model->name) => $id); } else { $dependentModel->recursive = -1; $conditions = array($dependentModel->escapeField($data['foreignKey']) => $id); if ($data['conditions']) { $conditions = array_merge((array) $data['conditions'], $conditions); } } if (isset($data['exclusive']) && $data['exclusive']) { $dependentModel->deleteAll($conditions); } else { $records = $dependentModel->find('all', array('conditions' => $conditions, 'fields' => $dependentModel->primaryKey)); if (!empty($records)) { foreach ($records as $record) { $dependentModel->delete($record[$dependentModel->alias][$dependentModel->primaryKey]); } } } } } } if (isset($savedAssociatons)) { $model->__backAssociation = $savedAssociatons; } }
/** * Remove the current node from the tree, and reparent all children up one level. * * If the parameter delete is false, the node will become a new top level node. Otherwise the node will be deleted * after the children are reparented. * * @param Model $Model Model instance * @param integer|string $id The ID of the record to remove * @param boolean $delete whether to delete the node after reparenting children (if any) * * @return boolean true on success, false on failure * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::removeFromTree */ public function removeFromTree(Model $Model, $id = null, $delete = false) { if (is_array($id)) { extract(array_merge(array('id' => null), $id)); } extract($this->settings[$Model->alias]); list($node) = array_values($Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $id), 'fields' => array($Model->primaryKey, $left, $right, $parent), 'recursive' => $recursive))); if ($node[$right] == $node[$left] + 1) { if ($delete) { return $Model->delete($id); } $Model->id = $id; return $Model->saveField($parent, null); } elseif ($node[$parent]) { list($parentNode) = array_values($Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $node[$parent]), 'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive))); } else { $parentNode[$right] = $node[$right] + 1; } $db = ConnectionManager::getDataSource($Model->useDbConfig); $Model->updateAll(array($parent => $db->value($node[$parent], $parent)), array($Model->escapeField($parent) => $node[$Model->primaryKey])); $this->_sync($Model, 1, '-', 'BETWEEN ' . ($node[$left] + 1) . ' AND ' . ($node[$right] - 1)); $this->_sync($Model, 2, '-', '> ' . $node[$right]); $Model->id = $id; if ($delete) { $Model->updateAll(array($Model->escapeField($left) => 0, $Model->escapeField($right) => 0, $Model->escapeField($parent) => null), array($Model->escapeField() => $id)); return $Model->delete($id); } $edge = $this->_getMax($Model, $scope, $right, $recursive); if ($node[$right] == $edge) { $edge = $edge - 2; } $Model->id = $id; return $Model->save(array($left => $edge + 1, $right => $edge + 2, $parent => null), array('callbacks' => false, 'validate' => false)); }
/** * Sets the parent of the given node * * The force parameter is used to override the "don't change the parent to the current parent" logic in the event * of recovering a corrupted table, or creating new nodes. Otherwise it should always be false. In reality this * method could be private, since calling save with parent_id set also calls setParent * * @param Model $Model Model instance * @param mixed $parentId * @param boolean $created * @return boolean true on success, false on failure */ protected function _setParent($Model, $parentId = null, $created = false) { extract($this->settings[$Model->alias]); list($node) = array_values($Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $Model->id), 'fields' => array($Model->primaryKey, $parent, $left, $right), 'recursive' => $recursive))); $edge = $this->_getMax($Model, $scope, $right, $recursive, $created); if (empty($parentId)) { $this->_sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created); $this->_sync($Model, $node[$right] - $node[$left] + 1, '-', '> ' . $node[$left], $created); } else { $values = $Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $parentId), 'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive)); if ($values === false) { return false; } $parentNode = array_values($values); if (empty($parentNode) || empty($parentNode[0])) { return false; } $parentNode = $parentNode[0]; if ($Model->id == $parentId) { return false; } elseif ($node[$left] < $parentNode[$left] && $parentNode[$right] < $node[$right]) { return false; } if (empty($node[$left]) && empty($node[$right])) { $this->_sync($Model, 2, '+', '>= ' . $parentNode[$right], $created); $result = $Model->save(array($left => $parentNode[$right], $right => $parentNode[$right] + 1, $parent => $parentId), array('validate' => false, 'callbacks' => false)); $Model->data = $result; } else { $this->_sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created); $diff = $node[$right] - $node[$left] + 1; if ($node[$left] > $parentNode[$left]) { if ($node[$right] < $parentNode[$right]) { $this->_sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created); $this->_sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created); } else { $this->_sync($Model, $diff, '+', 'BETWEEN ' . $parentNode[$right] . ' AND ' . $node[$right], $created); $this->_sync($Model, $edge - $parentNode[$right] + 1, '-', '> ' . $edge, $created); } } else { $this->_sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created); $this->_sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created); } } } return true; }
/** * Transform TaxonomyData array to a format that can be used for save operation * * @param array $data Array containing relevant Taxonomy data * @param string $typeAlias string Node type alias * @return array Formatted data * @throws InvalidArgumentException */ public function formatTaxonomyData(Model $model, &$data, $typeAlias) { $type = $model->Taxonomy->Vocabulary->Type->findByAlias($typeAlias); if (empty($type)) { throw new InvalidArgumentException(__d('croogo', 'Invalid Content Type')); } if (empty($data[$model->alias]['type'])) { $data[$model->alias]['type'] = $typeAlias; } $model->type = $type['Type']['alias']; if (!$model->Behaviors->enabled('Tree')) { $model->Behaviors->attach('Tree', array('scope' => array($model->escapeField('type') => $model->type))); } if (array_key_exists('TaxonomyData', $data)) { $foreignKey = $model->id; if (isset($data[$model->alias][$model->primaryKey])) { $foreignKey = $data[$model->alias][$model->primaryKey]; } $data['Taxonomy'] = array(); foreach ($data['TaxonomyData'] as $vocabularyId => $taxonomyIds) { if (empty($taxonomyIds)) { continue; } foreach ((array) $taxonomyIds as $taxonomyId) { $join = array('model' => $model->alias, 'foreign_key' => $foreignKey, 'taxonomy_id' => $taxonomyId); $data['Taxonomy'][] = $join; } } unset($data['TaxonomyData']); } $this->cacheTerms($model, $data); }
/** * Appends a join for translated fields. * * @param Model $Model The model being worked on. * @param array $query The query array to append a join to. * @param string $field The field name being joined. * @param string $aliasField The aliased field name being joined. * @param string|array $locale The locale(s) having joins added. * @return array The modified query */ protected function _addJoin(Model $Model, $query, $field, $aliasField, $locale) { $db = ConnectionManager::getDataSource($Model->useDbConfig); $RuntimeModel = $this->_runtimeModel; $joinTable = $this->_joinTable; $aliasVirtual = "i18n_{$field}"; $alias = "I18n__{$field}"; if (is_array($locale)) { foreach ($locale as $_locale) { $aliasVirtualLocale = "{$aliasVirtual}_{$_locale}"; $aliasLocale = "{$alias}__{$_locale}"; $Model->virtualFields[$aliasVirtualLocale] = "{$aliasLocale}.content"; if (!empty($query['fields']) && is_array($query['fields'])) { $query['fields'][] = $aliasVirtualLocale; } $query['joins'][] = array('type' => 'LEFT', 'alias' => $aliasLocale, 'table' => $joinTable, 'conditions' => array($Model->escapeField() => $db->identifier("{$aliasLocale}.foreign_key"), "{$aliasLocale}.model" => $Model->name, "{$aliasLocale}.{$RuntimeModel->displayField}" => $aliasField, "{$aliasLocale}.locale" => $_locale)); } } else { $Model->virtualFields[$aliasVirtual] = "{$alias}.content"; if (!empty($query['fields']) && is_array($query['fields'])) { $query['fields'][] = $aliasVirtual; } $query['joins'][] = array('type' => 'INNER', 'alias' => $alias, 'table' => $joinTable, 'conditions' => array("{$Model->alias}.{$Model->primaryKey}" => $db->identifier("{$alias}.foreign_key"), "{$alias}.model" => $Model->name, "{$alias}.{$RuntimeModel->displayField}" => $aliasField, "{$alias}.locale" => array($locale, Configure::read('webConfig.default_language')))); } return $query; }
/** * When doing any update all calls, you want to avoid updating the record * you've just modified, as the order will have been set already, so exclude * it with some conditions. * * @param Model $model Model object that method is triggered on * @return array Array Model.primary_key <> => $id */ protected function _conditionsNotCurrent(&$model) { return array($model->escapeField($model->primaryKey) . ' <>' => $model->id); }
/** * Perform this check after parseConditions has completed, since parseConditions is recursive * we have to perform this check in a separate method (or use a static variable or whatever, but, * I think this is cleaner) * * @param string $Model * @param array $filters * @return array * @author David Kullmann */ public function afterParseConditions(Model $Model, $filters = array()) { if (count($filters) > 1) { $filters = array('and' => $filters); } elseif (!empty($filters[0])) { $filters = $filters[0]; $fields = array('id', $Model->escapeField()); foreach ($fields as $field) { if (!empty($filters['term'][$field]) && count($filters['term'][$field]) === 1) { return $filters['term'][$field][0]; } } } return $filters; }
/** * Runs through to update the master key for deep copying. * * @param Model $Model * @return array */ protected function _updateMasterKey(Model $Model) { $record = $Model->find('first', array('conditions' => array($Model->escapeField() => $Model->id), 'contain' => $this->contain)); $record = $this->_masterKeyLoop($Model, $record, $Model->id); return $record; }