1<?php
2
3namespace Illuminate\Database\Migrations;
4
5use Closure;
6use Illuminate\Support\Str;
7use InvalidArgumentException;
8use Illuminate\Filesystem\Filesystem;
9
10class MigrationCreator
11{
12    /**
13     * The filesystem instance.
14     *
15     * @var \Illuminate\Filesystem\Filesystem
16     */
17    protected $files;
18
19    /**
20     * The registered post create hooks.
21     *
22     * @var array
23     */
24    protected $postCreate = [];
25
26    /**
27     * Create a new migration creator instance.
28     *
29     * @param  \Illuminate\Filesystem\Filesystem  $files
30     * @return void
31     */
32    public function __construct(Filesystem $files)
33    {
34        $this->files = $files;
35    }
36
37    /**
38     * Create a new migration at the given path.
39     *
40     * @param  string  $name
41     * @param  string  $path
42     * @param  string|null  $table
43     * @param  bool    $create
44     * @return string
45     *
46     * @throws \Exception
47     */
48    public function create($name, $path, $table = null, $create = false)
49    {
50        $this->ensureMigrationDoesntAlreadyExist($name);
51
52        // First we will get the stub file for the migration, which serves as a type
53        // of template for the migration. Once we have those we will populate the
54        // various place-holders, save the file, and run the post create event.
55        $stub = $this->getStub($table, $create);
56
57        $this->files->put(
58            $path = $this->getPath($name, $path),
59            $this->populateStub($name, $stub, $table)
60        );
61
62        // Next, we will fire any hooks that are supposed to fire after a migration is
63        // created. Once that is done we'll be ready to return the full path to the
64        // migration file so it can be used however it's needed by the developer.
65        $this->firePostCreateHooks($table);
66
67        return $path;
68    }
69
70    /**
71     * Ensure that a migration with the given name doesn't already exist.
72     *
73     * @param  string  $name
74     * @return void
75     *
76     * @throws \InvalidArgumentException
77     */
78    protected function ensureMigrationDoesntAlreadyExist($name)
79    {
80        if (class_exists($className = $this->getClassName($name))) {
81            throw new InvalidArgumentException("A {$className} class already exists.");
82        }
83    }
84
85    /**
86     * Get the migration stub file.
87     *
88     * @param  string|null  $table
89     * @param  bool    $create
90     * @return string
91     */
92    protected function getStub($table, $create)
93    {
94        if (is_null($table)) {
95            return $this->files->get($this->stubPath().'/blank.stub');
96        }
97
98        // We also have stubs for creating new tables and modifying existing tables
99        // to save the developer some typing when they are creating a new tables
100        // or modifying existing tables. We'll grab the appropriate stub here.
101        $stub = $create ? 'create.stub' : 'update.stub';
102
103        return $this->files->get($this->stubPath()."/{$stub}");
104    }
105
106    /**
107     * Populate the place-holders in the migration stub.
108     *
109     * @param  string  $name
110     * @param  string  $stub
111     * @param  string|null  $table
112     * @return string
113     */
114    protected function populateStub($name, $stub, $table)
115    {
116        $stub = str_replace('DummyClass', $this->getClassName($name), $stub);
117
118        // Here we will replace the table place-holders with the table specified by
119        // the developer, which is useful for quickly creating a tables creation
120        // or update migration from the console instead of typing it manually.
121        if (! is_null($table)) {
122            $stub = str_replace('DummyTable', $table, $stub);
123        }
124
125        return $stub;
126    }
127
128    /**
129     * Get the class name of a migration name.
130     *
131     * @param  string  $name
132     * @return string
133     */
134    protected function getClassName($name)
135    {
136        return Str::studly($name);
137    }
138
139    /**
140     * Get the full path to the migration.
141     *
142     * @param  string  $name
143     * @param  string  $path
144     * @return string
145     */
146    protected function getPath($name, $path)
147    {
148        return $path.'/'.$this->getDatePrefix().'_'.$name.'.php';
149    }
150
151    /**
152     * Fire the registered post create hooks.
153     *
154     * @param  string|null  $table
155     * @return void
156     */
157    protected function firePostCreateHooks($table)
158    {
159        foreach ($this->postCreate as $callback) {
160            call_user_func($callback, $table);
161        }
162    }
163
164    /**
165     * Register a post migration create hook.
166     *
167     * @param  \Closure  $callback
168     * @return void
169     */
170    public function afterCreate(Closure $callback)
171    {
172        $this->postCreate[] = $callback;
173    }
174
175    /**
176     * Get the date prefix for the migration.
177     *
178     * @return string
179     */
180    protected function getDatePrefix()
181    {
182        return date('Y_m_d_His');
183    }
184
185    /**
186     * Get the path to the stubs.
187     *
188     * @return string
189     */
190    public function stubPath()
191    {
192        return __DIR__.'/stubs';
193    }
194
195    /**
196     * Get the filesystem instance.
197     *
198     * @return \Illuminate\Filesystem\Filesystem
199     */
200    public function getFilesystem()
201    {
202        return $this->files;
203    }
204}
205