@@ -126,17 +126,37 @@ class MigrationRunner
126126 */
127127 protected $ cliMessages = [];
128128
129+ /**
130+ * Tracks whether we have already ensured
131+ * the table exists or not.
132+ *
133+ * @var boolean
134+ */
135+ protected $ tableChecked = false ;
136+
137+ /**
138+ * The full path to locate migration files.
139+ *
140+ * @var string
141+ */
142+ protected $ path ;
143+
129144 //--------------------------------------------------------------------
130145
131146 /**
132147 * Constructor.
133148 *
134- * @param BaseConfig $config
135- * @param \CodeIgniter\Database\ConnectionInterface $db
149+ * When passing in $db, you may pass any of the following to connect:
150+ * - group name
151+ * - existing connection instance
152+ * - array of database configuration values
153+ *
154+ * @param BaseConfig $config
155+ * @param \CodeIgniter\Database\ConnectionInterface|array|string $db
136156 *
137157 * @throws ConfigException
138158 */
139- public function __construct (BaseConfig $ config , ConnectionInterface $ db = null )
159+ public function __construct (BaseConfig $ config , $ db = null )
140160 {
141161 $ this ->enabled = $ config ->enabled ?? false ;
142162 $ this ->type = $ config ->type ?? 'timestamp ' ;
@@ -147,15 +167,10 @@ public function __construct(BaseConfig $config, ConnectionInterface $db = null)
147167 $ this ->namespace = APP_NAMESPACE ;
148168
149169 // get default database group
150- $ config = new \ Config \ Database ( );
170+ $ config = config ( ' Database ' );
151171 $ this ->group = $ config ->defaultGroup ;
152172 unset($ config );
153173
154- if (empty ($ this ->table ))
155- {
156- throw ConfigException::forMissingMigrationsTable ();
157- }
158-
159174 if (! in_array ($ this ->type , ['sequential ' , 'timestamp ' ]))
160175 {
161176 throw ConfigException::forInvalidMigrationType ($ this ->type );
@@ -166,9 +181,7 @@ public function __construct(BaseConfig $config, ConnectionInterface $db = null)
166181
167182 // If no db connection passed in, use
168183 // default database group.
169- $ this ->db = ! empty ($ db ) ? $ db : \Config \Database::connect ();
170-
171- $ this ->ensureTable ();
184+ $ this ->db = db_connect ($ db );
172185 }
173186
174187 //--------------------------------------------------------------------
@@ -192,6 +205,9 @@ public function version(string $targetVersion, string $namespace = null, string
192205 {
193206 throw ConfigException::forDisabledMigrations ();
194207 }
208+
209+ $ this ->ensureTable ();
210+
195211 // Set Namespace if not null
196212 if (! is_null ($ namespace ))
197213 {
@@ -204,6 +220,12 @@ public function version(string $targetVersion, string $namespace = null, string
204220 $ this ->setGroup ($ group );
205221 }
206222
223+ // Sequential versions need adjusting to 3 places so they can be found later.
224+ if ($ this ->type === 'sequential ' )
225+ {
226+ $ targetVersion = str_pad ($ targetVersion , 3 , '0 ' , STR_PAD_LEFT );
227+ }
228+
207229 $ migrations = $ this ->findMigrations ();
208230
209231 if (empty ($ migrations ))
@@ -284,6 +306,8 @@ public function version(string $targetVersion, string $namespace = null, string
284306 */
285307 public function latest (string $ namespace = null , string $ group = null )
286308 {
309+ $ this ->ensureTable ();
310+
287311 // Set Namespace if not null
288312 if (! is_null ($ namespace ))
289313 {
@@ -315,6 +339,8 @@ public function latest(string $namespace = null, string $group = null)
315339 */
316340 public function latestAll (string $ group = null )
317341 {
342+ $ this ->ensureTable ();
343+
318344 // Set database group if not null
319345 if (! is_null ($ group ))
320346 {
@@ -361,6 +387,8 @@ public function latestAll(string $group = null)
361387 */
362388 public function current (string $ group = null )
363389 {
390+ $ this ->ensureTable ();
391+
364392 // Set database group if not null
365393 if (! is_null ($ group ))
366394 {
@@ -380,18 +408,39 @@ public function current(string $group = null)
380408 public function findMigrations ()
381409 {
382410 $ migrations = [];
383- // Get namespace location form PSR4 paths.
384- $ config = new Autoload ();
411+ helper ('filesystem ' );
385412
386- $ location = $ config ->psr4 [$ this ->namespace ];
413+ // If $this->path contains a valid directory use it.
414+ if (! empty ($ this ->path ))
415+ {
416+ $ dir = rtrim ($ this ->path , DIRECTORY_SEPARATOR ) . '/ ' ;
417+ }
418+ // Otherwise, get namespace location form PSR4 paths
419+ // and add Database/Migrations for a standard loation.
420+ else
421+ {
422+ $ config = config ('Autoload ' );
387423
388- // Setting migration directories.
389- $ dir = rtrim ($ location , DIRECTORY_SEPARATOR ) . '/Database/Migrations/ ' ;
424+ $ location = $ config ->psr4 [$ this ->namespace ];
425+
426+ // Setting migration directories.
427+ $ dir = rtrim ($ location , DIRECTORY_SEPARATOR ) . '/Database/Migrations/ ' ;
428+ }
390429
391430 // Load all *_*.php files in the migrations path
392- foreach (glob ($ dir . '*_*.php ' ) as $ file )
431+ // We can't use glob if we want it to be testable....
432+ $ files = get_filenames ($ dir , true );
433+
434+ foreach ($ files as $ file )
393435 {
436+ if (substr ($ file , -4 ) !== '.php ' )
437+ {
438+ continue ;
439+ }
440+
441+ // Remove the extension
394442 $ name = basename ($ file , '.php ' );
443+
395444 // Filter out non-migration files
396445 if (preg_match ($ this ->regex , $ name ))
397446 {
@@ -400,7 +449,9 @@ public function findMigrations()
400449 // Get migration version number
401450 $ migration ->version = $ this ->getMigrationNumber ($ name );
402451 $ migration ->name = $ this ->getMigrationName ($ name );
403- $ migration ->path = $ file ;
452+ $ migration ->path = strpos ($ file , $ this ->path ) !== 0
453+ ? $ this ->path . $ file
454+ : $ file ;
404455
405456 // Add to migrations[version]
406457 $ migrations [$ migration ->version ] = $ migration ;
@@ -436,7 +487,7 @@ protected function checkMigrations(array $migrations, string $method, string $ta
436487 }
437488
438489 // Check if $targetversion file is found
439- if ($ targetversion !== ' 0 ' && ! array_key_exists ($ targetversion , $ migrations ))
490+ if (( int ) $ targetversion !== 0 && ! array_key_exists ($ targetversion , $ migrations ))
440491 {
441492 if ($ this ->silent )
442493 {
@@ -458,14 +509,14 @@ protected function checkMigrations(array $migrations, string $method, string $ta
458509 {
459510 if ($ this ->type === 'sequential ' && abs ($ migration ->version - $ loop ) > 1 )
460511 {
461- throw new \RuntimeException (lang ('Migration .gap ' ) . ' ' . $ migration ->version );
512+ throw new \RuntimeException (lang ('Migrations .gap ' ) . ' ' . $ migration ->version );
462513 }
463514 // Check if all old migration files are all available to do downgrading
464515 if ($ method === 'down ' )
465516 {
466517 if ($ loop <= $ history_size && $ history_migrations [$ loop ]['version ' ] !== $ migration ->version )
467518 {
468- throw new \RuntimeException (lang ('Migration .gap ' ) . ' ' . $ migration ->version );
519+ throw new \RuntimeException (lang ('Migrations .gap ' ) . ' ' . $ migration ->version );
469520 }
470521 }
471522 $ loop ++;
@@ -476,6 +527,22 @@ protected function checkMigrations(array $migrations, string $method, string $ta
476527
477528 //--------------------------------------------------------------------
478529
530+ /**
531+ * Sets the path to the base directory that will be used
532+ * when locating migrations. If left null, the value will
533+ * be chosen from $this->namespace's directory.
534+ *
535+ * @param string|null $path
536+ *
537+ * @return $this
538+ */
539+ public function setPath (string $ path = null )
540+ {
541+ $ this ->path = $ path ;
542+
543+ return $ this ;
544+ }
545+
479546 /**
480547 * Set namespace.
481548 * Allows other scripts to modify on the fly as needed.
@@ -514,10 +581,14 @@ public function setGroup(string $group)
514581 * Set migration Name.
515582 *
516583 * @param string $name
584+ *
585+ * @return \CodeIgniter\Database\MigrationRunner
517586 */
518587 public function setName (string $ name )
519588 {
520589 $ this ->name = $ name ;
590+
591+ return $ this ;
521592 }
522593
523594 //--------------------------------------------------------------------
@@ -531,6 +602,8 @@ public function setName(string $name)
531602 */
532603 public function getHistory (string $ group = 'default ' )
533604 {
605+ $ this ->ensureTable ();
606+
534607 $ query = $ this ->db ->table ($ this ->table )
535608 ->where ('group ' , $ group )
536609 ->where ('namespace ' , $ this ->namespace )
@@ -602,6 +675,8 @@ protected function getMigrationName(string $migration)
602675 */
603676 protected function getVersion ()
604677 {
678+ $ this ->ensureTable ();
679+
605680 $ row = $ this ->db ->table ($ this ->table )
606681 ->select ('version ' )
607682 ->where ('group ' , $ this ->group )
@@ -675,14 +750,14 @@ protected function removeHistory(string $version)
675750 * Ensures that we have created our migrations table
676751 * in the database.
677752 */
678- protected function ensureTable ()
753+ public function ensureTable ()
679754 {
680- if ($ this ->db ->tableExists ($ this ->table ))
755+ if ($ this ->tableChecked || $ this -> db ->tableExists ($ this ->table ))
681756 {
682757 return ;
683758 }
684759
685- $ forge = \Config \Database::forge ();
760+ $ forge = \Config \Database::forge ($ this -> db );
686761
687762 $ forge ->addField ([
688763 'version ' => [
@@ -713,6 +788,8 @@ protected function ensureTable()
713788 ]);
714789
715790 $ forge ->createTable ($ this ->table , true );
791+
792+ $ this ->tableChecked = true ;
716793 }
717794
718795 //--------------------------------------------------------------------
0 commit comments