Completed
Push — master ( 9060d6...83d0c7 )
by Anton
02:51
created

src/Generator.php (4 issues)

1
<?php declare(strict_types=1);
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Cycle\Annotated;
10
11
use Cycle\Annotated\Annotation\Column;
12
use Cycle\Annotated\Annotation\Entity;
13
use Cycle\Annotated\Exception\AnnotationException;
14
use Cycle\Schema\Definition\Entity as EntitySchema;
15
use Cycle\Schema\Definition\Field;
16
use Cycle\Schema\Generator\SyncTables;
17
use Doctrine\Common\Inflector\Inflector;
18
use Spiral\Annotations\Parser;
19
20
final class Generator
21
{
22
    /** @var Parser */
23
    private $parser;
24
25
    /**
26
     * @param Parser $parser
27
     */
28
    public function __construct(Parser $parser)
29
    {
30
        $this->parser = $parser;
31
    }
32
33
    /**
34
     * @param Entity           $ann
35
     * @param \ReflectionClass $class
36
     * @return EntitySchema
37
     */
38
    public function initEntity(Entity $ann, \ReflectionClass $class): EntitySchema
39
    {
40
        $e = new EntitySchema();
41
        $e->setClass($class->getName());
42
43
        $e->setRole($ann->getRole() ?? Inflector::camelize($class->getShortName()));
44
45
        // representing classes
46
        $e->setMapper($this->resolveName($ann->getMapper(), $class));
47
        $e->setRepository($this->resolveName($ann->getRepository(), $class));
48
        $e->setSource($this->resolveName($ann->getSource(), $class));
49
        $e->setConstrain($this->resolveName($ann->getConstrain(), $class));
50
51
        if ($ann->isReadonlySchema()) {
52
            $e->getOptions()->set(SyncTables::READONLY_SCHEMA, true);
53
        }
54
55
        return $e;
56
    }
57
58
    /**
59
     * @param EntitySchema     $entity
60
     * @param \ReflectionClass $class
61
     */
62
    public function initFields(EntitySchema $entity, \ReflectionClass $class)
63
    {
64
        foreach ($class->getProperties() as $property) {
65
            if ($property->getDocComment() === false) {
66
                continue;
67
            }
68
69
            $ann = $this->parser->parse($property->getDocComment());
0 ignored issues
show
It seems like $property->getDocComment() can also be of type true; however, parameter $body of Spiral\Annotations\Parser::parse() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

69
            $ann = $this->parser->parse(/** @scrutinizer ignore-type */ $property->getDocComment());
Loading history...
70
            if (!isset($ann[Column::NAME])) {
71
                continue;
72
            }
73
74
            $entity->getFields()->set($property->getName(), $this->initField($property->getName(), $ann[Column::NAME]));
75
        }
76
    }
77
78
    /**
79
     * @param EntitySchema     $entity
80
     * @param \ReflectionClass $class
81
     */
82
    public function initRelations(EntitySchema $entity, \ReflectionClass $class)
0 ignored issues
show
The parameter $entity is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

82
    public function initRelations(/** @scrutinizer ignore-unused */ EntitySchema $entity, \ReflectionClass $class)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $class is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

82
    public function initRelations(EntitySchema $entity, /** @scrutinizer ignore-unused */ \ReflectionClass $class)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
83
    {
84
85
    }
86
87
    /**
88
     * @param EntitySchema $entity
89
     * @param Column[]     $columns
90
     */
91
    public function initColumns(EntitySchema $entity, array $columns)
92
    {
93
        foreach ($columns as $name => $column) {
94
            if ($column->getColumn() === null && is_numeric($name)) {
95
                throw new AnnotationException(
96
                    "Column name definition is required on `{$entity->getClass()}`"
97
                );
98
            }
99
100
            if ($column->getType() === null) {
101
                throw new AnnotationException(
102
                    "Column type definition is required on `{$entity->getClass()}`"
103
                );
104
            }
105
106
            $entity->getFields()->set(
107
                $name ?? $column->getColumn(),
0 ignored issues
show
It seems like $name ?? $column->getColumn() can also be of type null; however, parameter $name of Cycle\Schema\Definition\Map\FieldMap::set() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

107
                /** @scrutinizer ignore-type */ $name ?? $column->getColumn(),
Loading history...
108
                $this->initField($column->getColumn() ?? $name, $column)
109
            );
110
        }
111
    }
112
113
    /**
114
     * @param string $name
115
     * @param Column $column
116
     * @return Field
117
     */
118
    public function initField(string $name, Column $column): Field
119
    {
120
        if ($column->getType() === null) {
121
            throw new AnnotationException(
122
                "Column type definition is required on `{$name}`"
123
            );
124
        }
125
126
        $field = new Field();
127
128
        $field->setType($column->getType());
129
        $field->setColumn($column->getColumn() ?? Inflector::tableize($name));
130
        $field->setPrimary($column->isPrimary());
131
        $field->setTypecast($column->getTypecast());
132
133
        if ($column->isNullable()) {
134
            $field->getOptions()->set(\Cycle\Schema\Table\Column::OPT_NULLABLE, true);
135
        }
136
137
        if ($column->hasDefault()) {
138
            $field->getOptions()->set(\Cycle\Schema\Table\Column::OPT_DEFAULT, $column->getDefault());
139
        }
140
141
        if ($column->isCastedDefault()) {
142
            $field->getOptions()->set(\Cycle\Schema\Table\Column::OPT_CAST_DEFAULT, true);
143
        }
144
145
        return $field;
146
    }
147
148
    /**
149
     * Resolve class or role name relative to the current class.
150
     *
151
     * @param string           $name
152
     * @param \ReflectionClass $class
153
     * @return string
154
     */
155
    public function resolveName(?string $name, \ReflectionClass $class): ?string
156
    {
157
        if (is_null($name) || class_exists($name, true)) {
158
            return $name;
159
        }
160
161
        $resolved = sprintf(
162
            "%s\\%s",
163
            $class->getNamespaceName(),
164
            ltrim(str_replace('/', '\\', $name), '\\')
165
        );
166
167
        if (class_exists($resolved, true)) {
168
            return $resolved;
169
        }
170
171
        return $name;
172
    }
173
}