DataSettings::get()   B
last analyzed

Complexity

Conditions 5
Paths 1

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 42
ccs 24
cts 24
cp 1
rs 8.9368
c 0
b 0
f 0
cc 5
nc 1
nop 1
crap 5
1
<?php declare(strict_types=1);
2
3
namespace Limoncello\Application\Packages\Data;
4
5
/**
6
 * Copyright 2015-2020 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Limoncello\Application\Data\ModelSchemaInfo;
22
use Limoncello\Common\Reflection\CheckCallableTrait;
23
use Limoncello\Common\Reflection\ClassIsTrait;
24
use Limoncello\Contracts\Application\ModelInterface;
25
use Limoncello\Contracts\Data\RelationshipTypes;
26
use Limoncello\Contracts\Settings\Packages\DataSettingsInterface;
27
use Limoncello\Contracts\Settings\SettingsInterface;
28
use Psr\Container\ContainerInterface;
29
use ReflectionException;
30
use function array_key_exists;
31
use function array_map;
32
use function assert;
33
use function file_exists;
34
use function glob;
35
use function iterator_to_array;
36
37
/**
38
 * @package Limoncello\Application
39
 */
40
abstract class DataSettings implements SettingsInterface, DataSettingsInterface
41 2
{
42
    use ClassIsTrait, CheckCallableTrait;
43 2
44
    /**
45 2
     * @inheritdoc
46 2
     *
47 2
     * @throws ReflectionException
48 2
     */
49 2
    final public function get(array $appConfig): array
50 2
    {
51
        $defaults = $this->getSettings();
52 2
53 2
        $modelsFolder       = $defaults[static::KEY_MODELS_FOLDER] ?? null;
54 2
        $modelsFileMask     = $defaults[static::KEY_MODELS_FILE_MASK] ?? null;
55
        $migrationsFolder   = $defaults[static::KEY_MIGRATIONS_FOLDER] ?? null;
56 2
        $migrationsListFile = $defaults[static::KEY_MIGRATIONS_LIST_FILE] ?? null;
57 2
        $seedsFolder        = $defaults[static::KEY_SEEDS_FOLDER] ?? null;
58 2
        $seedsListFile      = $defaults[static::KEY_SEEDS_LIST_FILE] ?? null;
59 2
60
        assert(
61 2
            $modelsFolder !== null && empty(glob($modelsFolder)) === false,
62 2
            "Invalid Models folder `$modelsFolder`."
63 2
        );
64 2
        assert(empty($modelsFileMask) === false, "Invalid Models file mask `$modelsFileMask`.");
65
        assert(
66 2
            $migrationsFolder !== null && empty(glob($migrationsFolder)) === false,
67
            "Invalid Migrations folder `$migrationsFolder`."
68 2
        );
69
        assert(file_exists($migrationsListFile) === true, "Invalid Migrations file `$migrationsListFile`.");
70 2
        assert(
71 2
            $seedsFolder !== null && empty(glob($seedsFolder)) === false,
72
            "Invalid Seeds folder `$seedsFolder`."
73 2
        );
74 2
        assert(file_exists($seedsListFile) === true, "Invalid Seeds file `$seedsListFile`.");
75
76 2
        $modelsPath = $modelsFolder . DIRECTORY_SEPARATOR . $modelsFileMask;
77
78
        $seedInit = $defaults[static::KEY_SEED_INIT] ?? null;
79 2
        assert(
80
            (
81 2
                $seedInit === null ||
82
                $this->checkPublicStaticCallable($seedInit, [ContainerInterface::class, 'string']) === true
83
            ),
84
            'Seed init should be either `null` or static callable.'
85
        );
86
87 2
        $defaults[static::KEY_MODELS_SCHEMA_INFO] = $this->getModelsSchemaInfo($modelsPath);
88
89
        return $defaults;
90 2
    }
91 2
92
    /**
93
     * @return array
94
     */
95
    protected function getSettings(): array
96
    {
97
        return [
98
            static::KEY_MODELS_FILE_MASK => '*.php',
99
            static::KEY_SEED_INIT        => null,
100
        ];
101
    }
102
103
    /**
104 2
     * @param string $modelsPath
105
     *
106
     * @return array
107 2
     *
108
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
109 2
     *
110 2
     * @throws ReflectionException
111
     */
112
    private function getModelsSchemaInfo(string $modelsPath): array
113 2
    {
114 2
        // check reverse relationships
115 2
        $requireReverseRel = true;
116 2
117 2
        $registered    = [];
118 2
        $modelSchemas  = new ModelSchemaInfo();
119 2
        $registerModel = function (string $modelClass) use ($modelSchemas, &$registered, $requireReverseRel) {
120
            /** @var ModelInterface $modelClass */
121
            $modelSchemas->registerClass(
122 2
                (string)$modelClass,
123
                $modelClass::getTableName(),
124 2
                $modelClass::getPrimaryKeyName(),
125 2
                $modelClass::getAttributeTypes(),
126
                $modelClass::getAttributeLengths(),
127 2
                $modelClass::getRawAttributes()
128 2
            );
129 2
130
            $relationships = $modelClass::getRelationships();
131
132
            if (array_key_exists(RelationshipTypes::BELONGS_TO, $relationships) === true) {
133 2
                foreach ($relationships[RelationshipTypes::BELONGS_TO] as $relName => list($rClass, $fKey, $rRel)) {
134 2
                    /** @var string $rClass */
135 2
                    $modelSchemas->registerBelongsToOneRelationship(
136 2
                        (string)$modelClass,
137
                        $relName,
138
                        $fKey,
139 2
                        $rClass,
140 2
                        $rRel
141
                    );
142
                    $registered[(string)$modelClass][$relName] = true;
143
                    $registered[$rClass][$rRel]                = true;
144 2
145 2
                    // Sanity check. Every `belongs_to` should be paired with `has_many` on the other side.
146
                    /** @var ModelInterface $rClass */
147
                    $rRelationships   = $rClass::getRelationships();
148 2
                    $isRelationshipOk = $requireReverseRel === false ||
149 2
                        (isset($rRelationships[RelationshipTypes::HAS_MANY][$rRel]) === true &&
150 2
                            $rRelationships[RelationshipTypes::HAS_MANY][$rRel] === [$modelClass, $fKey, $relName]);
151 2
                    /** @var string $modelClass */
152
153 2
                    assert($isRelationshipOk, "`belongsTo` relationship `$relName` of class $modelClass " .
154 2
                        "should be paired with `hasMany` relationship.");
155
                }
156
            }
157
158 2
            if (array_key_exists(RelationshipTypes::HAS_MANY, $relationships) === true) {
159 2
                foreach ($relationships[RelationshipTypes::HAS_MANY] as $relName => list($rClass, $fKey, $rRel)) {
160 2
                    // Sanity check. Every `has_many` should be paired with `belongs_to` on the other side.
161 2
                    /** @var ModelInterface $rClass */
162
                    $rRelationships   = $rClass::getRelationships();
163
                    $isRelationshipOk = $requireReverseRel === false ||
164 2
                        (isset($rRelationships[RelationshipTypes::BELONGS_TO][$rRel]) === true &&
165 2
                            $rRelationships[RelationshipTypes::BELONGS_TO][$rRel] === [$modelClass, $fKey, $relName]);
166 2
                    /** @var string $modelClass */
167 2
                    assert($isRelationshipOk, "`hasMany` relationship `$relName` of class $modelClass " .
168 2
                        "should be paired with `belongsTo` relationship.");
169 2
                }
170 2
            }
171 2
172 2
            if (array_key_exists(RelationshipTypes::BELONGS_TO_MANY, $relationships) === true) {
173
                foreach ($relationships[RelationshipTypes::BELONGS_TO_MANY] as $relName => $data) {
174 2
                    if (isset($registered[(string)$modelClass][$relName]) === true) {
175 2
                        continue;
176
                    }
177
                    /** @var string $rClass */
178 2
                    list($rClass, $iTable, $fKeyPrimary, $fKeySecondary, $rRel) = $data;
179
                    $modelSchemas->registerBelongsToManyRelationship(
180
                        $modelClass,
0 ignored issues
show
Bug introduced by
It seems like $modelClass can also be of type object<Limoncello\Contra...ication\ModelInterface>; however, Limoncello\Application\D...ngsToManyRelationship() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
181 2
                        $relName,
182 2
                        $iTable,
183
                        $fKeyPrimary,
184 2
                        $fKeySecondary,
185
                        $rClass,
186 2
                        $rRel
187
                    );
188
                    $registered[(string)$modelClass][$relName] = true;
189
                    $registered[$rClass][$rRel]                = true;
190
                }
191
            }
192
        };
193
194
        /** @noinspection PhpParamsInspection */
195
        $modelClasses = iterator_to_array($this->selectClasses($modelsPath, ModelInterface::class));
196
        array_map($registerModel, $modelClasses);
197
198
        $data = $modelSchemas->getData();
199
200
        return $data;
201
    }
202
}
203