CategoriesHelper::node()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 9
rs 10
cc 2
nc 1
nop 5
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2021 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
namespace App\View\Helper;
14
15
use Cake\Utility\Hash;
16
use Cake\View\Helper;
17
18
/**
19
 * Categories helper
20
 *
21
 * @property \Cake\View\Helper\FormHelper $Form
22
 * @property \App\View\Helper\PropertyHelper $Property
23
 */
24
class CategoriesHelper extends Helper
25
{
26
    public $helpers = ['Form', 'Property'];
27
28
    /**
29
     * Control for categories
30
     *
31
     * @param string $name The name
32
     * @param mixed|null $value The value
33
     * @return string
34
     */
35
    public function control(string $name, $value): string
36
    {
37
        $options = ['label' => false];
38
        if (!$this->isTree()) {
39
            return sprintf(
40
                '<div class="categories"><h3>%s</h3>%s</div>',
41
                __('Global'),
42
                $this->Property->control($name, $value, $options)
43
            );
44
        }
45
46
        return $this->html($name, $value, $options);
47
    }
48
49
    /**
50
     * Html for categories tree
51
     *
52
     * @param string $name The name
53
     * @param mixed|null $value The value
54
     * @param array $options The options
55
     * @return string
56
     */
57
    public function html(string $name, $value, array $options): string
58
    {
59
        $html = '';
60
        $tree = $this->tree();
61
        $hiddenField = true; // hiddenField false on all controls except the first
62
        foreach ($tree as $node) {
63
            $html .= $this->node($node, $name, $value, $options, $hiddenField);
64
        }
65
66
        return $html;
67
    }
68
69
    /**
70
     * Html for single node of categories tree
71
     *
72
     * @param array $node The category node
73
     * @param string $name The name
74
     * @param mixed|null $value The value
75
     * @param array $options The options
76
     * @param bool $hiddenField The hiddenField flag
77
     * @return string
78
     */
79
    public function node(array $node, string $name, $value, array $options, bool &$hiddenField): string
80
    {
81
        $title = sprintf('<h3>%s</h3>', $node['label'] = $node['label'] ?: $node['name']);
82
        $controlOptions = $this->controlOptions($node, $value, $options, $hiddenField);
83
84
        return sprintf(
85
            '<div class="categories">%s%s</div>',
86
            $title,
87
            $this->Form->control($name, $controlOptions)
88
        );
89
    }
90
91
    /**
92
     * Control options for category node.
93
     *
94
     * @param array $node The category node
95
     * @param mixed|null $value The value
96
     * @param array $options The options
97
     * @param bool $hiddenField The hiddenField flag
98
     * @return array
99
     */
100
    public function controlOptions(array $node, $value, array $options, bool &$hiddenField): array
101
    {
102
        $controlOptions = $options + [
103
            'type' => 'select',
104
            'multiple' => 'checkbox',
105
            'value' => (array)Hash::extract((array)$value, '{n}.name'),
106
            'hiddenField' => $hiddenField,
107
        ];
108
        $hiddenField = false;
109
        if (empty($node['children'])) {
110
            $controlOptions['options'][0] = ['value' => $node['name'], 'text' => $node['label'] = $node['label'] ?: $node['name']];
111
112
            return $controlOptions;
113
        }
114
        foreach ($node['children'] as $key => $child) {
115
            if ((bool)Hash::get($child, 'enabled', true)) {
116
                $controlOptions['options'][$key] = ['value' => $child['name'], 'text' => $child['label'] = $child['label'] ?: $child['name']];
117
            }
118
        }
119
120
        return $controlOptions;
121
    }
122
123
    /**
124
     * Categories tree.
125
     * Return roots with children in 'children'.
126
     *
127
     * @return array
128
     */
129
    public function tree(): array
130
    {
131
        $schema = (array)$this->_View->get('schema');
132
        $categories = (array)Hash::get($schema, 'categories');
133
        if (empty($categories)) {
134
            return [];
135
        }
136
137
        $globalCategories = [];
138
        $roots = [];
139
        foreach ($categories as $category) {
140
            if (empty($category['parent_id'])) { // root
141
                $roots[$category['id']] = array_merge($category, (array)Hash::get($roots, $category['id']));
142
            } else { // child
143
                $roots[$category['parent_id']]['children'][] = $category;
144
            }
145
        }
146
147
        foreach ($roots as $key => &$root) {
148
            if (!empty($root['children'])) {
149
                usort($root['children'], [$this, 'sortRoots']);
150
            }
151
            if (empty($root['parent_id']) && empty($root['children'])) {
152
                $globalCategories[] = $root;
153
                unset($roots[$key]);
154
            }
155
        }
156
157
        if (!empty($globalCategories)) {
158
            array_unshift($roots, [
159
                'id' => '0',
160
                'name' => '_',
161
                'label' => __('Global'),
162
                'parent_id' => null,
163
                'children' => $globalCategories,
164
            ]);
165
        }
166
167
        usort($roots, [$this, 'sortRoots']);
168
169
        return $roots;
170
    }
171
172
    /**
173
     * Sort roots. Global first.
174
     *
175
     * @param array $a The first node
176
     * @param array $b The second node
177
     * @return int
178
     */
179
    public function sortRoots(array $a, array $b): int
180
    {
181
        return strcmp(
182
            strtolower((string)Hash::get($a, 'name')),
183
            strtolower((string)Hash::get($b, 'name'))
184
        );
185
    }
186
187
    /**
188
     * Return true when a category parent id is not null, among schema.categories.
189
     * False otherwise.
190
     *
191
     * @return bool
192
     */
193
    public function isTree(): bool
194
    {
195
        $schema = (array)$this->_View->get('schema');
196
        $categories = (array)Hash::get($schema, 'categories');
197
        if (empty($categories)) {
198
            return false;
199
        }
200
        foreach ($categories as $category) {
201
            if (!empty($category['parent_id'])) {
202
                return true;
203
            }
204
        }
205
206
        return false;
207
    }
208
}
209