@@ -841,9 +841,11 @@ public function isBroken()
841
841
*
842
842
* Nodes with invalid parent are saved as roots.
843
843
*
844
- * @return int The number of fixed nodes
844
+ * @param null|NodeTrait|Model $root
845
+ *
846
+ * @return int The number of changed nodes
845
847
*/
846
- public function fixTree ()
848
+ public function fixTree ($ root = null )
847
849
{
848
850
$ columns = [
849
851
$ this ->model ->getKeyName (),
@@ -852,48 +854,78 @@ public function fixTree()
852
854
$ this ->model ->getRgtName (),
853
855
];
854
856
855
- $ dictionary = $ this ->model ->newNestedSetQuery ()
856
- ->defaultOrder ()
857
- ->get ($ columns )
858
- ->groupBy ($ this ->model ->getParentIdName ())
859
- ->all ();
857
+ $ dictionary = $ this ->model
858
+ ->newNestedSetQuery ()
859
+ ->when ($ root , function (self $ query , $ root ) {
860
+ $ query ->whereDescendantOf ($ root );
861
+ })
862
+ ->defaultOrder ()
863
+ ->get ($ columns )
864
+ ->groupBy ($ this ->model ->getParentIdName ())
865
+ ->all ();
866
+
867
+ return $ this ->fixNodes ($ dictionary , $ root );
868
+ }
860
869
861
- return self ::fixNodes ($ dictionary );
870
+ /**
871
+ * @param NodeTrait|Model $root
872
+ *
873
+ * @return int
874
+ */
875
+ public function fixSubtree ($ root )
876
+ {
877
+ return $ this ->fixTree ($ root );
862
878
}
863
879
864
880
/**
865
881
* @param array $dictionary
882
+ * @param NodeTrait|Model|null $parent
866
883
*
867
884
* @return int
868
885
*/
869
- protected static function fixNodes (array &$ dictionary )
886
+ protected function fixNodes (array &$ dictionary, $ parent = null )
870
887
{
871
- $ fixed = 0 ;
888
+ $ parentId = $ parent ? $ parent ->getKey () : null ;
889
+ $ cut = $ parent ? $ parent ->getLft () + 1 : 1 ;
890
+
891
+ $ updated = [];
892
+ $ moved = 0 ;
872
893
873
- $ cut = self ::reorderNodes ($ dictionary , $ fixed );
894
+ $ cut = self ::reorderNodes ($ dictionary , $ updated , $ parentId , $ cut );
874
895
875
896
// Save nodes that have invalid parent as roots
876
897
while ( ! empty ($ dictionary )) {
877
898
$ dictionary [null ] = reset ($ dictionary );
878
899
879
900
unset($ dictionary [key ($ dictionary )]);
880
901
881
- $ cut = self ::reorderNodes ($ dictionary , $ fixed , null , $ cut );
902
+ $ cut = self ::reorderNodes ($ dictionary , $ updated , $ parentId , $ cut );
903
+ }
904
+
905
+ if ($ parent && ($ grown = $ cut - $ parent ->getRgt ()) != 0 ) {
906
+ $ moved = $ this ->model ->newScopedQuery ()->makeGap ($ parent ->getRgt () + 1 , $ grown );
907
+
908
+ $ updated [] = $ parent ->rawNode ($ parent ->getLft (), $ cut , $ parent ->getParentId ());
909
+ }
910
+
911
+ foreach ($ updated as $ model ) {
912
+ $ model ->save ();
882
913
}
883
914
884
- return $ fixed ;
915
+ return count ( $ updated ) + $ moved ;
885
916
}
886
917
887
918
/**
888
919
* @param array $dictionary
889
- * @param int $fixed
920
+ * @param array $updated
890
921
* @param $parentId
891
922
* @param int $cut
892
923
*
893
924
* @return int
925
+ * @internal param int $fixed
894
926
*/
895
- protected static function reorderNodes (array & $ dictionary , & $ fixed ,
896
- $ parentId = null , $ cut = 1
927
+ protected static function reorderNodes (
928
+ array & $ dictionary , array & $ updated , $ parentId = null , $ cut = 1
897
929
) {
898
930
if ( ! isset ($ dictionary [$ parentId ])) {
899
931
return $ cut ;
@@ -903,17 +935,10 @@ protected static function reorderNodes(array &$dictionary, &$fixed,
903
935
foreach ($ dictionary [$ parentId ] as $ model ) {
904
936
$ lft = $ cut ;
905
937
906
- $ cut = self ::reorderNodes ($ dictionary ,
907
- $ fixed ,
908
- $ model ->getKey (),
909
- $ cut + 1 );
938
+ $ cut = self ::reorderNodes ($ dictionary , $ updated , $ model ->getKey (), $ cut + 1 );
910
939
911
- $ rgt = $ cut ;
912
-
913
- if ($ model ->rawNode ($ lft , $ rgt , $ parentId )->isDirty ()) {
914
- $ model ->save ();
915
-
916
- $ fixed ++;
940
+ if ($ model ->rawNode ($ lft , $ cut , $ parentId )->isDirty ()) {
941
+ $ updated [] = $ model ;
917
942
}
918
943
919
944
++$ cut ;
@@ -932,20 +957,29 @@ protected static function reorderNodes(array &$dictionary, &$fixed,
932
957
* @param array $data
933
958
* @param bool $delete Whether to delete nodes that exists but not in the data
934
959
* array
960
+ * @param null $root
935
961
*
936
962
* @return int
937
963
*/
938
- public function rebuildTree (array $ data , $ delete = false )
964
+ public function rebuildTree (array $ data , $ delete = false , $ root = null )
939
965
{
940
966
if ($ this ->model ->usesSoftDelete ()) {
941
967
$ this ->withTrashed ();
942
968
}
943
969
944
- $ existing = $ this ->get ()->getDictionary ();
970
+ $ existing = $ this
971
+ ->when ($ root , function (self $ query , $ root ) {
972
+ $ query ->whereDescendantOf ($ root );
973
+ })
974
+ ->get ()
975
+ ->getDictionary ();
976
+
945
977
$ dictionary = [];
978
+ $ parentId = $ root ? $ root ->getKey () : null ;
946
979
947
- $ this ->buildRebuildDictionary ($ dictionary , $ data , $ existing );
980
+ $ this ->buildRebuildDictionary ($ dictionary , $ data , $ existing, $ parentId );
948
981
982
+ /** @var Model|NodeTrait $model */
949
983
if ( ! empty ($ existing )) {
950
984
if ($ delete && ! $ this ->model ->usesSoftDelete ()) {
951
985
$ this ->model
@@ -967,7 +1001,19 @@ public function rebuildTree(array $data, $delete = false)
967
1001
}
968
1002
}
969
1003
970
- return $ this ->fixNodes ($ dictionary );
1004
+ return $ this ->fixNodes ($ dictionary , $ root );
1005
+ }
1006
+
1007
+ /**
1008
+ * @param $root
1009
+ * @param array $data
1010
+ * @param bool $delete
1011
+ *
1012
+ * @return int
1013
+ */
1014
+ public function rebuildSubtree ($ root , array $ data , $ delete = false )
1015
+ {
1016
+ return $ this ->rebuildTree ($ data , $ delete , $ root );
971
1017
}
972
1018
973
1019
/**
@@ -984,10 +1030,12 @@ protected function buildRebuildDictionary(array &$dictionary,
984
1030
$ keyName = $ this ->model ->getKeyName ();
985
1031
986
1032
foreach ($ data as $ itemData ) {
1033
+ /** @var NodeTrait|Model $model */
1034
+
987
1035
if ( ! isset ($ itemData [$ keyName ])) {
988
1036
$ model = $ this ->model ->newInstance ($ this ->model ->getAttributes ());
989
1037
990
- // We will save it as raw node since tree will be fixed
1038
+ // Set some values that will be fixed later
991
1039
$ model ->rawNode (0 , 0 , $ parentId );
992
1040
} else {
993
1041
if ( ! isset ($ existing [$ key = $ itemData [$ keyName ]])) {
@@ -996,6 +1044,9 @@ protected function buildRebuildDictionary(array &$dictionary,
996
1044
997
1045
$ model = $ existing [$ key ];
998
1046
1047
+ // Disable any tree actions
1048
+ $ model ->rawNode ($ model ->getLft (), $ model ->getRgt (), $ parentId );
1049
+
999
1050
unset($ existing [$ key ]);
1000
1051
}
1001
1052
0 commit comments