Completed
Push — master ( 3e728c...994056 )
by Jared
01:32
created

DefinitionBuilder::buildPolymorphic()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
cc 3
nc 4
nop 2
1
<?php
2
3
namespace Pulsar;
4
5
use ICanBoogie\Inflector;
6
use Pulsar\Relation\Relationship;
7
8
final class DefinitionBuilder
9
{
10
    const DEFAULT_ID_PROPERTY = [
11
        'type' => Type::INTEGER,
12
        'mutable' => Property::IMMUTABLE,
13
    ];
14
15
    const AUTO_TIMESTAMPS = [
16
        'created_at' => [
17
            'type' => Type::DATE,
18
            'validate' => 'timestamp|db_timestamp',
19
        ],
20
        'updated_at' => [
21
            'type' => Type::DATE,
22
            'validate' => 'timestamp|db_timestamp',
23
        ],
24
    ];
25
26
    const SOFT_DELETE_TIMESTAMPS = [
27
        'deleted_at' => [
28
            'type' => Type::DATE,
29
            'validate' => 'timestamp|db_timestamp',
30
            'null' => true,
31
        ],
32
    ];
33
34
    /** @var Definition[] */
35
    private static $definitions;
36
37
    /**
38
     * Gets the definition for a model. If needed the
39
     * definition will be generated. It will only be
40
     * generated once.
41
     */
42
    public static function get(string $modelClass): Definition
43
    {
44
        /** @var Model $modelClass */
45
        if (!isset(self::$definitions[$modelClass])) {
46
            self::$definitions[$modelClass] = $modelClass::buildDefinition();
47
        }
48
49
        return self::$definitions[$modelClass];
50
    }
51
52
    /**
53
     * Builds a model definition given certain parameters.
54
     */
55
    public static function build(array $properties, string $modelClass, bool $autoTimestamps, bool $softDelete): Definition
56
    {
57
        /** @var Model $modelClass */
58
        // add in the default ID property
59
        if (!isset($properties[Model::DEFAULT_ID_NAME]) && $modelClass::getIDProperties() == [Model::DEFAULT_ID_NAME]) {
60
            $properties[Model::DEFAULT_ID_NAME] = self::DEFAULT_ID_PROPERTY;
61
        }
62
63
        // generates created_at and updated_at timestamps
64
        if ($autoTimestamps) {
65
            $properties = array_replace(self::AUTO_TIMESTAMPS, $properties);
66
        }
67
68
        // generates deleted_at timestamps
69
        if ($softDelete) {
70
            $properties = array_replace(self::SOFT_DELETE_TIMESTAMPS, $properties);
71
        }
72
73
        $result = [];
74
        foreach ($properties as $k => $property) {
75
            // handle relationship shortcuts
76
            if (isset($property['relation']) && !isset($property['relation_type'])) {
77
                self::buildBelongsToLegacy($k, $property);
78
            } elseif (isset($property['belongs_to'])) {
79
                self::buildBelongsTo($k, $property, $result);
80
            } elseif (isset($property['has_one'])) {
81
                self::buildHasOne($property, $modelClass);
82
            } elseif (isset($property['belongs_to_many'])) {
83
                self::buildBelongsToMany($property, $modelClass);
84
            } elseif (isset($property['has_many'])) {
85
                self::buildHasMany($property, $modelClass);
86
            } elseif (isset($property['morphs_to'])) {
87
                self::buildPolymorphic($property, $k);
88
            }
89
90
            // install validation rule for encrypted properties
91
            if (isset($property['encrypted']) && $property['encrypted'] && !isset($property['validate'])) {
92
                $property['validate'] = 'encrypt';
93
            }
94
95
            $result[$k] = new Property($property, $k);
96
        }
97
98
        // order the properties array by name for consistency
99
        // since it is constructed in a random order
100
        ksort($result);
101
102
        return new Definition($result);
103
    }
104
105
    /////////////////////////////////
106
    // Relationship Shortcuts
107
    /////////////////////////////////
108
109
    /**
110
     * This is added for BC with older versions of pulsar
111
     * that only supported belongs to relationships.
112
     */
113
    private static function buildBelongsToLegacy(string $name, array &$property): void
114
    {
115
        $property['relation_type'] = Relationship::BELONGS_TO;
116
        // in the legacy configuration the default local key was the property name
117
        $property['local_key'] = $property['local_key'] ?? $name;
118
119
        // the default foreign key is `id`
120
        if (!isset($property['foreign_key'])) {
121
            $property['foreign_key'] = Model::DEFAULT_ID_NAME;
122
        }
123
    }
124
125
    private static function buildBelongsTo(string $name, array &$property, array &$result): void
126
    {
127
        $property['relation_type'] = Relationship::BELONGS_TO;
128
        $property['relation'] = $property['belongs_to'];
129
        $property['persisted'] = false;
130
131
        // the default foreign key is `id`
132
        if (!isset($property['foreign_key'])) {
133
            $property['foreign_key'] = Model::DEFAULT_ID_NAME;
134
        }
135
136
        // the default local key would look like `user_id`
137
        // for a property named `user`
138
        if (!isset($property['local_key'])) {
139
            $property['local_key'] = $name.'_id';
140
        }
141
142
        // when a belongs_to relationship is used then we automatically add a
143
        // new property for the ID field which gets persisted to the DB
144
        if (!isset($result[$property['local_key']])) {
145
            $result[$property['local_key']] = new Property([
146
                'type' => Type::INTEGER,
147
                'mutable' => $property['mutable'] ?? Property::MUTABLE,
148
            ], $property['local_key']);
149
        }
150
    }
151
152
    private static function buildBelongsToMany(array &$property, string $modelClass): void
153
    {
154
        /* @var Model $modelClass */
155
        $property['relation_type'] = Relationship::BELONGS_TO_MANY;
156
        $property['relation'] = $property['belongs_to_many'];
157
        $property['persisted'] = false;
158
159
        // the default local key would look like `user_id`
160
        // for a model named User
161
        if (!isset($property['local_key'])) {
162
            $inflector = Inflector::get();
163
            $property['local_key'] = strtolower($inflector->underscore($modelClass::modelName())).'_id';
164
        }
165
166
        if (!isset($property['foreign_key'])) {
167
            $inflector = Inflector::get();
168
            $property['foreign_key'] = strtolower($inflector->underscore($property['relation']::modelName())).'_id';
169
        }
170
171
        // the default pivot table name looks like
172
        // RoleUser for models named Role and User.
173
        // the tablename is built from the model names
174
        // in alphabetic order.
175
        if (!isset($property['pivot_tablename'])) {
176
            $names = [
177
                $modelClass::modelName(),
178
                $property['relation']::modelName(),
179
            ];
180
            sort($names);
181
            $property['pivot_tablename'] = implode($names);
182
        }
183
    }
184
185 View Code Duplication
    private static function buildHasOne(array &$property, string $modelClass): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
186
    {
187
        /* @var Model $modelClass */
188
        $property['relation_type'] = Relationship::HAS_ONE;
189
        $property['relation'] = $property['has_one'];
190
        $property['persisted'] = false;
191
192
        // the default foreign key would look like `user_id`
193
        // for a model named User
194
        if (!isset($property['foreign_key'])) {
195
            $inflector = Inflector::get();
196
            $property['foreign_key'] = strtolower($inflector->underscore($modelClass::modelName())).'_id';
197
        }
198
199
        if (!isset($property['local_key'])) {
200
            $property['local_key'] = Model::DEFAULT_ID_NAME;
201
        }
202
    }
203
204 View Code Duplication
    private static function buildHasMany(array &$property, string $modelClass): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
205
    {
206
        /* @var Model $modelClass */
207
        $property['relation_type'] = Relationship::HAS_MANY;
208
        $property['relation'] = $property['has_many'];
209
        $property['persisted'] = false;
210
211
        // the default foreign key would look like
212
        // `user_id` for a model named User
213
        if (!isset($property['foreign_key'])) {
214
            $inflector = Inflector::get();
215
            $property['foreign_key'] = strtolower($inflector->underscore($modelClass::modelName())).'_id';
216
        }
217
218
        // the default local key is `id`
219
        if (!isset($property['local_key'])) {
220
            $property['local_key'] = Model::DEFAULT_ID_NAME;
221
        }
222
    }
223
224
    private static function buildPolymorphic(array &$property, string $name): void
225
    {
226
        /* @var Model $modelClass */
227
        $property['relation_type'] = Relationship::POLYMORPHIC;
228
        $property['persisted'] = false;
229
230
        // the default foreign key is `id`
231
        if (!isset($property['foreign_key'])) {
232
            $property['foreign_key'] = Model::DEFAULT_ID_NAME;
233
        }
234
235
        // the default local key type is the property name
236
        if (!isset($property['local_key'])) {
237
            $property['local_key'] = $name;
238
        }
239
    }
240
}
241