Test Failed
Push — develop ( eeff2c...6435cb )
by Freddie
05:45
created

Schema::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 6
dl 0
loc 14
ccs 7
cts 7
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of FlexPHP.
4
 *
5
 * (c) Freddie Gar <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace FlexPHP\Schema;
11
12
use Exception;
13
use FlexPHP\Schema\Constants\Action;
14
use FlexPHP\Schema\Constants\Keyword;
15
use FlexPHP\Schema\Exception\InvalidFileSchemaException;
16
use FlexPHP\Schema\Exception\InvalidSchemaException;
17
use Symfony\Component\Yaml\Exception\ParseException;
18
use Symfony\Component\Yaml\Yaml;
19
20
final class Schema implements SchemaInterface
21
{
22
    /**
23
     * @var string
24
     */
25
    private $name;
26
27
    /**
28
     * @var string
29
     */
30
    private $title;
31
32
    /**
33
     * @var array<int,SchemaAttributeInterface>
34
     */
35
    private $attributes;
36
37
    /**
38
     * @var null|string
39
     */
40
    private $icon;
41
42
    /**
43
     * @var string
44
     */
45
    private $language;
46 12
47
    /**
48
     * @var array
49 12
     */
50 12
    private $actions = [];
51 12
52 12
    public static function fromArray(array $schema): SchemaInterface
53 12
    {
54
        /** @var string $name */
55 12
        $name = \key($schema) ?? '';
56
        $title = $schema[$name][Keyword::TITLE] ?? '';
57
        $attributes = $schema[$name][Keyword::ATTRIBUTES] ?? [];
58 3
        $icon = $schema[$name][Keyword::ICON] ?? null;
59
        $language = $schema[$name][Keyword::LANGUAGE] ?? null;
60
        $actions = $schema[$name][Keyword::ACTIONS] ?? [];
61 3
62 3
        if (\is_string($actions)) {
63 1
            $actions = \str_split($actions);
64 1
        }
65
66
        return new self($name, $title, $attributes, $icon, $language, $actions);
67 2
    }
68
69
    public static function fromFile(string $schemafile): SchemaInterface
70 22
    {
71
        try {
72
            $yaml = new Yaml();
73
            $schema = $yaml->parseFile($schemafile);
74
        } catch (ParseException $exception) {
75
            throw new InvalidFileSchemaException();
76
        }
77 22
78 15
        return self::fromArray($schema);
79 10
    }
80 8
81 8
    public function __construct(
82 8
        string $name,
83
        string $title,
84 11
        array $attributes,
85
        ?string $icon = null,
86 11
        ?string $language = null,
87
        array $actions = []
88
    ) {
89 4
        $this->setName($name);
90
        $this->setTitle($title);
91 4
        $this->setAttributes($attributes);
92
        $this->setIcon($icon);
93
        $this->setLanguage($language);
94 4
        $this->setActions($actions);
95
    }
96 4
97
    public function name(): string
98
    {
99 2
        return $this->name;
100
    }
101 2
102
    public function title(): string
103
    {
104 2
        return $this->title;
105
    }
106 2
107
    public function attributes(): array
108
    {
109 4
        return $this->attributes;
110
    }
111 4
112
    public function icon(): ?string
113
    {
114 4
        return $this->icon;
115 2
    }
116
117
    public function language(): string
118 4
    {
119 4
        return $this->language;
120
    }
121 4
122
    public function actions(): array
123
    {
124 2
        return $this->actions;
125
    }
126 2
127
    public function hasAction(string $action): bool
128
    {
129 2
        return \in_array($action, $this->actions(), true);
130 2
    }
131
132
    public function pkName(): string
133 2
    {
134 2
        $pkName = 'id';
135
136 2
        \array_filter($this->attributes(), function (SchemaAttributeInterface $property) use (&$pkName) {
137
            if ($property->isPk()) {
138
                $pkName = $property->name();
139 2
            }
140
141 2
            return true;
142 2
        });
143
144 2
        return $pkName;
145 2
    }
146 2
147 2
    public function pkTypeHint(): string
148 2
    {
149 2
        $pkTypeHint = 'string';
150 2
151 2
        \array_filter($this->attributes(), function (SchemaAttributeInterface $property) use (&$pkTypeHint) {
152 2
            if ($property->isPk()) {
153 2
                $pkTypeHint = $property->typeHint();
154 2
            }
155
156
            return true;
157
        });
158 2
159 2
        return $pkTypeHint;
160 2
    }
161
162
    public function fkRelations(): array
163 2
    {
164
        return \array_reduce(
165
            $this->attributes(),
166 22
            function (array $result, SchemaAttributeInterface $property): array {
167
                if ($property->isfk()) {
168 22
                    $result[$property->name()] = [
169 4
                        'pkTable' => $property->fkTable(),
170
                        'pkId' => $property->name(),
171
                        'pkDataType' => $property->dataType(),
172 18
                        'pkTypeHint' => $property->typeHint(),
173 3
                        'fkId' => $property->fkId(),
174
                        'fkName' => $property->fkName(),
175
                        'fkTable' => $this->name(),
176 15
                        'isBlameBy' => $property->isBlameBy(),
177 15
                        'isRequired' => $property->isRequired(),
178
                        'minChars' => $property->fchars(),
179 15
                    ];
180
                }
181 15
182 5
                return $result;
183
            },
184
            []
185 10
        );
186 10
    }
187
188 10
    private function setName(string $name): void
189
    {
190 10
        if (empty(\trim($name))) {
191
            throw new InvalidSchemaException('Schema name is required');
192 10
        }
193 7
194 2
        if (\preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $name) === 0) {
195
            throw new InvalidSchemaException('Schema name only accept alphanum and underscore');
196 2
        }
197
198
        $this->name = $name;
199 5
    }
200 5
201 5
    private function setTitle(string $title): void
202
    {
203
        if (empty(\trim($title))) {
204 5
            throw new InvalidSchemaException(\sprintf('Schema %s:title is required', $this->name()));
205 2
        }
206 2
207 5
        $this->title = $title;
208
    }
209
210
    private function setAttributes(array $attributes): void
211
    {
212 8
        $schemaAttributes = [];
213 8
214
        foreach ($attributes as $attribute) {
215 8
            if ($attribute instanceof SchemaAttributeInterface) {
216
                $schemaAttributes[] = $attribute;
217 8
218 8
                continue;
219
            }
220 8
221
            $name = $attribute[Keyword::NAME] ?? '';
222 8
            $dataType = $attribute[Keyword::DATATYPE] ?? '';
223 8
            $constraints = $attribute[Keyword::CONSTRAINTS] ?? '';
224
225
            try {
226 8
                $schemaAttributes[] = new SchemaAttribute($name, $dataType, $constraints);
227 8
            } catch (Exception $exception) {
228
                throw new InvalidSchemaException(
229
                    \sprintf('Schema %s > %s', $this->name(), $exception->getMessage())
230
                );
231
            }
232
        }
233
234
        $this->attributes = $schemaAttributes;
235
    }
236
237
    private function setIcon(?string $icon): void
238
    {
239
        $this->icon = $icon;
240
    }
241
242
    private function setLanguage(?string $language): void
243
    {
244
        if (empty($language)) {
245
            $language = 'en';
246
        }
247
248
        $this->language = $language;
249
    }
250
251
    private function setActions(array $actions): void
252
    {
253
        $invalidActions = [];
254
255
        if (empty($actions)) {
256
            $actions = [Action::INDEX, Action::CREATE, Action::READ, Action::UPDATE, Action::DELETE];
257
        }
258
259
        foreach ($actions as $action) {
260
            if (!\in_array($action, [
261
                Action::INDEX,
262
                Action::CREATE,
263
                Action::READ,
264
                Action::UPDATE,
265
                Action::DELETE,
266
                Action::FILTER,
267
            ])) {
268
                $invalidActions[] = $action;
269
            }
270
        }
271
272
        if (\count($invalidActions) > 0) {
273
            throw new InvalidSchemaException(
274
                \sprintf('Schema %s:action are invalid: %s', $this->name(), \implode(',', $invalidActions))
275
            );
276
        }
277
278
        $this->actions = $actions;
279
    }
280
}
281