Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Enh #322, #330: Improve output of `migrate:up`, `migrate:down`, `migrate:redo`, `migrate:new`, `migrate:history`, and
`migrate:create` commands: remove redundant messages, replace `>>>` with cleaner output, and move "Database
connection" info to the top (@samdark, @vjik)
- Enh #333: Use `newMigrationPath` and `newMigrationNamespace` if `sourceNamespaces` and `sourcePaths` are not specified (@Tigrov)

## 2.0.1 December 20, 2025

Expand Down
18 changes: 9 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,16 @@
"yiisoft/injector": "^1.2"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.3",
"friendsofphp/php-cs-fixer": "^3.89.2",
"phpunit/phpunit": "^10.5.45",
"rector/rector": "^2.0.10",
"bamarni/composer-bin-plugin": "^1.9.1",
"friendsofphp/php-cs-fixer": "^3.95.1",
"phpunit/phpunit": "^10.5.63",
"rector/rector": "^2.4.2",
"yiisoft/db-sqlite": "^2.0",
"yiisoft/di": "^1.3",
"yiisoft/files": "^2.0",
"yiisoft/psr-dummy-provider": "^1.0",
"yiisoft/test-support": "^3.0.2",
"yiisoft/yii-console": "^2.3"
"yiisoft/di": "^1.4.1",
"yiisoft/files": "^2.1",
"yiisoft/psr-dummy-provider": "^1.0.2",
"yiisoft/test-support": "^3.2.0",
"yiisoft/yii-console": "^2.4.2"
},
"autoload": {
"psr-4": {
Expand Down
7 changes: 5 additions & 2 deletions docs/guide/en/usage-standalone.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ use Yiisoft\Injector\Injector;
/** @var ConnectionInterface $database */
$migrator = new Migrator($database, new NullMigrationInformer());
$migrationService = new MigrationService($database, new Injector(), $migrator);
$migrationService->setSourcePaths([dirname(__DIR__, 2), 'migrations']);
$migrationService->setNewMigrationPath(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'migrations');
```

> [!NOTE]
> `sourceNamespaces`, `sourcePaths`, `newMigrationNamespace`, and `newMigrationPath` will be used to find migrations.

Then initialize the command for using without CLI. For example, for applying migrations it will be `UpdateCommand`:

```php
Expand All @@ -55,7 +58,7 @@ use Yiisoft\Db\Migration\Command\UpdateCommand;
use Yiisoft\Db\Migration\Runner\UpdateRunner;

$command = new UpdateCommand(new UpdateRunner($migrator), $migrationService, $migrator);
$command->setHelperSet(new HelperSet(['queestion' => new QuestionHelper()]));
$command->setHelperSet(new HelperSet(['question' => new QuestionHelper()]));
```

And, finally, run the command:
Expand Down
3 changes: 3 additions & 0 deletions docs/guide/en/usage-with-yii-console.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ Add to `config/console/params.php`:
...
```

> [!NOTE]
> `sourceNamespaces`, `sourcePaths`, `newMigrationNamespace`, and `newMigrationPath` will be used to find migrations.

Execute `composer du` in console to rebuild the configuration.

Now we have the `yiisoft/db-migration` package configured and it can be called in the console.
Expand Down
8 changes: 6 additions & 2 deletions docs/guide/pt-BR/usage-standalone.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@ use Yiisoft\Injector\Injector;
/** @var ConnectionInterface $database */
$migrator = new Migrator($database, new NullMigrationInformer());
$migrationService = new MigrationService($database, new Injector(), $migrator);
$migrationService->setSourcePaths([dirname(__DIR__, 2), 'migrations']);
$migrationService->setNewMigrationPath(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'migrations');
```

> [!NOTE]
> `sourceNamespaces`, `sourcePaths`, `newMigrationNamespace` e `newMigrationPath` serão usados para encontrar
> as migrações.

Em seguida, inicialize o comando para usar sem CLI. Por exemplo, para aplicar migrações será `UpdateCommand`:

```php
Expand All @@ -55,7 +59,7 @@ use Yiisoft\Db\Migration\Command\UpdateCommand;
use Yiisoft\Db\Migration\Runner\UpdateRunner;

$command = new UpdateCommand(new UpdateRunner($migrator), $migrationService, $migrator);
$command->setHelperSet(new HelperSet(['queestion' => new QuestionHelper()]));
$command->setHelperSet(new HelperSet(['question' => new QuestionHelper()]));
```

E, por fim, execute o comando:
Expand Down
4 changes: 4 additions & 0 deletions docs/guide/pt-BR/usage-with-yii-console.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ Adicione em `config/console/params.php`:
...
```

> [!NOTE]
> `sourceNamespaces`, `sourcePaths`, `newMigrationNamespace` e `newMigrationPath` serão usados para encontrar
> as migrações.

Execute `composer du` no console para reconstruir a configuração.

Agora temos o pacote [`yiisoft/db-migration`](https://github.com/yiisoft/db-migration) configurado e ele pode ser chamado no console.
Expand Down
2 changes: 2 additions & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\Config\RectorConfig;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector;
use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector;
use Rector\Php81\Rector\Property\ReadOnlyPropertyRector;
Expand All @@ -21,5 +22,6 @@
NullToStrictStringFuncCallArgRector::class,
ReadOnlyPropertyRector::class,
RemoveExtraParametersRector::class => [__DIR__ . '/src/Service/Generate/PhpRenderer.php'],
StringClassNameToClassConstantRector::class => [__DIR__ . '/tests/Common/Service/AbstractMigrationServiceTest.php'],
])
->withoutParallel();
54 changes: 38 additions & 16 deletions src/Service/MigrationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,13 @@ public function before(string $commandName): int
}
break;
case 'migrate:up':
if (empty($this->sourceNamespaces) && empty($this->sourcePaths)) {
if (empty($this->sourceNamespaces)
&& empty($this->sourcePaths)
&& empty($this->newMigrationNamespace)
&& empty($this->newMigrationPath)
) {
$this->io?->error(
'At least one of `sourceNamespaces` or `sourcePaths` should be specified.',
'At least one of `sourceNamespaces`, `sourcePaths`, `newMigrationNamespace` or `newMigrationPath` should be specified.',
);

return Command::INVALID;
Expand All @@ -124,20 +128,10 @@ public function getNewMigrations(): array
$applied[trim($class, '\\')] = true;
}

$migrationPaths = [];

foreach ($this->sourcePaths as $path) {
$migrationPaths[] = [$path, ''];
}

foreach ($this->sourceNamespaces as $namespace) {
$migrationPaths[] = [$this->getNamespacePath($namespace), $namespace];
}

$migrations = [];
foreach ($migrationPaths as $item) {
[$sourcePath, $namespace] = $item;
$migrationPaths = $this->findSourcePaths();

foreach ($migrationPaths as [$sourcePath, $namespace]) {
if (!is_dir($sourcePath)) {
continue;
}
Expand Down Expand Up @@ -167,8 +161,8 @@ public function getNewMigrations(): array
}
closedir($handle);
}
ksort($migrations);

ksort($migrations);
return array_values($migrations);
}

Expand Down Expand Up @@ -394,7 +388,7 @@ private function makeMigrationInstance(string $class): object

if (!str_contains($class, '\\')) {
$isIncluded = false;
foreach ($this->sourcePaths as $path) {
foreach ($this->findSourcePaths() as [$path]) {
$file = $path . DIRECTORY_SEPARATOR . $class . '.php';

if (is_file($file)) {
Expand All @@ -416,6 +410,34 @@ private function makeMigrationInstance(string $class): object
return $this->injector->make($class);
}

/**
* Returns the migration paths with namespaces if they are specified.
Comment thread
Tigrov marked this conversation as resolved.
*
* @return array<array{0: string, 1: string}>
*/
private function findSourcePaths(): array
{
$paths = [];

if ($this->newMigrationPath !== '') {
$paths = [[$this->newMigrationPath, '']];
} elseif ($this->newMigrationNamespace !== '') {
$newMigrationPath = $this->getNamespacePath($this->newMigrationNamespace);
$paths = [[$newMigrationPath, $this->newMigrationNamespace]];
}

foreach ($this->sourcePaths as $sourcePaths) {
$paths[] = [$sourcePaths, ''];
}

foreach ($this->sourceNamespaces as $namespace) {
$sourcePaths = $this->getNamespacePath($namespace);
$paths[] = [$sourcePaths, $namespace];
}

return $paths;
}

/**
* Returns the file path matching the give namespace.
*
Expand Down
50 changes: 44 additions & 6 deletions tests/Common/Command/AbstractUpdateCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,18 +274,56 @@ public function testWithoutSourcePath(): void
$exitCode = $command->execute([]);
$output = preg_replace('/(\R|\s)+/', ' ', $command->getDisplay(true));

$this->assertSame(Command::SUCCESS, $exitCode);
$this->assertStringContainsString('No new migrations found.', $output);
$this->assertStringContainsString('[OK] Your system is up-to-date.', $output);
}

public function testWithoutSourceNamespaces(): void
{
MigrationHelper::useMigrationsNamespace($this->container);

$this->container->get(MigrationService::class)->setSourceNamespaces([]);

$command = $this->createCommand($this->container);
$command->setInputs(['yes']);

$exitCode = $command->execute([]);
$output = preg_replace('/(\R|\s)+/', ' ', $command->getDisplay(true));

$this->assertSame(Command::SUCCESS, $exitCode);
$this->assertStringContainsString('No new migrations found.', $output);
$this->assertStringContainsString('[OK] Your system is up-to-date.', $output);
}

public function testWithoutMigrationPaths(): void
{
MigrationHelper::useMigrationsPath($this->container);

$migration = $this->container->get(MigrationService::class);
$migration->setSourcePaths([]);
$migration->setNewMigrationPath('');

$command = $this->createCommand($this->container);
$command->setInputs(['yes']);

$exitCode = $command->execute([]);
$output = preg_replace('/(\R|\s)+/', ' ', $command->getDisplay(true));

$this->assertSame(Command::INVALID, $exitCode);
$this->assertStringContainsStringCollapsingSpaces(
'At least one of `sourceNamespaces` or `sourcePaths` should be specified.',
$this->assertStringContainsString(
'At least one of `sourceNamespaces`, `sourcePaths`, `newMigrationNamespace` or `newMigrationPath` should be specified.',
$output,
);
}

public function testWithoutSourceNamespaces(): void
public function testWithoutMigrationNamespaces(): void
{
MigrationHelper::useMigrationsNamespace($this->container);

$this->container->get(MigrationService::class)->setSourceNamespaces([]);
$migration = $this->container->get(MigrationService::class);
$migration->setSourceNamespaces([]);
$migration->setNewMigrationNamespace('');

$command = $this->createCommand($this->container);
$command->setInputs(['yes']);
Expand All @@ -294,8 +332,8 @@ public function testWithoutSourceNamespaces(): void
$output = preg_replace('/(\R|\s)+/', ' ', $command->getDisplay(true));

$this->assertSame(Command::INVALID, $exitCode);
$this->assertStringContainsStringCollapsingSpaces(
'At least one of `sourceNamespaces` or `sourcePaths` should be specified.',
$this->assertStringContainsString(
'At least one of `sourceNamespaces`, `sourcePaths`, `newMigrationNamespace` or `newMigrationPath` should be specified.',
$output,
);
}
Expand Down
Loading
Loading