/** * Insert any data after object is retrieved such as computed aliases and converting * sub-resource fields into child objects. * * @param FREST\Resource $resource * @param array $objects * @param array $readSettings * @param string $resourceAlias * * @throws \FREST\Exception * @throws Exception */ protected function parseObjects($resource, &$objects, $readSettings, $resourceAlias = NULL) { // stores the read Setting that are just an ComputedRead $computedReadSettings = array(); $timerInstance = isset($resourceAlias) ? $resourceAlias . '-parse' : 'parse'; if (isset($readSettings)) { /** @var Setting\Read $readSetting */ foreach ($readSettings as $readSetting) { $this->frest->startTimingForLabel(Type\Timing::POST_PROCESSING, $timerInstance); $alias = $readSetting->getAlias(); $partialSubKey = isset($resourceAlias) ? "{$resourceAlias}.{$alias}" : $alias; if ($readSetting instanceof Setting\ComputedRead) { $computedReadSettings[$alias] = $readSetting; } else { if ($readSetting instanceof Setting\PluralResourceRead) { /** @var Setting\PluralResourceRead $readSetting */ $parameters = $readSetting->getParameters(); // overwrite 'fields' parameter if partial syntax found $newFields = $this->generateNewFields($partialSubKey); if (isset($newFields)) { $parameters['fields'] = implode(',', $newFields); } $requiredAliases = $readSetting->getRequiredAliases(); $loadedResource = $this->getLoadedResource($readSetting->getResourceName()); foreach ($objects as &$object) { $requestParameters = array(); foreach ($parameters as $field => $parameter) { if (isset($requiredAliases[$field])) { $requiredAlias = $requiredAliases[$field]; $requiredAliasValuePlaceholder = $loadedResource->injectValue($requiredAlias); $parameter = str_replace($requiredAliasValuePlaceholder, $object->{$requiredAlias}, $parameter); } $requestParameters[$field] = $parameter; } $this->frest->stopTimingForLabel(Type\Timing::POST_PROCESSING, $timerInstance); $request = new PluralRead($this->frest, $requestParameters, NULL, $readSetting->getAlias()); $request->setWasInternallyLoaded(TRUE); $request->setupWithResource($loadedResource); /** @var Result\PluralRead $result */ $result = $request->generateResult(); $this->frest->startTimingForLabel(Type\Timing::POST_PROCESSING, $timerInstance); $object->{$alias} = $result->getResourceObjects(); } } else { if ($readSetting instanceof Setting\SingularResourceRead) { /** @var Setting\SingularResourceRead $readSetting */ $loadedResource = $this->getLoadedResource($readSetting->getResourceName()); if (isset($this->partialSubReadSettings[$partialSubKey])) { $subReadSettings = $this->partialSubReadSettings[$partialSubKey]; $this->addRequiredReadSettings($loadedResource, $subReadSettings); } else { $subReadSettings = $this->getLoadedResourceReadSettings($loadedResource, $readSetting); } $subObjects = array(); // remove table-prefixed properties on object as they should be on a sub-object instead foreach ($objects as &$object) { if (!isset($object->{$alias})) { $object->{$alias} = new \stdClass(); $subObjects[] =& $object->{$alias}; } if (!isset($subReadSettings)) { continue; } foreach ($subReadSettings as $subReadSetting) { if ($subReadSetting instanceof Setting\FieldRead) { /** @var Setting\FieldRead $subReadSetting */ $subAlias = $subReadSetting->getAlias(); $subField = $loadedResource->getFieldForAlias($subAlias); $subTable = $loadedResource->getTableForField($subField); $subTableKey = isset($resourceAlias) ? "{$subTable}-{$resourceAlias}-{$alias}" : "{$subTable}-{$alias}"; $subTableAbbrv = $this->getTableAbbreviation($subTableKey); $subProperty = "{$subTableAbbrv}_{$subAlias}"; $subValue = $object->{$subProperty}; $object->{$alias}->{$subAlias} = $subValue; unset($object->{$subProperty}); } else { if ($subReadSetting instanceof Setting\SingularResourceRead) { // move properties of object that should belong to subObject (only nests once? idk) $subLoadedResource = $this->getLoadedResource($subReadSetting->getResourceName()); $subReadAlias = $subReadSetting->getAlias(); $partialDeepKey = isset($resourceAlias) ? "{$resourceAlias}.{$alias}.{$subReadAlias}" : "{$alias}.{$subReadAlias}"; if (isset($this->partialSubReadSettings[$partialDeepKey])) { $deepReadSettings = $this->partialSubReadSettings[$partialDeepKey]; $this->addRequiredReadSettings($subLoadedResource, $deepReadSettings); } else { $deepReadSettings = $this->getLoadedResourceReadSettings($subLoadedResource, $subReadSetting); } foreach ($deepReadSettings as $deepReadSetting) { if ($deepReadSetting instanceof Setting\FieldRead) { $deepAlias = $deepReadSetting->getAlias(); $deepField = $subLoadedResource->getFieldForAlias($deepAlias); $deepTable = $subLoadedResource->getTableForField($deepField); $deepTableKey = "{$deepTable}-{$alias}-{$subReadAlias}"; $deepTableAbbrv = $this->getTableAbbreviation($deepTableKey); $deepProperty = "{$deepTableAbbrv}_{$deepAlias}"; $deepValue = $object->{$deepProperty}; $object->{$alias}->{$deepProperty} = $deepValue; unset($object->{$deepProperty}); } } } } } } // parse sub-objects as well $subAlias = $readSetting->getAlias(); $subResourceAlias = isset($resourceAlias) ? "{$resourceAlias}.{$subAlias}" : $subAlias; $this->parseObjects($loadedResource, $subObjects, $subReadSettings, $subResourceAlias); } else { if ($readSetting instanceof Setting\FieldRead) { /** @var Setting\FieldRead $readSetting */ $fieldSetting = $resource->getFieldSettingForAlias($alias); $variableType = $fieldSetting->getVariableType(); foreach ($objects as &$object) { $value = Type\Variable::castValue($object->{$alias}, $variableType); $filterFunction = $readSetting->getFilterFunction(); if (isset($filterFunction)) { if (!method_exists($resource, $filterFunction)) { throw new FREST\Exception(FREST\Exception::FilterFunctionMissing, "Function name: '{$filterFunction}', resource: '{$resource->getName()}'"); } $value = $resource->{$filterFunction}($value); } $object->{$alias} = $value; } } } } } $this->frest->stopTimingForLabel(Type\Timing::POST_PROCESSING, $timerInstance); } } $this->frest->startTimingForLabel(Type\Timing::POST_PROCESSING, $timerInstance); // use first object as reference to see what aliases have been set so far (used for computed aliases below) $oldObjects = reset($objects); $refObject =& $oldObjects; /* // generate a list of aliases that have so-far been set on each object (used for computed aliases) $aliasesDefinedOnObjects = array(); $objectVars = get_object_vars($firstObject); foreach ($objectVars as $alias=>$value) { if (isset($value)) { $aliasesDefinedOnObjects[$alias] = $alias; } } */ // computed aliases $lastComputedReadSettingCount = count($computedReadSettings); $resourceComputer = NULL; if ($lastComputedReadSettingCount > 0) { $frestConfig = $this->getFREST()->getConfig(); $formattedResourceName = ucfirst($resource->getName()); $resourceDir = $frestConfig->getResourceDirectory(); $computerClassPath = "{$resourceDir}/{$formattedResourceName}.php"; $computerClassName = "\\FREST\\Computer\\{$formattedResourceName}"; /** @noinspection PhpIncludeInspection */ require_once $computerClassPath; if (!class_exists($computerClassName, FALSE)) { throw new Exception(Exception::Config, "Class '{$formattedResourceName}' not found in file '{$classPath}'"); } $resourceComputer = new $computerClassName($frestConfig->getContext()); } $failedComputingSettings = FALSE; while ($lastComputedReadSettingCount > 0) { /** @var Setting\ComputedRead $computedReadSetting */ foreach ($computedReadSettings as $computedReadSetting) { $alias = $computedReadSetting->getAlias(); // determine if all aliases required for this computed alias have been defined // (should only NOT be set if the required alias is also a computed column and // hasn't been computed yet) $hasAllAliasesRequired = TRUE; $requiredAliases = $computedReadSetting->getRequiredAliases(); foreach ($requiredAliases as $requiredAlias) { $subAliases = NULL; $requiredAliasFromPartial = self::getHandleAndValues($requiredAlias, $subAliases) ?: $requiredAlias; // TODO: error check required alias partial syntax if (!isset($refObject->{$requiredAliasFromPartial})) { $hasAllAliasesRequired = FALSE; break; } if (isset($subAliases)) { foreach ($subAliases as $subAlias) { if (!isset($refObject->{$requiredAliasFromPartial}->{$subAlias})) { $hasAllAliasesRequired = FALSE; break; } } } if (!$hasAllAliasesRequired) { break; } } // move on to the next computed alias (assuming there is one) // we'll compute this alias later once its required/computed alias // is determined if (!$hasAllAliasesRequired) { continue; } $function = $computedReadSetting->getFunction(); if (!method_exists($resourceComputer, $function)) { throw new FREST\Exception(FREST\Exception::ComputationFunctionMissing, "The function '{$function}' is not defined in resource computer '{$resource->getName()}'"); } // keep track of what aliases have been defined for use by other computed aliases unset($computedReadSettings[$alias]); foreach ($objects as &$object) { $object->{$alias} = $resourceComputer->{$function}($object); } } // if we run through loop and no aliases have been computed, break out of loop and fail $currentComputedReadSettingCount = count($computedReadSettings); if ($currentComputedReadSettingCount >= $lastComputedReadSettingCount) { $failedComputingSettings = TRUE; break; } $lastComputedReadSettingCount = $currentComputedReadSettingCount; } if ($failedComputingSettings) { throw new FREST\Exception(FREST\Exception::Config, 'All computed aliases could not be computed. Check your config and make sure there are no malformed requiredField settings'); } $resourceName = $resource->getName(); if (isset($readSettings)) { foreach ($readSettings as $readSetting) { $alias = $readSetting->getAlias(); // remove property if added only by requirement of other properties $subAliasesToRemove = isset($this->requiredAliasesAdded[$resourceName][$alias]) ? $this->requiredAliasesAdded[$resourceName][$alias] : NULL; if (isset($subAliasesToRemove)) { if (is_array($subAliasesToRemove)) { foreach ($objects as &$object) { foreach ($subAliasesToRemove as $subAlias) { unset($object->{$alias}->{$subAlias}); } $isEmpty = count((array) $object->{$alias}) == 0; if ($isEmpty) { unset($object->{$alias}); } } } else { foreach ($objects as &$object) { unset($object->{$alias}); } } } } } $this->frest->stopTimingForLabel(Type\Timing::POST_PROCESSING, $timerInstance); }
/** * @param FREST\Resource $resource * @return array * @throws FREST\Exception */ private function generateCreateSpecs($resource) { $createSpecs = array(); $createSettings = $resource->getCreateSettings(); if (isset($this->resourceID)) { /** @var Setting\Field $idFieldSetting */ $idFieldName = $this->resource->getIDField($idFieldSetting); $idAlias = $this->resource->getAliasForField($idFieldName); $idCreateSpec = new Spec\Create($idAlias, $idFieldName, $this->resourceID, $idFieldSetting->getVariableType()); $createSpecs[$idAlias] = $idCreateSpec; } /** @var Setting\Create $createSetting */ foreach ($createSettings as $createSetting) { $alias = $createSetting->getAlias(); if (isset($this->parameters[$alias])) { $value = $this->parameters[$alias]; $field = $resource->getFieldForAlias($alias); $fieldSetting = $resource->getFieldSettingForAlias($alias); $variableType = $fieldSetting->getVariableType(); // Type checking $castedValue = Type\Variable::castValue($value, $variableType); if (!isset($castedValue)) { $typeString = Type\Variable::getString($variableType); throw new FREST\Exception(FREST\Exception::InvalidType, "Expecting '{$alias}' to be of type '{$typeString}' but received '{$value}'"); } // Condition Func $conditionFunction = $createSetting->getConditionFunction(); if (isset($conditionFunction)) { if (!method_exists($resource, $conditionFunction)) { throw new FREST\Exception(FREST\Exception::ConditionFunctionMissing, "Function name: '{$conditionFunction}', resource: '{$this->resource->getName()}'"); } $isValueValid = $resource->{$conditionFunction}($castedValue); if (!$isValueValid) { throw new FREST\Exception(FREST\Exception::InvalidFieldValue, "Field: '{$alias}'"); } } // Filter Func $filterFunction = $createSetting->getFilterFunction(); if (isset($filterFunction)) { if (!method_exists($resource, $filterFunction)) { throw new FREST\Exception(FREST\Exception::FilterFunctionMissing, "Function name: '{$filterFunction}', resource: '{$resource->getName()}'"); } $castedValue = $resource->{$filterFunction}($castedValue); } $createSpec = new Spec\Create($alias, $field, $castedValue, $variableType); $createSpecs[$alias] = $createSpec; } else { if ($createSetting->getRequired()) { // get list of all parameters required but not set $missingParameters = array(); /** @var Setting\Create $aCreateSetting */ foreach ($createSettings as $aCreateSetting) { $alias = $aCreateSetting->getAlias(); if (!isset($this->parameters[$alias]) && $aCreateSetting->getRequired()) { $missingParameters[] = $alias; } } $missingParametersString = implode(', ', $missingParameters); throw new FREST\Exception(FREST\Exception::MissingRequiredParams, "Missing parameters: {$missingParametersString}"); } } } if (count($createSpecs) > 0) { return $createSpecs; } return NULL; }
/** * @param FREST\Resource $resource * @return array * @throws FREST\Exception */ private function generateUpdateSpecs($resource) { $updateSpecs = array(); $updateSettings = $resource->getUpdateSettings(); /** @var Setting\Update $updateSetting */ foreach ($updateSettings as $updateSetting) { $alias = $updateSetting->getAlias(); if (isset($this->parameters[$alias])) { $value = $this->parameters[$alias]; $field = $resource->getFieldForAlias($alias); $fieldSetting = $resource->getFieldSettingForAlias($alias); $variableType = $fieldSetting->getVariableType(); // Type checking $castedValue = Type\Variable::castValue($value, $variableType); if (!isset($castedValue)) { $typeString = Type\Variable::getString($variableType); throw new FREST\Exception(FREST\Exception::InvalidType, "Expecting '{$alias}' to be of type '{$typeString}' but received '{$value}'"); } // Condition Func $conditionFunction = $updateSetting->getConditionFunction(); if (isset($conditionFunction)) { if (!method_exists($resource, $conditionFunction)) { throw new FREST\Exception(FREST\Exception::ConditionFunctionMissing, "Function name: '{$conditionFunction}', resource: '{$resource->getName()}'"); } $isValueValid = $resource->{$conditionFunction}($castedValue); if (!$isValueValid) { throw new FREST\Exception(FREST\Exception::InvalidFieldValue, "Field: '{$alias}'"); } } // Filter Func $filterFunction = $updateSetting->getFilterFunction(); if (isset($filterFunction)) { if (!method_exists($resource, $filterFunction)) { throw new FREST\Exception(FREST\Exception::FilterFunctionMissing, "Function name: '{$filterFunction}', resource: '{$resource->getName()}'"); } $castedValue = $resource->{$filterFunction}($castedValue); } $updateSpec = new Spec\Update($alias, $field, $castedValue, $variableType); $updateSpecs[$alias] = $updateSpec; } } return $updateSpecs; }