Aggregate::sum()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 1
1
<?php
2
/*
3
 * This file is part of the Borobudur-Cqrs package.
4
 *
5
 * (c) Hexacodelabs <http://hexacodelabs.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Borobudur\Cqrs\ViewModel;
12
13
use Borobudur\Cqrs\Exception\InvalidArgumentException;
14
use Closure;
15
use ReflectionFunction;
16
use ReflectionObject;
17
18
/**
19
 * @author      Iqbal Maulana <[email protected]>
20
 * @created     8/20/15
21
 */
22
class Aggregate
23
{
24
    /**
25
     * @var array
26
     */
27
    private $data;
28
29
    /**
30
     * @var ReflectionObject
31
     */
32
    private $reflection;
33
34
    /**
35
     * @var string
36
     */
37
    private $prefixMethod;
38
39
    /**
40
     * @var ViewModelInterface
41
     */
42
    private $viewModel;
43
44
    /**
45
     * Constructor.
46
     *
47
     * @param array              $data
48
     * @param ViewModelInterface $viewModel
49
     * @param ReflectionObject   $reflection
50
     * @param string             $prefixMethod
51
     */
52
    public function __construct(array $data, ViewModelInterface $viewModel, ReflectionObject $reflection, $prefixMethod)
53
    {
54
        $this->data = $data;
55
        $this->reflection = $reflection;
56
        $this->prefixMethod = $prefixMethod;
57
        $this->viewModel = $viewModel;
58
    }
59
60
    /**
61
     * Data summary.
62
     *
63
     * @param string $fields
64
     *
65
     * @return int|float
66
     */
67
    public function sum($fields)
68
    {
69
        $total = 0;
70
        $this->walk($fields, function () use (&$total) {
71
            $total += array_sum(func_get_args());
72
        });
73
74
        return $total;
75
    }
76
77
    /**
78
     * Data average.
79
     *
80
     * @param string $fields
81
     *
82
     * @return float
83
     */
84
    public function avg($fields)
85
    {
86
        return $this->sum($fields) / count($this->data);
87
    }
88
89
    /**
90
     * Data maximum.
91
     *
92
     * @param string $fields
93
     *
94
     * @return int
95
     */
96
    public function max($fields)
97
    {
98
        $max = 0;
99
        $this->walk($fields, function () use (&$max) {
100
            $cur = max(func_get_args());
101
            if ($cur > $max) {
102
                $max = $cur;
103
            }
104
        });
105
106
        return $max;
107
    }
108
109
    /**
110
     * Data minimum.
111
     *
112
     * @param string $fields
113
     *
114
     * @return int
115
     */
116
    public function min($fields)
117
    {
118
        $min = null;
119
        $this->walk($fields, function () use (&$min) {
120
            $cur = min(func_get_args());
121
            if (null === $min) {
122
                $min = $cur;
123
            } elseif ($cur < $min) {
124
                $min = $cur;
125
            }
126
        });
127
128
        return $min;
129
    }
130
131
    /**
132
     * Data count.
133
     *
134
     * @param string $fields
135
     *
136
     * @return int
137
     */
138
    public function count($fields)
139
    {
140
        if (empty($fields) || '*' === $fields) {
141
            $this->viewModel->setForceSingle();
142
143
            return count($this->data);
144
        }
145
146
        $num = 0;
147
148
        $this->walk($fields, function () use (&$num) {
149
            $num += count(array_filter(func_get_args()));
150
        });
151
152
        return $num;
153
    }
154
155
    /**
156
     * Get first data.
157
     *
158
     * @return array|null
159
     */
160
    public function first()
161
    {
162
        $this->viewModel->setForceSingle();
163
164
        return count($this->data) > 0 ? $this->data[0] : null;
165
    }
166
167
    /**
168
     * Get last data.
169
     *
170
     * @return array|null
171
     */
172
    public function last()
173
    {
174
        $this->viewModel->setForceSingle();
175
176
        return count($this->data) > 0 ? end($this->data) : null;
177
    }
178
179
    /**
180
     * Custom aggregate with closure.
181
     *
182
     * @param Closure $callback
183
     *
184
     * @return bool
185
     */
186
    public function closure(Closure $callback)
187
    {
188
        $reflection = new ReflectionFunction($callback);
189
        $properties = $reflection->getParameters();
190
        $args = array();
191
192
        foreach ($properties as $prop) {
193
            $args[] = $prop->getName();
194
        }
195
196
        return $this->walk(implode(',', $args), $callback);
197
    }
198
199
    /**
200
     * Walking into collection.
201
     *
202
     * @param string  $fields
203
     * @param Closure $callback
204
     *
205
     * @return bool
206
     */
207
    private function walk($fields, Closure $callback)
208
    {
209
        if (empty($fields)) {
210
            throw new InvalidArgumentException(sprintf('Cannot walk with empty fields.'));
211
        }
212
213
        $this->viewModel->setForceSingle();
214
        $fields = explode(',', trim($fields));
215
216
        return array_walk($this->data, function (array $record) use ($fields, $callback) {
217
            $args = $this->getArguments($fields, $record);
218
219
            return call_user_func_array($callback, $args);
220
        });
221
    }
222
223
    /**
224
     * Get args.
225
     *
226
     * @param array $params
227
     * @param array $record
228
     *
229
     * @return array
230
     */
231
    private function getArguments(array $params, array $record)
232
    {
233
        $args = array();
234
        foreach ($params as $var) {
235
            $var = trim($var);
236
            $method = $this->getMethodName($var);
237
            if ($this->reflection->hasMethod($method)) {
238
                $args[] = $this->reflection->getMethod($method)->invokeArgs(
239
                    $this->viewModel, $this->viewModel->buildViewModelArgs($this->data, $record, $var)
240
                );
241
242
                continue;
243
            }
244
245
            if (isset($record[$var])) {
246
                $args[] = $record[$var];
247
                continue;
248
            }
249
250
            throw new InvalidArgumentException(sprintf(
251
                'Field "%s" is not defined.',
252
                $var
253
            ));
254
        }
255
256
        return $args;
257
    }
258
259
    /**
260
     * Get method name.
261
     *
262
     * @param string $name
263
     *
264
     * @return string
265
     */
266
    private function getMethodName($name)
267
    {
268
        return $this->prefixMethod.ucfirst($name);
269
    }
270
}
271