Completed
Push — master ( dcfc04...697864 )
by Neomerx
04:30
created

FluteSettings::createModelToSchemaMap()   B

Complexity

Conditions 7
Paths 2

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 32
ccs 18
cts 18
cp 1
rs 8.4746
c 0
b 0
f 0
cc 7
nc 2
nop 2
crap 7
1
<?php namespace Limoncello\Flute\Package;
2
3
use Limoncello\Common\Reflection\ClassIsTrait;
4
use Limoncello\Contracts\Application\ApplicationConfigurationInterface as A;
5
use Limoncello\Contracts\Settings\Packages\FluteSettingsInterface;
6
use Limoncello\Flute\Contracts\Schema\SchemaInterface;
7
use Limoncello\Flute\Contracts\Validation\FormRulesInterface;
8
use Limoncello\Flute\Contracts\Validation\JsonApiDataRulesInterface;
9
use Limoncello\Flute\Contracts\Validation\JsonApiQueryRulesInterface;
10
use Limoncello\Flute\L10n\Messages;
11
use Limoncello\Flute\Validation\Form\Execution\FormRulesSerializer;
12
use Limoncello\Flute\Validation\JsonApi\Execution\JsonApiDataRulesSerializer;
13
use Limoncello\Flute\Validation\JsonApi\Execution\JsonApiQueryRulesSerializer;
14
use Limoncello\Flute\Validation\JsonApi\Rules\DefaultQueryValidationRules;
15
use Limoncello\Validation\Execution\BlockSerializer;
16
use Neomerx\JsonApi\Exceptions\JsonApiException;
17
use ReflectionException;
18
19
/**
20
 * @package Limoncello\Flute
21
 */
22
abstract class FluteSettings implements FluteSettingsInterface
23
{
24
    use ClassIsTrait;
25
26
    /**
27
     * Namespace for string resources.
28
     */
29
    const GENERIC_NAMESPACE = Messages::RESOURCES_NAMESPACE;
30
31
    /**
32
     * Namespace for string resources.
33
     */
34
    public const VALIDATION_NAMESPACE = 'Limoncello.Flute.Validation';
35
36
    /**
37
     * Default page size.
38
     */
39
    public const DEFAULT_PAGE_SIZE = 10;
40
41
    /**
42
     * Default page size.
43
     */
44
    public const DEFAULT_MAX_PAGE_SIZE = 30;
45
46
    /**
47
     * Default JSON API version.
48
     */
49
    public const DEFAULT_JSON_API_VERSION = '1.0';
50
51
    /** Serialized validation data index */
52
    protected const JSON_API_DATA_VALIDATION_RULES_SERIALIZED = 0;
53
54
    /** Serialized validation data index */
55
    protected const JSON_API_QUERIES_VALIDATION_RULES_SERIALIZED = self::JSON_API_DATA_VALIDATION_RULES_SERIALIZED + 1;
56
57
    /**
58
     * @param array $appConfig
59
     *
60
     * @return array
61
     *
62
     * @throws ReflectionException
63
     */
64 30
    final public function get(array $appConfig): array
65
    {
66 30
        $defaults = $this->getSettings();
67
68 30
        $defaults[static::KEY_ROUTES_FOLDER]          = $appConfig[A::KEY_ROUTES_FOLDER];
69 30
        $defaults[static::KEY_WEB_CONTROLLERS_FOLDER] = $appConfig[A::KEY_WEB_CONTROLLERS_FOLDER];
70
71 30
        $apiFolder            = $defaults[static::KEY_API_FOLDER] ?? null;
72 30
        $valRulesFolder       = $defaults[static::KEY_JSON_VALIDATION_RULES_FOLDER] ?? null;
73 30
        $jsonCtrlFolder       = $defaults[static::KEY_JSON_CONTROLLERS_FOLDER] ?? null;
74 30
        $schemasFolder        = $defaults[static::KEY_SCHEMAS_FOLDER] ?? null;
75 30
        $schemasFileMask      = $defaults[static::KEY_SCHEMAS_FILE_MASK] ?? null;
76 30
        $jsonDataValFolder    = $defaults[static::KEY_JSON_VALIDATORS_FOLDER] ?? null;
77 30
        $jsonDataValFileMask  = $defaults[static::KEY_JSON_VALIDATORS_FILE_MASK] ?? null;
78 30
        $formsValFolder       = $defaults[static::KEY_FORM_VALIDATORS_FOLDER] ?? null;
79 30
        $formsValFileMask     = $defaults[static::KEY_FORM_VALIDATORS_FILE_MASK] ?? null;
80 30
        $jsonQueryValFolder   = $defaults[static::KEY_QUERY_VALIDATORS_FOLDER] ?? null;
81 30
        $jsonQueryValFileMask = $defaults[static::KEY_QUERY_VALIDATORS_FILE_MASK] ?? null;
82
83 30
        assert(
84 30
            $apiFolder !== null && empty(glob($apiFolder)) === false,
85 30
            "Invalid API folder `$apiFolder`."
86
        );
87 30
        assert(
88 30
            $valRulesFolder !== null && empty(glob($valRulesFolder)) === false,
89 30
            "Invalid validation rules folder `$valRulesFolder`."
90
        );
91 30
        assert(
92 30
            $jsonCtrlFolder !== null && empty(glob($jsonCtrlFolder)) === false,
93 30
            "Invalid JSON API controllers' folder `$jsonCtrlFolder`."
94
        );
95 30
        assert(
96 30
            $schemasFolder !== null && empty(glob($schemasFolder)) === false,
97 30
            "Invalid Schemas folder `$schemasFolder`."
98
        );
99 30
        assert(empty($schemasFileMask) === false, "Invalid Schemas file mask `$schemasFileMask`.");
100 30
        assert(
101 30
            $jsonDataValFolder !== null && empty(glob($jsonDataValFolder)) === false,
102 30
            "Invalid JSON Validators folder `$jsonDataValFolder`."
103
        );
104 30
        assert(empty($jsonDataValFileMask) === false, "Invalid JSON Validators file mask `$jsonDataValFileMask`.");
105 30
        assert(
106 30
            $formsValFolder !== null && empty(glob($formsValFolder)) === false,
107 30
            "Invalid Forms Validators folder `$formsValFolder`."
108
        );
109 30
        assert(empty($formsValFileMask) === false, "Invalid Forms Validators file mask `$formsValFileMask`.");
110 30
        assert(
111 30
            $jsonQueryValFolder !== null && empty(glob($jsonQueryValFolder)) === false,
112 30
            "Invalid Query Validators folder `$jsonQueryValFolder`."
113
        );
114 30
        assert(empty($jsonQueryValFileMask) === false, "Invalid Query Validators file mask `$jsonQueryValFileMask`.");
115
116 30
        $schemasPath         = $schemasFolder . DIRECTORY_SEPARATOR . $schemasFileMask;
117 30
        $jsonDataValPath     = $jsonDataValFolder . DIRECTORY_SEPARATOR . $jsonDataValFileMask;
118 30
        $formsValidatorsPath = $formsValFolder . DIRECTORY_SEPARATOR . $formsValFileMask;
119 30
        $jsonQueryValPath    = $jsonQueryValFolder . DIRECTORY_SEPARATOR . $jsonQueryValFileMask;
120
121 30
        $requireUniqueTypes = $defaults[static::KEY_SCHEMAS_REQUIRE_UNIQUE_TYPES] ?? true;
122
123 30
        $doNotLogExceptions = $defaults[static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST] ?? [];
124 30
        unset($defaults[static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST]);
125
126 30
        [$modelToSchemaMap, $typeToSchemaMap] = $this->createSchemaMapping($schemasPath, $requireUniqueTypes);
0 ignored issues
show
Bug introduced by
The variable $modelToSchemaMap does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $typeToSchemaMap does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
127
128
        return $defaults + [
129 30
                static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST__AS_KEYS => array_flip($doNotLogExceptions),
130
131 30
                static::KEY_MODEL_TO_SCHEMA_MAP => $modelToSchemaMap,
132 30
                static::KEY_TYPE_TO_SCHEMA_MAP  => $typeToSchemaMap,
133
134 30
                static::KEY_JSON_VALIDATION_RULE_SETS_DATA =>
135 30
                    $this->serializeJsonValidationRules($jsonDataValPath, $jsonQueryValPath),
136
137 30
                static::KEY_ATTRIBUTE_VALIDATION_RULE_SETS_DATA =>
138 30
                    $this->serializeFormValidationRules($formsValidatorsPath),
139
            ];
140
    }
141
142
    /**
143
     * @return array
144
     */
145 30
    protected function getSettings(): array
146
    {
147 30
        $jsonOptions = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES;
148
149
        return [
150 30
            static::KEY_SCHEMAS_REQUIRE_UNIQUE_TYPES              => true,
151 30
            static::KEY_SCHEMAS_FILE_MASK                         => '*.php',
152 30
            static::KEY_JSON_VALIDATORS_FILE_MASK                 => '*.php',
153 30
            static::KEY_FORM_VALIDATORS_FILE_MASK                 => '*.php',
154 30
            static::KEY_QUERY_VALIDATORS_FILE_MASK                => '*.php',
155 30
            static::KEY_THROWABLE_TO_JSON_API_EXCEPTION_CONVERTER => null,
156 30
            static::KEY_HTTP_CODE_FOR_UNEXPECTED_THROWABLE        => 500,
157 30
            static::KEY_DEFAULT_PAGING_SIZE                       => static::DEFAULT_PAGE_SIZE,
158 30
            static::KEY_MAX_PAGING_SIZE                           => static::DEFAULT_MAX_PAGE_SIZE,
159 30
            static::KEY_JSON_ENCODE_OPTIONS                       => $jsonOptions,
160 30
            static::KEY_JSON_ENCODE_DEPTH                         => 512,
161 30
            static::KEY_IS_SHOW_VERSION                           => false,
162 30
            static::KEY_META                                      => null,
163 30
            static::KEY_URI_PREFIX                                => '',
164
165 30
            static::KEY_DO_NOT_LOG_EXCEPTIONS_LIST => [
166
                JsonApiException::class,
167
            ],
168
        ];
169
    }
170
171
    /**
172
     * @param string $schemasPath
173
     * @param bool   $requireUniqueTypes
174
     *
175
     * @return array
176
     *
177
     * @throws ReflectionException
178
     */
179 30
    private function createSchemaMapping(string $schemasPath, bool $requireUniqueTypes): array
180
    {
181 30
        $modelMap = [];
182 30
        $typeMap  = [];
183 30
        foreach ($this->selectClasses($schemasPath, SchemaInterface::class) as $schemaClass) {
184 30
            assert(static::classImplements($schemaClass, SchemaInterface::class));
185
186
            /** @var SchemaInterface $schemaClass */
187 30
            $modelClass   = $schemaClass::MODEL;
188 30
            $resourceType = $schemaClass::TYPE;
189
190 30
            assert(is_string($modelClass) === true && empty($modelClass) === false);
191 30
            assert(is_string($resourceType) === true && empty($resourceType) === false);
192
193
            // By default it checks that all Schemas have unique resource types. That's a legit case
194
            // to have multiple Schemas for a same resource type however it's more likely that developer
195
            // just forgot to set a unique one. If you do need multiple Schemas for a resource feel free
196
            // to set to turn off this check.
197 30
            assert(
198 30
                $requireUniqueTypes === false || array_key_exists($resourceType, $typeMap) === false,
199 30
                "Are you sure it's not an error to use resource type `$resourceType` more than once?"
200
            );
201 30
            $typeMap[$resourceType] = $schemaClass;
202
203 30
            $modelMap[$modelClass] = $schemaClass;
204
        }
205
206 30
        return [$modelMap, $typeMap];
207
    }
208
209
    /**
210
     * @param string $rulesPath
211
     * @param string $queriesValPath
212
     *
213
     * @return array
214
     *
215
     * @throws ReflectionException
216
     */
217 30
    private function serializeJsonValidationRules(string $rulesPath, string $queriesValPath): array
218
    {
219
        // JSON API data validation rules
220 30
        $dataSerializer = new JsonApiDataRulesSerializer(new BlockSerializer());
221 30
        foreach ($this->selectClasses($rulesPath, JsonApiDataRulesInterface::class) as $rulesClass) {
222 30
            $dataSerializer->addRulesFromClass($rulesClass);
223
        }
224
225
        // JSON API query validation rules
226 30
        $querySerializer = new JsonApiQueryRulesSerializer(new BlockSerializer());
227
        // Add predefined rules for queries...
228 30
        $querySerializer->addRulesFromClass(DefaultQueryValidationRules::class);
229
        // ... and add user defined ones.
230 30
        foreach ($this->selectClasses($queriesValPath, JsonApiQueryRulesInterface::class) as $rulesClass) {
231 30
            $querySerializer->addRulesFromClass($rulesClass);
232
        }
233
234
        return [
235 30
            static::JSON_API_DATA_VALIDATION_RULES_SERIALIZED    => $dataSerializer->getData(),
236 30
            static::JSON_API_QUERIES_VALIDATION_RULES_SERIALIZED => $querySerializer->getData(),
237
        ];
238
    }
239
240
    /**
241
     * @param string $formsValPath
242
     *
243
     * @return array
244
     *
245
     * @throws ReflectionException
246
     */
247 30
    private function serializeFormValidationRules(string $formsValPath): array
248
    {
249 30
        $serializer = new FormRulesSerializer(new BlockSerializer());
250
251 30
        foreach ($this->selectClasses($formsValPath, FormRulesInterface::class) as $rulesClass) {
252 30
            $serializer->addRulesFromClass($rulesClass);
253
        }
254
255 30
        return $serializer->getData();
256
    }
257
258
    // serialization above makes some assumptions about format of returned data
259
    // the methods below help to deal with the data encapsulation
260
261
    /**
262
     * @param array $fluteSettings
263
     *
264
     * @return array
265
     */
266 12
    public static function getJsonDataSerializedRules(array $fluteSettings): array
267
    {
268 12
        $serializedRulesKey = static::KEY_JSON_VALIDATION_RULE_SETS_DATA;
269 12
        $dataSubKey         = static::JSON_API_DATA_VALIDATION_RULES_SERIALIZED;
270
271 12
        return $fluteSettings[$serializedRulesKey][$dataSubKey];
272
    }
273
274
    /**
275
     * @param array $fluteSettings
276
     *
277
     * @return array
278
     */
279 21
    public static function getJsonQuerySerializedRules(array $fluteSettings): array
280
    {
281 21
        $serializedRulesKey = static::KEY_JSON_VALIDATION_RULE_SETS_DATA;
282 21
        $dataSubKey         = static::JSON_API_QUERIES_VALIDATION_RULES_SERIALIZED;
283
284 21
        return $fluteSettings[$serializedRulesKey][$dataSubKey];
285
    }
286
287
    /**
288
     * @param array $fluteSettings
289
     *
290
     * @return array
291
     */
292 1
    public static function getFormSerializedRules(array $fluteSettings): array
293
    {
294 1
        return $fluteSettings[static::KEY_ATTRIBUTE_VALIDATION_RULE_SETS_DATA];
295
    }
296
}
297