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; }
/** * 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()); } }
/** * Event listener to encrypt data. * * @param \Cake\Event\Event $event Event. * @param \Cake\Datasource\EntityInterface $entity Entity. * @return void */ public function beforeSave(Event $event, EntityInterface $entity) { $driver = $this->_table->connection()->driver(); foreach ($this->config('fields') as $field => $type) { if (!$entity->has($field)) { continue; } $raw = $entity->get($field); $plain = Type::build($type)->toDatabase($raw, $driver); $entity->set($field, $this->encrypt($plain)); } }
/** * Save also related model data * * @param \Cake\Event\Event * @param \Cake\ORM\Entity; * @return void */ public function beforeSave(Event $event, EntityInterface $entity, \ArrayObject $options) { foreach ($this->config('fields') as $field => $mapped) { list($mappedTable, $mappedField) = explode('.', $mapped); if (!isset($this->_table->{$mappedTable}) || $this->_table->{$mappedTable}->isOwningSide($this->_table)) { throw new Exception(sprintf('Incorrect definition of related data to persist for %s', $mapped)); } $foreign = $entity->get($this->_table->{$mappedTable}->foreignKey()); if (!is_null($foreign)) { // get related entity $related = $this->_table->{$mappedTable}->get($foreign); // set field value $entity->set($field, $related->get($mappedField)); } } }
/** * Save also related model data * * @param \Cake\Event\Event * @param \Cake\ORM\Entity; * @return void */ public function beforeSave(Event $event, EntityInterface $entity, \ArrayObject $options) { $relatedEntities = []; foreach ($this->config('fields') as $field => $mapped) { list($mappedTable, $mappedField) = explode('.', $mapped); if (!isset($this->_table->{$mappedTable}) || $this->_table->{$mappedTable}->isOwningSide($this->_table)) { throw new Exception(sprintf('Incorrect definition of related data to persist for %s', $mapped)); } $foreignKeys = $entity->extract((array) $this->_table->{$mappedTable}->foreignKey()); $dirtyForeignKeys = $entity->extract((array) $this->_table->{$mappedTable}->foreignKey(), true); if (!empty($dirtyForeignKeys)) { // get related entity if (empty($relatedEntities[$mappedTable])) { $relatedEntities[$mappedTable] = $this->_table->{$mappedTable}->get($foreignKeys); } // set field value $entity->set($field, $relatedEntities[$mappedTable]->get($mappedField)); } } }
/** * Helper method used to generated multiple translated field entities * out of the data found in the `_translations` property in the passed * entity. The result will be put into its `_i18n` property * * @param \Cake\Datasource\EntityInterface $entity Entity * @return void */ protected function _bundleTranslatedFields($entity) { $translations = (array) $entity->get('_translations'); if (empty($translations) && !$entity->dirty('_translations')) { return; } $primaryKey = (array) $this->_table->primaryKey(); $key = $entity->get(current($primaryKey)); foreach ($translations as $lang => $translation) { if (!$translation->id) { $update = ['id' => $key, 'locale' => $lang]; $translation->set($update, ['guard' => false]); } } $entity->set('_i18n', $translations); }
/** * Update a field, if it hasn't been updated already * * @param \Cake\Datasource\EntityInterface $entity Entity instance. * @param string $field Field name * @param bool $refreshTimestamp Whether to refresh timestamp. * @return void */ protected function _updateField($entity, $field, $refreshTimestamp) { if ($entity->dirty($field)) { return; } $entity->set($field, $this->timestamp(null, $refreshTimestamp)); }
/** * 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. * * ### Options: * * * associated: Associations listed here will be marshalled as well. * * 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 = []) { $propertyMap = $this->_buildPropertyMap($options); $tableName = $this->_table->alias(); if (isset($data[$tableName])) { $data = $data[$tableName]; } $schema = $this->_table->schema(); $properties = []; foreach ($data as $key => $value) { $columnType = $schema->columnType($key); $original = $entity->get($key); if (isset($propertyMap[$key])) { $assoc = $propertyMap[$key]['association']; $value = $this->_mergeAssociation($original, $assoc, $value, $propertyMap[$key]); } 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); return $entity; } foreach ((array) $options['fieldList'] as $field) { if (isset($properties[$field])) { $entity->set($field, $properties[$field]); } } return $entity; }
/** * 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; }
/** * Auxiliary function to handle the insert of an entity's data in the table * * @param \Cake\Datasource\EntityInterface $entity the subject entity from were $data was extracted * @param array $data The actual data that needs to be saved * @return \Cake\Datasource\EntityInterface|bool * @throws \RuntimeException if not all the primary keys where supplied or could * be generated when the table has composite primary keys. Or when the table has no primary key. */ protected function _insert($entity, $data) { $primary = (array) $this->primaryKey(); if (empty($primary)) { $msg = sprintf('Cannot insert row in "%s" table, it has no primary key.', $this->table()); throw new RuntimeException($msg); } $keys = array_fill(0, count($primary), null); $id = (array) $this->_newId($primary) + $keys; $primary = array_combine($primary, $id); $filteredKeys = array_filter($primary, 'strlen'); $data = $data + $filteredKeys; if (count($primary) > 1) { $schema = $this->schema(); foreach ($primary as $k => $v) { if (!isset($data[$k]) && empty($schema->column($k)['autoIncrement'])) { $msg = 'Cannot insert row, some of the primary key values are missing. '; $msg .= sprintf('Got (%s), expecting (%s)', implode(', ', $filteredKeys + $entity->extract(array_keys($primary))), implode(', ', array_keys($primary))); throw new RuntimeException($msg); } } } $success = false; if (empty($data)) { return $success; } $statement = $this->query()->insert(array_keys($data))->values($data)->execute(); if ($statement->rowCount() !== 0) { $success = $entity; $entity->set($filteredKeys, ['guard' => false]); $schema = $this->schema(); $driver = $this->connection()->driver(); foreach ($primary as $key => $v) { if (!isset($data[$key])) { $id = $statement->lastInsertId($this->table(), $key); $type = $schema->columnType($key); $entity->set($key, Type::build($type)->toPHP($id, $driver)); break; } } } $statement->closeCursor(); return $success; }
/** * Replaces existing association links between the source entity and the target * with the ones passed. This method does a smart cleanup, links that are already * persisted and present in `$targetEntities` will not be deleted, new links will * be created for the passed target entities that are not already in the database * and the rest will be removed. * * For example, if an author has many articles, such as 'article1','article 2' and 'article 3' and you pass * to this method an array containing the entities for articles 'article 1' and 'article 4', * only the link for 'article 1' will be kept in database, the links for 'article 2' and 'article 3' will be * deleted and the link for 'article 4' will be created. * * Existing links are not deleted and created again, they are either left untouched * or updated. * * This method does not check link uniqueness. * * On success, the passed `$sourceEntity` will contain `$targetEntities` as value * in the corresponding property for this association. * * Additional options for new links to be saved can be passed in the third argument, * check `Table::save()` for information on the accepted options. * * ### Example: * * ``` * $author->articles = [$article1, $article2, $article3, $article4]; * $authors->save($author); * $articles = [$article1, $article3]; * $authors->association('articles')->replaceLinks($author, $articles); * ``` * * `$author->get('articles')` will contain only `[$article1, $article3]` at the end * * @param \Cake\Datasource\EntityInterface $sourceEntity an entity persisted in the source table for * this association * @param array $targetEntities list of entities from the target table to be linked * @param array $options list of options to be passed to the internal `save`/`delete` calls * when persisting/updating new links, or deleting existing ones * @throws \InvalidArgumentException if non persisted entities are passed or if * any of them is lacking a primary key value * @return bool success */ public function replace(EntityInterface $sourceEntity, array $targetEntities, array $options = []) { $property = $this->property(); $sourceEntity->set($property, $targetEntities); $saveStrategy = $this->saveStrategy(); $this->saveStrategy(self::SAVE_REPLACE); $result = $this->saveAssociated($sourceEntity, $options); $ok = $result instanceof EntityInterface; if ($ok) { $sourceEntity = $result; } $this->saveStrategy($saveStrategy); return $ok; }
/** * When default server timezone is set as UTC (-> database stores dates as UTC) and the user's locale is not UTC, dates properties are updated in forms in the user's locale * As CakePHP 3.0.0-alpha2 marshalls dates values sent from forms in the default locale UTC, the timezones must be corrected to be saved correctly * * Using this Listener allows to change the saved datetime timezones easily * * Usage: * AppController: * * Configure::write('display_timezone', 'Europe/Zurich); * * UsersController: * * $this->Users->eventManager()->attach(new TimezoneEventListener()); * * $user = $this->Users->patchEntity($user, $this->request->data); * * @param Event $event * @param EntityInterface $entity * @param unknown $options * @param Validator $validator * @return boolean */ public function update_datetime_fields_timezone(Event $event, EntityInterface $entity, $options = [], Validator $validator) { $display_timezone = isset($this->_config['display_timezone']) ? $this->_config['display_timezone'] : Configure::read('display_timezone'); $default_timezone = date_default_timezone_get(); if (!empty($display_timezone) && $display_timezone != $default_timezone) { $data = $entity->toArray(); foreach ($data as $property => $value) { if (!in_array($property, $this->_config['skipped_properties'])) { $type = $event->subject()->schema()->columnType($property); if ($type == 'datetime') { if (is_a($data[$property], 'Cake\\I18n\\Time')) { /* * At this step, as the datetime has already been marshalled, the datetime has the value selected in the view, but its timezone is wrong * * Create a new Time object with the values from the saved datetime, but with the timezone used for display */ $timezoned_value = Time::create($data[$property]->year, $data[$property]->month, $data[$property]->day, $data[$property]->hour, $data[$property]->minute, $data[$property]->second, $display_timezone); } elseif (is_array($data[$property])) { /* * Actually if the Listener is attached to 'Model.beforeValidate', we probably never fall here as the date array has already been marshalled */ $data[$property]['year'] = isset($data[$property]['year']) ? $data[$property]['year'] : null; $data[$property]['month'] = isset($data[$property]['month']) ? $data[$property]['month'] : null; $data[$property]['day'] = isset($data[$property]['day']) ? $data[$property]['day'] : null; $data[$property]['hour'] = isset($data[$property]['hour']) ? $data[$property]['hour'] : null; $data[$property]['minute'] = isset($data[$property]['minute']) ? $data[$property]['minute'] : null; $data[$property]['second'] = isset($data[$property]['second']) ? $data[$property]['second'] : null; $timezoned_value = Time::create($data[$property]['year'], $data[$property]['month'], $data[$property]['day'], $data[$property]['hour'], $data[$property]['minute'], $data[$property]['second'], $display_timezone); } if (isset($timezoned_value)) { /* * Transform the Time object to UTC timezone */ $timezoned_value->setTimezone($default_timezone); $entity->set($property, $timezoned_value); } } } } } return true; }
/** * 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); } }
/** * Sets up hashid for model. * * @param \Cake\Datasource\EntityInterface $entity The entity that is going to be saved * @return bool True if save should proceed, false otherwise */ public function encode(EntityInterface $entity) { $idField = $this->_primaryKey; $id = $entity->get($idField); if (!$id) { return false; } $field = $this->_config['field']; if (!$field) { return false; } $hashid = $this->encodeId($id); $entity->set($field, $hashid); $entity->dirty($field, false); return true; }
/** * Prepares entity's cache-columns (those defined using `cache` option). * * @param \Cake\Datasource\EntityInterface $entity The entity to prepare * @return \Cake\Datasource\EntityInterfa Modified entity */ protected function _prepareCachedColumns(EntityInterface $entity) { if ($this->config('cacheMap')) { foreach ((array) $this->config('cacheMap') as $column => $fields) { if (in_array($column, $entity->visibleProperties())) { $string = $entity->get($column); if ($string == serialize(false) || @unserialize($string) !== false) { $entity->set($column, unserialize($string)); } else { $entity->set($column, new CachedColumn()); } } } } return $entity; }
/** * Helper method used to generated multiple translated field entities * out of the data found in the `_translations` property in the passed * entity. The result will be put into its `_i18n` property * * @param \Cake\Datasource\EntityInterface $entity Entity * @return void */ protected function _bundleTranslatedFields($entity) { $translations = (array) $entity->get('_translations'); if (empty($translations) && !$entity->dirty('_translations')) { return; } $fields = $this->_config['fields']; $primaryKey = (array) $this->_table->primaryKey(); $key = $entity->get(current($primaryKey)); $find = []; foreach ($translations as $lang => $translation) { foreach ($fields as $field) { if (!$translation->dirty($field)) { continue; } $find[] = ['locale' => $lang, 'field' => $field, 'foreign_key' => $key]; $contents[] = new Entity(['content' => $translation->get($field)], ['useSetters' => false]); } } if (empty($find)) { return; } $results = $this->_findExistingTranslations($find); foreach ($find as $i => $translation) { if (!empty($results[$i])) { $contents[$i]->set('id', $results[$i], ['setter' => false]); $contents[$i]->isNew(false); } else { $translation['model'] = $this->_config['referenceName']; $contents[$i]->set($translation, ['setter' => false, 'guard' => false]); $contents[$i]->isNew(true); } } $entity->set('_i18n', $contents); }
/** * 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` * * @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 * @return bool|\Cake\Datasource\EntityInterface false if $entity could not be saved, otherwise it returns * the saved entity * @see Table::save() * @throws \InvalidArgumentException when the association data cannot be traversed. */ public function saveAssociated(EntityInterface $entity, array $options = []) { $targetEntities = $entity->get($this->property()); if (empty($targetEntities)) { return $entity; } if (!is_array($targetEntities) && !$targetEntities instanceof \Traversable) { $name = $this->property(); $message = sprintf('Could not save %s, it cannot be traversed', $name); throw new InvalidArgumentException($message); } $properties = array_combine((array) $this->foreignKey(), $entity->extract((array) $this->bindingKey())); $target = $this->target(); $original = $targetEntities; $options['_sourceTable'] = $this->source(); foreach ($targetEntities as $k => $targetEntity) { if (!$targetEntity instanceof EntityInterface) { break; } if (!empty($options['atomic'])) { $targetEntity = clone $targetEntity; } $targetEntity->set($properties, ['guard' => false]); if ($target->save($targetEntity, $options)) { $targetEntities[$k] = $targetEntity; continue; } if (!empty($options['atomic'])) { $original[$k]->errors($targetEntity->errors()); $entity->set($this->property(), $original); return false; } } $entity->set($this->property(), $targetEntities); return $entity; }
/** * Callback to obfuscate the record(s)' primary key returned after a save operation. * * @param \Cake\ORM\Behavior\Event $event Event. * @param \Cake\ORM\Behavior\EntityInterface $entity Entity. * @param \ArrayObject $options Options. * @return void */ public function afterSave(Event $event, EntityInterface $entity, ArrayObject $options) { $pk = $this->_table->primaryKey(); $entity->set($pk, $this->obfuscate($entity->{$pk})); $entity->dirty($pk, false); }
/** * 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` * * @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 * @return bool|\Cake\Datasource\EntityInterface false if $entity could not be saved, otherwise it returns * the saved entity * @see Table::save() */ public function saveAssociated(EntityInterface $entity, array $options = []) { $targetEntity = $entity->get($this->property()); if (empty($targetEntity) || !$targetEntity instanceof EntityInterface) { return $entity; } $table = $this->target(); $targetEntity = $table->save($targetEntity, $options); if (!$targetEntity) { return false; } $properties = array_combine((array) $this->foreignKey(), $targetEntity->extract((array) $this->bindingKey())); $entity->set($properties, ['guard' => false]); return $entity; }
/** * Merges `$data` into `$entity` and recursively does the same for each one of * the association names passed in `$include`. 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. * * @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 $include The list of associations to be merged * @return \Cake\Datasource\EntityInterface */ public function merge(EntityInterface $entity, array $data, array $include = []) { $propertyMap = $this->_buildPropertyMap($include); $tableName = $this->_table->alias(); if (isset($data[$tableName])) { $data = $data[$tableName]; } $schema = $this->_table->schema(); $properties = []; foreach ($data as $key => $value) { $columnType = $schema->columnType($key); $original = $entity->get($key); if (isset($propertyMap[$key])) { $assoc = $propertyMap[$key]['association']; $nested = $propertyMap[$key]['nested']; $value = $this->_mergeAssociation($original, $assoc, $value, $nested); } elseif ($columnType) { $converter = Type::build($columnType); $value = $converter->marshal($value); if ($original == $value) { continue; } } $properties[$key] = $value; } $entity->set($properties); return $entity; }
/** * Restores all (or given) trashed row(s). * * @param \Cake\Datasource\EntityInterface|null $entity to restore. * @return bool|\Cake\Datasource\EntityInterface|int|mixed */ public function restoreTrash(EntityInterface $entity = null) { $data = [$this->getTrashField(false) => null]; if ($entity instanceof EntityInterface) { if ($entity->dirty()) { throw new RuntimeException('Can not restore from a dirty entity.'); } $entity->set($data); return $this->_table->save($entity); } return $this->_table->updateAll($data, $this->_getUnaryExpression()); }
/** * 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; }
/** * Ensures that the provided entity contains non-empty values for the left and * right fields * * @param \Cake\Datasource\EntityInterface $entity The entity to ensure fields for * @return void */ protected function _ensureFields($entity) { $config = $this->config(); $fields = [$config['left'], $config['right']]; $values = array_filter($entity->extract($fields)); if (count($values) === count($fields)) { return; } $fresh = $this->_table->get($entity->get($this->_getPrimaryKey()), $fields); $entity->set($fresh->extract($fields), ['guard' => false]); foreach ($fields as $field) { $entity->dirty($field, false); } }
/** * 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; }