@@ -182,35 +182,75 @@ private static function scanDirectoryFiles($directory)
182182 array_walk ($ files , function (&$ value , $ index ) use ($ directory ) {
183183 $ value = rtrim ($ directory , '/ ' ) . "/ {$ value }" ;
184184 });
185+
186+ dd (
187+ self ::sortParentFiles ($ files )
188+ );
185189
186190 return self ::sortParentFiles ($ files );
187191 }
188192
189193 /**
190194 * Sort parent files according to their names
191195 *
196+ * Ensures that base tables (e.g., "ads", "users") are migrated
197+ * before their derivative/child tables (e.g., "ads_data", "users_address", "ads_info").
198+ *
192199 * @param array $files
193- * @return void
200+ * @return array
194201 */
195202 private static function sortParentFiles (array $ files )
196203 {
197- // Custom sort: parent tables before child tables
198- usort ($ files , function ($ a , $ b ) {
199- $ aName = basename ($ a );
200- $ bName = basename ($ b );
201-
202- // ensure "ads_table" comes before "ads_data_table"
203- if (preg_match ('/create_(\w+)_data_table/ ' , $ aName , $ am ) &&
204- preg_match ('/create_ ' . $ am [1 ] . '_table/ ' , $ bName )) {
205- return 1 ; // b before a
204+ // Robust sort: parent tables before child tables of the same base
205+ $ parse = function (string $ path ): array {
206+ $ name = basename ($ path );
207+ // extract between "create_" and "_table"
208+ if (preg_match ('/create_(.+)_table\.php$/ ' , $ name , $ m )) {
209+ $ full = $ m [1 ]; // e.g., "ads", "ads_data", "users_address"
210+ $ parts = explode ('_ ' , $ full ); // split by underscore
211+ $ base = $ parts [0 ]; // the base entity
212+ return [
213+ 'name ' => $ name ,
214+ 'full ' => $ full ,
215+ 'base ' => $ base ,
216+ 'parts ' => $ parts ,
217+ 'is_parent ' => count ($ parts ) === 1 ,
218+ ];
206219 }
220+ // Non-standard name: treat as parent so it runs early
221+ return [
222+ 'name ' => $ name ,
223+ 'full ' => $ name ,
224+ 'base ' => $ name ,
225+ 'parts ' => [$ name ],
226+ 'is_parent ' => true ,
227+ ];
228+ };
207229
208- if (preg_match ('/create_(\w+)_data_table/ ' , $ bName , $ bm ) &&
209- preg_match ('/create_ ' . $ bm [1 ] . '_table/ ' , $ aName )) {
210- return -1 ; // a before b
230+ usort ($ files , function (string $ a , string $ b ) use ($ parse ) {
231+ $ ai = $ parse ($ a );
232+ $ bi = $ parse ($ b );
233+
234+ // Same base entity (e.g., "ads" vs "ads_data", "users" vs "users_address")
235+ if ($ ai ['base ' ] === $ bi ['base ' ]) {
236+ // Parent must come before children
237+ if ($ ai ['is_parent ' ] !== $ bi ['is_parent ' ]) {
238+ return $ ai ['is_parent ' ] ? -1 : 1 ;
239+ }
240+
241+ // Both children or both parents:
242+ // fewer segments first (e.g., users_address before users_address_history)
243+ $ cmpParts = count ($ ai ['parts ' ]) <=> count ($ bi ['parts ' ]);
244+ if ($ cmpParts !== 0 ) {
245+ return $ cmpParts ;
246+ }
247+
248+ // then alphabetical by the entity part for stability
249+ return strcmp ($ ai ['full ' ], $ bi ['full ' ]);
211250 }
212251
213- return strcmp ($ aName , $ bName ); // fallback alphabetical
252+ // Different bases: keep natural alphabetical (includes timestamp prefix)
253+ return strcmp ($ ai ['name ' ], $ bi ['name ' ]);
214254 });
215255
216256 return $ files ;
0 commit comments