TreeHelper::reorderNodes()   B
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 28
c 0
b 0
f 0
ccs 15
cts 15
cp 1
rs 8.5806
cc 4
eloc 17
nc 4
nop 4
crap 4
1
<?php namespace Arcanedev\LaravelNestedSet\Utilities;
2
3
use Arcanedev\LaravelNestedSet\Contracts\Nodeable;
4
use Arcanedev\LaravelNestedSet\NodeTrait;
5
use Illuminate\Database\Eloquent\ModelNotFoundException;
6
use Illuminate\Support\Arr;
7
8
/**
9
 * Class     TreeHelper
10
 *
11
 * @package  Arcanedev\LaravelNestedSet\Utilities
12
 * @author   ARCANEDEV <[email protected]>
13
 */
14
class TreeHelper
15
{
16
    /* -----------------------------------------------------------------
17
     |  Main Methods
18
     | -----------------------------------------------------------------
19
     */
20
    /**
21
     * @param  array                                           $data
22
     * @param  array                                           $existing
23
     * @param  \Arcanedev\LaravelNestedSet\Contracts\Nodeable  $model
24
     * @param  bool                                            $delete
25
     *
26
     * @return int
27
     */
28 9
    public static function rebuild(array $data, $existing, Nodeable $model, $delete = false)
29
    {
30 9
        $dictionary = [];
31
32 9
        self::rebuildDictionary($dictionary, $data, $existing, $model);
33
34 6
        if ( ! empty($existing)) {
35 6
            self::cleaningDictionary($dictionary, $existing, $model, $delete);
36 2
        }
37
38 6
        return self::fixNodes($dictionary);
39
    }
40
41
    /**
42
     * Fix nodes.
43
     *
44
     * @param  array  $dictionary
45
     *
46
     * @return int
47
     */
48 9
    public static function fixNodes(array &$dictionary)
49
    {
50 9
        $fixed = 0;
51 9
        $cut   = self::reorderNodes($dictionary, $fixed);
52
53
        // Save nodes that have invalid parent as roots
54 9
        while ( ! empty($dictionary)) {
55 3
            $dictionary[null] = reset($dictionary);
56 3
            unset($dictionary[key($dictionary)]);
57
58 3
            $cut = self::reorderNodes($dictionary, $fixed, null, $cut);
59 1
        }
60
61 9
        return $fixed;
62
    }
63
64
    /* -----------------------------------------------------------------
65
     |  Other Methods
66
     | -----------------------------------------------------------------
67
     */
68
    /**
69
     * Rebuild the dictionary.
70
     *
71
     * @param  array                                           $dictionary
72
     * @param  array                                           $data
73
     * @param  array                                           $existing
74
     * @param  \Arcanedev\LaravelNestedSet\Contracts\Nodeable  $model
75
     * @param  mixed                                           $parentId
76
     *
77
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
78
     */
79 9
    protected static function rebuildDictionary(
80
        array &$dictionary,
81
        array $data,
82
        array &$existing,
83
        Nodeable &$model,
84
        $parentId = null
85
    ) {
86 9
        $keyName = $model->getKeyName();
87
88 9
        foreach ($data as $itemData) {
89 9
            $node = self::retrieveNode($existing, $model, $parentId, $itemData, $keyName);
90
91 6
            $node->fill(Arr::except($itemData, 'children'))->save();
92 6
            $dictionary[$parentId][] = $node;
93
94 6
            if (isset($itemData['children'])) {
95 4
                self::rebuildDictionary($dictionary, $itemData['children'], $existing, $model, $node->getKey());
96 1
            }
97 2
        }
98 6
    }
99
100
    /**
101
     * @param  array                                           $existing
102
     * @param  \Arcanedev\LaravelNestedSet\Contracts\Nodeable  $model
103
     * @param  mixed                                           $parentId
104
     * @param  array                                           $itemData
105
     * @param  mixed                                           $keyName
106
     *
107
     * @return \Arcanedev\LaravelNestedSet\Contracts\Nodeable
108
     */
109 9
    protected static function retrieveNode(array &$existing, Nodeable &$model, $parentId, $itemData, $keyName)
110
    {
111 9
        if ( ! isset($itemData[$keyName])) {
112
            // We will save it as raw node since tree will be fixed
113 6
            $node = $model->newInstance()->rawNode(0, 0, $parentId);
114 2
        }
115 6
        elseif ( ! isset($existing[ $key = $itemData[$keyName] ])) {
116 3
            throw new ModelNotFoundException;
117
        }
118
        else {
119 3
            $node = $existing[$key];
120 3
            unset($existing[$key]);
121
        }
122
123 6
        return $node;
124
    }
125
126
    /**
127
     * Reorder nodes.
128
     *
129
     * @param  array     $dictionary
130
     * @param  int       $fixed
131
     * @param  int|null  $parentId
132
     * @param  int       $cut
133
     *
134
     * @return int
135
     */
136 9
    protected static function reorderNodes(
137
        array &$dictionary,
138
        &$fixed,
139
        $parentId = null,
140
        $cut = 1
141
    ) {
142 9
        if ( ! isset($dictionary[$parentId])) {
143 9
            return $cut;
144
        }
145
146
        /** @var NodeTrait $model */
147 9
        foreach ($dictionary[$parentId] as $model) {
148 9
            $lft = $cut;
149 9
            $cut = self::reorderNodes($dictionary, $fixed, $model->getKey(), $cut + 1);
150 9
            $rgt = $cut;
151
152 9
            if ($model->rawNode($lft, $rgt, $parentId)->isDirty()) {
153 9
                $model->save();
154 9
                $fixed++;
155 3
            }
156
157 9
            ++$cut;
158 3
        }
159
160 9
        unset($dictionary[$parentId]);
161
162 9
        return $cut;
163
    }
164
165
    /**
166
     * Cleaning existing nodes.
167
     *
168
     * @param  array                                           $dictionary
169
     * @param  array                                           $existing
170
     * @param  \Arcanedev\LaravelNestedSet\Contracts\Nodeable  $model
171
     * @param  bool                                            $delete
172
     */
173 6
    private static function cleaningDictionary(&$dictionary, $existing, Nodeable $model, $delete)
174
    {
175 6
        if ($delete) {
176 3
            $model->newScopedQuery()
177 3
                  ->whereIn($model->getKeyName(), array_keys($existing))
178 3
                  ->forceDelete();
179
180 3
            return;
181
        }
182
183 3
        foreach ($existing as $node) {
184
            /** @var \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node */
185 3
            $dictionary[ $node->getParentId() ][] = $node;
186 1
        }
187 3
    }
188
}
189