/** * Parse Validation input request data. * * @param $attribute * @param $value * @param $parameters * @return array */ protected function parseJsRemoteRequest($attribute, $value, $parameters) { parse_str("{$value}=", $attr_parts); $attr_parts = is_null($attr_parts) ? [] : $attr_parts; $newAttr = array_keys(Arr::dot($attr_parts)); return [$attribute, array_pop($newAttr), $parameters]; }
/** * The output of the table rows. * * @return array */ private function tableRows() { $allLanguages = $this->manager->languages(); $filesContent = []; $output = []; foreach ($this->files as $fileName => $fileLanguages) { foreach ($fileLanguages as $languageKey => $filePath) { $lines = $filesContent[$fileName][$languageKey] = Arr::dot($this->manager->getFileContent($filePath)); foreach ($lines as $key => $line) { if (!is_array($line) && stripos($line, $this->argument('keyword')) !== false) { $output[$fileName . '.' . $key][$languageKey] = "<bg=yellow;fg=black>{$line}</>"; } } } } // Now that we collected all values that matches the keyword argument // in a close match, we collect the values for the rest of the // languages for the found keys to complete the table view. foreach ($output as $fullKey => $values) { list($fileName, $key) = explode('.', $fullKey, 2); $original = []; foreach ($allLanguages as $languageKey) { $original[$languageKey] = isset($values[$languageKey]) ? $values[$languageKey] : isset($filesContent[$fileName][$languageKey][$key]) ? $filesContent[$fileName][$languageKey][$key] : ''; } // Sort the language values based on language name ksort($original); $output[$fullKey] = array_merge(['key' => "<fg=yellow>{$fullKey}</>"], $original); } return array_values($output); }
/** * Resolve CSV header. * * @return array */ protected function resolveCsvHeader() { $header = []; if (!$this->isEmpty()) { $single = $this->first(); $header = array_keys(Arr::dot($single)); } return $header; }
/** * Prepare bindings for text replacement. * * @param array $replacements * @param string $prefix * @param string $suffix * * @return array */ protected static function prepareBinding(array $replacements = [], $prefix = '{', $suffix = '}') { $replacements = Arr::dot($replacements); $bindings = []; foreach ($replacements as $key => $value) { $bindings["{$prefix}{$key}{$suffix}"] = $value; } return $bindings; }
/** * Get the custom error message from translator. * * @param string $customKey * @return string */ protected function getCustomMessageFromTranslator($customKey) { if (($message = $this->translator->trans($customKey)) !== $customKey) { return $message; } $shortKey = preg_replace('/^lpanel::validation\\.custom\\./', '', $customKey); $customMessages = Arr::dot((array) $this->translator->trans('lpanel::validation.custom')); foreach ($customMessages as $key => $message) { if (Str::contains($key, ['*']) && Str::is($key, $shortKey)) { return $message; } } return $customKey; }
public function index() { Audit::log(Auth::user()->id, trans('admin/settings/general.audit-log.category'), trans('admin/settings/general.audit-log.msg-index')); $page_title = trans('admin/settings/general.page.index.title'); // "Admin | Settings"; $page_description = trans('admin/settings/general.page.index.description'); // "List of Settings"; // $settings = (new SettingModel())->all(); $settings = Setting::all(); $settings = Arr::dot($settings); $settingsFiltered = Utils::FilterOutUserSettings($settings); $settingsFiltered = Arr::sortRecursive($settingsFiltered); return view('admin.settings.index', compact('settingsFiltered', 'page_title', 'page_description')); }
public function setConfig(Application $app, $data) { $db = $app['config']->get('database'); if (!$db) { return false; } $driver = $db['default']; $post_data = ['connections' => [$driver => $data]]; $config = array_merge(Arr::dot($db), Arr::dot($post_data)); $a = []; foreach ($config as $k => $v) { Arr::set($a, $k, $v); } $app['config']->set('database', $a); return $driver; }
public function setConfig(Application $app, DatabaseRequest $request) { $driver = $request->get('driver'); $connections = $this->connections(); $config = array_merge($connections[$driver], $request->get($driver)); if (isset($config['default'])) { unset($config['default']); } $config = array_merge(Arr::dot($app['config']->get('database')), Arr::dot(['default' => $driver, 'connections' => [$driver => $config]])); $a = []; foreach ($config as $k => $v) { Arr::set($a, $k, $v); } $app['config']->set('database', $a); return $driver; }
/** * __construct. * * @method __construct * * @param \Illuminate\Contracts\Config\Repository $repository * @param \Recca0120\Config\Config $model * @param string $config */ public function __construct(Repository $repository, Config $model, Filesystem $filesystem, $config = []) { parent::__construct($repository); $this->original = $repository->all(); $this->model = $model; $this->filesystem = $filesystem; $this->config = $config; $data = value(function () { $file = $this->getStorageFile(); if ($this->filesystem->exists($file) === true) { return json_decode($this->filesystem->get($file), true); } $data = $this->getModel()->value; $this->storeToFile($data); return $data; }); foreach (Arr::dot($data) as $key => $value) { $repository->set($key, $value); } }
/** * Rename the given oldKey to the newKey. * * @return void */ private function renameKey() { try { list($file, $key) = explode('.', $this->argument('oldKey'), 2); } catch (\ErrorException $e) { $this->error('Could not recognize the key you want to rename.'); return; } if (Str::contains($this->argument('newKey'), '.')) { $this->error('Please provide the new key must not contain a dot.'); return; } $newKey = preg_replace('/(\\w+)$/i', $this->argument('newKey'), $key); $files = $this->manager->files()[$file]; $currentValues = []; foreach ($files as $languageKey => $filePath) { $content = Arr::dot($this->manager->getFileContent($filePath)); $currentValues[$languageKey] = isset($content[$key]) ? $content[$key] : ''; } $this->manager->removeKey($file, $key); $this->manager->fillKeys($file, [$newKey => $currentValues]); }
/** * Synchronize keys that exist in a language but not the other. * * @param $translationFiles * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException * @return void */ private function syncKeysBetweenLanguages($translationFiles) { $this->info('Synchronizing language files...'); $filesResults = []; // Here we collect the file results foreach ($translationFiles as $fileName => $languageFiles) { foreach ($languageFiles as $languageKey => $filePath) { $filesResults[$fileName][$languageKey] = $this->manager->getFileContent($filePath); } } $values = Arr::dot($filesResults); $missing = $this->manager->getKeysExistingInALanguageButNotTheOther($values); foreach ($missing as &$missingKey) { list($file, $key) = explode('.', $missingKey, 2); list($key, $language) = explode(':', $key, 2); $this->fillMissingKeys($file, [$key], $language); } }
public static function dot(array $data) { return Arr::dot($data); }
/** * Go through an array field and deal with internal id references * * @param Model $model The eloquent model * @param mixed $receivingId Internal id of the model * @param string $field Field to work on * @param array $fieldData Current field data * @param array $rules Array of match rules * @return array Transformed field data */ protected function toDbIds(Model $model, $receivingId, $field, array $fieldData, array $rules) { // Convert to dot array $dotData = Arr::dot($fieldData); // Iterate through all of the values foreach ($dotData as $path => $data) { $this->state->push($path); // Debug // Match only against data that starts with the internal id prefix if (is_string($data) && strlen($data) > 0 && substr($data, 0, strlen($this->prefix)) === $this->prefix) { // Iterate through the rules and see if any patterns match our data key foreach ($rules as $pattern => $mixed) { if (is_array($mixed)) { // [/regexp/ => [/regexp/ => Model]] is only for content search & replace (see further down) continue; } // Match against exact dot pattern or regexp if it starts with a / if ($path === $pattern || $pattern[0] === "/" && preg_match($pattern, $path) === 1) { // Match - Update (or defer update if needed) the field with the db id if (!$this->bookKeeper->update($model, "{$field}.{$path}", $receivingId, $data)) { // Set to null for now Arr::set($fieldData, $path, null); } else { // It was set directly, update $fieldData with the set value Arr::set($fieldData, $path, Arr::get($model->{$field}, $path)); } } } } elseif (is_string($data)) { // Another possibility: we want to match, search & replace refs in text // Iterate through the rules and see if any patterns match in our data string foreach ($rules as $pattern => $mixed) { if (!is_array($mixed)) { // [/regexp/ => Model] is only for key => @id matching (see above) continue; } // Match against exact dot pattern or regexp if it starts with a / if ($path === $pattern || $pattern[0] === "/" && preg_match($pattern, $path) === 1) { // Key match // Start out with making sure we actually have a string to work on if (!is_string($model->{$field})) { // Nope, let's superimpose the blueprint $model->{$field} = $fieldData; } // Match the string against the rules given in the $mixed array foreach ($mixed as $valuePattern => $fqcn) { // Fetch all matches preg_match_all($valuePattern, $data, $matches); // 0 => ["foo=@1", "foo=@2"] // 1 => ["@1", "@2"] if (count($matches) !== 2) { continue; } if (count($matches[0]) === 0) { continue; } $searchStrings = $matches[0]; $refs = $matches[1]; // Filter out dupes and invalids $uniqueRefs = []; foreach ($refs as $index => $ref) { // Ref must not already be matched and must start with the internal id prefix if (!isset($uniqueRefs[$ref]) && isset($ref[0]) && substr($ref, 0, strlen($this->prefix)) === $this->prefix) { $uniqueRefs[$ref] = true; } else { // Clear from the matches unset($refs[$index]); unset($searchStrings[$index]); } } // Re-set array indices $refs = array_values($refs); $searchStrings = array_values($searchStrings); foreach ($refs as $index => $ref) { $searchString = $searchStrings[$index]; // Go through the matches, try to set directly if possible, defer otherwise if ($this->bookKeeper->searchAndReplace($model, "{$field}.{$path}", $receivingId, $searchString, $ref)) { // It was set directly, update $fieldData with the set value Arr::set($fieldData, $path, Arr::get($model->{$field}, $path)); } } } } } } $this->state->pop(); // Debug } // Return transformed field data with db ids or temporarily set null values return $fieldData; }
/** * Get all ordered modules. * * @param string $direction * * @return array */ public function getOrdered($direction = 'asc') { $modules = Arr::dot($this->all()); uasort($modules, function ($a, $b) use($direction) { if ($a->getOrder() == $b->getOrder()) { return 0; } if ($direction == 'desc') { return $a->getOrder() < $b->getOrder() ? 1 : -1; } return $a->getOrder() > $b->getOrder() ? 1 : -1; }); return $modules; }
/** * Query the payload using dot syntax to find specific data * * @param array $payload * @param string|null $path * * @return mixed */ private function queryPayload($payload, $path = null) { if (is_null($path)) { return $payload; } if (array_key_exists($path, $payload)) { return $payload[$path]; } $dotData = Arr::dot($payload); if (array_key_exists($path, $dotData)) { return $dotData[$path]; } return null; }
/** * Validate an attribute is unique among other values. * * @param string $attribute * @param mixed $value * @param array $parameters * @return bool */ protected function validateDistinct($attribute, $value, $parameters) { $attributeName = $this->getPrimaryAttribute($attribute); $explicitPath = $this->getLeadingExplicitAttributePath($attributeName); $attributeData = $this->extractDataFromPath($explicitPath); $data = Arr::where(Arr::dot($attributeData), function ($value, $key) use($attribute, $attributeName) { return $key != $attribute && Str::is($attributeName, $key); }); return !in_array($value, array_values($data)); }
/** * Filter an array of model attributes to the allowed and existing set. * * @param array $attributes * @param array $allowedKeys * @return array */ protected function filterAttributes(array $attributes = [], array $allowedKeys = []) { $allowedKeys = empty($allowedKeys) ? $this->getFillable() : array_intersect($this->getFillable(), $allowedKeys); $array = array_intersect_key(Arr::dot($attributes, ''), array_flip($allowedKeys)); $return = []; foreach ($array as $key => $value) { Arr::set($return, $key, $value); } return $return; }
/** * Determine if the attribute passes any optional check. * * @param string $attribute * @return bool */ protected function passesOptionalCheck($attribute) { if ($this->hasRule($attribute, ['Sometimes'])) { return array_key_exists($attribute, Arr::dot($this->data)) || in_array($attribute, array_keys($this->data)) || array_key_exists($attribute, $this->files); } return true; }
/** * Get an array of keys that have missing values with a hint * from another language translation file if possible. * * ex: [ ['key' => 'product.color.nl', 'hint' => 'en = "color"'] ] * * @param array $languages * @return array */ private function getMissing(array $languages) { $files = $this->manager->files(); // Array of content of all files indexed by fileName.languageKey $filesResults = []; // The final output of the method $missing = []; // Here we collect the file results foreach ($files as $fileName => $languageFiles) { foreach ($languageFiles as $languageKey => $filePath) { $filesResults[$fileName][$languageKey] = $this->manager->getFileContent($filePath); } } $values = Arr::dot($filesResults); $emptyValues = array_filter($values, function ($value) { return $value == ''; }); // Adding all keys that has values = '' foreach ($emptyValues as $dottedValue => $emptyValue) { list($fileName, $languageKey, $key) = explode('.', $dottedValue, 3); $missing[] = "{$fileName}.{$key}:{$languageKey}"; } $missing = array_merge($missing, $this->manager->getKeysExistingInALanguageButNotTheOther($values)); return $missing; }
/** * Resolve table name. * * @param \Illuminate\Database\Eloquent\Model $entity * @param string|null $name * * @return string|null */ protected function bindWithKey(Model $entity, $name) { if (is_null($name) || strpos($name, '{') === false && strpos($name, '}') === false) { return $name; } $id = $entity->getKey(); if (!isset($this->data[$id])) { $data = array_merge(Arr::dot(['entity' => $entity->toArray()]), compact('id')); $this->data[$id] = $data; } return Str::replace($name, $this->data[$id]); }
/** * The output of the table rows. * * @return array */ private function tableRows() { $output = []; $filesContent = []; foreach ($this->files as $languageKey => $file) { foreach ($filesContent[$languageKey] = Arr::dot($this->manager->getFileContent($file)) as $key => $value) { if (!$this->shouldShowKey($key)) { continue; } $output[$key]['key'] = $key; $output[$key][$languageKey] = $value ?: ''; } } // Now that we have collected all existing values, we are going to fill the // missing ones with emptiness indicators to balance the table structure // and alert developers so that they can take proper actions. foreach ($output as $key => $values) { $original = []; foreach ($this->languages as $languageKey) { $original[$languageKey] = isset($values[$languageKey]) && $values[$languageKey] ? $values[$languageKey] : '<bg=red> MISSING </>'; } // Sort the language values based on language name ksort($original); $output[$key] = array_merge(['key' => "<fg=yellow>{$key}</>"], $original); } return array_values($output); }
/** * Resolve table name. * * @param \Illuminate\Database\Eloquent\Model $entity * @param string|null $name * * @return string|null */ protected function bindWithKey(Model $entity, $name) { if (is_null($name)) { return $name; } $id = $entity->getKey(); if (!isset($this->data[$id])) { $data = Arr::dot(['entity' => $entity->toArray()]); $data['id'] = $id; $this->data[$id] = $data; } return Str::replace($name, $this->data[$id]); }
/** * Unset the item at a given offset. * * @param string $key * @return void */ public function offsetUnset($key) { $result = $this->convert(); Arr::forget($result, $key); $this->items = Arr::dot($result); }
/** * Prepare input data for insert. * * @param $inserts * @return array */ public function prepareInsert($inserts) { $first = current($inserts); if (is_array($first) && Arr::isAssoc($first)) { $inserts = Arr::dot($inserts); } foreach ($inserts as $column => $value) { if (is_null($field = $this->getFieldByColumn($column))) { unset($inserts[$column]); continue; } if (method_exists($field, 'prepare')) { $inserts[$column] = $field->prepare($value); } } $prepared = []; foreach ($inserts as $key => $value) { Arr::set($prepared, $key, $value); } return $prepared; }
/** * Go through the deserialized data recursively * * @param mixed $template Seriplater template/rule * @param mixed $data Scope data * @param string $dotPath Dot path to scope * @return array Resulting entity data to create with the repository * @TODO Needs refactoring * @throws IntegrityException when illegal structures or missing pieces are encountered */ private function walkDeserializedData($template, $data, $dotPath = "") { if (is_array($template)) { $entityData = []; foreach ($template as $field => $content) { if ($content instanceof RuleInterface) { if ($content->isId() && isset($data["_id"])) { $this->idName = $data["_id"]; continue; } elseif ($content->isHasMany()) { continue; } elseif ($content->isConditions()) { // If we've got a truthy condition that resolves to an inheritance, we need to inherit $value = $content->getValue(); $conditionsField = $value["field"]; $cases = $value["cases"]; $defaultCase = $value["defaultCase"]; if (!isset($this->toUnserialize[$conditionsField])) { throw new IntegrityException("Required conditions field '{$conditionsField}' missing'"); } $fieldsToInherit = []; $recurseIntoRule = true; $caseMatch = false; foreach ($cases as $case => $rule) { if ($this->toUnserialize[$conditionsField] == $case) { $caseMatch = true; if ($rule instanceof RuleInterface && $rule->isInherited()) { $fieldsToInherit = $rule->getValue(); $recurseIntoRule = false; break; } } } // Inheritance detected if (!$recurseIntoRule) { // Inherit value from parent $foundInheritance = null; // Go through prioritized inheritance array foreach ($fieldsToInherit as $fieldToInherit) { if (isset($this->inherited[$fieldToInherit])) { $foundInheritance = $this->inherited[$fieldToInherit]; break; } } // If no inheritance was found if (is_null($foundInheritance)) { throw new IntegrityException("Required inheritance to field '{$field}' wasn't supplied"); } $entityData[$field] = $foundInheritance; continue; } // If default case if (!$caseMatch) { if (is_null($defaultCase)) { throw new IntegrityException("No conditions matched, and no default case provided"); } elseif ($defaultCase->isInherited()) { // Default case is an inheritance // Inherit value from parent $fieldsToInherit = $defaultCase->getValue(); $foundInheritance = null; // Go through prioritized inheritance array foreach ($fieldsToInherit as $fieldToInherit) { if (isset($this->inherited[$fieldToInherit])) { $foundInheritance = $this->inherited[$fieldToInherit]; break; } } // If no inheritance was found if (is_null($foundInheritance)) { throw new IntegrityException("Required inheritance to field '{$field}' wasn't supplied"); } $entityData[$field] = $foundInheritance; continue; } } } elseif ($content->isInherited()) { // Inherit value from parent $fieldsToInherit = $content->getValue(); $foundInheritance = null; // Go through prioritized inheritance array foreach ($fieldsToInherit as $fieldToInherit) { if (isset($this->inherited[$fieldToInherit])) { $foundInheritance = $this->inherited[$fieldToInherit]; break; } } // If no inheritance was found if (is_null($foundInheritance)) { throw new IntegrityException("Required inheritance to field '{$field}' wasn't supplied"); } $entityData[$field] = $foundInheritance; continue; } elseif ($content->isIncrementing()) { // Inherit incrementing value from parent $entityData[$field] = $this->inherited["@{$field}"]; continue; } elseif (!isset($data[$field]) && $content->isOptional()) { // Optional - and missing - value encountered continue; } elseif (!array_key_exists($field, $data)) { throw new IntegrityException("Required field '{$field}' missing"); } } elseif (is_scalar($content)) { // Scalar value, just use it and move on $entityData[$field] = $content; continue; } elseif (!array_key_exists($field, $data)) { throw new IntegrityException("Required field '{$field}' missing"); } $fieldValue = $this->walkDeserializedData($content, $data[$field], $this->mergeDotPaths($dotPath, $field)); if ($fieldValue !== ["_null"]) { $entityData[$field] = $fieldValue; } } return $entityData; } else { if (is_scalar($template)) { // Scalar field, return it right away return $template; } elseif ($template->isValue()) { // Value field, return the scope data return $data; } elseif ($template->isInherited() || $template->isIncrementing() || $template->isHasMany()) { return ["_null"]; } elseif ($template->isReference()) { // Reference field, save for putting into the id resolver later if ($data === null) { // Reference to a null value if ($template->isNullable()) { // Allowed return null; } else { // Not allowed throw new IntegrityException("Encountered NULL when deserializing a reference at {$dotPath}"); } } $fallback = $template->getValue()["fallback"]; $this->updatesToDefer[] = ["internalId" => $data["_ref"], "fullDotPath" => $dotPath, "fallback" => $fallback]; if (is_null($fallback)) { return 0; } else { return $fallback; } } elseif ($template->isConditions()) { // Conditional field, go through the conditions and recurse $value = $template->getValue(); $field = $value["field"]; $cases = $value["cases"]; $defaultCase = $value["defaultCase"]; // Go through the cases foreach ($cases as $case => $rule) { if ($this->toUnserialize[$field] == $case) { return $this->walkDeserializedData($rule, $data, $dotPath); } } // Default case return $this->walkDeserializedData($defaultCase, $data, $dotPath); } elseif ($template->isDeep()) { // Deep field, use regexp to find and apply rules $finders = $template->getValue(); $newData = $data; $dotData = Arr::dot($data); foreach ($finders as $pattern => $rule) { foreach ($dotData as $innerDotPath => $innerDotValue) { // Back up one step if we're on ._ref if (preg_match("/\\._ref\$/", $innerDotPath) === 1) { $innerDotPath = substr($innerDotPath, 0, -1 * strlen("._ref")); $innerDotValue = ["_ref" => $innerDotValue]; } if (preg_match($pattern, $innerDotPath) === 1) { Arr::set($newData, $innerDotPath, $this->walkDeserializedData($rule, $innerDotValue, $this->mergeDotPaths($dotPath, $innerDotPath))); } } } return $newData; } else { throw new IntegrityException("Invalid template rule"); } } }
/** * Get the custom error message from translator. * * @param string $customKey * @return string */ protected function getCustomMessageFromTranslator($customKey) { $shortKey = str_replace('validation.custom.', '', $customKey); $customMessages = Arr::dot((array) $this->translator->trans('validation.custom')); foreach ($customMessages as $key => $message) { if ($key === $shortKey || Str::contains($key, ['*']) && Str::is($key, $shortKey)) { return $message; } } return $customKey; }
/** * Iterate over the given patterned rules and apply them to the given field data * * @param array $fieldData Field data * @param array $rules Associative array of rules in the form of: * [ * "some_pattern" => "Namespace\To\Model", * "/some\\.regexp_?pattern$/" => "Namespace\To\Model", * ] * @return array Transformed field data */ protected function toInternalIds(array $fieldData, array $rules) { // Convert to dot array $dotData = Arr::dot($fieldData); // Iterate through all of the values foreach ($dotData as $path => $data) { $this->state->push($path); // Debug // Only numeric data can be keys @todo if (is_numeric($data) && $data > 0) { // Iterate through the rules and see if any patterns match our data key foreach ($rules as $pattern => $mixed) { if (is_array($mixed)) { // [/regexp/ => [/regexp/ => Model]] is only for content search & replace (see further down) continue; } // Match against exact dot pattern or regexp if it starts with a / if ($path === $pattern || $pattern[0] === "/" && preg_match($pattern, $path) === 1) { // Match - Replace with an internal id Arr::set($fieldData, $path, $this->getIdRef($mixed, $data)); } } } elseif (is_string($data)) { // Another possibility: we want to match, search & replace refs in text // Iterate through the rules and see if any patterns match in our data string foreach ($rules as $pattern => $mixed) { if (!is_array($mixed)) { // [/regexp/ => Model] is only for key => @id matching (see above) continue; } // Match against exact dot pattern or regexp if it starts with a / if ($path === $pattern || $pattern[0] === "/" && preg_match($pattern, $path) === 1) { // Key match // Match the string against the rules given in the $mixed array foreach ($mixed as $valuePattern => $fqcn) { // Fetch all matches preg_match_all($valuePattern, $data, $matches); // 0 => ["foo=1", "foo=2"] // 1 => ["1", "2"] if (count($matches) !== 2) { continue; } if (count($matches[0]) === 0) { continue; } $searchStrings = $matches[0]; $ids = $matches[1]; // Get the field data to work on $localFieldData = Arr::get($fieldData, $path); // Iterate through the search strings to replace the ids with internal references foreach ($searchStrings as $index => $searchString) { // Create replacement string $replacement = str_replace($ids[$index], $this->getIdRef($fqcn, $ids[$index]), $searchString); // Actually replace the matched id strings with internal ref'd strings $localFieldData = str_replace($searchString, $replacement, $localFieldData); } Arr::set($fieldData, $path, $localFieldData); } } } } $this->state->pop(); // Debug } // Returned transformed data return $fieldData; }
/** * Flatten a multi-dimensional associative array with dots. * * @param array $array * @param string $prepend * @return array */ function array_dot($array, $prepend = '') { return Arr::dot($array, $prepend); }
/** * Go through the unserialized data recursively * * @param mixed $template Seriplater template/rule * @param mixed $data Scope data * @param string $dotPath Dot path to scope * @return array Serialized results * @throws IntegrityException when illegal structures or missing pieces are encountered */ private function walkUnserializedData($template, $data, $dotPath = "") { if (is_array($template)) { $serialized = []; foreach ($template as $field => $content) { if ($content instanceof RuleInterface) { if (!isset($data[$field]) && $content->isOptional() || $content->isHasMany() || $content->isInherited()) { // Skip serializing if field is missing & optional, or a hasMany field, or field is inherited continue; } elseif ($content->isId()) { // If it's an id field set the _id and keep going $serialized["_id"] = $this->idFactory->get($content->getValue(), $data[$field]); continue; } elseif (!array_key_exists($field, $data)) { // Missing field with no excuses throw new IntegrityException("Required field '{$field}' missing"); } } elseif (!array_key_exists($field, $data)) { // Missing field with no excuses throw new IntegrityException("Required field '{$field}' missing"); } $fieldValue = $this->walkUnserializedData($content, $data[$field], $this->mergeDotPaths($dotPath, $field)); if ($fieldValue !== ["_null"]) { $serialized[$field] = $fieldValue; } } return $serialized; } else { if (is_scalar($template)) { // Scalar value, just save it as is return $template; } elseif ($template->isValue()) { // Regular value return $data; } elseif ($template->isInherited() || $template->isIncrementing() || $template->isHasMany()) { // Skip fields with inheritance, increments or hasMany return ["_null"]; } elseif ($template->isReference()) { // Reference if ($data === null) { // Reference to a null value if ($template->isNullable()) { // Allowed return null; } else { // Not allowed throw new IntegrityException("Encountered NULL when parsing a '" . $template->getValue()["entityName"] . "'' reference"); } } if ($template->isCollection()) { // An array of references $refs = []; foreach ($data as $dbId) { $refs[] = ["_ref" => $this->idFactory->get($template->getValue()["entityName"], $dbId)]; } return $refs; } else { // A single reference return ["_ref" => $this->idFactory->get($template->getValue()["entityName"], $data)]; } } elseif ($template->isConditions()) { // Conditions rule $value = $template->getValue(); $field = $value["field"]; $cases = $value["cases"]; $defaultCase = $value["defaultCase"]; if (!isset($this->toSerialize[$field])) { throw new IntegrityException("Required conditions field '{$field}' missing'"); } foreach ($cases as $case => $rule) { if ($this->toSerialize[$field] == $case) { return $this->walkUnserializedData($rule, $data, $dotPath); } } if (is_null($defaultCase)) { throw new IntegrityException("No conditions matched, and no default case provided"); } else { return $this->walkUnserializedData($rule, $data, $dotPath); } } elseif ($template->isDeep()) { // Regexp deep rule $finders = $template->getValue(); $newData = $data; $dotData = Arr::dot($data); foreach ($finders as $pattern => $rule) { foreach ($dotData as $innerDotPath => $innerDotValue) { if (preg_match($pattern, $innerDotPath) === 1) { Arr::set($newData, $innerDotPath, $this->walkUnserializedData($rule, $innerDotValue, $this->mergeDotPaths($dotPath, $innerDotPath))); } } } return $newData; } else { throw new IntegrityException("Invalid template rule"); } } }