Passed
Push — master ( 7263be...72ea90 )
by Alex
02:23
created

ListBuilder   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 375
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 109
c 1
b 0
f 0
dl 0
loc 375
rs 8.96
wmc 43

18 Methods

Rating   Name   Duplication   Size   Complexity  
A listingForm() 0 18 4
A needActions() 0 7 4
A transformRecord() 0 10 2
A listingHeaderCells() 0 26 6
A simpleListingItems() 0 18 2
A listingNoItems() 0 5 1
A listingHeader() 0 10 2
A __construct() 0 5 1
A simpleListingForm() 0 15 2
A getFields() 0 3 1
A listOfButtons() 0 5 1
B listingItemsCells() 0 26 7
A listingItems() 0 16 2
A listingHeaderContent() 0 11 2
A setRecordTransformer() 0 3 1
A getCreatePageEndpoint() 0 7 2
A simpleListingHeader() 0 10 2
A setCustomActions() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like ListBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ListBuilder, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Mezon\Gui\ListBuilder;
3
4
use Mezon\Functional\Fetcher;
5
use Mezon\Gui\WidgetsRegistry\BootstrapWidgets;
6
use Mezon\TemplateEngine\TemplateEngine;
7
use Mezon\Functional\Functional;
8
9
/**
10
 * Class ListBuilder
11
 *
12
 * @package CrudService
13
 * @subpackage ListBuilder
14
 * @author Dodonov A.A.
15
 * @version v.1.0 (2019/08/12)
16
 * @copyright Copyright (c) 2019, aeon.org
17
 */
18
define('DESCRIPTION_FIELD_NAME', 'description');
19
20
/**
21
 * Class constructs grids.
22
 */
23
class ListBuilder
24
{
25
26
    /**
27
     * Fields
28
     *
29
     * @var array
30
     */
31
    protected $fields = [];
32
33
    /**
34
     * Service logic adapter
35
     *
36
     * @var \Mezon\Gui\ListBuilder\ListBuilderAdapter
37
     */
38
    protected $listBuilderAdapter = false;
39
40
    /**
41
     * List item transformation callback
42
     *
43
     * @var array
44
     */
45
    protected $recordTransformer = [];
46
47
    /**
48
     * Custom actions for each record
49
     *
50
     * @var string
51
     */
52
    private $customActions = null;
53
54
    /**
55
     * Constructor
56
     *
57
     * @param array $fields
58
     *            List of fields
59
     * @param \Mezon\Gui\ListBuilder\ListBuilderAdapter $listBuilderAdapter
60
     *            Adapter for the data source
61
     */
62
    public function __construct(array $fields, \Mezon\Gui\ListBuilder\ListBuilderAdapter $listBuilderAdapter)
63
    {
64
        $this->fields = $fields;
65
66
        $this->listBuilderAdapter = $listBuilderAdapter;
67
    }
68
69
    /**
70
     * Method sets custom actions
71
     *
72
     * @param string $actions
73
     */
74
    public function setCustomActions(string $actions): void
75
    {
76
        $this->customActions = $actions;
77
    }
78
79
    /**
80
     * Setting record transformer
81
     *
82
     * @param mixed $recordTransformer
83
     *            callable record transformer
84
     * @codeCoverageIgnore
85
     */
86
    public function setRecordTransformer($recordTransformer): void
87
    {
88
        $this->recordTransformer = $recordTransformer;
89
    }
90
91
    /**
92
     * Method returns end point for the create page form
93
     *
94
     * @return string Create page endpoint
95
     */
96
    protected function getCreatePageEndpoint(): string
97
    {
98
        if (isset($_GET['create-page-endpoint'])) {
99
            return $_GET['create-page-endpoint'];
100
        }
101
102
        return '../create/';
103
    }
104
105
    /**
106
     * Method shows "no records" message instead of listing
107
     *
108
     * @return string Compiled list view
109
     */
110
    protected function listingNoItems(): string
111
    {
112
        $content = BootstrapWidgets::get('listing-no-items');
113
114
        return str_replace('{create-page-endpoint}', $this->getCreatePageEndpoint(), $content);
115
    }
116
117
    /**
118
     * Method displays list of possible buttons
119
     *
120
     * @param int $id
121
     *            Id of the record
122
     * @return string Compiled list buttons
123
     */
124
    protected function listOfButtons(int $id): string
125
    {
126
        $content = BootstrapWidgets::get('list-of-buttons');
127
128
        return str_replace('{id}', $id, $content);
129
    }
130
131
    /**
132
     * Need to display actions in list
133
     *
134
     * @return bool Do we need add actions
135
     */
136
    protected function needActions(): bool
137
    {
138
        if (@$_GET['update-button'] == 1 || @$_GET['delete-button'] == 1 || $this->customActions !== null) {
139
            return true;
140
        }
141
142
        return false;
143
    }
144
145
    /**
146
     * Method compiles listing items cells
147
     *
148
     * @param array|object $record
149
     *            record data
150
     * @param bool $addActions
151
     *            Do we need to add actions
152
     * @return string Compiled row
153
     */
154
    protected function listingItemsCells($record, bool $addActions = true): string
155
    {
156
        $content = '';
157
158
        foreach ($this->fields as $name) {
159
            if ($name == 'domain_id') {
160
                continue;
161
            }
162
            if ($name == 'id') {
163
                $content .= BootstrapWidgets::get('listing-row-centered-cell');
164
            } else {
165
                $content .= BootstrapWidgets::get('listing-row-cell');
166
            }
167
            $content = str_replace('{name}', '{' . $name . '}', $content);
168
        }
169
170
        if ($addActions && $this->needActions()) {
171
            $content .= BootstrapWidgets::get('listing-actions');
172
173
            $content = str_replace(
174
                '{actions}',
175
                $this->customActions === null ? $this->listOfButtons(Fetcher::getField($record, 'id')) : $this->customActions,
0 ignored issues
show
Bug introduced by
It seems like Mezon\Functional\Fetcher::getField($record, 'id') can also be of type null; however, parameter $id of Mezon\Gui\ListBuilder\ListBuilder::listOfButtons() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

175
                $this->customActions === null ? $this->listOfButtons(/** @scrutinizer ignore-type */ Fetcher::getField($record, 'id')) : $this->customActions,
Loading history...
176
                $content);
177
        }
178
179
        return $content;
180
    }
181
182
    /**
183
     * Method transforms database record
184
     *
185
     * @param array $record
186
     *            Transforming record
187
     * @return object Transformed record
188
     */
189
    protected function transformRecord(object $record): object
190
    {
191
        // here we assume that we get from service
192
        // already transformed
193
        // and here we provide only additional transformations
194
        if (is_callable($this->recordTransformer)) {
195
            $record = call_user_func($this->recordTransformer, $record);
196
        }
197
198
        return $record;
199
    }
200
201
    /**
202
     * Method compiles listing items
203
     *
204
     * @param array $records
205
     *            Listof records
206
     * @return string Compiled list items
207
     */
208
    protected function listingItems(array $records): string
209
    {
210
        $content = '';
211
212
        foreach ($records as $record) {
213
            $content .= BootstrapWidgets::get('listing-row');
214
            $content = str_replace('{items}', $this->listingItemsCells($record), $content);
215
216
            $record = $this->transformRecord($record);
217
218
            $record = $this->listBuilderAdapter->preprocessListItem($record);
219
220
            $content = TemplateEngine::printRecord($content, $record);
221
        }
222
223
        return $content;
224
    }
225
226
    /**
227
     * Method compiles header cells
228
     *
229
     * @param bool $addActions
230
     *            Do we need to add actions
231
     * @return string Compiled header
232
     */
233
    protected function listingHeaderCells(bool $addActions = true): string
234
    {
235
        $content = '';
236
237
        foreach ($this->fields as $name) {
238
            if ($name == 'domain_id') {
239
                continue;
240
            }
241
242
            $idStyle = $name == 'id' ? 'style="text-align: center;"' : '';
243
244
            $content .= BootstrapWidgets::get('listing-header-cell');
245
            $content = str_replace([
246
                '{id-style}',
247
                '{title}'
248
            ], [
249
                $idStyle,
250
                $name
251
            ], $content);
252
        }
253
254
        if ($addActions && $this->needActions()) {
255
            $content .= BootstrapWidgets::get('listing-header-actions');
256
        }
257
258
        return $content;
259
    }
260
261
    /**
262
     * Method returns listing header content
263
     *
264
     * @param
265
     *            string Compiled header
266
     */
267
    protected function listingHeaderContent(): string
268
    {
269
        if (@$_GET['create-button'] == 1) {
270
            $content = BootstrapWidgets::get('listing-header');
271
272
            $content = str_replace('{create-page-endpoint}', $this->getCreatePageEndpoint(), $content);
273
        } else {
274
            $content = BootstrapWidgets::get('simple-listing-header');
275
        }
276
277
        return $content;
278
    }
279
280
    /**
281
     * Method compiles listing header
282
     *
283
     * @return string Compiled header
284
     */
285
    protected function listingHeader(): string
286
    {
287
        $content = $this->listingHeaderContent();
288
289
        $content = str_replace(
290
            '{description}',
291
            isset($_GET[DESCRIPTION_FIELD_NAME]) ? $_GET[DESCRIPTION_FIELD_NAME] : 'Выберите необходимое действие',
292
            $content);
293
294
        return str_replace('{cells}', $this->listingHeaderCells(), $content);
295
    }
296
297
    /**
298
     * Method compiles listing header
299
     *
300
     * @return string Compiled header
301
     */
302
    protected function simpleListingHeader(): string
303
    {
304
        $content = BootstrapWidgets::get('simple-listing-header');
305
306
        $content = str_replace(
307
            '{description}',
308
            isset($_GET[DESCRIPTION_FIELD_NAME]) ? $_GET[DESCRIPTION_FIELD_NAME] : 'Выберите необходимое действие',
309
            $content);
310
311
        return str_replace('{cells}', $this->listingHeaderCells(false), $content);
312
    }
313
314
    /**
315
     * Method compiles listing items
316
     *
317
     * @param array $records
318
     *            List of records
319
     * @return string Compiled simple list
320
     */
321
    protected function simpleListingItems(array $records): string
322
    {
323
        $content = '';
324
325
        foreach ($records as $record) {
326
            $content .= str_replace(
327
                '{items}',
328
                $this->listingItemsCells($record, false),
329
                BootstrapWidgets::get('listing-row'));
330
331
            $record = $this->transformRecord($record);
332
333
            $record = $this->listBuilderAdapter->preprocessListItem($record);
334
335
            $content = TemplateEngine::printRecord($content, $record);
336
        }
337
338
        return $content;
339
    }
340
341
    /**
342
     * Method compiles listing form
343
     *
344
     * @return string Compiled listing form
345
     */
346
    public function listingForm(): string
347
    {
348
        // TODO split into SimpleListBuilder and ListBuilder
349
        $records = $this->listBuilderAdapter->getRecords([
350
            'field' => 'id',
351
            'order' => 'ASC'
352
        ], isset($_GET['from']) ? $_GET['from'] : 0, isset($_GET['limit']) ? $_GET['limit'] : 100);
353
354
        if (! empty($records)) {
355
            $header = $this->listingHeader();
356
357
            $items = $this->listingItems($records);
358
359
            $footer = BootstrapWidgets::get('listing-footer');
360
361
            return $header . $items . $footer;
362
        } else {
363
            return $this->listingNoItems();
364
        }
365
    }
366
367
    /**
368
     * Method compiles simple_listing form
369
     *
370
     * @return string Compiled simple listing form
371
     */
372
    public function simpleListingForm(): string
373
    {
374
        $records = $this->listBuilderAdapter->all();
375
376
        if (! empty($records)) {
377
            $header = $this->simpleListingHeader();
378
379
            $items = $this->simpleListingItems($records);
380
381
            // they are the same with full feature listing
382
            $footer = BootstrapWidgets::get('listing-footer');
383
384
            return $header . $items . $footer;
385
        } else {
386
            return BootstrapWidgets::get('listing-no-items');
387
        }
388
    }
389
390
    /**
391
     * Method returns fields of the list
392
     *
393
     * @return array fields list
394
     */
395
    public function getFields(): array
396
    {
397
        return $this->fields;
398
    }
399
}
400