33namespace Mpociot \LaravelTestFactoryHelper \Console ;
44
55use Illuminate \Console \Command ;
6- use Illuminate \Contracts \Filesystem \FileNotFoundException ;
76use Illuminate \Database \Eloquent \Relations \Relation ;
87use Illuminate \Filesystem \Filesystem ;
98use Illuminate \Support \Str ;
10- use Symfony \Component \Console \ Input \ InputOption ;
9+ use Symfony \Component \ClassLoader \ ClassMapGenerator ;
1110use Symfony \Component \Console \Input \InputArgument ;
11+ use Symfony \Component \Console \Input \InputOption ;
1212use Symfony \Component \Console \Output \OutputInterface ;
13- use Symfony \Component \ClassLoader \ClassMapGenerator ;
1413
1514class GenerateCommand extends Command
1615{
@@ -26,11 +25,6 @@ class GenerateCommand extends Command
2625 */
2726 protected $ name = 'test-factory-helper:generate ' ;
2827
29- /**
30- * @var string
31- */
32- protected $ filename = 'database/factories/ModelFactory.php ' ;
33-
3428 /**
3529 * @var string
3630 */
@@ -64,7 +58,7 @@ class GenerateCommand extends Command
6458 /**
6559 * @var
6660 */
67- protected $ reset ;
61+ protected $ force ;
6862
6963 /**
7064 * @param Filesystem $files
@@ -83,25 +77,32 @@ public function __construct(Filesystem $files, $view)
8377 */
8478 public function handle ()
8579 {
86- $ filename = $ this ->option ('filename ' );
87- $ this ->dirs = $ this ->option ('dir ' );
88- $ model = $ this ->argument ('model ' );
89- $ ignore = $ this ->option ('ignore ' );
90- $ this ->reset = $ this ->option ('reset ' );
80+ $ this ->dir = $ this ->option ('dir ' );
81+ $ this ->force = $ this ->option ('force ' );
9182
92- try {
93- $ this ->existingFactories = $ this ->files ->get ($ filename );
94- } catch (FileNotFoundException $ e ) {
95- $ this ->existingFactories = '' ;
96- }
83+ $ models = $ this ->loadModels ($ this ->argument ('model ' ));
84+
85+ foreach ($ models as $ model ) {
86+ $ filename = 'database/factories/ ' . class_basename ($ model ) . 'Factory.php ' ;
9787
98- $ result = $ this ->generateFactories ($ model , $ ignore );
88+ if ($ this ->files ->exists ($ filename ) && !$ this ->force ) {
89+ $ this ->warn ('Model factory exists, use --force to overwrite: ' . $ filename );
9990
100- $ written = $ this ->files ->put ('database/factories/ModelFactory.php ' , $ result );
101- if ($ written !== false ) {
102- $ this ->info ("Model factories were written successfully to " .$ filename );
103- } else {
104- $ this ->error ("Failed to write model factories to " .$ filename );
91+ continue ;
92+ }
93+
94+ $ result = $ this ->generateFactory ($ model );
95+
96+ if ($ result === false ) {
97+ continue ;
98+ }
99+
100+ $ written = $ this ->files ->put ($ filename , $ result );
101+ if ($ written !== false ) {
102+ $ this ->info ('Model factory created: ' . $ filename );
103+ } else {
104+ $ this ->error ('Failed to create model factory: ' . $ filename );
105+ }
105106 }
106107 }
107108
@@ -126,92 +127,77 @@ protected function getArguments()
126127 protected function getOptions ()
127128 {
128129 return array (
129- array ('filename ' , 'F ' , InputOption::VALUE_OPTIONAL , 'The path to the model factory file ' , $ this ->filename ),
130- array ('dir ' , 'D ' , InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY , 'The model dir ' , array ($ this ->dir )),
131- array ('reset ' , 'R ' , InputOption::VALUE_NONE , 'Remove the original ModelFactory instead of appending ' ),
132- array ('ignore ' , 'I ' , InputOption::VALUE_OPTIONAL , 'Which models to ignore ' , '' ),
130+ array ('dir ' , 'D ' , InputOption::VALUE_OPTIONAL , 'The model directory ' , array ($ this ->dir )),
131+ array ('force ' , 'F ' , InputOption::VALUE_NONE , 'Overwrite any existing model factory ' ),
133132 );
134133 }
135134
136- protected function generateFactories ( $ loadModels , $ ignore = '' )
135+ protected function generateFactory ( $ model )
137136 {
137+ $ output = '<?php ' . "\n\n" ;
138138
139- if (empty ($ loadModels )) {
140- $ models = $ this ->loadModels ();
141- } else {
142- $ models = array ();
143- foreach ($ loadModels as $ model ) {
144- $ models = array_merge ($ models , explode (', ' , $ model ));
139+ $ this ->properties = [];
140+ if (!class_exists ($ model )) {
141+ if ($ this ->output ->getVerbosity () >= OutputInterface::VERBOSITY_VERBOSE ) {
142+ $ this ->error ("Unable to find ' $ model' class " );
145143 }
144+ return false ;
146145 }
147146
148- $ output = ($ this ->reset || !$ this ->files ->exists ('database/factories/ModelFactory.php ' )) ? '<?php ' ."\n\n" : $ this ->existingFactories ;
149- $ ignore = explode (', ' , $ ignore );
147+ try {
148+ // handle abstract classes, interfaces, ...
149+ $ reflectionClass = new \ReflectionClass ($ model );
150150
151- foreach ($ models as $ name ) {
152- if (in_array ($ name , $ ignore )) {
153- if ($ this ->output ->getVerbosity () >= OutputInterface::VERBOSITY_VERBOSE ) {
154- $ this ->comment ("Ignoring model ' $ name' " );
155- }
156- continue ;
151+ if (!$ reflectionClass ->isSubclassOf ('Illuminate\Database\Eloquent\Model ' )) {
152+ return false ;
157153 }
158154
159- $ this ->properties = array ();
160- if (class_exists ($ name )) {
161- try {
162- // handle abstract classes, interfaces, ...
163- $ reflectionClass = new \ReflectionClass ($ name );
155+ if ($ this ->output ->getVerbosity () >= OutputInterface::VERBOSITY_VERBOSE ) {
156+ $ this ->comment ("Loading model ' $ model' " );
157+ }
164158
165- if (!$ reflectionClass ->isSubclassOf ('Illuminate\Database\Eloquent\Model ' )) {
166- continue ;
167- }
159+ if (!$ reflectionClass ->IsInstantiable ()) {
160+ // ignore abstract class or interface
161+ return false ;
162+ }
168163
169- if (!$ this ->reset && preg_match ("/ \\\$factory->define\((.*?) " .preg_quote ($ reflectionClass ->getName ())."::class(.*?),/ " , $ this ->existingFactories )) {
170- if ($ this ->output ->getVerbosity () >= OutputInterface::VERBOSITY_VERBOSE ) {
171- $ this ->error ("Model ' $ name' already has a factory " );
172- }
173- continue ;
174- }
164+ $ model = $ this ->laravel ->make ($ model );
175165
176- if ($ this ->output ->getVerbosity () >= OutputInterface::VERBOSITY_VERBOSE ) {
177- $ this ->comment ("Loading model ' $ name' " );
178- }
166+ $ this ->getPropertiesFromTable ($ model );
167+ $ this ->getPropertiesFromMethods ($ model );
179168
180- if (!$ reflectionClass ->IsInstantiable ()) {
181- // ignore abstract class or interface
182- continue ;
183- }
184-
185- $ model = $ this ->laravel ->make ($ name );
169+ $ output .= $ this ->createFactory ($ model );
170+ } catch (\Exception $ e ) {
171+ $ this ->error ("Exception: " . $ e ->getMessage () . "\nCould not analyze class $ model. " );
172+ }
186173
187- $ this ->getPropertiesFromTable ($ model );
174+ return $ output ;
175+ }
188176
189- $ this ->getPropertiesFromMethods ($ model );
190177
191- $ output .= $ this ->createFactory ($ name );
192- $ ignore [] = $ name ;
193- } catch (\Exception $ e ) {
194- $ this ->error ("Exception: " . $ e ->getMessage () . "\nCould not analyze class $ name. " );
178+ protected function loadModels ($ models = [])
179+ {
180+ if (!empty ($ models )) {
181+ return array_map (function ($ name ) {
182+ if (strpos ($ name , '\\' ) !== false ) {
183+ return $ name ;
195184 }
196- }
197185
186+ return str_replace (
187+ [DIRECTORY_SEPARATOR , basename ($ this ->laravel ->path ()) . '\\' ],
188+ ['\\' , $ this ->laravel ->getNamespace ()],
189+ $ this ->dir . DIRECTORY_SEPARATOR . $ name
190+ );
191+ }, $ models );
198192 }
199- return $ output ;
200- }
201193
202194
203- protected function loadModels ()
204- {
205- $ models = array ();
206- foreach ($ this ->dirs as $ dir ) {
207- $ dir = base_path () . '/ ' . $ dir ;
208- if (file_exists ($ dir )) {
209- foreach (ClassMapGenerator::createMap ($ dir ) as $ model => $ path ) {
210- $ models [] = $ model ;
211- }
212- }
195+ $ dir = base_path ($ this ->dir );
196+ if (!file_exists ($ dir )) {
197+ return [];
213198 }
214- return $ models ;
199+
200+ return array_keys (ClassMapGenerator::createMap ($ dir ));
215201 }
216202
217203 /**
@@ -251,7 +237,7 @@ protected function getPropertiesFromTable($model)
251237 $ name !== $ model ::CREATED_AT &&
252238 $ name !== $ model ::UPDATED_AT
253239 ) {
254- if (!method_exists ($ model ,'getDeletedAtColumn ' ) || (method_exists ($ model ,'getDeletedAtColumn ' ) && $ name !== $ model ->getDeletedAtColumn ())) {
240+ if (!method_exists ($ model , 'getDeletedAtColumn ' ) || (method_exists ($ model , 'getDeletedAtColumn ' ) && $ name !== $ model ->getDeletedAtColumn ())) {
255241 $ this ->setProperty ($ name , $ type );
256242 }
257243 }
@@ -307,17 +293,10 @@ protected function getPropertiesFromMethods($model)
307293 */
308294 protected function setProperty ($ name , $ type = null )
309295 {
310- if (!isset ($ this ->properties [$ name ])) {
311- $ this ->properties [$ name ] = array ();
312- $ this ->properties [$ name ]['type ' ] = 'mixed ' ;
313- $ this ->properties [$ name ]['faker ' ] = false ;
314- }
315- if ($ type !== null ) {
316- $ this ->properties [$ name ]['type ' ] = $ type ;
317- }
296+ if ($ type !== null && Str::startsWith ($ type , 'factory( ' )) {
297+ $ this ->properties [$ name ] = $ type ;
318298
319- if (Str::startsWith ($ type ,'function () ' )) {
320- $ this ->properties [$ name ]['faker ' ] = true ;
299+ return ;
321300 }
322301
323302 $ fakeableTypes = [
@@ -365,13 +344,11 @@ protected function setProperty($name, $type = null)
365344 ];
366345
367346 if (isset ($ fakeableNames [$ name ])) {
368- $ this ->properties [$ name ]['faker ' ] = true ;
369- $ this ->properties [$ name ]['type ' ] = $ fakeableNames [$ name ];
347+ $ this ->properties [$ name ] = $ fakeableNames [$ name ];
370348 }
371349
372- if (isset ($ fakeableTypes [$ type ]) && !$ this ->properties [$ name ]['faker ' ]) {
373- $ this ->properties [$ name ]['faker ' ] = true ;
374- $ this ->properties [$ name ]['type ' ] = $ fakeableTypes [$ type ];
350+ if (isset ($ fakeableTypes [$ type ])) {
351+ $ this ->properties [$ name ] = $ fakeableTypes [$ type ];
375352 }
376353 }
377354
0 commit comments