Completed
Push — master ( 32e652...8e829c )
by Neomerx
11:56
created

DataSettings::get()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 42
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 42
c 0
b 0
f 0
rs 8.439
ccs 17
cts 17
cp 1
cc 5
eloc 28
nc 1
nop 0
crap 5
1
<?php namespace Limoncello\Application\Packages\Data;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Limoncello\Application\Data\ModelSchemeInfo;
20
use Limoncello\Contracts\Application\ModelInterface;
21
use Limoncello\Contracts\Data\RelationshipTypes;
22
use Limoncello\Contracts\Settings\SettingsInterface;
23
use Limoncello\Core\Reflection\CheckCallableTrait;
24
use Limoncello\Core\Reflection\ClassIsTrait;
25
use Psr\Container\ContainerInterface;
26
27
/**
28
 * @package Limoncello\Application
29
 */
30
abstract class DataSettings implements SettingsInterface
31
{
32
    use ClassIsTrait, CheckCallableTrait;
33
34
    /** Settings key */
35
    const KEY_MODELS_FOLDER = 0;
36
37
    /** Settings key */
38
    const KEY_MODELS_FILE_MASK = self::KEY_MODELS_FOLDER + 1;
39
40
    /** Settings key */
41
    const KEY_MIGRATIONS_FOLDER = self::KEY_MODELS_FILE_MASK + 1;
42
43
    /** Settings key */
44
    const KEY_MIGRATIONS_LIST_FILE = self::KEY_MIGRATIONS_FOLDER + 1;
45
46
    /** Settings key */
47
    const KEY_SEEDS_FOLDER = self::KEY_MIGRATIONS_LIST_FILE + 1;
48
49
    /** Settings key */
50
    const KEY_SEEDS_LIST_FILE = self::KEY_SEEDS_FOLDER + 1;
51
52
    /** Settings key */
53
    const KEY_SEED_INIT = self::KEY_SEEDS_LIST_FILE + 1;
54
55
    /** Settings key */
56
    const KEY_MODELS_SCHEME_INFO = self::KEY_SEED_INIT + 1;
57
58
    /** Settings key */
59
    const KEY_LAST = self::KEY_MODELS_SCHEME_INFO;
60
61
    /**
62
     * @inheritdoc
63
     */
64
    final public function get(): array
65
    {
66
        $defaults = $this->getSettings();
67
68
        $modelsFolder       = $defaults[static::KEY_MODELS_FOLDER] ?? null;
69
        $modelsFileMask     = $defaults[static::KEY_MODELS_FILE_MASK] ?? null;
70
        $migrationsFolder   = $defaults[static::KEY_MIGRATIONS_FOLDER] ?? null;
71
        $migrationsListFile = $defaults[static::KEY_MIGRATIONS_LIST_FILE] ?? null;
72 2
        $seedsFolder        = $defaults[static::KEY_SEEDS_FOLDER] ?? null;
73
        $seedsListFile      = $defaults[static::KEY_SEEDS_LIST_FILE] ?? null;
74 2
75 2
        assert(
76
            $modelsFolder !== null && empty(glob($modelsFolder)) === false,
77
            "Invalid Models folder `$modelsFolder`."
78 2
        );
79 2
        assert(empty($modelsFileMask) === false, "Invalid Models file mask `$modelsFileMask`.");
80 2
        assert(
81 2
            $migrationsFolder !== null && empty(glob($migrationsFolder)) === false,
82
            "Invalid Migrations folder `$migrationsFolder`."
83
        );
84
        assert(file_exists($migrationsListFile) === true, "Invalid Migrations file `$migrationsListFile`.");
85
        assert(
86
            $seedsFolder !== null && empty(glob($seedsFolder)) === false,
87
            "Invalid Seeds folder `$seedsFolder`."
88
        );
89
        assert(file_exists($seedsListFile) === true, "Invalid Seeds file `$seedsListFile`.");
90
91
        $modelsPath = $modelsFolder . DIRECTORY_SEPARATOR . $modelsFileMask;
92 2
93
        $seedInit = $defaults[static::KEY_SEED_INIT] ?? null;
94
        assert(
95 2
            (
96
                $seedInit === null ||
97 2
                $this->checkPublicStaticCallable($seedInit, [ContainerInterface::class, 'string']) === true
98 2
            ),
99 2
            'Seed init should be either `null` or static callable.'
100
        );
101 2
102 2
        $defaults[static::KEY_MODELS_SCHEME_INFO] = $this->getModelsSchemeInfo($modelsPath);
103 2
104 2
        return $defaults;
105 2
    }
106 2
107
    /**
108
     * @return array
109 2
     */
110
    protected function getSettings(): array
111 2
    {
112 2
        return [
113
            static::KEY_MODELS_FILE_MASK => '*.php',
114 2
            static::KEY_SEED_INIT        => null,
115 2
        ];
116 2
    }
117
118
    /**
119
     * @param string $modelsPath
120 2
     *
121 2
     * @return array
122 2
     *
123 2
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
124
     */
125
    private function getModelsSchemeInfo(string $modelsPath): array
126 2
    {
127 2
        // check reverse relationships
128
        $requireReverseRel = true;
129
130
        $registered    = [];
131 2
        $modelSchemes  = new ModelSchemeInfo();
132 2
        $registerModel = function ($modelClass) use ($modelSchemes, &$registered, $requireReverseRel) {
133
            /** @var ModelInterface $modelClass */
134
            $modelSchemes->registerClass(
135 2
                $modelClass,
0 ignored issues
show
Documentation introduced by
$modelClass is of type object<Limoncello\Contra...ication\ModelInterface>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
136 2
                $modelClass::getTableName(),
137 2
                $modelClass::getPrimaryKeyName(),
138 2
                $modelClass::getAttributeTypes(),
139
                $modelClass::getAttributeLengths()
140 2
            );
141 2
142
            $relationships = $modelClass::getRelationships();
143
144
            if (array_key_exists(RelationshipTypes::BELONGS_TO, $relationships) === true) {
145 2
                foreach ($relationships[RelationshipTypes::BELONGS_TO] as $relName => list($rClass, $fKey, $rRel)) {
146 2
                    /** @var string $rClass */
147 2
                    $modelSchemes->registerBelongsToOneRelationship($modelClass, $relName, $fKey, $rClass, $rRel);
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...ongsToOneRelationship() 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...
148 2
                    $registered[(string)$modelClass][$relName] = true;
149
                    $registered[$rClass][$rRel]                = true;
150
151 2
                    // Sanity check. Every `belongs_to` should be paired with `has_many` on the other side.
152 2
                    /** @var ModelInterface $rClass */
153 2
                    $rRelationships   = $rClass::getRelationships();
154 2
                    $isRelationshipOk = $requireReverseRel === false ||
155 2
                        (isset($rRelationships[RelationshipTypes::HAS_MANY][$rRel]) === true &&
156 2
                            $rRelationships[RelationshipTypes::HAS_MANY][$rRel] === [$modelClass, $fKey, $relName]);
157 2
                    /** @var string $modelClass */
158 2
159 2
                    assert($isRelationshipOk, "`belongsTo` relationship `$relName` of class $modelClass " .
160
                        "should be paired with `hasMany` relationship.");
161 2
                }
162 2
            }
163
164
            if (array_key_exists(RelationshipTypes::HAS_MANY, $relationships) === true) {
165 2
                foreach ($relationships[RelationshipTypes::HAS_MANY] as $relName => list($rClass, $fKey, $rRel)) {
166
                    // Sanity check. Every `has_many` should be paired with `belongs_to` on the other side.
167
                    /** @var ModelInterface $rClass */
168
                    $rRelationships   = $rClass::getRelationships();
169
                    $isRelationshipOk = $requireReverseRel === false ||
170
                        (isset($rRelationships[RelationshipTypes::BELONGS_TO][$rRel]) === true &&
171
                            $rRelationships[RelationshipTypes::BELONGS_TO][$rRel] === [$modelClass, $fKey, $relName]);
172
                    /** @var string $modelClass */
173
                    assert($isRelationshipOk, "`hasMany` relationship `$relName` of class $modelClass " .
174
                        "should be paired with `belongsTo` relationship.");
175
                }
176
            }
177
178
            if (array_key_exists(RelationshipTypes::BELONGS_TO_MANY, $relationships) === true) {
179
                foreach ($relationships[RelationshipTypes::BELONGS_TO_MANY] as $relName => $data) {
180
                    if (isset($registered[(string)$modelClass][$relName]) === true) {
181
                        continue;
182
                    }
183
                    /** @var string $rClass */
184
                    list($rClass, $iTable, $fKeyPrimary, $fKeySecondary, $rRel) = $data;
185
                    $modelSchemes->registerBelongsToManyRelationship(
186
                        $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...
187
                        $relName,
188
                        $iTable,
189
                        $fKeyPrimary,
190
                        $fKeySecondary,
191
                        $rClass,
192
                        $rRel
193
                    );
194
                    $registered[(string)$modelClass][$relName] = true;
195
                    $registered[$rClass][$rRel]                = true;
196
                }
197
            }
198
        };
199
200
        $modelClasses = iterator_to_array($this->selectClasses($modelsPath, ModelInterface::class));
201
        array_map($registerModel, $modelClasses);
202
203
        $data = $modelSchemes->getData();
204
205
        return $data;
206
    }
207
}
208