Passed
Pull Request — master (#83)
by Jan
12:08 queued 07:34
created

TreeViewGenerator::getTreeView()   F

Complexity

Conditions 15
Paths 408

Size

Total Lines 62
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 15
eloc 32
c 2
b 0
f 0
nc 408
nop 4
dl 0
loc 62
rs 2.5722

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 * Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as published
9
 * by the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
declare(strict_types=1);
22
23
/**
24
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
25
 *
26
 * Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
27
 *
28
 * This program is free software; you can redistribute it and/or
29
 * modify it under the terms of the GNU General Public License
30
 * as published by the Free Software Foundation; either version 2
31
 * of the License, or (at your option) any later version.
32
 *
33
 * This program is distributed in the hope that it will be useful,
34
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
 * GNU General Public License for more details.
37
 *
38
 * You should have received a copy of the GNU General Public License
39
 * along with this program; if not, write to the Free Software
40
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
41
 */
42
43
namespace App\Services\Trees;
44
45
use App\Entity\Base\AbstractDBElement;
46
use App\Entity\Base\AbstractNamedDBElement;
47
use App\Entity\Base\AbstractStructuralDBElement;
48
use App\Helpers\Trees\TreeViewNode;
49
use App\Helpers\Trees\TreeViewNodeIterator;
50
use App\Helpers\Trees\TreeViewNodeState;
51
use App\Repository\StructuralDBElementRepository;
52
use App\Services\EntityURLGenerator;
53
use App\Services\UserCacheKeyGenerator;
54
use Doctrine\ORM\EntityManagerInterface;
55
use Symfony\Contracts\Cache\ItemInterface;
56
use Symfony\Contracts\Cache\TagAwareCacheInterface;
57
use Symfony\Contracts\Translation\TranslatorInterface;
58
59
class TreeViewGenerator
60
{
61
    protected $urlGenerator;
62
    protected $em;
63
    protected $cache;
64
    protected $keyGenerator;
65
    protected $translator;
66
67
    public function __construct(EntityURLGenerator $URLGenerator, EntityManagerInterface $em,
68
        TagAwareCacheInterface $treeCache, UserCacheKeyGenerator $keyGenerator, TranslatorInterface $translator)
69
    {
70
        $this->urlGenerator = $URLGenerator;
71
        $this->em = $em;
72
        $this->cache = $treeCache;
73
        $this->keyGenerator = $keyGenerator;
74
        $this->translator = $translator;
75
    }
76
77
    /**
78
     * Gets a TreeView list for the entities of the given class.
79
     *
80
     * @param string                           $class           The class for which the treeView should be generated
81
     * @param AbstractStructuralDBElement|null $parent          The root nodes in the tree should have this element as parent (use null, if you want to get all entities)
82
     * @param string                           $href_type       The link type that will be generated for the hyperlink section of each node (see EntityURLGenerator for possible values).
83
     *                                                          Set to empty string, to disable href field.
84
     * @param AbstractDBElement|null           $selectedElement The element that should be selected. If set to null, no element will be selected.
85
     *
86
     * @return TreeViewNode[] an array of TreeViewNode[] elements of the root elements
87
     */
88
    public function getTreeView(string $class, ?AbstractStructuralDBElement $parent = null, string $mode = 'list_parts', ?AbstractDBElement $selectedElement = null): array
89
    {
90
        $head = [];
91
92
        $href_type = $mode;
93
94
        //When we use the newEdit type, add the New Element node.
95
        if ('newEdit' === $mode) {
96
            //Generate the url for the new node
97
            $href = $this->urlGenerator->createURL(new $class());
98
            $new_node = new TreeViewNode($this->translator->trans('entity.tree.new'), $href);
99
            //When the id of the selected element is null, then we have a new element, and we need to select "new" node
100
            if (null === $selectedElement || null === $selectedElement->getID()) {
101
                $new_node->setSelected(true);
102
            }
103
            $head[] = $new_node;
104
            //Add spacing
105
            $head[] = (new TreeViewNode(''))->setDisabled(true);
106
107
            //Every other treeNode will be used for edit
108
            $href_type = 'edit';
109
        }
110
111
        if ($mode === 'list_parts_root') {
112
            $href_type = 'list_parts';
113
        }
114
115
        if ($mode === 'devices') {
116
            $href_type = '';
117
        }
118
119
        $generic = $this->getGenericTree($class, $parent);
120
        $treeIterator = new TreeViewNodeIterator($generic);
121
        $recursiveIterator = new \RecursiveIteratorIterator($treeIterator, \RecursiveIteratorIterator::SELF_FIRST);
122
        foreach ($recursiveIterator as $item) {
123
            /** @var TreeViewNode $item */
124
            if (null !== $selectedElement && $item->getId() === $selectedElement->getID()) {
125
                $item->setSelected(true);
126
            }
127
128
            if (!empty($item->getNodes())) {
129
                $item->addTag((string) \count($item->getNodes()));
130
            }
131
132
            if (!empty($href_type) && null !== $item->getId()) {
133
                $entity = $this->em->getPartialReference($class, $item->getId());
134
                $item->setHref($this->urlGenerator->getURL($entity, $href_type));
135
            }
136
137
            //Translate text if text starts with $$
138
            if (0 === strpos($item->getText(), '$$')) {
139
                $item->setText($this->translator->trans(substr($item->getText(), 2)));
140
            }
141
        }
142
143
        if ($mode === 'list_parts_root' ||$mode === 'devices') {
144
            $root_node = new TreeViewNode($this->translator->trans('tree.root_node.text'), null, $generic);
145
            $root_node->setExpanded(true);
146
            $generic = [$root_node];
147
        }
148
149
        return array_merge($head, $generic);
150
    }
151
152
    /**
153
     * /**
154
     * Gets a tree of TreeViewNode elements. The root elements has $parent as parent.
155
     * The treeview is generic, that means the href are null and ID values are set.
156
     *
157
     * @param string                           $class  The class for which the tree should be generated
158
     * @param AbstractStructuralDBElement|null $parent the parent the root elements should have
159
     *
160
     * @return TreeViewNode[]
161
     */
162
    public function getGenericTree(string $class, ?AbstractStructuralDBElement $parent = null): array
163
    {
164
        if (!is_a($class, AbstractNamedDBElement::class, true)) {
165
            throw new \InvalidArgumentException('$class must be a class string that implements StructuralDBElement or NamedDBElement!');
166
        }
167
        if (null !== $parent && !is_a($parent, $class)) {
168
            throw new \InvalidArgumentException('$parent must be of the type $class!');
169
        }
170
171
        /** @var StructuralDBElementRepository $repo */
172
        $repo = $this->em->getRepository($class);
173
174
        //If we just want a part of a tree, dont cache it
175
        if (null !== $parent) {
176
            return $repo->getGenericNodeTree($parent);
177
        }
178
179
        $secure_class_name = str_replace('\\', '_', $class);
180
        $key = 'treeview_'.$this->keyGenerator->generateKey().'_'.$secure_class_name;
181
182
        return $this->cache->get($key, function (ItemInterface $item) use ($repo, $parent, $secure_class_name) {
183
            // Invalidate when groups, a element with the class or the user changes
184
            $item->tag(['groups', 'tree_treeview', $this->keyGenerator->generateKey(), $secure_class_name]);
185
186
            return $repo->getGenericNodeTree($parent);
187
        });
188
    }
189
}
190