Skip to content

Commit 3516f98

Browse files
authored
Merge pull request #1007 from amosfolz/hotfix-migrate-clean-bakeryCommand
migrate:clean bakery command
2 parents 21e43db + 7ba71a4 commit 3516f98

3 files changed

Lines changed: 114 additions & 2 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
3+
/*
4+
* UserFrosting (http://www.userfrosting.com)
5+
*
6+
* @link https://github.com/userfrosting/UserFrosting
7+
* @copyright Copyright (c) 2019 Alexander Weissman
8+
* @license https://github.com/userfrosting/UserFrosting/blob/master/LICENSE.md (MIT License)
9+
*/
10+
11+
namespace UserFrosting\Sprinkle\Core\Bakery;
12+
13+
use Illuminate\Support\Collection;
14+
use Symfony\Component\Console\Input\InputInterface;
15+
use Symfony\Component\Console\Input\InputOption;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
use UserFrosting\Sprinkle\Core\Database\Migrator\Migrator;
18+
19+
/**
20+
* migrate:clean Bakery Command
21+
* Remove stale migrations from the database.
22+
*
23+
* @author Amos Folz
24+
*/
25+
class MigrateCleanCommand extends MigrateCommand
26+
{
27+
/**
28+
* {@inheritdoc}
29+
*/
30+
protected function configure()
31+
{
32+
$this->setName('migrate:clean')
33+
->setDescription('Remove stale migrations from the database.')
34+
->setHelp('Removes stale migrations, which are simply migration class files that have been removed from the Filesystem. E.g. if you run a migration and then delete the migration class file prior to running `down()` for that migration it becomes stale. If a migration is a dependency of another migration you probably want to try to restore the files instead of running this command to avoid further issues.')
35+
->addOption('database', 'd', InputOption::VALUE_REQUIRED, 'The database connection to use.')
36+
->addOption('force', 'f', InputOption::VALUE_NONE, 'Do not prompt for confirmation.');
37+
}
38+
39+
/**
40+
* {@inheritdoc}
41+
*/
42+
protected function execute(InputInterface $input, OutputInterface $output)
43+
{
44+
$this->io->title('Migration clean');
45+
46+
// Get migrator
47+
$migrator = $this->ci->migrator;
48+
49+
// Set connection to the selected database
50+
$migrator->setConnection($input->getOption('database'));
51+
52+
// Get ran migrations. If repository doesn't exist, there's no ran
53+
if (!$migrator->repositoryExists()) {
54+
$ran = collect();
55+
} else {
56+
$ran = $migrator->getRepository()->getMigrations();
57+
}
58+
59+
// Get available migrations
60+
$available = $migrator->getAvailableMigrations();
61+
62+
$stale = $this->getStaleRecords($ran, $available);
63+
64+
if ($stale->count() > 0) {
65+
if (!$input->getOption('force')) {
66+
$this->io->section('Stale migrations');
67+
$this->io->listing($stale->toArray());
68+
69+
if (!$this->io->confirm('Continue and remove stale migrations?', false)) {
70+
exit;
71+
}
72+
}
73+
$this->io->section('Cleaned migrations');
74+
$this->cleanStaleRecords($stale, $migrator);
75+
$this->io->listing($stale->toArray());
76+
} else {
77+
$this->io->note('No stale migrations');
78+
}
79+
}
80+
81+
/**
82+
* Delete stale migrations from the database.
83+
*
84+
* @param Collection $stale Collection of stale migartion classes.
85+
* @param Migrator $migrator Migrator object
86+
*/
87+
protected function cleanStaleRecords(Collection $stale, Migrator $migrator)
88+
{
89+
$migrationRepository = $migrator->getRepository();
90+
91+
//Delete the stale migration classes from the database.
92+
$stale->each(function ($class) use ($migrationRepository) {
93+
$migrationRepository->delete($class);
94+
});
95+
}
96+
97+
/**
98+
* Return an array of stale migrations.
99+
* A migration is stale if not found in the available stack (class is not in the Filesystem).
100+
*
101+
* @param Collection $ran The ran migrations
102+
* @param array $available The available migrations
103+
*
104+
* @return Collection Collection of stale migration classes.
105+
*/
106+
protected function getStaleRecords(Collection $ran, array $available)
107+
{
108+
return $filtered = collect($ran)->filter(function ($migration) use ($available) {
109+
return !in_array($migration->migration, $available);
110+
})->pluck('migration');
111+
}
112+
}

app/sprinkles/core/src/Database/Migrator/MigrationDependencyAnalyser.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ protected function getMigrationDependencies($migration)
210210

211211
// Make sure class exists
212212
if (!class_exists($migration)) {
213-
throw new BadClassNameException("Unable to find the migration class '$migration'.");
213+
throw new BadClassNameException("Unable to find the migration class '$migration'. Run 'php bakery migrate:clean' to remove stale migrations.");
214214
}
215215

216216
// If the `dependencies` property exist and is static, use this one.

app/sprinkles/core/src/Database/Migrator/Migrator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ protected function getQueries(MigrationInterface $migration, $method)
453453
public function resolve($migrationClassName)
454454
{
455455
if (!class_exists($migrationClassName)) {
456-
throw new BadClassNameException("Unable to find the migration class '$migrationClassName'.");
456+
throw new BadClassNameException("Unable to find the migration class '$migrationClassName'. Run 'php bakery migrate:clean' to remove stale migrations.");
457457
}
458458

459459
$migration = new $migrationClassName($this->getSchemaBuilder());

0 commit comments

Comments
 (0)