@@ -81,40 +81,81 @@ protected function setUp(): void
8181 });
8282 }
8383
84- private function buildTree ($ parent = null ): array |Collection
84+ private function buildTree (): Collection
85+ {
86+ // Start with two separate query builders
87+ $ nullParentQuery = $ this ->getRelationship ()->getRelated ()->query ()->where ($ this ->getParentAttribute (), $ this ->getParentNullValue ());
88+ $ nonNullParentQuery = $ this ->getRelationship ()->getRelated ()->query ()->whereNot ($ this ->getParentAttribute (), $ this ->getParentNullValue ());
89+
90+ // If we're not at the root level and a modification callback is provided, apply it to null query
91+ if ($ this ->modifyQueryUsing ) {
92+ $ nullParentQuery = $ this ->evaluate ($ this ->modifyQueryUsing , ['query ' => $ nullParentQuery ]);
93+ }
94+
95+ // Fetch results for both queries
96+ $ nullParentResults = $ nullParentQuery ->get ();
97+ $ nonNullParentResults = $ nonNullParentQuery ->get ();
98+
99+ // Combine the results from both queries
100+ $ combinedResults = $ nullParentResults ->concat ($ nonNullParentResults );
101+
102+ return $ this ->buildTreeFromResults ($ combinedResults );
103+ }
104+
105+ private function buildTreeFromResults ($ results , $ parent = null ): Collection
85106 {
86107 // Assign the parent's null value to the $parent variable if it's not null
87108 if ($ parent == null || $ parent == $ this ->getParentNullValue ()) {
88109 $ parent = $ this ->getParentNullValue () ?? $ parent ;
89110 }
90111
91- // Create a default query to retrieve related items.
92- $ defaultQuery = $ this ->getRelationship ()
93- ->getRelated ()
94- ->query ()
95- ->where ($ this ->getParentAttribute (), $ parent );
112+ // Create a collection to store the tree
113+ $ tree = collect ();
96114
97- // If we're not at the root level and a modification callback is provided, apply it to the query.
98- if (! $ parent && $ this ->modifyQueryUsing ) {
99- $ defaultQuery = $ this ->evaluate ($ this ->modifyQueryUsing , ['query ' => $ defaultQuery ]);
115+ // Create a mapping of results by their parent IDs for faster lookup
116+ $ resultMap = [];
117+
118+ // Group results by their parent IDs
119+ foreach ($ results as $ result ) {
120+ $ parentId = $ result ->{$ this ->getParentAttribute ()};
121+ if (! isset ($ resultMap [$ parentId ])) {
122+ $ resultMap [$ parentId ] = [];
123+ }
124+ $ resultMap [$ parentId ][] = $ result ;
100125 }
101126
102- // Fetch the results from the default query.
103- $ results = $ defaultQuery ->get ();
127+ // Recursively build the tree starting from the root (null parent)
128+ $ rootResults = $ resultMap [$ parent ] ?? [];
129+ foreach ($ rootResults as $ result ) {
130+ // Build a node and add it to the tree
131+ $ node = $ this ->buildNode ($ result , $ resultMap );
132+ $ tree ->push ($ node );
133+ }
104134
105- // Map the results into a tree structure.
106- return $ results -> map ( function ( $ result ) {
135+ return $ tree;
136+ }
107137
108- // Recursively build children trees for the current result.
109- $ children = $ this ->buildTree ($ result ->id );
138+ private function buildNode ($ result , $ resultMap ): array
139+ {
140+ // Create a node with 'name' and 'value' attributes
141+ $ node = [
142+ 'name ' => $ result ->{$ this ->getTitleAttribute ()},
143+ 'value ' => $ result ->id ,
144+ ];
145+
146+ // Check if the result has children
147+ if (isset ($ resultMap [$ result ->id ])) {
148+ $ children = collect ();
149+ // Recursively build child nodes
150+ foreach ($ resultMap [$ result ->id ] as $ child ) {
151+ $ childNode = $ this ->buildNode ($ child , $ resultMap );
152+ $ children ->push ($ childNode );
153+ }
154+ // Add children to the node
155+ $ node ['children ' ] = $ children ->toArray ();
156+ }
110157
111- // Create an array representation of the current result with children.
112- return [
113- 'name ' => $ result ->{$ this ->getTitleAttribute ()},
114- 'value ' => $ result ->id ,
115- 'children ' => $ children ->isEmpty () ? null : $ children ->toArray (),
116- ];
117- });
158+ return $ node ;
118159 }
119160
120161 public function relationship (string $ relationship , string $ titleAttribute , string $ parentAttribute , Closure $ modifyQueryUsing = null ): self
0 commit comments