RecordsLocator::locate()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 13
nc 3
nop 0
1
<?php
2
/**
3
 * Spiral Framework, IDE Helper
4
 *
5
 * @author    Dmitry Mironov <[email protected]>
6
 * @licence   MIT
7
 */
8
9
namespace Spiral\IdeHelper\Locators;
10
11
use Psr\Log\LoggerInterface;
12
use Spiral\Database\Schemas\Prototypes\AbstractColumn;
13
use Spiral\IdeHelper\Model\ClassDefinition;
14
use Spiral\IdeHelper\Model\ClassMember;
15
use Spiral\IdeHelper\Model\ClassProperty;
16
use Spiral\ORM\Configs\RelationsConfig;
17
use Spiral\ORM\ORM;
18
use Spiral\ORM\RecordEntity;
19
use Spiral\ORM\Schemas\RecordSchema;
20
use Spiral\ORM\Schemas\RelationInterface;
21
22
/**
23
 * Class RecordsLocator
24
 *
25
 * @package Spiral\IdeHelper\Locators
26
 */
27
class RecordsLocator implements LocatorInterface
28
{
29
    /**
30
     * @var ORM
31
     */
32
    private $orm;
33
34
    /**
35
     * @var RelationsConfig
36
     */
37
    private $relationsConfig;
38
39
    /**
40
     * @var LoggerInterface
41
     */
42
    private $logger;
43
44
    /**
45
     * RecordsLocator constructor.
46
     *
47
     * @param ORM             $orm
48
     * @param RelationsConfig $config
49
     */
50
    public function __construct(ORM $orm, RelationsConfig $config)
51
    {
52
        $this->orm = $orm;
53
        $this->relationsConfig = $config;
54
    }
55
56
    /**
57
     * @inheritDoc
58
     */
59
    public function locate(): array
60
    {
61
        $builder = $this->orm->schemaBuilder()->renderSchema();
62
        $schemas = $builder->getSchemas();
63
        $relations = $builder->getRelations();
64
65
        $records = [];
66
        foreach ($schemas as $schema) {
67
            if ($schema instanceof RecordSchema) {
68
                $columns = $builder->requestTable($schema->getTable(), $schema->getDatabase())->getColumns();
69
                $members = $this->scan($schema, $columns, $relations);
70
71
                $records[] = new ClassDefinition($schema->getClass(), $members);
72
            } else {
73
                $this->logger->warning("Schema for {$schema->getClass()} is not " . RecordSchema::class . " instance");
74
            }
75
        }
76
77
        return $records;
78
    }
79
80
    /**
81
     * @param RecordSchema        $context
82
     * @param AbstractColumn[]    $columns
83
     * @param RelationInterface[] $relations
84
     *
85
     * @return ClassMember[]
86
     */
87
    private function scan(RecordSchema $context, array $columns, array $relations)
88
    {
89
        $flatColumns = $this->processColumns($columns);
90
91
        $fields = $this->processFields($context->getFields(), $columns);
92
        $relations = $this->findRelations($context->getClass(), $relations);
93
        $properties = array_merge($flatColumns, $fields, $relations);
94
95
        $docs = [];
96
        foreach ($properties as $name => $type) {
97
            $docs[] = new ClassProperty($name, $type);
98
        }
99
100
        return $docs;
101
    }
102
103
    /**
104
     * @param AbstractColumn[] $columns
105
     *
106
     * @return array
107
     */
108
    private function processColumns(array $columns): array
109
    {
110
        $results = [];
111
112
        foreach ($columns as $column) {
113
            $types = [$column->phpType()];
114
115
            if ($column->isNullable()) {
116
                $types[] = 'null';
117
            }
118
119
            $results[$column->getName()] = $types;
120
        }
121
122
        return $results;
123
    }
124
125
    /**
126
     * @param array            $fields
127
     * @param AbstractColumn[] $columns
128
     *
129
     * @return array
130
     */
131
    private function processFields(array $fields, array $columns): array
132
    {
133
        $results = [];
134
135
        foreach (array_keys($fields) as $field) {
136
            $column = $columns[$field];
137
            $types = [$column->phpType()];
138
            if ($column->isNullable()) {
139
                $types[] = 'null';
140
            }
141
142
            $results[$field] = $types;
143
        }
144
145
        return $results;
146
    }
147
148
    /**
149
     * @param string              $class
150
     * @param RelationInterface[] $relations
151
     *
152
     * @return array
153
     */
154
    private function findRelations(string $class, array $relations): array
155
    {
156
        $results = [];
157
158
        foreach ($relations as $relation) {
159
            $definition = $relation->getDefinition();
160
            if ($definition->sourceContext()->getClass() === $class) {
161
                $name = $definition->getName();
162
                $types = [];
163
164
                $relationType = $definition->getType();
165
                if (in_array($relationType, [RecordEntity::MANY_TO_MANY, RecordEntity::HAS_MANY])) {
166
                    $types[] = $definition->getTarget() . '[]';
167
                } else {
168
                    $types[] = $definition->getTarget();
169
                }
170
171
                $types[] = $this->relationsConfig->relationClass($relationType,
172
                    RelationsConfig::ACCESS_CLASS);
173
174
                $options = $definition->getOptions();
175
                if (array_key_exists(RecordEntity::NULLABLE, $options)
176
                    && true === $options[RecordEntity::NULLABLE]
177
                ) {
178
                    $types[] = 'null';
179
                }
180
181
                $results[$name] = $types;
182
            }
183
        }
184
185
        return $results;
186
    }
187
}
188