@@ -58,6 +58,18 @@ public function setCategoryIds($ids)
5858 $ ids = [(int )$ ids ];
5959 }
6060 $ this ->_selectedIds = $ ids ;
61+
62+ // Pre-compute expanded paths for all selected categories so that
63+ // ancestors are marked as expanded before the JSON is generated.
64+ $ this ->_expandedPath = [];
65+ if (!empty ($ ids )) {
66+ $ collection = $ this ->_categoryFactory ->create ()->getCollection ();
67+ $ collection ->addAttributeToSelect ('path ' )
68+ ->addAttributeToFilter ('entity_id ' , ['in ' => $ ids ]);
69+ foreach ($ collection as $ category ) {
70+ $ this ->setExpandedPath ($ category ->getPath ());
71+ }
72+ }
6173 return $ this ;
6274 }
6375
@@ -122,4 +134,74 @@ protected function _getNodeJson($node, $level = 1)
122134 $ item ['expanded ' ] = in_array ($ node ->getId (), $ this ->getExpandedPath ());
123135 return $ item ;
124136 }
137+
138+ /**
139+ * Get tree structure
140+ *
141+ * Ensure that deeply selected categories are present by building the tree
142+ * around selected IDs rather than the default 3-level root-only tree.
143+ *
144+ * @param mixed|null $parenNodeCategory
145+ * @return array
146+ */
147+ public function getTree ($ parenNodeCategory = null )
148+ {
149+ // For AJAX child loads, respect the requested parent node
150+ if ($ parenNodeCategory !== null ) {
151+ $ root = $ this ->getRoot ($ parenNodeCategory );
152+ } else {
153+ $ root = empty ($ this ->_selectedIds )
154+ ? $ this ->getRoot ($ parenNodeCategory )
155+ : $ this ->getRootByIds ($ this ->_selectedIds );
156+ }
157+
158+ $ rootArray = $ this ->_getNodeJson ($ root );
159+ return $ rootArray ['children ' ] ?? [];
160+ }
161+
162+ /**
163+ * Get tree json
164+ *
165+ * Ensure that deeply selected categories are present in the JSON output.
166+ *
167+ * @param mixed|null $parenNodeCategory
168+ * @return string
169+ */
170+ public function getTreeJson ($ parenNodeCategory = null )
171+ {
172+ // For AJAX child loads, respect the requested parent node
173+ if ($ parenNodeCategory !== null ) {
174+ $ root = $ this ->getRoot ($ parenNodeCategory );
175+ } else {
176+ $ root = empty ($ this ->_selectedIds )
177+ ? $ this ->getRoot ($ parenNodeCategory )
178+ : $ this ->getRootByIds ($ this ->_selectedIds );
179+ }
180+
181+ $ rootArray = $ this ->_getNodeJson ($ root );
182+ return $ this ->_jsonEncoder ->encode ($ rootArray ['children ' ] ?? []);
183+ }
184+
185+ /**
186+ * Override parent's implementation to avoid using cached registry root
187+ * which might have been built with limited recursion and miss deep nodes.
188+ *
189+ * @param array $ids
190+ * @return \Magento\Framework\Data\Tree\Node|array|null
191+ */
192+ public function getRootByIds ($ ids )
193+ {
194+ $ ids = $ this ->_categoryTree ->getExistingCategoryIdsBySpecifiedIds ($ ids );
195+ $ tree = $ this ->_categoryTree ->loadByIds ($ ids );
196+ $ rootId = \Magento \Catalog \Model \Category::TREE_ROOT_ID ;
197+ $ root = $ tree ->getNodeById ($ rootId );
198+ if ($ root ) {
199+ $ root ->setIsVisible (true );
200+ if ($ root ->getId () == \Magento \Catalog \Model \Category::TREE_ROOT_ID ) {
201+ $ root ->setName (__ ('Root ' ));
202+ }
203+ }
204+ $ tree ->addCollectionData ($ this ->getCategoryCollection ());
205+ return $ root ;
206+ }
125207}
0 commit comments