Completed
Push — master ( 7b9d9c...6bacbf )
by Neomerx
08:04
created

DataSettings   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 18
lcom 1
cbo 4
dl 0
loc 151
ccs 82
cts 82
cp 1
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
B get() 0 42 5
A getSettings() 0 7 1
C getModelsSchemaInfo() 0 82 12
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\ModelSchemaInfo;
20
use Limoncello\Contracts\Application\ModelInterface;
21
use Limoncello\Contracts\Data\RelationshipTypes;
22
use Limoncello\Contracts\Settings\Packages\DataSettingsInterface;
23
use Limoncello\Contracts\Settings\SettingsInterface;
24
use Limoncello\Core\Reflection\CheckCallableTrait;
25
use Limoncello\Core\Reflection\ClassIsTrait;
26
use Psr\Container\ContainerInterface;
27
28
/**
29
 * @package Limoncello\Application
30
 */
31
abstract class DataSettings implements SettingsInterface, DataSettingsInterface
32
{
33
    use ClassIsTrait, CheckCallableTrait;
34
35
    /**
36
     * @inheritdoc
37
     */
38 2
    final public function get(array $appConfig): array
39
    {
40 2
        $defaults = $this->getSettings();
41
42 2
        $modelsFolder       = $defaults[static::KEY_MODELS_FOLDER] ?? null;
43 2
        $modelsFileMask     = $defaults[static::KEY_MODELS_FILE_MASK] ?? null;
44 2
        $migrationsFolder   = $defaults[static::KEY_MIGRATIONS_FOLDER] ?? null;
45 2
        $migrationsListFile = $defaults[static::KEY_MIGRATIONS_LIST_FILE] ?? null;
46 2
        $seedsFolder        = $defaults[static::KEY_SEEDS_FOLDER] ?? null;
47 2
        $seedsListFile      = $defaults[static::KEY_SEEDS_LIST_FILE] ?? null;
48
49 2
        assert(
50 2
            $modelsFolder !== null && empty(glob($modelsFolder)) === false,
51 2
            "Invalid Models folder `$modelsFolder`."
52
        );
53 2
        assert(empty($modelsFileMask) === false, "Invalid Models file mask `$modelsFileMask`.");
54 2
        assert(
55 2
            $migrationsFolder !== null && empty(glob($migrationsFolder)) === false,
56 2
            "Invalid Migrations folder `$migrationsFolder`."
57
        );
58 2
        assert(file_exists($migrationsListFile) === true, "Invalid Migrations file `$migrationsListFile`.");
59 2
        assert(
60 2
            $seedsFolder !== null && empty(glob($seedsFolder)) === false,
61 2
            "Invalid Seeds folder `$seedsFolder`."
62
        );
63 2
        assert(file_exists($seedsListFile) === true, "Invalid Seeds file `$seedsListFile`.");
64
65 2
        $modelsPath = $modelsFolder . DIRECTORY_SEPARATOR . $modelsFileMask;
66
67 2
        $seedInit = $defaults[static::KEY_SEED_INIT] ?? null;
68 2
        assert(
69
            (
70 2
                $seedInit === null ||
71 2
                $this->checkPublicStaticCallable($seedInit, [ContainerInterface::class, 'string']) === true
72
            ),
73 2
            'Seed init should be either `null` or static callable.'
74
        );
75
76 2
        $defaults[static::KEY_MODELS_SCHEMA_INFO] = $this->getModelsSchemaInfo($modelsPath);
77
78 2
        return $defaults;
79
    }
80
81
    /**
82
     * @return array
83
     */
84 2
    protected function getSettings(): array
85
    {
86
        return [
87 2
            static::KEY_MODELS_FILE_MASK => '*.php',
88 2
            static::KEY_SEED_INIT        => null,
89
        ];
90
    }
91
92
    /**
93
     * @param string $modelsPath
94
     *
95
     * @return array
96
     *
97
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
98
     */
99 2
    private function getModelsSchemaInfo(string $modelsPath): array
100
    {
101
        // check reverse relationships
102 2
        $requireReverseRel = true;
103
104 2
        $registered    = [];
105 2
        $modelSchemas  = new ModelSchemaInfo();
106 2
        $registerModel = function ($modelClass) use ($modelSchemas, &$registered, $requireReverseRel) {
107
            /** @var ModelInterface $modelClass */
108 2
            $modelSchemas->registerClass(
109 2
                $modelClass,
110 2
                $modelClass::getTableName(),
111 2
                $modelClass::getPrimaryKeyName(),
112 2
                $modelClass::getAttributeTypes(),
113 2
                $modelClass::getAttributeLengths()
114
            );
115
116 2
            $relationships = $modelClass::getRelationships();
117
118 2
            if (array_key_exists(RelationshipTypes::BELONGS_TO, $relationships) === true) {
119 2
                foreach ($relationships[RelationshipTypes::BELONGS_TO] as $relName => list($rClass, $fKey, $rRel)) {
120
                    /** @var string $rClass */
121 2
                    $modelSchemas->registerBelongsToOneRelationship($modelClass, $relName, $fKey, $rClass, $rRel);
122 2
                    $registered[(string)$modelClass][$relName] = true;
123 2
                    $registered[$rClass][$rRel]                = true;
124
125
                    // Sanity check. Every `belongs_to` should be paired with `has_many` on the other side.
126
                    /** @var ModelInterface $rClass */
127 2
                    $rRelationships   = $rClass::getRelationships();
128 2
                    $isRelationshipOk = $requireReverseRel === false ||
129 2
                        (isset($rRelationships[RelationshipTypes::HAS_MANY][$rRel]) === true &&
130 2
                            $rRelationships[RelationshipTypes::HAS_MANY][$rRel] === [$modelClass, $fKey, $relName]);
131
                    /** @var string $modelClass */
132
133 2
                    assert($isRelationshipOk, "`belongsTo` relationship `$relName` of class $modelClass " .
134 2
                        "should be paired with `hasMany` relationship.");
135
                }
136
            }
137
138 2
            if (array_key_exists(RelationshipTypes::HAS_MANY, $relationships) === true) {
139 2
                foreach ($relationships[RelationshipTypes::HAS_MANY] as $relName => list($rClass, $fKey, $rRel)) {
140
                    // Sanity check. Every `has_many` should be paired with `belongs_to` on the other side.
141
                    /** @var ModelInterface $rClass */
142 2
                    $rRelationships   = $rClass::getRelationships();
143 2
                    $isRelationshipOk = $requireReverseRel === false ||
144 2
                        (isset($rRelationships[RelationshipTypes::BELONGS_TO][$rRel]) === true &&
145 2
                            $rRelationships[RelationshipTypes::BELONGS_TO][$rRel] === [$modelClass, $fKey, $relName]);
146
                    /** @var string $modelClass */
147 2
                    assert($isRelationshipOk, "`hasMany` relationship `$relName` of class $modelClass " .
148 2
                        "should be paired with `belongsTo` relationship.");
149
                }
150
            }
151
152 2
            if (array_key_exists(RelationshipTypes::BELONGS_TO_MANY, $relationships) === true) {
153 2
                foreach ($relationships[RelationshipTypes::BELONGS_TO_MANY] as $relName => $data) {
154 2
                    if (isset($registered[(string)$modelClass][$relName]) === true) {
155 2
                        continue;
156
                    }
157
                    /** @var string $rClass */
158 2
                    list($rClass, $iTable, $fKeyPrimary, $fKeySecondary, $rRel) = $data;
159 2
                    $modelSchemas->registerBelongsToManyRelationship(
160 2
                        $modelClass,
161 2
                        $relName,
162 2
                        $iTable,
163 2
                        $fKeyPrimary,
164 2
                        $fKeySecondary,
165 2
                        $rClass,
166 2
                        $rRel
167
                    );
168 2
                    $registered[(string)$modelClass][$relName] = true;
169 2
                    $registered[$rClass][$rRel]                = true;
170
                }
171
            }
172 2
        };
173
174 2
        $modelClasses = iterator_to_array($this->selectClasses($modelsPath, ModelInterface::class));
175 2
        array_map($registerModel, $modelClasses);
176
177 2
        $data = $modelSchemas->getData();
178
179 2
        return $data;
180
    }
181
}
182