Completed
Push — master ( a318f5...917f2c )
by Jared
01:37
created

DefinitionBuilder::get()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 1
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($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
            }
87
88
            $result[$k] = new Property($property, $k);
89
        }
90
91
        // order the properties array by name for consistency
92
        // since it is constructed in a random order
93
        ksort($result);
94
95
        return new Definition($result);
96
    }
97
98
    /////////////////////////////////
99
    // Relationship Shortcuts
100
    /////////////////////////////////
101
102
    /**
103
     * This is added for BC with older versions of pulsar
104
     * that only supported belongs to relationships.
105
     */
106
    private static function buildBelongsToLegacy(string $name, array &$property): void
107
    {
108
        $property['relation_type'] = Relationship::BELONGS_TO;
109
        // in the legacy configuration the default local key was the property name
110
        $property['local_key'] = $property['local_key'] ?? $name;
111
112
        // the default foreign key is `id`
113
        if (!isset($property['foreign_key'])) {
114
            $property['foreign_key'] = Model::DEFAULT_ID_NAME;
115
        }
116
    }
117
118
    private static function buildBelongsTo(array &$property, array &$result): void
119
    {
120
        $property['relation_type'] = Relationship::BELONGS_TO;
121
        $property['relation'] = $property['belongs_to'];
122
        $property['persisted'] = false;
123
124
        // the default foreign key is `id`
125
        if (!isset($property['foreign_key'])) {
126
            $property['foreign_key'] = Model::DEFAULT_ID_NAME;
127
        }
128
129
        // the default local key would look like `user_id`
130
        // for a model named User
131
        if (!isset($property['local_key'])) {
132
            $inflector = Inflector::get();
133
            $property['local_key'] = strtolower($inflector->underscore($property['relation']::modelName())).'_id';
134
        }
135
136
        // when a belongs_to relationship is used then we automatically add a
137
        // new property for the ID field which gets persisted to the DB
138
        if (!isset($result[$property['local_key']])) {
139
            $result[$property['local_key']] = new Property([
140
                'type' => Type::INTEGER,
141
            ], $property['local_key']);
142
        }
143
    }
144
145
    private static function buildBelongsToMany(array &$property, string $modelClass): void
146
    {
147
        /* @var Model $modelClass */
148
        $property['relation_type'] = Relationship::BELONGS_TO_MANY;
149
        $property['relation'] = $property['belongs_to_many'];
150
        $property['persisted'] = false;
151
152
        // the default local key would look like `user_id`
153
        // for a model named User
154
        if (!isset($property['local_key'])) {
155
            $inflector = Inflector::get();
156
            $property['local_key'] = strtolower($inflector->underscore($property['relation']::modelName())).'_id';
157
        }
158
159
        if (!isset($property['foreign_key'])) {
160
            $property['foreign_key'] = Model::DEFAULT_ID_NAME;
161
        }
162
163
        // the default pivot table name looks like
164
        // RoleUser for models named Role and User.
165
        // the tablename is built from the model names
166
        // in alphabetic order.
167
        if (!isset($property['pivot_tablename'])) {
168
            $names = [
169
                $modelClass::modelName(),
170
                $property['relation']::modelName(),
171
            ];
172
            sort($names);
173
            $property['pivot_tablename'] = implode($names);
174
        }
175
    }
176
177 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...
178
    {
179
        /* @var Model $modelClass */
180
        $property['relation_type'] = Relationship::HAS_ONE;
181
        $property['relation'] = $property['has_one'];
182
        $property['persisted'] = false;
183
184
        // the default foreign key would look like
185
        // `user_id` for a model named User
186
        if (!isset($property['foreign_key'])) {
187
            $inflector = Inflector::get();
188
            $property['foreign_key'] = strtolower($inflector->underscore($modelClass::modelName())).'_id';
189
        }
190
191
        if (!isset($property['local_key'])) {
192
            $property['local_key'] = Model::DEFAULT_ID_NAME;
193
        }
194
    }
195
196 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...
197
    {
198
        /* @var Model $modelClass */
199
        $property['relation_type'] = Relationship::HAS_MANY;
200
        $property['relation'] = $property['has_many'];
201
        $property['persisted'] = false;
202
203
        // the default foreign key would look like
204
        // `user_id` for a model named User
205
        if (!isset($property['foreign_key'])) {
206
            $inflector = Inflector::get();
207
            $property['foreign_key'] = strtolower($inflector->underscore($modelClass::modelName())).'_id';
208
        }
209
210
        // the default local key is `id`
211
        if (!isset($property['local_key'])) {
212
            $property['local_key'] = Model::DEFAULT_ID_NAME;
213
        }
214
    }
215
}
216