Completed
Push — master ( c56598...026f51 )
by ARCANEDEV
8s
created

TreeHelper::retrieveNode()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

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