Completed
Push — master ( 56265b...886536 )
by Thomas
09:04
created

GenerateActionCommand::generateDomain()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
ccs 11
cts 11
cp 1
rs 9.4285
cc 2
eloc 9
nc 2
nop 1
crap 2
1
<?php
2
namespace keeko\tools\command;
3
4
use gossi\codegen\model\PhpClass;
5
use gossi\codegen\model\PhpTrait;
6
use keeko\framework\schema\ActionSchema;
7
use keeko\framework\utils\NameUtils;
8
use keeko\tools\generator\action\BlankActionGenerator;
9
use keeko\tools\generator\action\NoopActionGenerator;
10
use keeko\tools\generator\action\ToManyRelationshipAddActionGenerator;
11
use keeko\tools\generator\action\ToManyRelationshipReadActionGenerator;
12
use keeko\tools\generator\action\ToManyRelationshipRemoveActionGenerator;
13
use keeko\tools\generator\action\ToManyRelationshipUpdateActionGenerator;
14
use keeko\tools\generator\action\ToOneRelationshipReadActionGenerator;
15
use keeko\tools\generator\action\ToOneRelationshipUpdateActionGenerator;
16
use keeko\tools\generator\domain\DomainGenerator;
17
use keeko\tools\generator\domain\DomainTraitGenerator;
18
use keeko\tools\generator\domain\ReadOnlyDomainTraitGenerator;
19
use keeko\tools\generator\GeneratorFactory;
20
use keeko\tools\helpers\QuestionHelperTrait;
21
use keeko\tools\utils\NamespaceResolver;
22
use phootwork\file\File;
23
use phootwork\lang\Text;
24
use Propel\Generator\Model\ForeignKey;
25 20
use Propel\Generator\Model\Table;
26 20
use Symfony\Component\Console\Input\InputArgument;
27 20
use Symfony\Component\Console\Input\InputInterface;
28 20
use Symfony\Component\Console\Input\InputOption;
29 20
use Symfony\Component\Console\Output\OutputInterface;
30 20
use Symfony\Component\Console\Question\ConfirmationQuestion;
31 20
use Symfony\Component\Console\Question\Question;
32
33 20
class GenerateActionCommand extends AbstractGenerateCommand {
34 20
35 20
	use QuestionHelperTrait;
36 20
	
37 20
	private $twig;
38 20
39
	protected function configure() {
40 20
		$this
41 20
			->setName('generate:action')
42 20
			->setDescription('Generates an action')
43 20
			->addArgument(
44 20
				'name',
45
				InputArgument::OPTIONAL,
46 20
				'The name of the action, which should be generated. Typically in the form %nomen%-%verb% (e.g. user-create)'
47 20
			)
48 20
			->addOption(
49 20
				'classname',
50 20
				'c',
51
				InputOption::VALUE_OPTIONAL,
52 20
				'The main class name (If ommited, class name will be guessed from action name)',
53 20
				null
54 20
			)
55 20
			->addOption(
56 20
				'model',
57
				'm',
58 20
				InputOption::VALUE_OPTIONAL,
59 20
				'The model for which the actions should be generated, when there is no name argument (if ommited all models will be generated)'
60 20
			)
61 20
			->addOption(
62
				'title',
63 20
				'',
64
				InputOption::VALUE_OPTIONAL,
65
				'The title for the generated option'
66
			)
67
			->addOption(
68
				'type',
69
				'',
70
				InputOption::VALUE_OPTIONAL,
71 1
				'The type of this action (list|create|read|update|delete) (if ommited template is guessed from action name)'
72
			)->addOption(
73 20
				'acl',
74
				'',
75 20
				InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
76 20
				'The acl\s for this action (guest, user and/or admin)'
77
			)
78
// 			->addOption(
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
79
// 				'schema',
80
// 				's',
81
// 				InputOption::VALUE_OPTIONAL,
82 10
// 				'Path to the database schema (if ommited, database/schema.xml is used)',
83 10
// 				null
84 10
// 			)
85 1
		;
86 4
		
87 9
		$this->configureGenerateOptions();
88
		
89
		parent::configure();
90
	}
91
92
	protected function initialize(InputInterface $input, OutputInterface $output) {
93
		parent::initialize($input, $output);
94
95
		$loader = new \Twig_Loader_Filesystem($this->service->getConfig()->getTemplateRoot() . '/actions');
96
		$this->twig = new \Twig_Environment($loader);
97
	}
98
99
	/**
100
	 * Checks whether actions can be generated at all by reading composer.json and verify
101
	 * all required information are available
102
	 */
103
	private function preCheck() {
104
		$module = $this->packageService->getModule();
105
		if ($module === null) {
106
			throw new \DomainException('No module definition found in composer.json - please run `keeko init`.');
107
		}
108
	}
109
	
110
	protected function interact(InputInterface $input, OutputInterface $output) {
111
		$this->preCheck();
112
		
113
		// check if the dialog can be skipped
114
		$name = $input->getArgument('name');
115
		$model = $input->getOption('model');
116
		
117
		if ($model !== null) {
118
			return;
119
		} else if ($name !== null) {
120
			$generateModel = false;
121
		} else {
122
			$modelQuestion = new ConfirmationQuestion('Do you want to generate an action based off a model?');
123
			$generateModel = $this->askConfirmation($modelQuestion);
124
		}
125
		
126
		// ask questions for a model
127
		if ($generateModel /*&& !($this->package->getVendor() === 'keeko' && $this->modelService->isCoreSchema())*/) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
128
129
			$schema = str_replace(getcwd(), '', $this->modelService->getSchema());
130
			$allQuestion = new ConfirmationQuestion(sprintf('For all models in the schema (%s)?', $schema));
131
			$allModels = $this->askConfirmation($allQuestion);
132
133
			if (!$allModels) {
134
				$modelQuestion = new Question('Which model');
135
				$modelQuestion->setAutocompleterValues($this->modelService->getModelNames());
136
				$model = $this->askQuestion($modelQuestion);
137
				$input->setOption('model', $model);
138
			}
139
		} else if (!$generateModel) {
140
			$action = $this->getAction($name);
141
			
142
			// ask for title
143
			$pkgTitle = $action->getTitle();
144
			$title = $input->getOption('title');
145
			if ($title === null && !empty($pkgTitle)) {
146
				$title = $pkgTitle;
147
			}
148
			$titleQuestion = new Question('What\'s the title for your action?', $title);
149
			$title = $this->askQuestion($titleQuestion);
150
			$input->setOption('title', $title);
151
			
152 10
			// ask for classname
153 10
			$pkgClass = $action->getClass();
154
			$classname = $input->getOption('classname');
155
			if ($classname === null) {
156
				if (!empty($pkgClass)) {
157
					$classname = $pkgClass;
158
				} else {
159 9
					$classname = $this->guessClassname($name);
160 9
				}
161
			}
162
			$classname = $this->askQuestion(new Question('Classname', $classname));
163 9
			$input->setOption('classname', $classname);
164 3
			
165 2
			// ask for acl
166
			$acls = $this->getAcl($action);
167
			$aclQuestion = new Question('ACL (comma separated list, with these options: guest, user, admin)', implode(', ', $acls));
168 6
			$acls = $this->askQuestion($aclQuestion);
169 2
			$input->setOption('acl', $acls);
170 2
		}
171
	}
172
173 4
	protected function execute(InputInterface $input, OutputInterface $output) {
174 3
		$this->preCheck();
175 3
		
176 2
		// 1. find out which action(s) to generate
177 2
		// 2. generate the information in the package
178 2
		// 3. generate the code for the action
179 1
		
180
		$name = $input->getArgument('name');
181 3
		$model = $input->getOption('model');
182
183
		// only a specific action
184
		if ($name) {
185 1
			$this->generateAction($name);
186 1
		}
187 1
188
		// create action(s) from a model
189
		else if ($model) {
190 8
			$this->generateModel($model);
191 8
		}
192
		
193 5
		// if this is a core-module, find the related model
194 5
// 		else /*if ($this->package->getVendor() == 'keeko' && $this->modelService->isCoreSchema()) */ {
195 5
// 			$model = $this->package->getName();
196 5
// 			if ($this->modelService->hasModel($model)) {
197 5
// 				$input->setOption('model', $model);
198 1
// 				$this->generateModel($model);
199 1
// 			} else {
200 4
// 				$this->logger->error('Tried to find model on my own, wasn\'t lucky - please provide model with the --model option');
201
// 			}
202
// 		}
203 5
204 5
		// anyway, generate all
205 5
		else {
206 5
			foreach ($this->modelService->getModels() as $model) {
207 5
				$modelName = $model->getOriginCommonName();
208 5
				$input->setOption('model', $modelName);
209 4
				$this->generateModel($modelName);
210 5
			}
211 5
		}
212 5
		
213
		$this->packageService->savePackage();
214 5
	}
215 5
216
	private function generateModel($modelName) {
217 4
		$this->logger->info('Generate Action from Model: ' . $modelName);
218
		$input = $this->io->getInput();
219 4
		$model = $this->modelService->getModel($modelName);
220 3
		
221
		// generate domain
222 4
		$this->generateDomain($model);
1 ignored issue
show
Bug introduced by
It seems like $model defined by $this->modelService->getModel($modelName) on line 219 can be null; however, keeko\tools\command\Gene...mmand::generateDomain() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
223 4
224 4
		// generate action type(s)
225 4
		$typeDump = $input->getOption('type');
226 4
		if ($typeDump !== null) {
227
			$types = [$typeDump];
228
		} else {
229
			$types = ['create', 'read', 'list', 'update', 'delete'];
230
		}
231
		
232
		foreach ($types as $type) {
233
			$input->setOption('acl', ['admin']);
1 ignored issue
show
Documentation introduced by
array('admin') is of type array<integer,string,{"0":"string"}>, but the function expects a string|boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
234
			$input->setOption('type', $type);
235
			$actionName = $modelName . '-' . $type;
236
			
237 8
			if ($model->isReadOnly() && in_array($type, ['create', 'update', 'delete'])) {
238 8
				$this->logger->info(sprintf('Skip generate Action (%s), because Model (%s) is read-only', $actionName, $modelName));
239 8
				continue;
240
			}
241
			
242 8
			$action = $this->getAction($actionName);
243
			if (Text::create($action->getTitle())->isEmpty()) {
244 8
				$action->setTitle($this->getActionTitle($modelName, $type));
245 2
			}
246 2
			$this->generateAction($actionName);
247
		}
248 8
		
249 1
		// generate relationship actions
250
		if (!$model->isReadOnly()) {
251
			$relationships = $this->modelService->getRelationships($model);
1 ignored issue
show
Bug introduced by
It seems like $model defined by $this->modelService->getModel($modelName) on line 219 can be null; however, keeko\tools\services\Mod...ice::getRelationships() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
252 7
				
253 2
			// to-one relationships
254 2
			foreach ($relationships['one'] as $one) {
255
				$fk = $one['fk'];
256
				$this->generateToOneRelationshipAction($model, $fk->getForeignTable(), $fk);
1 ignored issue
show
Bug introduced by
It seems like $model defined by $this->modelService->getModel($modelName) on line 219 can be null; however, keeko\tools\command\Gene...OneRelationshipAction() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
257 7
			}
258 4
			
259 4
			// to-many relationships
260
			foreach ($relationships['many'] as $many) {
261
				$fk = $many['fk'];
262 7
				$cfk = $many['cfk'];
263 7
				$this->generateToManyRelationshipAction($model, $fk->getForeignTable(), $cfk->getMiddleTable());
1 ignored issue
show
Bug introduced by
It seems like $model defined by $this->modelService->getModel($modelName) on line 219 can be null; however, keeko\tools\command\Gene...anyRelationshipAction() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
264 7
			}
265
		}
266
		
267
		$input->setOption('type', $typeDump);
268
	}
269
270
	private function getActionTitle($modelName, $type) {
271 7
		$name = NameUtils::dasherize($modelName);
1 ignored issue
show
Bug introduced by
The method dasherize() cannot be called from this context as it is declared private in class keeko\framework\utils\NameUtils.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
272
		switch ($type) {
273
			case 'list':
274 7
				return 'List all ' . NameUtils::pluralize($name);
275 7
276
			case 'create':
277 4
			case 'read':
278 4
			case 'update':
279 4
			case 'delete':
280
				return ucfirst($type) . 's ' . (in_array($name[0], ['a', 'e', 'i', 'o', 'u']) ? 'an' : 'a') . ' ' . $name;
281
		}
282
	}
283
284
	/**
285
	 * Generates a domain with trait for the given model
286
	 * 
287 8
	 * @TODO: Externalize this into its own command and call the command from here 
288 8
	 * 
289 8
	 * @param Table $model
290 7
	 */
291 7
	private function generateDomain(Table $model) {
292 7
		// generate class
293 7
		$generator = new DomainGenerator($this->service);
294 8
		$class = $generator->generate($model);
295
		$this->codegenService->dumpStruct($class, true);
296
		
297 7
		// generate trait
298 7
		$generator = $model->isReadOnly()
299 7
			? new ReadOnlyDomainTraitGenerator($this->service)
300 7
			: new DomainTraitGenerator($this->service);
301 7
		$trait = $generator->generate($model);
302 2
		$this->codegenService->dumpStruct($trait, true);
303 2
	}
304 7
	
305 7
	/**
306 1
	 * Generates an action.
307 1
	 *  
308 1
	 * @param string $actionName
309 1
	 * @param ActionSchema $action the action node from composer.json
0 ignored issues
show
Documentation introduced by
There is no parameter named $action. Did you maybe mean $actionName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
310 1
	 */
311 6
	private function generateAction($actionName) {
312
		$this->logger->info('Generate Action: ' . $actionName);
313 7
		$input = $this->io->getInput();
314
		
315 7
		// get action and create it if it doesn't exist
316
		$action = $this->getAction($actionName);
317
		
318
		if (($title = $input->getOption('title')) !== null) {
319
			$action->setTitle($title);
320
		}
321
322
		if (Text::create($action->getTitle())->isEmpty()) {
323
			throw new \RuntimeException(sprintf('Cannot create action %s, because I am missing a title for it', $actionName));
324
		}
325
326
		if (($classname = $input->getOption('classname')) !== null) {
327
			$action->setClass($classname);
328
		}
329
		
330
		// guess classname if there is none set yet
331 7
		if (Text::create($action->getClass())->isEmpty()) {
332 7
			$action->setClass($this->guessClassname($actionName));
333 7
		}
334
		
335
		// guess title if there is none set yet
336 7
		if (Text::create($action->getTitle())->isEmpty() 
337 7
				&& $this->modelService->isModelAction($action)
338 7
				&& $this->modelService->isCrudAction($action)) {
339 7
			$modelName = $this->modelService->getModelNameByAction($action);
340 7
			$type = $this->modelService->getOperationByAction($action);
341
			$action->setTitle($this->getActionTitle($modelName, $type));
342
		}
343 7
		
344
		// set acl
345 1
		$action->setAcl($this->getAcl($action));
346 1
		
347
		// generate code
348 1
		$this->generateCode($action);
349 1
	}
350 1
	
351
	private function guessClassname($name) {
352
		$namespace = NamespaceResolver::getNamespace('src/action', $this->package);
353 1
		return $namespace . '\\' . NameUtils::toStudlyCase($name) . 'Action';
354 1
	}
355 1
	
356
	/**
357
	 * 
358
	 * @param string $actionName
359 6
	 * @return ActionSchema
360 6
	 */
361 6
	private function getAction($actionName) {
362 6
		$action = $this->packageService->getAction($actionName);
363 6
		if ($action == null) {
364
			$action = new ActionSchema($actionName);
365
			$module = $this->packageService->getModule();
366
			$module->addAction($action);
367 7
		}
368 5
		return $action;
369 1
	}
370 1
	
371 1
	private function getAcl(ActionSchema $action) {
372 5
		$acls = [];
373 5
		$acl = $this->io->getInput()->getOption('acl');
374
		if ($acl !== null && count($acl) > 0) {
375 5
			if (!is_array($acl)) {
376 5
				$acl = [$acl];
377
			}
378 5
			foreach ($acl as $group) {
379 4
				if (strpos($group, ',') !== false) {
380 4
					$groups = explode(',', $group);
381 4
					foreach ($groups as $g) {
382 5
						$acls[] = trim($g);
383
					}
384 2
				} else {
385 2
					$acls[] = $group;
386 2
				}
387 2
			}
388 2
			
389
			return $acls;
390
		}
391 7
		
392 7
		// read default from package
393
		if (!$action->getAcl()->isEmpty()) {
394
			return $action->getAcl()->toArray();
395
		}
396
397
		return $acls;
398
	}
399
	
400
	/**
401
	 * Generates code for an action
402
	 * 
403
	 * @param ActionSchema $action
404
	 */
405
	private function generateCode(ActionSchema $action) {
406
		$input = $this->io->getInput();
407
		$trait = null;
0 ignored issues
show
Unused Code introduced by
$trait is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
408
409
		// class
410
		$class = new PhpClass($action->getClass());
411
		$filename = $this->codegenService->getFilename($class);
412
		$traitNs = $class->getNamespace() . '\\base';
413
		$traitName = $class->getName() . 'Trait';
414
		$overwrite = false;
415
		
416
		// load from file, when class exists
417
		if (file_exists($filename)) {
418
			// load trait
419
			$trait = new PhpTrait($traitNs . '\\' . $traitName);
420
			$traitFile = new File($this->codegenService->getFilename($trait));
421
422
			if ($traitFile->exists()) {
423
				$trait = PhpTrait::fromFile($traitFile->getPathname());
0 ignored issues
show
Unused Code introduced by
$trait is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
424
			}
425
		
426
			// load class
427
			$class = PhpClass::fromFile($filename);
428
		}
429
		
430
		// anyway seed class information
431
		else {
432
			$overwrite = true;
433
			$class->setParentClassName('AbstractAction');
434
			$class->setDescription($action->getTitle());
435
			$class->setLongDescription($action->getDescription());
436
			$this->codegenService->addAuthors($class, $this->package);
437
		}
438
		
439
		// create base trait
440
		$modelName = $input->getOption('model');
441
		if ($modelName !== null) {
442
			$type = $this->packageService->getActionType($action->getName(), $modelName);
443
			$generator = GeneratorFactory::createActionTraitGenerator($type, $this->service);
444
			$trait = $generator->generate($traitNs . '\\' . $traitName, $action);
445
446
			$this->codegenService->addAuthors($trait, $this->package);
447
			$this->codegenService->dumpStruct($trait, true);
448
			
449
			if (!$class->hasTrait($trait)) {
450
				$class->addTrait($trait);
451
				$overwrite = true;
452
			}
453
		}
454
		
455
		// create class generator
456
		if ($modelName === null && !$class->hasMethod('run')) {
457
			$overwrite = true;
458
			$generator = new BlankActionGenerator($this->service);
459
		} else {
460
			$generator = new NoopActionGenerator($this->service);
461
		}
462
463
		$class = $generator->generate($class);
464
		$overwrite = $overwrite || $input->getOption('force');
465
466
		$this->codegenService->dumpStruct($class, $overwrite);
467
	}
468
	
469
	private function generateToOneRelationshipAction(Table $model, Table $foreign, ForeignKey $fk) {
470
		$module = $this->package->getKeeko()->getModule();
471
		$fkModelName = $foreign->getPhpName();
472
		$actionNamePrefix = sprintf('%s-to-%s-relationship', $model->getOriginCommonName(), $foreign->getOriginCommonName());
473
	
474
		$generators = [
475
			'read' => new ToOneRelationshipReadActionGenerator($this->service),
476
			'update' => new ToOneRelationshipUpdateActionGenerator($this->service)
477
		];
478
		$titles = [
479
			'read' => 'Reads the relationship of {model} to {foreign}',
480
			'update' => 'Updates the relationship of {model} to {foreign}'
481
		];
482
	
483
		foreach (array_keys($generators) as $type) {
484
			// generate fqcn
485
			$className = sprintf('%s%s%sAction', $model->getPhpName(), $fkModelName, ucfirst($type));
486
			$fqcn = $this->packageService->getNamespace() . '\\action\\' . $className;
487
	
488
			// generate action
489
			$action = new ActionSchema($actionNamePrefix . '-' . $type);
490
			$action->addAcl('admin');
491
			$action->setClass($fqcn);
492
			$action->setTitle(str_replace(
493
				['{model}', '{foreign}'],
494
				[$model->getOriginCommonName(), $foreign->getoriginCommonName()],
495
				$titles[$type])
496
			);
497
			$module->addAction($action);
498
	
499
			// generate class
500
			$generator = $generators[$type];
501
			$class = $generator->generate(new PhpClass($fqcn), $model, $foreign, $fk);
502
			$this->codegenService->dumpStruct($class, true);
503
		}
504
	}
505
	
506
	private function generateToManyRelationshipAction(Table $model, Table $foreign, Table $middle) {
507
		$module = $this->package->getKeeko()->getModule();
508
		$fkModelName = $foreign->getPhpName();
509
		$actionNamePrefix = sprintf('%s-to-%s-relationship', $model->getOriginCommonName(), $foreign->getOriginCommonName());
510
		
511
		$generators = [
512
			'read' => new ToManyRelationshipReadActionGenerator($this->service),
513
			'update' => new ToManyRelationshipUpdateActionGenerator($this->service),
514
			'add' => new ToManyRelationshipAddActionGenerator($this->service),
515
			'remove' => new ToManyRelationshipRemoveActionGenerator($this->service)
516
		];
517
		$titles = [
518
			'read' => 'Reads the relationship of {model} to {foreign}',
519
			'update' => 'Updates the relationship of {model} to {foreign}',
520
			'add' => 'Adds {foreign} as relationship to {model}',
521
			'remove' => 'Removes {foreign} as relationship of {model}'
522
		];
523
	
524
		foreach (array_keys($generators) as $type) {
525
			// generate fqcn
526
			$className = sprintf('%s%s%sAction', $model->getPhpName(), $fkModelName, ucfirst($type));
527
			$fqcn = $this->packageService->getNamespace() . '\\action\\' . $className;
528
	
529
			// generate action
530
			$action = new ActionSchema($actionNamePrefix . '-' . $type);
531
			$action->addAcl('admin');
532
			$action->setClass($fqcn);
533
			$action->setTitle(str_replace(
534
				['{model}', '{foreign}'],
535
				[$model->getOriginCommonName(), $foreign->getoriginCommonName()],
536
				$titles[$type])
537
			);
538
			$module->addAction($action);
539
	
540
			// generate class
541
			$generator = $generators[$type];
542
			$class = $generator->generate(new PhpClass($fqcn), $model, $foreign, $middle);
543
			$this->codegenService->dumpStruct($class, true);
544
		}
545
	}
546
547
}
548