protected function withProps($model, array $props): array
    {
        if (!(\is_array($model) || $model instanceof \ArrayAccess)) {
            throw new \InvalidArgumentException('Expect array or object implementing \ArrayAccess');
        }

        // Given an array of props (which may include a definition value), we
        // cycle through each, resolve the value from the model, and apply any
        // conversions to the value as necessary.
        $data = [];
        foreach ($props as $key => $definition) {
            if (\is_int($key)) {
                // This isn't a key => value pair, so there is no definition.
                $key = $definition;
                $definition = null;
            }

            // Convert property to a snake-case version.
            // It may be a nested (dot-notation) key.
            $propKey = StrHelper::snakeCase($key);
            if ($definition instanceof \Closure) {
                // If the definition is a function, execute it on the value
                $value = $definition->bindTo($this)($model, $propKey);
            } else {
                // Format the field according to the definition
                $value = $this->getPropValue($model, $this->parsePropDefinition($propKey, $definition));
            }

            // Set the prop value in the array
            ArrayHelper::mutate($data, $key, $value);
        }

        return $data;
    }