Пример #1
0
 /**
  * Perform the delete operation.
  *
  * Will soft delete the entity provided. Will remove rows from any
  * dependent associations, and clear out join tables for BelongsToMany associations.
  *
  * @param \Cake\DataSource\EntityInterface $entity The entity to soft delete.
  * @param \ArrayObject $options The options for the delete.
  * @throws \InvalidArgumentException if there are no primary key values of the
  * passed entity
  * @return bool success
  */
 protected function _processDelete($entity, $options)
 {
     if ($entity->isNew()) {
         return false;
     }
     $primaryKey = (array) $this->primaryKey();
     if (!$entity->has($primaryKey)) {
         $msg = 'Deleting requires all primary key values.';
         throw new \InvalidArgumentException($msg);
     }
     if ($options['checkRules'] && !$this->checkRules($entity, RulesChecker::DELETE, $options)) {
         return false;
     }
     $event = $this->dispatchEvent('Model.beforeDelete', ['entity' => $entity, 'options' => $options]);
     if ($event->isStopped()) {
         return $event->result;
     }
     $this->_associations->cascadeDelete($entity, ['_primary' => false] + $options->getArrayCopy());
     $query = $this->query();
     $conditions = (array) $entity->extract($primaryKey);
     $statement = $query->update()->set([$this->getSoftDeleteField() => 0])->where($conditions)->execute();
     $success = $statement->rowCount() > 0;
     if (!$success) {
         return $success;
     }
     $this->dispatchEvent('Model.afterDelete', ['entity' => $entity, 'options' => $options]);
     return $success;
 }
Пример #2
0
 public function beforeSave(Event $event, EntityInterface $entity)
 {
     if ($entity === null) {
         return true;
     }
     $isNew = $entity->isNew();
     $fields = $this->config('fields');
     $ip = self::$_request->clientIp();
     foreach ($fields as $field => $when) {
         $when = strtolower($when);
         if (!in_array($when, ['always', 'new'])) {
             throw new UnexpectedValueException(sprintf('"When" should be one of "always", "new". The passed value "%s" is invalid', $when));
         }
         switch ($when) {
             case 'always':
                 $entity->set($field, $ip);
                 continue;
                 break;
             case 'new':
                 if ($isNew) {
                     $entity->set($field, $ip);
                     continue;
                 }
                 break;
         }
     }
     return true;
 }
Пример #3
0
 public function beforeSave(Event $event, EntityInterface $entity)
 {
     $config = $this->config();
     $new = $entity->isNew();
     if ($config['when'] === 'always' || $config['when'] === 'new' && $new || $config['when'] === 'existing' && !$new) {
         $this->slug($entity);
     }
 }
 /**
  * afterSave callback
  *
  * Does not call the parent to avoid that the regular file storage event listener saves the image already
  *
  * @param \Cake\Event\Event $event
  * @param \Cake\Datasource\EntityInterface $entity
  * @param array $options
  * @return boolean
  */
 public function afterSave(Event $event, EntityInterface $entity, $options)
 {
     if ($entity->isNew()) {
         $this->dispatchEvent('ImageStorage.afterSave', ['record' => $entity, 'storage' => $this->storageAdapter($entity->get('adapter'))]);
         $this->deleteOldFileOnSave($entity);
     }
     return true;
 }
 /**
  * Generates IDs for an entity before it is saved to the database.
  *
  * @param Event $event Instance of save event
  * @param EntityInterface $entity Entity being saved
  */
 public function beforeSave(Event $event, EntityInterface $entity)
 {
     // Check if entity is being created in database
     // If so, update appropriate ID fields if present
     if ($entity->isNew()) {
         $entity->set($this->config('base64.field'), $this->generateBase64Id());
         $entity->set($this->config('uuid.field'), $this->generateUuid());
     }
 }
 /**
  * afterSave Callback
  *
  * @param Event $event CakePHP Event
  * @param EntityInterface $entity Entity that was saved
  * @return void
  */
 public function afterSave(Event $event, EntityInterface $entity)
 {
     $action = $entity->isNew() ? ModelHistory::ACTION_CREATE : ModelHistory::ACTION_UPDATE;
     $dirtyFields = null;
     if ($action === ModelHistory::ACTION_UPDATE && isset($this->_dirtyFields[$entity->id])) {
         $dirtyFields = $this->_dirtyFields[$entity->id];
         unset($this->_dirtyFields[$entity->id]);
     }
     $this->ModelHistory->add($entity, $action, $this->_getUserId(), ['dirtyFields' => $dirtyFields]);
 }
 /**
  * Save the file to the storage backend after the record was created.
  *
  * @param \Cake\Event\Event $event
  * @param \Cake\Datasource\EntityInterface $entity
  * @return void
  */
 public function afterSave(Event $event, EntityInterface $entity)
 {
     if ($this->_checkEvent($event) && $entity->isNew()) {
         $fileField = $this->config('fileField');
         $entity['hash'] = $this->getFileHash($entity, $fileField);
         $entity['path'] = $this->pathBuilder()->path($entity);
         if (!$this->_storeFile($event)) {
             return;
         }
         $event->stopPropagation();
     }
 }
Пример #8
0
 /**
  * Performs the uniqueness check
  *
  * @param \Cake\Datasource\EntityInterface $entity The entity from where to extract the fields
  * @param array $options Options passed to the check,
  * where the `repository` key is required.
  * @return bool
  */
 public function __invoke(EntityInterface $entity, array $options)
 {
     if (!$entity->extract($this->_fields, true)) {
         return true;
     }
     $conditions = $entity->extract($this->_fields);
     if ($entity->isNew() === false) {
         $keys = (array) $options['repository']->primaryKey();
         $keys = $entity->extract($keys);
         if (array_filter($keys, 'strlen')) {
             $conditions['NOT'] = $keys;
         }
     }
     return !$options['repository']->exists($conditions);
 }
Пример #9
0
 /**
  * There is only one event handler, it can be configured to be called for any event
  *
  * @param \Cake\Event\Event $event Event instance.
  * @param \Cake\Datasource\EntityInterface $entity Entity instance.
  * @throws \UnexpectedValueException if a field's when value is misdefined
  * @return true (irrespective of the behavior logic, the save will not be prevented)
  * @throws \UnexpectedValueException When the value for an event is not 'always', 'new' or 'existing'
  */
 public function handleEvent(Event $event, EntityInterface $entity)
 {
     $eventName = $event->name();
     $events = $this->_config['events'];
     $new = $entity->isNew() !== false;
     $refresh = $this->_config['refreshTimestamp'];
     foreach ($events[$eventName] as $field => $when) {
         if (!in_array($when, ['always', 'new', 'existing'])) {
             throw new UnexpectedValueException(sprintf('When should be one of "always", "new" or "existing". The passed value "%s" is invalid', $when));
         }
         if ($when === 'always' || $when === 'new' && $new || $when === 'existing' && !$new) {
             $this->_updateField($entity, $field, $refresh);
         }
     }
     return true;
 }
 /**
  * Save the file to the storage backend after the record was created.
  *
  * @param \Cake\Event\Event $event
  * @param \Cake\Datasource\EntityInterface $entity
  * @return void
  */
 public function afterSave(Event $event, EntityInterface $entity)
 {
     if ($this->_checkEvent($event) && $entity->isNew()) {
         $fileField = $this->config('fileField');
         $entity['hash'] = $this->getFileHash($entity, $fileField);
         $entity['path'] = $this->pathBuilder()->fullPath($entity);
         if (!$this->_storeFile($event)) {
             return;
         }
         if ($this->_config['imageProcessing'] === true) {
             $this->autoProcessImageVersions($entity, 'create');
         }
         $event->result = true;
         $event->stopPropagation();
     }
 }
 /**
  * Modify entity
  *
  * @param \Cake\Datasource\EntityInterface entity
  * @param \Cake\ORM\Association table
  * @param string path prefix
  * @return void
  */
 protected function _modifyEntity(EntityInterface $entity, Association $table = null, $pathPrefix = '')
 {
     if (is_null($table)) {
         $table = $this->_table;
     }
     // unset primary key
     unset($entity->{$table->primaryKey()});
     // unset foreign key
     if ($table instanceof Association) {
         unset($entity->{$table->foreignKey()});
     }
     // unset configured
     foreach ($this->config('remove') as $field) {
         $field = $this->_fieldByPath($field, $pathPrefix);
         if ($field) {
             unset($entity->{$field});
         }
     }
     // set / prepend / append
     foreach (['set', 'prepend', 'append'] as $action) {
         foreach ($this->config($action) as $field => $value) {
             $field = $this->_fieldByPath($field, $pathPrefix);
             if ($field) {
                 if ($action == 'prepend') {
                     $value .= $entity->{$field};
                 }
                 if ($action == 'append') {
                     $value = $entity->{$field} . $value;
                 }
                 $entity->{$field} = $value;
             }
         }
     }
     // set as new
     $entity->isNew(true);
     // modify related entities
     foreach ($this->config('contain') as $contain) {
         if (preg_match('/^' . preg_quote($pathPrefix, '/') . '([^.]+)/', $contain, $matches)) {
             foreach ($entity->{Inflector::tableize($matches[1])} as $related) {
                 if ($related->isNew()) {
                     continue;
                 }
                 $this->_modifyEntity($related, $table->{$matches[1]}, $pathPrefix . $matches[1] . '.');
             }
         }
     }
 }
 /**
  * Regenerates snapshot after new content type is created.
  *
  * @param \Cake\Event\Event $event The event that was triggered
  * @param \Cake\Datasource\EntityInterface $entity The entity that was saved
  * @param \ArrayObject $options Array of options
  * @return void
  */
 public function afterSave(Event $event, EntityInterface $entity, ArrayObject $options = null)
 {
     if ($entity->isNew()) {
         snapshot();
     }
 }
 /**
  * Persists an resource based on the fields that are marked as dirty and
  * returns the same resource after a successful save or false in case
  * of any error.
  *
  * @param \Cake\Datasource\EntityInterface $resource the resource to be saved
  * @param array|\ArrayAccess $options The options to use when saving.
  *
  * @return \Cake\Datasource\EntityInterface|bool
  */
 public function save(EntityInterface $resource, $options = [])
 {
     $options = new ArrayObject($options + ['checkRules' => true]);
     $mode = $resource->isNew() ? RulesChecker::CREATE : RulesChecker::UPDATE;
     if ($options['checkRules'] && !$this->checkRules($resource, $mode, $options)) {
         return false;
     }
     $data = $resource->extract($this->schema()->columns(), true);
     if ($resource->isNew()) {
         $query = $this->query()->create();
     } else {
         $query = $this->query()->update()->where([$this->primaryKey() => $resource->get($this->primaryKey())]);
     }
     $query->set($data);
     $result = $query->execute();
     if (!$result) {
         return false;
     }
     if ($resource->isNew() && $result instanceof EntityInterface) {
         return $result;
     }
     $className = get_class($resource);
     return new $className($resource->toArray(), ['markNew' => false, 'markClean' => true]);
 }
Пример #14
0
 /**
  * Throws an exception should any of the passed entities is not persisted.
  *
  * @param \Cake\Datasource\EntityInterface $sourceEntity the row belonging to the `source` side
  *   of this association
  * @param array $targetEntities list of entities belonging to the `target` side
  *   of this association
  * @return bool
  * @throws \InvalidArgumentException
  */
 protected function _checkPersistenceStatus($sourceEntity, array $targetEntities)
 {
     if ($sourceEntity->isNew()) {
         $error = 'Source entity needs to be persisted before proceeding';
         throw new InvalidArgumentException($error);
     }
     foreach ($targetEntities as $entity) {
         if ($entity->isNew()) {
             $error = 'Cannot link not persisted entities';
             throw new InvalidArgumentException($error);
         }
     }
     return true;
 }
Пример #15
0
 /**
  * Persists an entity based on the fields that are marked as dirty and
  * returns the same entity after a successful save or false in case
  * of any error.
  *
  * Triggers the `Model.beforeSave` and `Model.afterSave` events.
  *
  * ## Options
  *
  * - `checkRules` Defaults to true. Check deletion rules before deleting the record.
  *
  * @param \Cake\Datasource\EntityInterface $entity The entity to be saved
  * @param array $options An array of options to be used for the event
  * @return \Cake\Datasource\EntityInterface|bool
  */
 public function save(EntityInterface $entity, $options = [])
 {
     $options += ['checkRules' => true];
     $options = new ArrayObject($options);
     $event = $this->dispatchEvent('Model.beforeSave', ['entity' => $entity, 'options' => $options]);
     if ($event->isStopped()) {
         return $event->result;
     }
     if ($entity->errors()) {
         return false;
     }
     $mode = $entity->isNew() ? RulesChecker::CREATE : RulesChecker::UPDATE;
     if ($options['checkRules'] && !$this->checkRules($entity, $mode, $options)) {
         return false;
     }
     $type = $this->connection()->getIndex()->getType($this->name());
     $id = $entity->id ?: null;
     $data = $entity->toArray();
     unset($data[$id]);
     $doc = new ElasticaDocument($id, $data);
     $doc->setAutoPopulate(true);
     $result = $type->addDocument($doc);
     $entity->id = $doc->getId();
     $entity->_version = $doc->getVersion();
     $entity->isNew(false);
     $entity->source($this->name());
     $entity->clean();
     $this->dispatchEvent('Model.afterSave', ['entity' => $entity, 'options' => $options]);
     return $entity;
 }
 /**
  * Generates a list of words after each entity is saved.
  *
  * Triggers the following events:
  *
  * - `Model.beforeIndex`: Before entity gets indexed by the configured search
  *   engine adapter. First argument is the entity instance being indexed.
  *
  * - `Model.afterIndex`: After entity was indexed by the configured search
  *   engine adapter. First argument is the entity instance that was indexed, and
  *   second indicates whether the indexing process completed correctly or not.
  *
  * @param \Cake\Event\Event $event The event that was triggered
  * @param \Cake\Datasource\EntityInterface $entity The entity that was saved
  * @param \ArrayObject $options Additional options
  * @return void
  */
 public function afterSave(Event $event, EntityInterface $entity, ArrayObject $options)
 {
     $isNew = $entity->isNew();
     if ($this->config('on') === 'update' && $isNew || $this->config('on') === 'insert' && !$isNew || isset($options['index']) && $options['index'] === false) {
         return;
     }
     $this->_table->dispatchEvent('Model.beforeIndex', compact('entity'));
     $success = $this->searchEngine()->index($entity);
     $this->_table->dispatchEvent('Model.afterIndex', compact('entity', 'success'));
 }
 /**
  * beforeSave callback.
  *
  * @param \Cake\Event\Event $event Event.
  * @param \Cake\Datasource\EntityInterface $entity Entity.
  * @return void
  */
 public function beforeSave(Event $event, EntityInterface $entity)
 {
     $field = $this->_config['discriminatorField'];
     if ($entity->isNew() && !$entity->has($field)) {
         $discriminator = $this->discriminator();
         $entity->set($field, $discriminator);
     }
 }
Пример #18
0
 /**
  * {@inheritDoc}
  *
  * ### Options
  *
  * The options array can receive the following keys:
  *
  * - atomic: Whether to execute the save and callbacks inside a database
  * transaction (default: true)
  * - validate: Whether or not validate the entity before saving, if validation
  * fails, it will abort the save operation. If this key is set to a string value,
  * the validator object registered in this table under the provided name will be
  * used instead of the default one. (default:true)
  * - associated: If true it will save all associated entities as they are found
  * in the passed `$entity` whenever the property defined for the association
  * is marked as dirty. Associated records are saved recursively unless told
  * otherwise. If an array, it will be interpreted as the list of associations
  * to be saved. It is possible to provide different options for saving on associated
  * table objects using this key by making the custom options the array value.
  * If false no associated records will be saved. (default: true)
  *
  * ### Events
  *
  * When saving, this method will trigger four events:
  *
  * - Model.beforeValidate: Will be triggered right before any validation is done
  * for the passed entity if the validate key in $options is not set to false.
  * Listeners will receive as arguments the entity, the options array and the
  * validation object to be used for validating the entity. If the event is
  * stopped the validation result will be set to the result of the event itself.
  * - Model.afterValidate: Will be triggered right after the `validate()` method is
  * called in the entity. Listeners will receive as arguments the entity, the
  * options array and the validation object to be used for validating the entity.
  * If the event is stopped the validation result will be set to the result of
  * the event itself.
  * - Model.beforeSave: Will be triggered just before the list of fields to be
  * persisted is calculated. It receives both the entity and the options as
  * arguments. The options array is passed as an ArrayObject, so any changes in
  * it will be reflected in every listener and remembered at the end of the event
  * so it can be used for the rest of the save operation. Returning false in any
  * of the listeners will abort the saving process. If the event is stopped
  * using the event API, the event object's `result` property will be returned.
  * This can be useful when having your own saving strategy implemented inside a
  * listener.
  * - Model.afterSave: Will be triggered after a successful insert or save,
  * listeners will receive the entity and the options array as arguments. The type
  * of operation performed (insert or update) can be determined by checking the
  * entity's method `isNew`, true meaning an insert and false an update.
  *
  * This method will determine whether the passed entity needs to be
  * inserted or updated in the database. It does that by checking the `isNew`
  * method on the entity, if no information can be found there, it will go
  * directly to the database to check the entity's status.
  *
  * ### Saving on associated tables
  *
  * This method will by default persist entities belonging to associated tables,
  * whenever a dirty property matching the name of the property name set for an
  * association in this table. It is possible to control what associations will
  * be saved and to pass additional option for saving them.
  *
  * {{{
  * // Only save the comments association
  * $articles->save($entity, ['associated' => ['Comments']);
  *
  * // Save the company, the employees and related addresses for each of them.
  * // For employees use the 'special' validation group
  * $companies->save($entity, [
  *   'associated' => [
  *     'Employees' => [
  *       'associated' => ['Addresses'],
  *       'validate' => 'special'
  *     ]
  *   ]
  * ]);
  *
  * // Save no associations
  * $articles->save($entity, ['associated' => false]);
  * }}}
  *
  */
 public function save(EntityInterface $entity, $options = [])
 {
     $options = new \ArrayObject($options + ['atomic' => true, 'validate' => true, 'associated' => true]);
     if ($entity->isNew() === false && !$entity->dirty()) {
         return $entity;
     }
     if ($options['atomic']) {
         $connection = $this->connection();
         $success = $connection->transactional(function () use($entity, $options) {
             return $this->_processSave($entity, $options);
         });
     } else {
         $success = $this->_processSave($entity, $options);
     }
     return $success;
 }
 /**
  * Creates changelog report in string format.
  *
  * Example:
  *
  * Subject: changed from 'Foo' to 'Bar'.
  * Content: changed from 'Hello world' to 'Hi there'.
  *
  * @param \Cake\Datasource\EntityInterface $entity Entity instance
  * @return string
  */
 protected function _getChangelog(EntityInterface $entity)
 {
     $result = '';
     // plain changelog if entity is new
     if ($entity->isNew()) {
         return $result;
     }
     // get entity's modified fields
     $fields = $entity->extractOriginalChanged($entity->visibleProperties());
     if (empty($fields)) {
         return $result;
     }
     // remove ignored fields
     foreach ($this->_ignoredFields as $field) {
         if (!array_key_exists($field, $fields)) {
             continue;
         }
         unset($fields[$field]);
     }
     if (empty($fields)) {
         return $result;
     }
     foreach ($fields as $k => $v) {
         $result .= sprintf(static::CHANGELOG, Inflector::humanize($k), $v, $entity->{$k});
     }
     return $result;
 }
Пример #20
0
 /**
  * Calculates the changes done to the entity and stores the audit log event object into the
  * log queue inside the `_auditQueue` key in $options.
  *
  * @param Cake\Event\Event The Model event that is enclosed inside a transaction
  * @param Cake\Datasource\EntityInterface $entity The entity that is to be saved
  * @param ArrayObject $options Options array containing the `_auditQueue` key
  * @return void
  */
 public function afterSave(Event $event, EntityInterface $entity, $options)
 {
     if (!isset($options['_auditQueue'])) {
         return;
     }
     $config = $this->_config;
     if (empty($config['whitelist'])) {
         $config['whitelist'] = $this->_table->schema()->columns();
         $config['whitelist'] = array_merge($config['whitelist'], $this->getAssociationProperties(array_keys($options['associated'])));
     }
     $config['whitelist'] = array_diff($config['whitelist'], $config['blacklist']);
     $changed = $entity->extract($config['whitelist'], true);
     if (!$changed) {
         return;
     }
     $original = $entity->extractOriginal(array_keys($changed));
     $properties = $this->getAssociationProperties(array_keys($options['associated']));
     foreach ($properties as $property) {
         unset($changed[$property], $original[$property]);
     }
     if (!$changed || $original === $changed && !$entity->isNew()) {
         return;
     }
     $primary = $entity->extract((array) $this->_table->primaryKey());
     $auditEvent = $entity->isNew() ? AuditCreateEvent::class : AuditUpdateEvent::class;
     $transaction = $options['_auditTransaction'];
     $auditEvent = new $auditEvent($transaction, $primary, $this->_table->table(), $changed, $original);
     if (!empty($options['_sourceTable'])) {
         $auditEvent->setParentSourceName($options['_sourceTable']->table());
     }
     $options['_auditQueue']->attach($entity, $auditEvent);
 }
Пример #21
0
 /**
  * Perform the delete operation.
  *
  * Will delete the entity provided. Will remove rows from any
  * dependent associations, and clear out join tables for BelongsToMany associations.
  *
  * @param \Cake\DataSource\EntityInterface $entity The entity to delete.
  * @param \ArrayObject $options The options for the delete.
  * @throws \InvalidArgumentException if there are no primary key values of the
  * passed entity
  * @return bool success
  */
 protected function _processDelete($entity, $options)
 {
     $eventManager = $this->getEventManager();
     $event = new Event('Model.beforeDelete', $this, ['entity' => $entity, 'options' => $options]);
     $eventManager->dispatch($event);
     if ($event->isStopped()) {
         return $event->result;
     }
     if ($entity->isNew()) {
         return false;
     }
     $primaryKey = (array) $this->primaryKey();
     $conditions = (array) $entity->extract($primaryKey);
     if (!array_filter($conditions, 'strlen')) {
         $msg = 'Deleting requires a primary key value';
         throw new \InvalidArgumentException($msg);
     }
     $this->_associations->cascadeDelete($entity, $options->getArrayCopy());
     $query = $this->query();
     $statement = $query->delete()->where($conditions)->execute();
     $success = $statement->rowCount() > 0;
     if (!$success) {
         return $success;
     }
     $event = new Event('Model.afterDelete', $this, ['entity' => $entity, 'options' => $options]);
     $eventManager->dispatch($event);
     return $success;
 }
Пример #22
0
 /**
  * Merges `$data` into `$entity`.
  *
  * ### Options:
  *
  * * validate: Whether or not to validate data before hydrating the entities. Can
  *   also be set to a string to use a specific validator. Defaults to true/default.
  * * fieldList: A whitelist of fields to be assigned to the entity. If not present
  *   the accessible fields list in the entity will be used.
  * * accessibleFields: A list of fields to allow or deny in entity accessible fields.
  *
  * @param \Cake\Datasource\EntityInterface $entity the entity that will get the
  * data merged in
  * @param array $data key value list of fields to be merged into the entity
  * @param array $options List of options.
  * @return \Cake\Datasource\EntityInterface
  */
 public function merge(EntityInterface $entity, array $data, array $options = [])
 {
     list($data, $options) = $this->_prepareDataAndOptions($data, $options);
     $isNew = $entity->isNew();
     $keys = [];
     if (!$isNew) {
         $keys = $entity->extract((array) $this->_endpoint->primaryKey());
     }
     if (isset($options['accessibleFields'])) {
         foreach ((array) $options['accessibleFields'] as $key => $value) {
             $entity->accessible($key, $value);
         }
     }
     $errors = $this->_validate($data + $keys, $options, $isNew);
     $properties = [];
     foreach ($data as $key => $value) {
         if (!empty($errors[$key])) {
             continue;
         }
         $properties[$key] = $value;
     }
     if (!isset($options['fieldList'])) {
         $entity->set($properties);
         $entity->errors($errors);
         return $entity;
     }
     foreach ((array) $options['fieldList'] as $field) {
         if (array_key_exists($field, $properties)) {
             $entity->set($field, $properties[$field]);
         }
     }
     $entity->errors($errors);
     return $entity;
 }
 /**
  * _checkEntityBeforeSave
  *
  * @param \Cake\Datasource\EntityInterface $entity
  * @return void
  */
 protected function _checkEntityBeforeSave(EntityInterface &$entity)
 {
     if ($entity->isNew()) {
         if (empty($entity->model)) {
             $entity->model = $this->table();
         }
         if (empty($entity->adapter)) {
             $entity->adapter = $this->_defaultAdapter;
         }
     }
 }
 /**
  * Merges `$data` into `$document`.
  *
  * ### Options:
  *
  * * fieldList: A whitelist of fields to be assigned to the entity. If not present
  *   the accessible fields list in the entity will be used.
  *
  * @param \Cake\Datasource\EntityInterface $entity the entity that will get the
  * data merged in
  * @param array $data key value list of fields to be merged into the entity
  * @param array $options List of options.
  * @return \Cake\Datasource\EntityInterface
  */
 public function merge(EntityInterface $entity, array $data, array $options = [])
 {
     list($data, $options) = $this->_prepareDataAndOptions($data, $options);
     $errors = $this->_validate($data, $options, $entity->isNew());
     $entity->errors($errors);
     foreach (array_keys($errors) as $badKey) {
         unset($data[$badKey]);
     }
     foreach ($this->type->embedded() as $embed) {
         $property = $embed->property();
         if (in_array($embed->alias(), $options['associated']) && isset($data[$property])) {
             $data[$property] = $this->mergeNested($embed, $entity->{$property}, $data[$property]);
         }
     }
     if (!isset($options['fieldList'])) {
         $entity->set($data);
         return $entity;
     }
     foreach ((array) $options['fieldList'] as $field) {
         if (array_key_exists($field, $data)) {
             $entity->set($field, $data[$field]);
         }
     }
     return $entity;
 }
Пример #25
0
 /**
  * Takes an entity from the source table and looks if there is a field
  * matching the property name for this association. The found entity will be
  * saved on the target table for this association by passing supplied
  * `$options`
  *
  * When using the 'append' strategy, this function will only create new links
  * between each side of this association. It will not destroy existing ones even
  * though they may not be present in the array of entities to be saved.
  *
  * When using the 'replace' strategy, existing links will be removed and new links
  * will be created in the joint table. If there exists links in the database to some
  * of the entities intended to be saved by this method, they will be updated,
  * not deleted.
  *
  * @param \Cake\Datasource\EntityInterface $entity an entity from the source table
  * @param array|\ArrayObject $options options to be passed to the save method in
  * the target table
  * @throws \InvalidArgumentException if the property representing the association
  * in the parent entity cannot be traversed
  * @return bool|\Cake\Datasource\EntityInterface false if $entity could not be saved, otherwise it returns
  * the saved entity
  * @see Table::save()
  * @see BelongsToMany::replaceLinks()
  */
 public function saveAssociated(EntityInterface $entity, array $options = [])
 {
     $targetEntity = $entity->get($this->property());
     $strategy = $this->saveStrategy();
     $isEmpty = in_array($targetEntity, [null, [], '', false], true);
     if ($isEmpty && $entity->isNew()) {
         return $entity;
     }
     if ($isEmpty) {
         $targetEntity = [];
     }
     if ($strategy === self::SAVE_APPEND) {
         return $this->_saveTarget($entity, $targetEntity, $options);
     }
     if ($this->replaceLinks($entity, $targetEntity, $options)) {
         return $entity;
     }
     return false;
 }
Пример #26
0
 /**
  * Merges `$data` into `$entity` and recursively does the same for each one of
  * the association names passed in `$options`. When merging associations, if an
  * entity is not present in the parent entity for a given association, a new one
  * will be created.
  *
  * When merging HasMany or BelongsToMany associations, all the entities in the
  * `$data` array will appear, those that can be matched by primary key will get
  * the data merged, but those that cannot, will be discarded. `ids` option can be used
  * to determine whether the association must use the `_ids` format.
  *
  * ### Options:
  *
  * - associated: Associations listed here will be marshalled as well.
  * - validate: Whether or not to validate data before hydrating the entities. Can
  *   also be set to a string to use a specific validator. Defaults to true/default.
  * - fieldList: A whitelist of fields to be assigned to the entity. If not present
  *   the accessible fields list in the entity will be used.
  * - accessibleFields: A list of fields to allow or deny in entity accessible fields.
  *
  * The above options can be used in each nested `associated` array. In addition to the above
  * options you can also use the `onlyIds` option for HasMany and BelongsToMany associations.
  * When true this option restricts the request data to only be read from `_ids`.
  *
  * ```
  * $result = $marshaller->merge($entity, $data, [
  *   'associated' => ['Tags' => ['onlyIds' => true]]
  * ]);
  * ```
  *
  * @param \Cake\Datasource\EntityInterface $entity the entity that will get the
  * data merged in
  * @param array $data key value list of fields to be merged into the entity
  * @param array $options List of options.
  * @return \Cake\Datasource\EntityInterface
  */
 public function merge(EntityInterface $entity, array $data, array $options = [])
 {
     list($data, $options) = $this->_prepareDataAndOptions($data, $options);
     $isNew = $entity->isNew();
     $keys = [];
     if (!$isNew) {
         $keys = $entity->extract((array) $this->_table->primaryKey());
     }
     if (isset($options['accessibleFields'])) {
         foreach ((array) $options['accessibleFields'] as $key => $value) {
             $entity->accessible($key, $value);
         }
     }
     $errors = $this->_validate($data + $keys, $options, $isNew);
     $schema = $this->_table->schema();
     $options['isMerge'] = true;
     $propertyMap = $this->_buildPropertyMap($data, $options);
     $properties = $marshalledAssocs = [];
     foreach ($data as $key => $value) {
         if (!empty($errors[$key])) {
             if ($entity instanceof InvalidPropertyInterface) {
                 $entity->invalid($key, $value);
             }
             continue;
         }
         $original = $entity->get($key);
         if (isset($propertyMap[$key])) {
             $value = $propertyMap[$key]($value, $entity);
             // Don't dirty scalar values and objects that didn't
             // change. Arrays will always be marked as dirty because
             // the original/updated list could contain references to the
             // same objects, even though those objects may have changed internally.
             if (is_scalar($value) && $original === $value || $value === null && $original === $value || is_object($value) && !$value instanceof EntityInterface && $original == $value) {
                 continue;
             }
         }
         $properties[$key] = $value;
     }
     $entity->errors($errors);
     if (!isset($options['fieldList'])) {
         $entity->set($properties);
         foreach ($properties as $field => $value) {
             if ($value instanceof EntityInterface) {
                 $entity->dirty($field, $value->dirty());
             }
         }
         return $entity;
     }
     foreach ((array) $options['fieldList'] as $field) {
         if (array_key_exists($field, $properties)) {
             $entity->set($field, $properties[$field]);
             if ($properties[$field] instanceof EntityInterface) {
                 $entity->dirty($field, $properties[$field]->dirty());
             }
         }
     }
     return $entity;
 }
Пример #27
0
 /**
  * Merges `$data` into `$entity` and recursively does the same for each one of
  * the association names passed in `$options`. When merging associations, if an
  * entity is not present in the parent entity for a given association, a new one
  * will be created.
  *
  * When merging HasMany or BelongsToMany associations, all the entities in the
  * `$data` array will appear, those that can be matched by primary key will get
  * the data merged, but those that cannot, will be discarded. `ids` option can be used
  * to determine whether the association must use the `_ids` format.
  *
  * ### Options:
  *
  * - associated: Associations listed here will be marshalled as well.
  * - validate: Whether or not to validate data before hydrating the entities. Can
  *   also be set to a string to use a specific validator. Defaults to true/default.
  * - fieldList: A whitelist of fields to be assigned to the entity. If not present
  *   the accessible fields list in the entity will be used.
  * - accessibleFields: A list of fields to allow or deny in entity accessible fields.
  *
  * The above options can be used in each nested `associated` array. In addition to the above
  * options you can also use the `onlyIds` option for HasMany and BelongsToMany associations.
  * When true this option restricts the request data to only be read from `_ids`.
  *
  * ```
  * $result = $marshaller->merge($entity, $data, [
  *   'associated' => ['Tags' => ['onlyIds' => true]]
  * ]);
  * ```
  *
  * @param \Cake\Datasource\EntityInterface $entity the entity that will get the
  * data merged in
  * @param array $data key value list of fields to be merged into the entity
  * @param array $options List of options.
  * @return \Cake\Datasource\EntityInterface
  */
 public function merge(EntityInterface $entity, array $data, array $options = [])
 {
     list($data, $options) = $this->_prepareDataAndOptions($data, $options);
     $propertyMap = $this->_buildPropertyMap($options);
     $isNew = $entity->isNew();
     $keys = [];
     if (!$isNew) {
         $keys = $entity->extract((array) $this->_table->primaryKey());
     }
     if (isset($options['accessibleFields'])) {
         foreach ((array) $options['accessibleFields'] as $key => $value) {
             $entity->accessible($key, $value);
         }
     }
     $errors = $this->_validate($data + $keys, $options, $isNew);
     $schema = $this->_table->schema();
     $properties = $marshalledAssocs = [];
     foreach ($data as $key => $value) {
         if (!empty($errors[$key])) {
             if ($entity instanceof InvalidPropertyInterface) {
                 $entity->invalid($key, $value);
             }
             continue;
         }
         $columnType = $schema->columnType($key);
         $original = $entity->get($key);
         if (isset($propertyMap[$key])) {
             $assoc = $propertyMap[$key]['association'];
             $value = $this->_mergeAssociation($original, $assoc, $value, $propertyMap[$key]);
             $marshalledAssocs[$key] = true;
         } elseif ($columnType) {
             $converter = Type::build($columnType);
             $value = $converter->marshal($value);
             $isObject = is_object($value);
             if (!$isObject && $original === $value || $isObject && $original == $value) {
                 continue;
             }
         }
         $properties[$key] = $value;
     }
     if (!isset($options['fieldList'])) {
         $entity->set($properties);
         $entity->errors($errors);
         foreach (array_keys($marshalledAssocs) as $field) {
             if ($properties[$field] instanceof EntityInterface) {
                 $entity->dirty($field, $properties[$field]->dirty());
             }
         }
         return $entity;
     }
     foreach ((array) $options['fieldList'] as $field) {
         if (array_key_exists($field, $properties)) {
             $entity->set($field, $properties[$field]);
             if ($properties[$field] instanceof EntityInterface && isset($marshalledAssocs[$field])) {
                 $entity->dirty($field, $properties[$field]->dirty());
             }
         }
     }
     $entity->errors($errors);
     return $entity;
 }
Пример #28
0
 /**
  * Persists an resource based on the fields that are marked as dirty and
  * returns the same resource after a successful save or false in case
  * of any error.
  *
  * @param \Cake\Datasource\EntityInterface $resource the resource to be saved
  * @param array|\ArrayAccess $options The options to use when saving.
  *
  * @return \Cake\Datasource\EntityInterface|bool
  */
 public function save(EntityInterface $resource, $options = [])
 {
     $options = new ArrayObject($options + ['checkRules' => true]);
     $mode = $resource->isNew() ? RulesChecker::CREATE : RulesChecker::UPDATE;
     if ($options['checkRules'] && !$this->checkRules($resource, $mode, $options)) {
         return false;
     }
     if ($resource->isNew()) {
         $query = $this->query()->create()->set($resource->toArray());
     } else {
         $query = $this->query()->update()->where([$this->primaryKey() => $resource->get($this->primaryKey())]);
         $fieldsToUpdate = [];
         foreach ($resource as $field => $value) {
             if (!$resource->dirty($field)) {
                 continue;
             }
             $fieldsToUpdate[$field] = $value;
         }
         $query->set($fieldsToUpdate);
     }
     $result = $query->execute();
     if (!$result) {
         return false;
     }
     if ($resource->isNew() && $result instanceof EntityInterface) {
         return $result;
     }
     $className = get_class($resource);
     return new $className($resource->toArray(), ['markNew' => false, 'markClean' => true]);
 }
Пример #29
0
 /**
  * After save listener.
  *
  * Manages updating level of descendents of currently saved entity.
  *
  * @param \Cake\Event\Event $event The beforeSave event that was fired
  * @param \Cake\Datasource\EntityInterface $entity the entity that is going to be saved
  * @return void
  */
 public function afterSave(Event $event, EntityInterface $entity)
 {
     if (!$this->_config['level'] || $entity->isNew()) {
         return;
     }
     $this->_setChildrenLevel($entity);
 }
Пример #30
0
 /**
  * Performs the actual saving of an entity based on the passed options.
  *
  * @param \Cake\Datasource\EntityInterface $entity the entity to be saved
  * @param \ArrayObject $options the options to use for the save operation
  * @return \Cake\Datasource\EntityInterface|bool
  * @throws \RuntimeException When an entity is missing some of the primary keys.
  */
 protected function _processSave($entity, $options)
 {
     $primaryColumns = (array) $this->primaryKey();
     if ($options['checkExisting'] && $primaryColumns && $entity->isNew() && $entity->has($primaryColumns)) {
         $alias = $this->alias();
         $conditions = [];
         foreach ($entity->extract($primaryColumns) as $k => $v) {
             $conditions["{$alias}.{$k}"] = $v;
         }
         $entity->isNew(!$this->exists($conditions));
     }
     $mode = $entity->isNew() ? RulesChecker::CREATE : RulesChecker::UPDATE;
     if ($options['checkRules'] && !$this->checkRules($entity, $mode, $options)) {
         return false;
     }
     $options['associated'] = $this->_associations->normalizeKeys($options['associated']);
     $event = $this->dispatchEvent('Model.beforeSave', compact('entity', 'options'));
     if ($event->isStopped()) {
         return $event->result;
     }
     $saved = $this->_associations->saveParents($this, $entity, $options['associated'], ['_primary' => false] + $options->getArrayCopy());
     if (!$saved && $options['atomic']) {
         return false;
     }
     $data = $entity->extract($this->schema()->columns(), true);
     $isNew = $entity->isNew();
     if ($isNew) {
         $success = $this->_insert($entity, $data);
     } else {
         $success = $this->_update($entity, $data);
     }
     if ($success) {
         $success = $this->_associations->saveChildren($this, $entity, $options['associated'], ['_primary' => false] + $options->getArrayCopy());
         if ($success || !$options['atomic']) {
             $entity->clean();
             $this->dispatchEvent('Model.afterSave', compact('entity', 'options'));
             if (!$options['atomic'] && !$options['_primary']) {
                 $entity->isNew(false);
                 $entity->source($this->registryAlias());
             }
             $success = true;
         }
     }
     if (!$success && $isNew) {
         $entity->unsetProperty($this->primaryKey());
         $entity->isNew(true);
     }
     if ($success) {
         return $entity;
     }
     return false;
 }