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

TreeHydrator::hydrate()   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 2
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;
12
13
use Core\Entity\Tree\NodeInterface;
14
use Doctrine\Common\Collections\Collection;
15
use Zend\Hydrator\HydratorInterface;
16
17
/**
18
 * This hydrator handles trees for usage in forms.
19
 *
20
 * Flatten the tree structure when extracting and
21
 * rebuilds the tree when hydrating.
22
 * 
23
 * @author Mathias Gelhausen <[email protected]>
24
 * @since 0.29
25
 */
26
class TreeHydrator implements HydratorInterface
27
{
28
29
    /**
30
     * Hydratable data.
31
     *
32
     * @internal
33
     *      Used as interim storage.
34
     *
35
     * @var array
36
     */
37
    protected $hydrateData = [];
38
39
    /**
40
     * Extract tree items.
41
     *
42
     * Flattens the tree structure to an one dimensional array and returns it in an array
43
     * under the key'items'.
44
     *
45
     * The returned array can be directly bound to \Core\Form\Tree\ManagementFieldset.
46
     *
47
     * @param  object $object The root of the tree.
48
     *
49
     * @return array
50
     */
51
    public function extract($object)
52
    {
53
        $data = [];
54
        $this->flattenTree($object, $data);
55
56
        return ['items' => $data ];
57
58
    }
59
60
    /**
61
     * Recursively flattens a tree structure.
62
     *
63
     * @param NodeInterface $tree
64
     * @param array $data
65
     * @param string $curId
66
     */
67
    private function flattenTree($tree, &$data, $curId = '1')
68
    {
69
70
        $data[] =
71
        new \ArrayObject([
72
            'id' => $tree->getId(),
73
            'current' => $curId,
74
            'name' => $tree->getName(),
75
            'value' => $tree->getValue(),
76
            'priority' => $tree->getPriority(),
77
            'do' => 'nothing',
78
        ]);
79
80
        if ($tree->hasChildren()) {
81
            foreach ($tree->getChildren() as $i => $child) {
82
                $this->flattenTree($child, $data, $curId . '-' . ($i + 1) );
83
            }
84
        }
85
    }
86
87
    /**
88
     * Hydrate a tree structure from form values.
89
     *
90
     * Takes the post values for a \Core\Tree\ManegementFieldset and rebuilds the
91
     * tree structure.
92
     *
93
     * @param  array  $data Form values
94
     * @param  Collection $object
95
     *
96
     * @return object
97
     */
98
    public function hydrate(array $data, $object)
99
    {
100
        $this->prepareHydrateData($data);
101
102
        return $this->hydrateTree($object);
0 ignored issues
show
Documentation introduced by
$object is of type object<Doctrine\Common\Collections\Collection>, but the function expects a object<Core\Entity\Tree\NodeInterface>.

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...
103
    }
104
105
    /**
106
     * Prepares the form values for hydrating.
107
     *
108
     * @internal
109
     *      Populates the {@link hydrateData} array
110
     *
111
     * @param array $data
112
     */
113
    private function prepareHydrateData(array $data)
114
    {
115
        /*
116
         * unflatten tree
117
         */
118
        $items = $data['items'];
119
        $tree = [ '__root__' => array_shift($items) ];
120
121
        foreach ($items as $item) {
122
            $parent = substr($item['current'], 0, strrpos($item['current'], '-'));
123
            $tree[$parent][] = $item;
124
        }
125
126
        $this->hydrateData = $tree;
127
    }
128
129
    /**
130
     * Recursively hydrate the tree structure.
131
     *
132
     * Items not present in the {@link hydrateData} are removed,
133
     * new items are created accordingly.
134
     *
135
     * @param NodeInterface $object
136
     * @param \ArrayObject $currentData
0 ignored issues
show
Documentation introduced by
Should the type for parameter $currentData not be null|\ArrayObject?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
137
     *
138
     * @return NodeInterface
139
     */
140
    private function hydrateTree(NodeInterface $object, \ArrayObject $currentData = null)
141
    {
142
143
        if (null === $currentData) {
144
            $currentData = $this->hydrateData['__root__'];
145
        }
146
147
        if ('set' == $currentData['do']) {
148
            $object
149
                ->setName($currentData['name'])
150
                ->setValue($currentData['value'])
151
                ->setPriority($currentData['priority'])
152
            ;
153
        }
154
155
        if (isset($this->hydrateData[$currentData['current']])) {
156
            foreach ($this->hydrateData[$currentData['current']] as $childData) {
157
                $child = $this->findOrCreateChild($object, $childData['id']);
158
                if ('remove' == $childData['do']) {
159
                    $object->removeChild($child);
160
161
                } else {
162
                    $this->hydrateTree($child, $childData);
163
                }
164
            }
165
        }
166
167
        return $object;
168
    }
169
170
    /**
171
     * Finds an item in a tree structure or create a new item.
172
     *
173
     * @param NodeInterface $tree
174
     * @param $id
175
     *
176
     * @return NodeInterface
177
     */
178
    private function findOrCreateChild($tree, $id)
179
    {
180
        /* @var NodeInterface $node */
181
        foreach ($tree->getChildren() as $node) {
182
            if ($id && $node->getId() == $id) { return $node; }
183
        }
184
185
        $nodeClass = get_class($tree);
186
        $node = new $nodeClass();
187
        $tree->addChild($node);
188
189
        return $node;
190
    }
191
}