Completed
Push — master ( cd662c...9cefff )
by Filipe
02:43
created

EntityDescriptor::addRelation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
ccs 7
cts 7
cp 1
rs 9.6666
cc 2
eloc 5
nc 2
nop 2
crap 2
1
<?php
2
3
/**
4
 * This file is part of slick/orm package
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Orm\Descriptor;
11
12
use Slick\Common\Inspector;
13
use Slick\Common\Utils\Text;
14
use Slick\Orm\Annotations\Column;
15
use Slick\Orm\Descriptor\Field\FieldDescriptor;
16
use Slick\Orm\Descriptor\Field\FieldsCollection;
17
use Slick\Orm\Exception\InvalidArgumentException;
18
use Slick\Orm\Mapper\Relation\BelongsTo;
19
use Slick\Orm\Mapper\RelationInterface;
20
21
/**
22
 * Entity Descriptor
23
 *
24
 * @package Slick\Orm\Descriptor
25
 * @author  Filipe Silva <[email protected]>
26
 */
27
class EntityDescriptor implements EntityDescriptorInterface
28
{
29
30
    /**
31
     * @var string Entity class name
32
     */
33
    protected $entity;
34
35
    /**
36
     * @var string
37
     */
38
    protected $tableName;
39
40
    /**
41
     * @var Inspector
42
     */
43
    protected $inspector;
44
45
    /**
46
     * @var FieldsCollection
47
     */
48
    protected $fields;
49
50
    /**
51
     * @var FieldDescriptor
52
     */
53
    protected $primaryKey;
54
55
    /**
56
     * @var string
57
     */
58
    protected $adapterAlias = '__undefined__';
59
60
    /**
61
     * @var RelationsMap
62
     */
63
    protected $relationsMap;
64
65
    protected static $knownRelations = [
66
        'belongsTo' => BelongsTo::class
67
    ];
68
69
    /**
70
     * EntityDescriptor need an entity FQ class name.
71
     *
72
     * @param string $entity
73
     */
74 14
    public function __construct($entity)
75
    {
76 14
        $this->entity = is_object($entity)
77 8
            ? get_class($entity)
78 7
            : $entity;
79 14
        $this->inspector = Inspector::forClass($entity);
80 14
        $this->createEntityRelationsMap();
81 14
    }
82
83
    /**
84
     * Gets entity table name
85
     *
86
     * @return string
87
     */
88 10
    public function getTableName()
89
    {
90 10
        if (null == $this->tableName) {
91 4
            $this->tableName = $this->determineTableName();
92 2
        }
93 10
        return $this->tableName;
94
    }
95
96
    /**
97
     * Returns entity fields
98
     *
99
     * @return FieldsCollection|FieldDescriptor[]
100
     */
101 12
    public function getFields()
102
    {
103 12
        if (null == $this->fields) {
104 6
            $properties = $this->inspector->getClassProperties();
105 6
            $this->fields = new FieldsCollection();
106 6
            foreach ($properties as $property) {
107 6
                $this->addDescriptor($property);
108 3
            }
109 3
        }
110 12
        return $this->fields;
111
    }
112
113
    /**
114
     * Returns the primary key field
115
     *
116
     * @return FieldDescriptor|null
117
     */
118 8
    public function getPrimaryKey()
119
    {
120 8
        if (null == $this->primaryKey) {
121 4
            foreach ($this->getFields() as $field) {
122 4
                if ($field->isPrimaryKey()) {
123 4
                    $this->primaryKey = $field;
124 4
                    break;
125
                }
126 2
            }
127 2
        }
128 8
        return $this->primaryKey;
129
    }
130
131
    /**
132
     * Adds a relation class to the list of known relation classes
133
     *
134
     * @param string $annotationName The annotation name to map
135
     * @param string $relationClass  The FQ relation class name
136
     *
137
     * @throws InvalidArgumentException If the provided class does not implements
138
     *      the RelationInterface interface.
139
     */
140 22
    public static function addRelation($annotationName, $relationClass)
141
    {
142 22
        if (!is_subclass_of($relationClass, RelationInterface::class)) {
143 2
            throw new InvalidArgumentException(
144 2
                "'{$relationClass}' is not a RelationInterface class."
145 1
            );
146
        }
147 22
        static::$knownRelations[$annotationName] = $relationClass;
148 22
    }
149
150
    /**
151
     * Determines the table name for current entity
152
     *
153
     * If there is an annotation @table present it will be used
154
     * otherwise the name will be parsed by convention using the
155
     * EntityDescriptor::parseTableName() method.
156
     *
157
     * @return string
158
     */
159 4
    private function determineTableName()
160
    {
161 4
        $annotations = $this->inspector->getClassAnnotations();
162 4
        $name = self::parseTableName($this->entity);
163 4
        if ($annotations->hasAnnotation('@table')) {
164 4
            $name = $annotations->getAnnotation('@table')->getValue();
165 2
        }
166 4
        return $name;
167
    }
168
169
    /**
170
     * Creates the descriptor if provided property has annotation @column
171
     *
172
     * @param $property
173
     *
174
     * @return self|$this|EntityDescriptor
175
     */
176 6
    private function addDescriptor($property)
177
    {
178 6
        $annotations = $this->inspector
179 6
            ->getPropertyAnnotations($property);
180 6
        if ($annotations->hasAnnotation('column')) {
181
            /** @var Column $annotation */
182 6
            $annotation = $annotations->getAnnotation('column');
183 6
            $descriptor = new FieldDescriptor($annotation->getParameters());
184 6
            $this->fields[] = $descriptor->setName($property);
185 3
        }
186 6
        return $this;
187
    }
188
189
    /**
190
     * Parses the table name from the class name
191
     *
192
     * @param string $className
193
     *
194
     * @return string
195
     */
196 10
    public static function parseTableName($className)
197
    {
198 10
        $parts = explode('\\', $className);
199 10
        $name = end($parts);
200 10
        $tableName = null;
201
202 10
        $words = explode('#', Text::camelCaseToSeparator($name, "#"));
203 10
        $last = array_pop($words);
204 10
        $last = Text::plural(strtolower($last));
205 10
        array_push($words, $last);
206 10
        foreach ($words as $word) {
207 10
            $tableName .= ucfirst($word);
208 5
        }
209 10
        return lcfirst($tableName);
210
    }
211
212
    /**
213
     * Returns the adapter alias name to use with this entity
214
     *
215
     * @return string
216
     */
217 6
    public function getAdapterAlias()
218
    {
219 6
        if ('__undefined__' == $this->adapterAlias) {
220 4
            $this->adapterAlias = null;
221 4
            $annotations = $this->inspector->getClassAnnotations();
222 4
            if ($annotations->hasAnnotation('@adapter')) {
223 4
                $this->adapterAlias = $annotations
224 4
                    ->getAnnotation('@adapter')
225 4
                    ->getValue();
226 2
            }
227 2
        }
228 6
        return $this->adapterAlias;
229
    }
230
231
    /**
232
     * Gets entity class name
233
     *
234
     * @return string
235
     */
236 8
    public function className()
237
    {
238 8
        return $this->entity;
239
    }
240
241
    /**
242
     * Gets relations map for this entity
243
     *
244
     * @return RelationsMap
245
     */
246 2
    public function getRelationsMap()
247
    {
248 2
        return $this->relationsMap;
249
    }
250
251 14
    private function createEntityRelationsMap()
252
    {
253 14
        $properties = $this->inspector->getClassProperties();
254 14
        $this->relationsMap = new RelationsMap();
255 14
        foreach ($properties as $property) {
256 14
            $this->checkRelation($property);
257 7
        }
258 14
    }
259
260 14
    private function checkRelation($property)
261
    {
262 14
        $annotations = $this->inspector->getPropertyAnnotations($property);
263 14
        foreach (static::$knownRelations as $knownRelation => $class) {
264 14
            if ($annotations->hasAnnotation($knownRelation)) {
265 12
                $relation = new $class(
266
                    [
267 12
                        'propertyName' => $property,
268 12
                        'entityDescriptor' => $this,
269 12
                        'annotation' => $annotations->getAnnotation($knownRelation)
270 6
                    ]
271 6
                );
272 12
                $this->relationsMap->set($property, $relation);
273 13
                break;
274
            }
275 7
        }
276
    }
277
}