Passed
Push — master ( 699494...ac796f )
by Mathieu
01:44
created

ExporterService::exportDirectlyNestedByWrapping()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 5
rs 10
ccs 3
cts 3
cp 1
nc 1
nop 1
cc 1
crap 1
1
<?php
2
3
namespace MathieuTu\Exporter;
4
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Collection;
7
8
class ExporterService
9
{
10
    protected const WILDCARD = '*';
11
12
    protected $exportable;
13
14 20
    public function __construct($exportable)
15
    {
16 20
        $this->exportable = $exportable;
17 20
    }
18
19
    /**
20
     * @param mixed $exportable
21
     * @param mixed $attributes
22
     * @return Collection|mixed
23
     */
24 7
    public static function exportFrom($exportable, $attributes)
25
    {
26 7
        return (new self($exportable))->export($attributes);
27
    }
28
29
    /**
30
     * @param array|int|string $attributes
31
     * @return Collection|mixed
32
     */
33 20
    public function export($attributes)
34
    {
35 20
        $this->validateAttributesTypes($attributes);
36
37 19
        if (!is_array($attributes) || $this->hasWildcard($attributes)) {
38 4
            return $this->exportDirectlyNestedByWrapping($attributes);
39
        }
40
41 19
        return $this->collect($attributes)
42 19
            ->mapWithKeys(function ($attribute, $key) {
43 19
                if (is_array($attribute)) {
44 5
                    return $this->exportArray($key, $attribute);
45
                }
46
47 19
                if (!is_int($key)) {
48 5
                    return $this->exportNestedAttribute($key, $attribute);
49
                }
50
51 16
                return $this->exportAttribute($attribute);
52 19
            });
53
    }
54
55 20
    private function validateAttributesTypes($attributes): void
56
    {
57 20
        if (!is_array($attributes) && !is_string($attributes) && !is_int($attributes)) {
58 1
            $type = gettype($attributes);
59
60 1
            throw new \InvalidArgumentException("Exporter only accept array, string or int attribute. '{$type}' passed.");
61
        }
62 19
    }
63
64 19
    protected function hasWildcard(array $array): bool
65
    {
66 19
        return array_keys($array) === [self::WILDCARD];
67
    }
68
69 4
    private function exportDirectlyNestedByWrapping($attributes)
70
    {
71 4
        $key = '***ExporterWrapperKey***';
72
73 4
        return self::exportFrom([$key => $this->exportable], [$key => $attributes])[$key];
74
    }
75
76 19
    protected function collect($items): Collection
77
    {
78 19
        return Collection::make($items);
79
    }
80
81 5
    protected function exportArray(string $key, array $attribute): array
82
    {
83 5
        if ($this->hasWildcard($attribute)) {
84 4
            return $this->exportWildcard($key, $attribute);
85
        }
86
87 2
        return $this->exportNestedAttributes($key, $attribute);
88
    }
89
90 4
    protected function exportWildcard(string $key, array $array): array
91
    {
92 4
        return [$key => $this->collect($this->getAttributeValue($key))->map(function ($exportable) use ($array) {
93 4
            return self::exportFrom($exportable, $array['*']);
94 4
        })];
95
    }
96
97 18
    protected function getAttributeValue($attributes)
98
    {
99 18
        $attributes = is_array($attributes) ? $attributes : explode('.', $attributes);
100
101 18
        $target = $this->exportable;
102 18
        while (($segment = array_shift($attributes)) !== null) {
103 18
            if ($segment === self::WILDCARD) {
104 2
                return $this->getAttributeValueWithWildcard($target, $attributes);
105
            }
106
107
            try {
108 18
                $target = $this->getNewTarget($target, $segment);
109 2
            } catch (NotFoundException $e) {
110 2
                return null;
111
            }
112
        }
113
114 16
        return $target;
115
    }
116
117 2
    protected function getAttributeValueWithWildcard($target, $attribute): ?array
118
    {
119 2
        if (!is_iterable($target)) {
120 2
            return null;
121
        }
122
123 2
        $result = Arr::pluck($target, $attribute);
124
125 2
        return in_array(self::WILDCARD, $attribute) ? Arr::collapse($result) : $result;
126
    }
127
128 18
    protected function getNewTarget($target, $segment)
129
    {
130 18
        if (Arr::accessible($target) && Arr::exists($target, $segment)) {
131 14
            return $target[$segment];
132
        }
133
134 6
        if (is_object($target) && isset($target->{$segment})) {
135 6
            return $target->{$segment};
136
        }
137
138 4
        if (is_object($target) && method_exists($target, $getter = 'get' . ucfirst($segment))) {
139 2
            return call_user_func([$target, $getter]);
140
        }
141
142 2
        throw new NotFoundException($segment, $target);
143
    }
144
145 2
    protected function exportNestedAttributes(string $key, array $array): array
146
    {
147 2
        return [$key => self::exportFrom($this->getAttributeValue($key), $array)];
148
    }
149
150 5
    protected function exportNestedAttribute($key, $attribute): array
151
    {
152 5
        return [$key => $this->getAttributeValue("$key.$attribute")];
153
    }
154
155 16
    protected function exportAttribute(string $attribute): ?array
156
    {
157 16
        if ($export = $this->attributeIsAFunction($attribute)) {
158 2
            return $export;
159
        }
160
161 15
        if (preg_match('/^(.*) as (.*)$/', $attribute, $matches)) {
162 2
            return [$matches[2] => $this->getAttributeValue($matches[1])];
163
        }
164
165 14
        return [$attribute => $this->getAttributeValue($attribute)];
166
    }
167
168 16
    protected function attributeIsAFunction($attribute): ?array
169
    {
170 16
        if (preg_match("/(.*)\((.*)\)$/", $attribute, $matches)) {
171 2
            return [$matches[1] => call_user_func_array([$this->exportable, $matches[1]], array_map('trim', explode(',', $matches[2])))];
172
        }
173
174 15
        return null;
175
    }
176
}
177