migrator.php 5.95 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
<?php namespace Laravel\CLI\Tasks\Migrate;

use Laravel\Str;
use Laravel\File;
use Laravel\Bundle;
use Laravel\CLI\Tasks\Task;
use Laravel\Database\Schema;

class Migrator extends Task {

	/**
	 * The migration resolver instance.
	 *
	 * @var Resolver
	 */
	protected $resolver;

	/**
	 * The migration database instance.
	 *
	 * @var Database
	 */
	protected $database;

	/**
	 * Create a new instance of the Migrator CLI task.
	 *
	 * @param  Resolver  $resolver
	 * @param  Database  $database
	 * @return void
	 */
	public function __construct(Resolver $resolver, Database $database)
	{
		$this->resolver = $resolver;
		$this->database = $database;
	}

	/**
	 * Run a database migration command.
	 *
	 * @param  array  $arguments
	 * @return void
	 */
	public function run($arguments = array())
	{
		// If no arguments were passed to the task, we will just migrate
		// to the latest version across all bundles. Otherwise, we will
		// parse the arguments to determine the bundle for which the
		// database migrations should be run.
		if (count($arguments) == 0)
		{
			$this->migrate();
		}
		else
		{
			$this->migrate(array_get($arguments, 0));
		}
	}

	/**
	 * Run the outstanding migrations for a given bundle.
	 *
	 * @param  string  $bundle
	 * @param  int     $version
	 * @return void
	 */
	public function migrate($bundle = null, $version = null)
	{
		$migrations = $this->resolver->outstanding($bundle);

		if (count($migrations) == 0)
		{
			echo "No outstanding migrations.";

			return;
		}

Taylor Otwell committed
78 79 80
		// We need to grab the latest batch ID and increment it by one.
		// This allows us to group the migrations so we can easily
		// determine which migrations need to roll back.
81 82 83 84 85 86 87 88
		$batch = $this->database->batch() + 1;

		foreach ($migrations as $migration)
		{
			$migration['migration']->up();

			echo 'Migrated: '.$this->display($migration).PHP_EOL;

Taylor Otwell committed
89 90 91
			// After running a migration, we log its execution in the migration
			// table so that we can easily determine which migrations we'll
			// reverse in the event of a migration rollback.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
			$this->database->log($migration['bundle'], $migration['name'], $batch);
		}
	}

	/**
	 * Rollback the latest migration command.
	 *
	 * @param  array  $arguments
	 * @return bool
	 */
	public function rollback($arguments = array())
	{
		$migrations = $this->resolver->last();

		if (count($migrations) == 0)
		{
			echo "Nothing to rollback.";

			return false;
		}

		// The "last" method on the resolver returns an array of migrations,
		// along with their bundles and names. We will iterate through each
Taylor Otwell committed
115
		// migration and run the "down" method.
116
		foreach (array_reverse($migrations) as $migration)
117 118 119 120 121 122 123
		{
			$migration['migration']->down();

			echo 'Rolled back: '.$this->display($migration).PHP_EOL;

			// By only removing the migration after it has successfully rolled back,
			// we can re-run the rollback command in the event of any errors with
Taylor Otwell committed
124
			// the migration and pick up where we left off.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
			$this->database->delete($migration['bundle'], $migration['name']);
		}

		return true;
	}

	/**
	 * Rollback all of the executed migrations.
	 *
	 * @param  array  $arguments
	 * @return void
	 */
	public function reset($arguments = array())
	{
		while ($this->rollback()) {};
	}

	/**
	 * Install the database tables used by the migration system.
	 *
	 * @return void
	 */
	public function install()
	{
		Schema::table('laravel_migrations', function($table)
		{
			$table->create();

			// Migrations can be run for a specific bundle, so we'll use
			// the bundle name and string migration name as an unique ID
			// for the migrations, allowing us to easily identify which
			// migrations have been run for each bundle.
157
			$table->string('bundle', 50);
158

159
			$table->string('name', 200);
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

			// When running a migration command, we will store a batch
			// ID with each of the rows on the table. This will allow
			// us to grab all of the migrations that were run for the
			// last command when performing rollbacks.
			$table->integer('batch');

			$table->primary(array('bundle', 'name'));
		});

		echo "Migration table created successfully.";
	}

	/**
	 * Generate a new migration file.
	 *
176 177
	 * @param  array   $arguments
	 * @return string
178 179 180 181 182 183 184 185 186 187
	 */
	public function make($arguments = array())
	{
		if (count($arguments) == 0)
		{
			throw new \Exception("I need to know what to name the migration.");
		}

		list($bundle, $migration) = Bundle::parse($arguments[0]);

188
		// The migration path is prefixed with the date timestamp, which
189 190 191
		// is a better way of ordering migrations than a simple integer
		// incrementation, since developers may start working on the
		// next migration at the same time unknowingly.
192
		$prefix = date('Y_m_d_His');
193

194
		$path = Bundle::path($bundle).'migrations'.DS;
195

196 197 198 199
		// If the migration directory does not exist for the bundle,
		// we will create the directory so there aren't errors when
		// when we try to write the migration file.
		if ( ! is_dir($path)) mkdir($path);
200

201 202 203
		$file = $path.$prefix.'_'.$migration.EXT;

		File::put($file, $this->stub($bundle, $migration));
204 205

		echo "Great! New migration created!";
206 207 208

		// Once the migration has been created, we'll return the
		// migration file name so it can be used by the task
Taylor Otwell committed
209
		// consumer if necessary for futher work.
210
		return $file;
211 212 213 214 215 216 217 218 219 220 221
	}

	/**
	 * Get the stub migration with the proper class name.
	 *
	 * @param  string  $bundle
	 * @param  string  $migration
	 * @return string
	 */
	protected function stub($bundle, $migration)
	{
Taylor Otwell committed
222
		$stub = File::get(path('sys').'cli/tasks/migrate/stub'.EXT);
223

Taylor Otwell committed
224 225
		$prefix = Bundle::class_prefix($bundle);

226 227
		// The class name is formatted simialrly to tasks and controllers,
		// where the bundle name is prefixed to the class if it is not in
Taylor Otwell committed
228 229
		// the default "application" bundle.
		$class = $prefix.Str::classify($migration);
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

		return str_replace('{{class}}', $class, $stub);
	}

	/**
	 * Get the migration bundle and name for display.
	 *
	 * @param  array   $migration
	 * @return string
	 */
	protected function display($migration)
	{
		return $migration['bundle'].'/'.$migration['name'];
	}

}