Completed
Branch feature/pre-split (ce4b6b)
by Anton
05:23
created

RelationOptions::proposedOptions()   B

Complexity

Conditions 6
Paths 18

Size

Total Lines 55
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 23
nc 18
nop 1
dl 0
loc 55
rs 8.7752
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\ORM\Helpers;
8
9
use Doctrine\Common\Inflector\Inflector;
10
use Spiral\ORM\Exceptions\OptionsException;
11
use Spiral\ORM\Record;
12
use Spiral\ORM\Schemas\Definitions\RelationDefinition;
13
14
/**
15
 * Provides ability to work with user defined relations and fill missing options based on source
16
 * and target contexts.
17
 */
18
class RelationOptions
19
{
20
    /**
21
     * @var RelationDefinition
22
     */
23
    private $definition;
24
25
    /**
26
     * Most of relations provides ability to specify many different configuration options, such
27
     * as key names, pivot table schemas, foreign key request, ability to be nullabe and etc.
28
     *
29
     * To simple schema definition in real projects we can fill some of this values automatically
30
     * based on some "environment" values such as parent/outer record table, role name, primary key
31
     * and etc.
32
     *
33
     * Example:
34
     * Record::INNER_KEY => '{outer:role}_{outer:primaryKey}'
35
     *
36
     * Result:
37
     * Outer Record is User with primary key "id" => "user_id"
38
     *
39
     * @var array
40
     */
41
    private $template;
42
43
    /**
44
     * Options calculated based on values provided by RelationDefinition and calculated via
45
     * template.
46
     *
47
     * @var array
48
     */
49
    private $options;
50
51
    /**
52
     * @param RelationDefinition $definition
53
     * @param array              $template Relation options template.
54
     */
55
    public function __construct(RelationDefinition $definition, array $template = [])
56
    {
57
        $this->definition = $definition;
58
        $this->template = $template;
59
60
        $this->options = $this->calculateOptions($definition->getOptions());
61
    }
62
63
    /**
64
     * Get value for a specific option. Attention, option MUST exist in template in order to
65
     * be retrievable.
66
     *
67
     * @param string $option
68
     *
69
     * @return mixed
70
     *
71
     * @throws OptionsException
72
     */
73
    public function define(string $option)
74
    {
75
        if (!array_key_exists($option, $this->options)) {
76
            throw new OptionsException("Undefined relation option '{$option}'");
77
        }
78
79
        return $this->options[$option];
80
    }
81
82
    /**
83
     * All relation options.
84
     *
85
     * @param array $options Options to be defined.
86
     *
87
     * @return array
88
     */
89
    public function defineMultiple(array $options): array
90
    {
91
        return array_intersect_key($this->options, array_flip($options));
92
    }
93
94
    /**
95
     * Calculate options based on given template
96
     *
97
     * @param array $userOptions Options provided by user.
98
     *
99
     * @return array Calculated options.
100
     */
101
    protected function calculateOptions(array $userOptions): array
102
    {
103
        foreach ($this->template as $property => $pattern) {
104
            if (isset($userOptions[$property])) {
105
                //Specified by user
106
                continue;
107
            }
108
109
            if (!is_string($pattern)) {
110
                //Some options are actually array of options
111
                $userOptions[$property] = $pattern;
112
                continue;
113
            }
114
115
            //Let's create option value using default proposer values
116
            $userOptions[$property] = \Spiral\interpolate(
117
                $pattern,
118
                $this->proposedOptions($userOptions)
119
            );
120
        }
121
122
        return $userOptions;
123
    }
124
125
    /**
126
     * Create set of options to specify missing relation definition fields.
127
     *
128
     * @param array $userOptions User options.
129
     *
130
     * @return array
131
     */
132
    protected function proposedOptions(array $userOptions): array
133
    {
134
        $source = $this->definition->sourceContext();
135
136
        $proposed = [
137
            //Relation name
138
            'relation:name'     => $this->definition->getName(),
139
140
            //Relation name in plural form
141
            'relation:plural'   => Inflector::pluralize($this->definition->getName()),
142
143
            //Relation name in singular form
144
            'relation:singular' => Inflector::singularize($this->definition->getName()),
145
146
            //Parent record role name
147
            'source:role'       => $source->getRole(),
148
149
            //Parent record table name
150
            'source:table'      => $source->getTable(),
151
152
            //Parent record primary key
153
            'source:primaryKey' => !empty($source->getPrimary()) ? $source->getPrimary()->getName() : '',
154
        ];
155
156
        //Some options may use values declared in other definition fields
157
        $aliases = [
158
            Record::OUTER_KEY   => 'outerKey',
159
            Record::INNER_KEY   => 'innerKey',
160
            Record::PIVOT_TABLE => 'pivotTable',
161
        ];
162
163
        foreach ($aliases as $property => $alias) {
164
            if (isset($userOptions[$property])) {
165
                //Let's create some default options based on user specified values
166
                $proposed['option:' . $alias] = $userOptions[$property];
167
            }
168
        }
169
170
        if (!empty($this->definition->targetContext())) {
171
            $target = $this->definition->targetContext();
172
173
            $proposed = $proposed + [
174
                    //Outer role name
175
                    'target:role'       => $target->getRole(),
176
177
                    //Outer record table
178
                    'target:table'      => $target->getTable(),
179
180
                    //Outer record primary key
181
                    'target:primaryKey' => !empty($target->getPrimary()) ? $target->getPrimary()->getName() : '',
182
                ];
183
        }
184
185
        return $proposed;
186
    }
187
}