Completed
Pull Request — develop (#323)
by Mathias
15:36
created

TreeSelectStrategy::setAllowSelectMultipleItems()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
/**
3
 * YAWIK
4
 *
5
 * @filesource
6
 * @license MIT
7
 * @copyright  2013 - 2016 Cross Solution <http://cross-solution.de>
8
 */
9
  
10
/** */
11
namespace Core\Form\Hydrator\Strategy;
12
13
use Core\Entity\Tree\AbstractLeafs;
14
use Core\Entity\Tree\NodeInterface;
15
use Doctrine\Common\Collections\ArrayCollection;
16
use Doctrine\Common\Collections\Collection;
17
use Zend\Hydrator\Strategy\StrategyInterface;
18
19
/**
20
 * Hydrator strategy for TreeSelect form element.
21
 * 
22
 * @author Mathias Gelhausen <[email protected]>
23
 * @since 0.29
24
 */
25
class TreeSelectStrategy implements StrategyInterface
26
{
27
28
    /**
29
     * The selected leafs.
30
     *
31
     * @var AbstractLeafs
32
     */
33
    private $attachedLeafs;
34
35
    /**
36
     * The root node.
37
     *
38
     * @var NodeInterface
39
     */
40
    private $treeRoot;
41
42
    /**
43
     * Flag wether multiple selections are allowed.
44
     *
45
     * @var bool|callable
46
     */
47
    private $allowSelectMultipleItems = false;
48
49
    /**
50
     * Set the selected leafs.
51
     *
52
     * @param AbstractLeafs $attachedLeafs
53
     *
54
     * @return self
55
     */
56
    public function setAttachedLeafs(AbstractLeafs $attachedLeafs)
57
    {
58
        $this->attachedLeafs = $attachedLeafs;
59
60
        return $this;
61
    }
62
63
    /**
64
     * Get the selected leafs.
65
     *
66
     * @return AbstractLeafs
67
     */
68
    public function getAttachedLeafs()
69
    {
70
        return $this->attachedLeafs;
71
    }
72
73
    /**
74
     * Set the root node.
75
     *
76
     * @param NodeInterface $treeRoot
77
     *
78
     * @return self
79
     */
80
    public function setTreeRoot(NodeInterface $treeRoot)
81
    {
82
        $this->treeRoot = $treeRoot;
83
84
        return $this;
85
    }
86
87
    /**
88
     * Get the root node.
89
     *
90
     * @return NodeInterface
91
     */
92
    public function getTreeRoot()
93
    {
94
        return $this->treeRoot;
95
    }
96
97
    /**
98
     * Set the allow multiple selections flag.
99
     *
100
     * @param Callable|bool $flagOrCallback When a Callable is passed, it must return bool.
101
     *
102
     * @return self
103
     */
104
    public function setAllowSelectMultipleItems($flagOrCallback)
105
    {
106
        $this->allowSelectMultipleItems = $flagOrCallback;
107
108
        return $this;
109
    }
110
111
    /**
112
     * Are multiple selections allowed?
113
     *
114
     * @return bool
115
     */
116
    public function allowSelectMultipleItems()
0 ignored issues
show
Coding Style introduced by
function allowSelectMultipleItems() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
117
    {
118
        $flagOrCallback = $this->allowSelectMultipleItems;
119
120
        return is_callable($flagOrCallback) ? (bool) $flagOrCallback() : (bool) $flagOrCallback;
121
    }
122
123
    public function extract($value)
124
    {
125
        if (!$value instanceOf AbstractLeafs) {
126
            throw new \InvalidArgumentException('$value must be an instance of ' . AbstractLeafs::class);
127
        }
128
        /* @var AbstractLeafs $value
129
         * @var NodeInterface $item */
130
131
        $this->setAttachedLeafs($value);
132
133
        if (!$this->allowSelectMultipleItems()) {
134
            $item = $value->getItems()->first();
135
            return $item ? $this->getItemValue($item) : null;
136
        }
137
138
        $data = [];
139
        foreach ($value->getItems() as $item) {
140
            $data[] = $this->getItemValue($item);
141
        }
142
143
        return $data;
144
    }
145
146
    public function hydrate($value)
147
    {
148
        $root = $this->getTreeRoot();
149
        $object = $this->getAttachedLeafs();
150
        $items = new ArrayCollection();
151
152
        if (!$this->allowSelectMultipleItems()) {
153
            $value = [$value];
154
        }
155
156
        foreach ($value as $itemValue) {
157
            $leaf = $this->findLeaf($root, $itemValue);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $leaf is correct as $this->findLeaf($root, $itemValue) (which targets Core\Form\Hydrator\Strat...ectStrategy::findLeaf()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
158
159
            if ($leaf) {
160
                $items->add($leaf);
161
            }
162
        }
163
164
        $object->setItems($items);
165
166
        return $object;
167
    }
168
169
    /**
170
     * Get item value for use in the option tag.
171
     *
172
     * @param NodeInterface $item
173
     *
174
     * @return string item value prepended with all parent values except root node.
175
     */
176 View Code Duplication
    private function getItemValue(NodeInterface $item)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
177
    {
178
        $parts = [ $item->getValue() ];
179
180
        while ($item = $item->getParent()) {
181
            $parts[] = $item->getValue();
182
        }
183
184
        array_pop($parts); // No root node.
185
186
        $parts = array_reverse($parts);
187
        $value = join('-', $parts);
188
189
        return $value;
190
    }
191
192
    /**
193
     * Find a leaf with a concrete value in the tree.
194
     *
195
     * @param NodeInterface $leaf
196
     * @param string        $value
197
     *
198
     * @return NodeInterface|null
199
     */
200
    private function findLeaf(NodeInterface $leaf, $value)
201
    {
202
        $parts = is_array($value) ? $value : explode('-', $value);
203
        $value = array_shift($parts);
204
205
        /* @var NodeInterface $item */
206
        foreach ($leaf->getChildren() as $item) {
207
            if ($item->getValue() == $value) {
208
                if (count($parts)) {
209
                    return $this->findLeaf($item, $parts);
0 ignored issues
show
Documentation introduced by
$parts is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
210
                }
211
212
                return $item;
213
            }
214
        }
215
216
        return null;
217
    }
218
}