/**
  * Handles a nested update, link or create for a single model, returning
  * the result.
  *
  * @param mixed        $data
  * @param RelationInfo $info
  * @param string       $attribute
  * @param null|int     $index       optional, for to-many list indexes to append after attribute
  * @return UpdateResult|false       false if no model available
  * @throws DisallowedNestedActionException
  */
 protected function handleNestedSingleUpdateOrCreate($data, RelationInfo $info, $attribute, $index = null)
 {
     // handle model before, use results to save foreign key on the model later
     $nestedKey = $this->appendNestedKey($attribute, $index);
     $data = $this->normalizeNestedSingularData($data, $info->model()->getKeyName(), $nestedKey);
     // if this data set has a temporary id, create the model and convert to link operation instead
     // note that we cannot assume that it is being created explicitly for this relation, it
     // may simply be the first time it is referenced while processing the data tree.
     if ($this->isHandlingTemporaryIds() && $this->hasTemporaryIds() && array_key_exists($this->getTemporaryIdAttributeKey(), $data)) {
         $temporaryId = $data[$this->getTemporaryIdAttributeKey()];
         if (!$this->temporaryIds->hasId($temporaryId)) {
             return $this->makeUpdateResult();
         }
         $model = $this->temporaryIds->getModelForId($temporaryId);
         if (!$model) {
             // if it has not been created, attempt to create it
             $data = $this->temporaryIds->getDataForId($temporaryId);
             // safeguard, this should never happen
             if (null === $data) {
                 return $this->makeUpdateResult();
             }
             $updater = $this->makeNestedParser($info->updater(), [get_class($info->model()), $attribute, $nestedKey, $this->model, $this->config]);
             $updateResult = $updater->create($data);
             // if for some reason the update or create was not succesful or
             // did not return a model, dissociate the relationship
             if (!$updateResult->model()) {
                 return $this->makeUpdateResult();
             }
             $this->temporaryIds->setModelForId($temporaryId, $updateResult->model());
             return $updateResult;
         }
         // it has been created, so can reference it here, converting the data to link-only
         $data = [$model->getKeyName() => $model->getKey()];
     }
     $updateId = Arr::get($data, $info->model()->getKeyName());
     // if the key is present, but the data is empty, the relation should be dissociated
     if (empty($data)) {
         return $this->makeUpdateResult();
     }
     // if we're not allowed to perform creates or updates, only handle the link
     // -- and this is not possible, stop the process or make sure it is handled right
     if (!$info->isUpdateAllowed()) {
         // if we cannot create it, we cannot proceed
         if (empty($updateId)) {
             throw (new DisallowedNestedActionException("Not allowed to create new for link-only nested relation"))->setNestedKey($nestedKey);
         }
         // strip everything but the key, so it is treated as a link-only operation
         $data = [$info->model()->getKeyName() => $updateId];
     }
     // get the existing model, if we have an update ID, or null if no match exists
     if (!empty($updateId)) {
         $existingModel = $this->getModelByLookupAtribute($updateId, $info->model()->getKeyName(), get_class($info->model()), $nestedKey, false);
     } else {
         $existingModel = null;
     }
     // if a model for a given 'updateId' does not exist yet, and the model's key is
     // not an incrementing key, this should be treated as an attempt to create a record
     $creatingWithKey = !$info->model()->getIncrementing() && !empty($updateId) && !$existingModel;
     // if this is a link-only operation, mark it
     $onlyLinking = count($data) == 1 && !empty($updateId) && !$creatingWithKey;
     // if we are allowed to update, but only the key is provided, treat this as a link-only operation
     // throw an exception if we couldn't find the model
     if (!$info->isUpdateAllowed() || $onlyLinking) {
         if (!$existingModel) {
             throw (new NestedModelNotFoundException())->setModel(get_class($info->model()))->setNestedKey($nestedKey);
         }
         return $this->makeUpdateResult($existingModel);
     }
     // otherwise, create or update, depending on whether the primary key is present in the data
     // if it is a create operation, make sure we're allowed to
     if ((empty($updateId) || $creatingWithKey) && !$info->isCreateAllowed()) {
         throw (new DisallowedNestedActionException("Not allowed to create new for update-only nested relation"))->setNestedKey($nestedKey);
     }
     $updater = $this->makeNestedParser($info->updater(), [get_class($info->model()), $attribute, $nestedKey, $this->model, $this->config]);
     $updateResult = empty($updateId) || $creatingWithKey ? $updater->create($data) : $updater->update($data, $updateId, $info->model()->getKeyName());
     // if for some reason the update or create was not succesful or
     // did not return a model, dissociate the relationship
     if (!$updateResult->model()) {
         return $this->makeUpdateResult();
     }
     return $updateResult;
 }
 /**
  * @param RelationInfo $info
  * @param string       $attribute   key of attribute
  * @param null|int     $index       if data is plural for this attribute, the index for it
  * @return array
  */
 protected function getNestedRelationValidationRulesForSingleItem(RelationInfo $info, $attribute, $index = null)
 {
     $rules = [];
     $dotKey = $attribute . (null !== $index ? '.' . (int) $index : '');
     $data = array_get($this->data, $dotKey);
     // if the data is scalar, it is treated as the primary key in a link-only operation, which should be allowed
     // if the relation is allowed in nesting at all -- if the data is null, it should be considered a detach
     // operation, which is allowed aswell.
     if (is_scalar($data) || null === $data) {
         // add rule if we know that the primary key should be an integer
         if ($info->model()->getIncrementing()) {
             $rules[$this->getNestedKeyPrefix() . $dotKey] = 'integer';
         }
         return $rules;
     }
     // if not a scalar or null, the only other value allowed is an array
     $rules[$this->getNestedKeyPrefix() . $dotKey] = 'array';
     $keyName = $info->model()->getKeyName();
     $keyIsRequired = false;
     $keyMustExist = false;
     // if it is a link-only or update-only nested relation, require a primary key field
     // it also helps to check whether the key actually exists, to prevent problems with
     // a non-existant non-incrementing keys, which would be interpreted as a create action
     if (!$info->isCreateAllowed()) {
         $keyIsRequired = true;
         $keyMustExist = true;
     } elseif (!$info->model()->getIncrementing()) {
         // if create is allowed, then the primary key is only required for non-incrementing key models,
         // for which it should always be present
         $keyIsRequired = true;
     }
     // if the primary key is not present, this is a create operation, so we must apply the model's create rules
     // otherwise, it's an update operation -- if the model is non-incrementing, however, the create/update
     // distinction depends on whether the given key exists
     if ($info->model()->getIncrementing()) {
         $creating = !array_has($data, $keyName);
     } else {
         $key = array_get($data, $keyName);
         $creating = !$key || !$this->checkModelExistsByLookupAtribute($key, $keyName, get_class($info->model()));
     }
     if (!$creating) {
         $keyMustExist = true;
     }
     // build up rules for primary key
     $keyRules = [];
     if ($info->model()->getIncrementing()) {
         $keyRules[] = 'integer';
     }
     if ($keyIsRequired) {
         $keyRules[] = 'required';
     }
     if ($keyMustExist) {
         $keyRules[] = 'exists:' . $info->model()->getTable() . ',' . $keyName;
     }
     if (count($keyRules)) {
         $rules[$this->getNestedKeyPrefix() . $dotKey . '.' . $keyName] = $keyRules;
     }
     // get and merge rules for model fields by deferring to a nested validator
     /** @var NestedValidatorInterface $validator */
     $validator = $this->makeNestedParser($info->validator(), [get_class($info->model()), $attribute, $this->appendNestedKey($attribute, $index), $this->model, $this->config, $this->modelClass]);
     $rules = $this->mergeInherentRulesWithCustomModelRules($rules, $validator->validationRules($data, $creating));
     return $rules;
 }