Passed
Push — master ( 93db86...4f4054 )
by Carlos
02:54
created

EmbeddableTrait   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Test Coverage

Coverage 96.15%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 55
dl 0
loc 175
ccs 50
cts 52
cp 0.9615
rs 10
c 1
b 0
f 0
wmc 26

6 Methods

Rating   Name   Duplication   Size   Complexity  
B toArray() 0 49 11
A extractFieldsFor() 0 4 2
A resolveExpandList() 0 20 5
A getExpandEnvelope() 0 3 1
A resolveFieldList() 0 16 5
A processField() 0 5 2
1
<?php
2
3
namespace roaresearch\yii2\roa\hal;
4
5
use yii\base\Arrayable;
6
use yii\base\ArrayableTrait;
7
use yii\helpers\ArrayHelper;
8
use yii\web\Link;
9
use yii\web\Linkable;
10
11
/**
12
 * Interface to get a the information of a file associated to a model.
13
 *
14
 * @author Angel (Faryshta) Guevara <[email protected]>
15
 */
16
trait EmbeddableTrait
17
{
18
    use ArrayableTrait {
19
        extractFieldsFor as baseExtractFieldsFor;
20
    }
21
22
    /**
23
     * @inheritdoc
24
     */
25
    public abstract function fields();
26
27
    /**
28
     * @inheritdoc
29
     */
30
    public abstract function extraFields();
31
32
    /**
33
     * @inheritdoc
34
     */
35 9
    public function toArray(
36
        array $fields = [],
37
        array $expand = [],
38
        $recursive = true
39
    ) {
40 9
        $data = [];
41 9
        foreach ($this->resolveFieldList($fields) as $field => $definition) {
42 9
            $data[$field] = $this->processField($field, $definition);
43
        }
44
45 9
        foreach ($this->resolveExpandList($expand) as $field => $definition) {
46 4
            $attribute = $this->processField($field, $definition);
47
48 4
            if ($recursive) {
49 4
                $nestedFields = $this->extractFieldsFor($fields, $field);
50 4
                $nestedExpand = $this->extractFieldsFor($expand, $field);
51 4
                if ($attribute instanceof Arrayable) {
52 2
                    $attribute = $attribute->toArray(
53
                        $nestedFields,
54
                        $nestedExpand
55
                    );
56 2
                } elseif (is_array($attribute)) {
57 2
                    $attribute = array_map(
58 2
                        function ($item) use ($nestedFields, $nestedExpand) {
59 2
                            if ($item instanceof Arrayable) {
60 2
                                return $item->toArray(
61
                                    $nestedFields,
62
                                    $nestedExpand
63
                                );
64
                            }
65
                            return $item;
66
                        },
67
                        $attribute
68
                    );
69
                }
70
            }
71
72 4
            if ($envelope = $this->getExpandEnvelope()) {
73 4
                $data[$envelope][$field] = $attribute;
74
            } else {
75
                $data[$field] = $attribute;
76
            }
77
        }
78
79 9
        if ($this instanceof Linkable && !in_array('!_links', $fields)) {
80 9
            $data['_links'] = Link::serialize($this->getLinks());
81
        }
82
83 9
        return $recursive ? ArrayHelper::toArray($data) : $data;
84
    }
85
86
    /**
87
     * @return string property which will contain all the expanded parameters.
88
     */
89 4
    public function getExpandEnvelope(): string
90
    {
91 4
        return Embeddable::EMBEDDED_PROPERTY;
92
    }
93
    
94
    
95
    /**
96
     * Extract nested fields from a fields collection for a given root field
97
     * Nested fields are separated with dots (.). e.g: "item.id"
98
     * The previous example would extract "id".
99
     *
100
     * Since 4.0.1 the field "!_links" is supported and handled specialy in
101
     * the sense that if its present on root it will be inherited to any
102
     * nested item.
103
     *
104
     * @param array $fields The fields requested for extraction
105
     * @param string $rootField The root field for which we want to extract the nested fields
106
     * @return array nested fields extracted for the given field
107
     */
108 4
    protected function extractFieldsFor(array $fields, $rootField)
109
    {
110 4
        return $this->baseExtractFieldsFor($fields, $rootField)
111 4
            + (in_array('!_links', $fields) ? ['!_links'] : []);
112
    }
113
114
    /**
115
     * Determines which fields can be returned by [[toArray()]].
116
     * This method will first extract the root fields from the given fields.
117
     * Then it will check the requested root fields against those declared in
118
     * [[fields()]] to determine which fields can be returned.
119
     *
120
     * @param array $fields the fields being requested for exporting
121
     * @return array the list of fields to be exported. The array keys are the
122
     * field names, and the array values are the corresponding object property
123
     * names or PHP callables returning the field values.
124
     */
125 9
    protected function resolveFieldList($fields): array
126
    {
127 9
        $fields = $this->extractRootFields($fields);
128 9
        $result = [];
129
130 9
        foreach ($this->fields() as $field => $definition) {
131 9
            if (is_int($field)) {
132 7
                $field = $definition;
133
            }
134
135 9
            if (empty($fields) || in_array($field, $fields, true)) {
136 9
                $result[$field] = $definition;
137
            }
138
        }
139
140 9
        return $result;
141
    }
142
143
    /**
144
     * Determines which expand fields can be returned by [[toArray()]].
145
     * This method will first extract the root fields from the given expand.
146
     * Then it will check the requested root fields against those declared in
147
     * [[extraFields()]] to determine which fields can be returned.
148
     *
149
     * @param array $expand the expand fields being requested for exporting
150
     * @return array the list of fields to be exported. The array keys are the
151
     * field names, and the array values are the corresponding object property
152
     * names or PHP callables returning the field values.
153
     */
154 9
    protected function resolveExpandList($expand): array
155
    {
156 9
        if (empty($expand)) {
157 9
            return [];
158
        }
159
160 4
        $fields = $this->extractRootFields($expand);
161 4
        $result = [];
162
163 4
        foreach ($this->extraFields() as $field => $definition) {
164 4
                if (is_int($field)) {
165 4
                    $field = $definition;
166
                }
167
168 4
                if (in_array($field, $fields, true)) {
169 4
                    $result[$field] = $definition;
170
                }
171
        }
172
173 4
        return $result;
174
    }
175
176
    /**
177
     * @param string $field name of the field to be resolved.
178
     * @param string|callable $definition the field definition, it its an string
179
     * it will be used as a property name, or a callable with signature.
180
     *
181
     * ```php
182
     * function ($model, string $field)
183
     * ```
184
     * @return mixed data obtained from the model.
185
     */
186 9
    protected function processField($field, $definition)
187
    {
188 9
        return is_string($definition)
189 9
            ? $this->$definition
190 9
            : $definition($this, $field);
191
    }
192
}
193