1: <?php namespace Laravel\CLI\Tasks\Migrate;
2:
3: use Laravel\Bundle;
4:
5: class Resolver {
6:
7: /**
8: * The migration database instance.
9: *
10: * @var Database
11: */
12: protected $database;
13:
14: /**
15: * Create a new instance of the migration resolver.
16: *
17: * @param Database $database
18: * @return void
19: */
20: public function __construct(Database $database)
21: {
22: $this->database = $database;
23: }
24:
25: /**
26: * Resolve all of the outstanding migrations for a bundle.
27: *
28: * @param string $bundle
29: * @return array
30: */
31: public function outstanding($bundle = null)
32: {
33: $migrations = array();
34:
35: // If no bundle was given to the command, we'll grab every bundle for
36: // the application, including the "application" bundle, which is not
37: // returned by "all" method on the Bundle class.
38: if (is_null($bundle))
39: {
40: $bundles = array_merge(Bundle::names(), array('application'));
41: }
42: else
43: {
44: $bundles = array($bundle);
45: }
46:
47: foreach ($bundles as $bundle)
48: {
49: // First we need to grab all of the migrations that have already
50: // run for this bundle, as well as all of the migration files
51: // for the bundle. Once we have these, we can determine which
52: // migrations are still outstanding.
53: $ran = $this->database->ran($bundle);
54:
55: $files = $this->migrations($bundle);
56:
57: // To find outstanding migrations, we will simply iterate over
58: // the migration files and add the files that do not exist in
59: // the array of ran migrations to the outstanding array.
60: foreach ($files as $key => $name)
61: {
62: if ( ! in_array($name, $ran))
63: {
64: $migrations[] = compact('bundle', 'name');
65: }
66: }
67: }
68:
69: return $this->resolve($migrations);
70: }
71:
72: /**
73: * Resolve an array of the last batch of migrations.
74: *
75: * @return array
76: */
77: public function last()
78: {
79: return $this->resolve($this->database->last());
80: }
81:
82: /**
83: * Resolve an array of migration instances.
84: *
85: * @param array $migrations
86: * @return array
87: */
88: protected function resolve($migrations)
89: {
90: $instances = array();
91:
92: foreach ($migrations as $migration)
93: {
94: $migration = (array) $migration;
95:
96: // The migration array contains the bundle name, so we will get the
97: // path to the bundle's migrations and resolve an instance of the
98: // migration using the name.
99: $bundle = $migration['bundle'];
100:
101: $path = Bundle::path($bundle).'migrations/';
102:
103: // Migrations are not resolved through the auto-loader, so we will
104: // manually instantiate the migration class instances for each of
105: // the migration names we're given.
106: $name = $migration['name'];
107:
108: require_once $path.$name.EXT;
109:
110: // Since the migration name will begin with the numeric ID, we'll
111: // slice off the ID so we are left with the migration class name.
112: // The IDs are for sorting when resolving outstanding migrations.
113: //
114: // Migrations that exist within bundles other than the default
115: // will be prefixed with the bundle name to avoid any possible
116: // naming collisions with other bundle's migrations.
117: $prefix = Bundle::class_prefix($bundle);
118:
119: $class = $prefix.\Laravel\Str::classify(substr($name, 18));
120:
121: $migration = new $class;
122:
123: // When adding to the array of instances, we will actually
124: // add the migration instance, the bundle, and the name.
125: // This allows the migrator to log the bundle and name
126: // when the migration is executed.
127: $instances[] = compact('bundle', 'name', 'migration');
128: }
129:
130: // At this point the migrations are only sorted within their
131: // bundles so we need to resort them by name to ensure they
132: // are in a consistent order.
133: usort($instances, function($a, $b)
134: {
135: return strcmp($a['name'], $b['name']);
136: });
137:
138: return $instances;
139: }
140:
141: /**
142: * Grab all of the migration filenames for a bundle.
143: *
144: * @param string $bundle
145: * @return array
146: */
147: protected function migrations($bundle)
148: {
149: $files = glob(Bundle::path($bundle).'migrations/*_*'.EXT);
150:
151: // When open_basedir is enabled, glob will return false on an
152: // empty directory, so we will return an empty array in this
153: // case so the application doesn't bomb out.
154: if ($files === false)
155: {
156: return array();
157: }
158:
159: // Once we have the array of files in the migration directory,
160: // we'll take the basename of the file and remove the PHP file
161: // extension, which isn't needed.
162: foreach ($files as &$file)
163: {
164: $file = str_replace(EXT, '', basename($file));
165: }
166:
167: // We'll also sort the files so that the earlier migrations
168: // will be at the front of the array and will be resolved
169: // first by this class' resolve method.
170: sort($files);
171:
172: return $files;
173: }
174:
175: }