Completed
Push — master ( dae493...440cc5 )
by Thomas
08:24
created

ModelDomainTraitGenerator   D

Complexity

Total Complexity 31

Size/Duplication

Total Lines 483
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 26

Importance

Changes 11
Bugs 1 Features 0
Metric Value
wmc 31
c 11
b 1
f 0
lcom 1
cbo 26
dl 0
loc 483
rs 4.9

12 Methods

Rating   Name   Duplication   Size   Complexity  
B generate() 0 41 6
B generateEvent() 0 77 6
A generateDispatch() 0 18 1
B generateNormalize() 0 33 5
A generateCreate() 0 14 1
A generateUpdate() 0 17 1
A generateDelete() 0 14 1
B generateToOneRelationshipSet() 0 27 1
A generateToManyRelationshipAdd() 0 61 3
A generateToManyRelationshipRemove() 0 61 3
B generateOneToManyRelationshipUpdate() 0 33 1
A generateManyToManyRelationshipUpdate() 0 68 2
1
<?php
2
namespace keeko\tools\generator\domain\base;
3
4
use gossi\codegen\model\PhpClass;
5
use gossi\codegen\model\PhpMethod;
6
use gossi\codegen\model\PhpParameter;
7
use gossi\codegen\model\PhpProperty;
8
use gossi\codegen\model\PhpTrait;
9
use keeko\framework\utils\NameUtils;
10
use keeko\tools\model\Relationship;
11
use Propel\Generator\Model\Table;
12
use keeko\tools\model\ManyToManyRelationship;
13
use keeko\tools\model\OneToManyRelationship;
14
use phootwork\lang\Text;
15
16
class ModelDomainTraitGenerator extends ReadOnlyModelDomainTraitGenerator {
17
18
	public function generate(Table $model) {
19
		$trait = parent::generate($model);
20
21
		// generate event
22
		$event = $this->generateEvent($model);
23
		$trait->addUseStatement($event->getQualifiedName());
24
		$this->generateDispatch($trait, $model);
25
26
		// generate CUD methods
27
		$this->generateNormalize($trait, $model);
28
		$this->generateCreate($trait, $model);
29
		$this->generateUpdate($trait, $model);
30
		$this->generateDelete($trait, $model);
31
32
		// generate relationship methods
33
		if (!$model->isReadOnly()) {
34
			$relationships = $this->modelService->getRelationships($model);
35
36
			foreach ($relationships->getAll() as $relationship) {
37
				switch ($relationship->getType()) {
38
					case Relationship::ONE_TO_ONE:
39
						$this->generateToOneRelationshipSet($trait, $relationship);
40
						break;
41
42
					case Relationship::ONE_TO_MANY:
43
						$this->generateToManyRelationshipAdd($trait, $relationship);
44
						$this->generateToManyRelationshipRemove($trait, $relationship);
45
						$this->generateOneToManyRelationshipUpdate($trait, $relationship);
46
						break;
47
48
					case Relationship::MANY_TO_MANY:
49
						$this->generateToManyRelationshipAdd($trait, $relationship);
50
						$this->generateToManyRelationshipRemove($trait, $relationship);
51
						$this->generateManyToManyRelationshipUpdate($trait, $relationship);
52
						break;
53
				}
54
			}
55
		}
56
57
		return $trait;
58
	}
59
60
	/**
61
	 *
62
	 * @param Table $model
63
	 * @return PhpClass
64
	 */
65
	protected function generateEvent(Table $model) {
66
		$package = $this->packageService->getPackage();
67
		$slug = $package->getKeeko()->getModule()->getSlug();
68
		$modelName = $model->getOriginCommonName();
69
70
		$class = new PhpClass();
71
		$class->setNamespace(str_replace('model', 'event', $model->getNamespace()));
72
		$class->setName($model->getPhpName() . 'Event');
73
		$class->setParentClassName('Event');
74
		if ($model->getPhpName() == 'Event') {
75
			$class->addUseStatement($model->getNamespace() . '\\' . $model->getPhpName(), 'Model');
76
		} else {
77
			$class->addUseStatement($model->getNamespace() . '\\' . $model->getPhpName());
78
		}
79
		$class->addUseStatement('Symfony\Component\EventDispatcher\Event');
80
81
		// constants
82
		$class->setConstant('PRE_CREATE', sprintf('%s.%s.pre_create', $slug, $modelName));
83
		$class->setConstant('POST_CREATE', sprintf('%s.%s.post_create', $slug, $modelName));
84
		$class->setConstant('PRE_UPDATE', sprintf('%s.%s.pre_update', $slug, $modelName));
85
		$class->setConstant('POST_UPDATE', sprintf('%s.%s.post_update', $slug, $modelName));
86
		$class->setConstant('PRE_SAVE', sprintf('%s.%s.pre_save', $slug, $modelName));
87
		$class->setConstant('POST_SAVE', sprintf('%s.%s.post_save', $slug, $modelName));
88
		$class->setConstant('PRE_DELETE', sprintf('%s.%s.pre_delete', $slug, $modelName));
89
		$class->setConstant('POST_DELETE', sprintf('%s.%s.post_delete', $slug, $modelName));
90
91
		// generate relationship constants
92
		if (!$model->isReadOnly()) {
93
			$relationships = $this->modelService->getRelationships($model);
94
95
			foreach ($relationships->getAll() as $relationship) {
96
				// one-to-one relationships
97
				if ($relationship->getType() == Relationship::ONE_TO_ONE) {
98
					$snake = NameUtils::toSnakeCase($relationship->getRelatedName());
99
					$name = strtoupper($snake);
100
					$class->setConstant('PRE_' . $name . '_UPDATE', sprintf('%s.%s.pre_%s_update', $slug, $modelName, $snake));
101
					$class->setConstant('POST_' . $name . '_UPDATE', sprintf('%s.%s.post_%s_update', $slug, $modelName, $snake));
102
				}
103
104
				// others
105
				else {
106
					$snake = NameUtils::toSnakeCase($relationship->getRelatedPluralName());
107
					$name = strtoupper($snake);
108
					$class->setConstant('PRE_' . $name . '_ADD', sprintf('%s.%s.pre_%s_add', $slug, $modelName, $snake));
109
					$class->setConstant('POST_' . $name . '_ADD', sprintf('%s.%s.post_%s_add', $slug, $modelName, $snake));
110
					$class->setConstant('PRE_' . $name . '_REMOVE', sprintf('%s.%s.pre_%s_add', $slug, $modelName, $snake));
111
					$class->setConstant('POST_' . $name . '_REMOVE', sprintf('%s.%s.post_%s_add', $slug, $modelName, $snake));
112
					$class->setConstant('PRE_' . $name . '_UPDATE', sprintf('%s.%s.pre_%s_update', $slug, $modelName, $snake));
113
					$class->setConstant('POST_' . $name . '_UPDATE', sprintf('%s.%s.post_%s_update', $slug, $modelName, $snake));
114
				}
115
			}
116
		}
117
118
		// properties
119
		$modelVariableName = $model->getCamelCaseName();
120
		$class->setProperty(PhpProperty::create($modelVariableName)
121
			->setType($model->getPackage())
122
			->setVisibility(PhpProperty::VISIBILITY_PROTECTED)
123
		);
124
125
		// constructor
126
		$type = $model->getPhpName() == 'Event' ? 'Model' : $model->getPhpName();
127
		$class->setMethod(PhpMethod::create('__construct')
128
			->addParameter(PhpParameter::create($modelVariableName)->setType($type))
129
			->setBody('$this->' . $modelVariableName . ' = $' . $modelVariableName .';')
130
		);
131
132
		// getModel()
133
		$class->setMethod(PhpMethod::create('get' . $model->getPhpName())
134
			->setType($model->getPhpName())
135
			->setBody('return $this->' . $modelVariableName .';')
136
		);
137
138
		$this->codeService->dumpStruct($class, true);
139
140
		return $class;
141
	}
142
143
	protected function generateDispatch(PhpTrait $trait, Table $model) {
144
		$trait->setMethod(PhpMethod::create('dispatch')
145
			->addParameter(PhpParameter::create('type')
146
				->setType('string')
147
			)
148
			->addParameter(PhpParameter::create('model')
149
				->setType($model->getPhpName())
150
			)
151
			->addParameter(PhpParameter::create('data')
152
				->setType('array')
153
				->setExpression('[]')
154
			)
155
			->setBody($this->twig->render('dispatch.twig', [
156
				'class' => $model->getPhpName()
157
			]))
158
			->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
159
		);
160
	}
161
162
	protected function generateNormalize(PhpTrait $trait, Table $model) {
163
		if ($model->isReadOnly()) {
164
			$body = 'return $data';
165
		} else {
166
			$modelName = $model->getOriginCommonName();
167
			$normalizer = $this->project->getGeneratorDefinition()->getNormalizer($modelName);
168
			$fields = $this->generatorDefinitionService->getWriteFields($modelName);
169
			$code = '';
170
171
			foreach ($fields as $field) {
172
				if ($normalizer->has($field)) {
173
					$class = new Text($normalizer->get($field));
174
					if (!$class->startsWith('\\')) {
175
						$class = $class->prepend('\\');
176
					}
177
					$code .= $this->twig->render('normalizer-field.twig', [
178
						'class' => $class->toString(),
179
						'field' => $field
180
					]);
181
				}
182
			}
183
184
			$body = $this->twig->render('normalizer.twig', [
185
				'code' => $code
186
			]);
187
		}
188
189
		$trait->setMethod(PhpMethod::create('normalize')
190
			->addParameter(PhpParameter::create('data')->setType('array'))
191
			->setBody($body)
192
			->setType('array', 'normalized data')
193
		);
194
	}
195
196
	protected function generateCreate(PhpTrait $trait, Table $model) {
197
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\Created');
198
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotFound');
199
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotValid');
200
201
		$trait->setMethod(PhpMethod::create('create')
202
			->addParameter(PhpParameter::create('data'))
203
			->setBody($this->twig->render('create.twig', [
204
				'class' => $model->getPhpName()
205
			]))
206
			->setDescription('Creates a new ' . $model->getPhpName() . ' with the provided data')
207
			->setType('PayloadInterface')
208
		);
209
	}
210
211
	protected function generateUpdate(PhpTrait $trait, Table $model) {
212
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\Updated');
213
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotUpdated');
214
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotFound');
215
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotValid');
216
217
		$trait->setMethod(PhpMethod::create('update')
218
			->addParameter(PhpParameter::create('id'))
219
			->addParameter(PhpParameter::create('data'))
220
			->setBody($this->twig->render('update.twig', [
221
				'class' => $model->getPhpName()
222
			]))
223
			->setDescription('Updates a ' . $model->getPhpName() . ' with the given id' .
224
				'and the provided data')
225
			->setType('PayloadInterface')
226
		);
227
	}
228
229
	protected function generateDelete(PhpTrait $trait, Table $model) {
230
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\Deleted');
231
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotDeleted');
232
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotFound');
233
234
		$trait->setMethod(PhpMethod::create('delete')
235
			->addParameter(PhpParameter::create('id'))
236
			->setBody($this->twig->render('delete.twig', [
237
				'class' => $model->getPhpName()
238
			]))
239
			->setDescription('Deletes a ' . $model->getPhpName() . ' with the given id')
240
			->setType('PayloadInterface')
241
		);
242
	}
243
244
	protected function generateToOneRelationshipSet(PhpTrait $trait, Relationship $relationship) {
245
		$model = $relationship->getModel();
246
		$name = $relationship->getRelatedName();
247
		$trait->setMethod(PhpMethod::create('set' . $name . 'Id')
248
			->setDescription(str_replace('{foreign}', $relationship->getRelatedName(), 'Sets the {foreign} id'))
249
			->addParameter(PhpParameter::create('id'))
250
			->addParameter(PhpParameter::create('relatedId'))
251
			->setBody($this->twig->render('to-one-set.twig', [
252
				'class' => $model->getPhpName(),
253
				'related_name' => $name,
254
				'const' => strtoupper(NameUtils::toSnakeCase($relationship->getRelatedName()))
255
			]))
256
			->setType('PayloadInterface')
257
		);
258
259
		$trait->setMethod(PhpMethod::create('doSet' . $name . 'Id')
260
			->setDescription(str_replace('{foreign}', $relationship->getRelatedName(), 'Internal mechanism to set the {foreign} id'))
261
			->addParameter(PhpParameter::create('model')
262
				->setType($model->getPhpName())
263
			)
264
			->addParameter(PhpParameter::create('relatedId'))
265
			->setBody($this->twig->render('do-to-one-set.twig', [
266
				'local' => $relationship->getForeignKey()->getLocalColumn()->getPhpName()
267
			]))
268
			->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
269
		);
270
	}
271
272
	protected function generateToManyRelationshipAdd(PhpTrait $trait, Relationship $relationship) {
273
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotValid');
274
275
		$model = $relationship->getModel();
276
		$foreign = $relationship->getForeign();
277
		$trait->addUseStatement($foreign->getNamespace() . '\\' . $foreign->getPhpName() . 'Query');
278
		$trait->setMethod(PhpMethod::create('add' . $relationship->getRelatedPluralName())
279
			->setDescription('Adds ' . $relationship->getRelatedPluralName() . ' to ' . $model->getPhpName())
280
			->addParameter(PhpParameter::create('id'))
281
			->addParameter(PhpParameter::create('data'))
282
			->setBody($this->twig->render('to-many-add.twig', [
283
				'class' => $model->getPhpName(),
284
				'related_name' => $relationship->getRelatedPluralName(),
285
				'const' => strtoupper(NameUtils::toSnakeCase($relationship->getRelatedPluralName()))
286
			]))
287
			->setType('PayloadInterface')
288
		);
289
290
		$methodNameGenerator = $this->factory->getRelationshipMethodNameGenerator();
291
		$trait->setMethod(PhpMethod::create('doAdd' . $relationship->getRelatedPluralName())
292
			->setDescription('Interal mechanism to add ' . $relationship->getRelatedPluralName() . ' to ' . $model->getPhpName())
293
			->addParameter(PhpParameter::create('model')
294
				->setType($model->getPhpName())
295
			)
296
			->addParameter(PhpParameter::create('data'))
297
			->setBody($this->twig->render('do-to-many-add.twig', [
298
				'foreign_class' => $foreign->getPhpName(),
299
				'method_name' => $methodNameGenerator->generateMethodName($relationship)
300
			]))
301
			->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
302
		);
303
304
		// reflexive add method on many-to-many relationship
305
		if ($relationship->getType() == Relationship::MANY_TO_MANY && $relationship->isReflexive()) {
306
			$trait->setMethod(PhpMethod::create('add' . $relationship->getReverseRelatedPluralName())
307
				->setDescription('Adds ' . $relationship->getReverseRelatedPluralName() . ' to ' . $model->getPhpName())
308
				->addParameter(PhpParameter::create('id'))
309
				->addParameter(PhpParameter::create('data'))
310
				->setBody($this->twig->render('to-many-add.twig', [
311
					'class' => $model->getPhpName(),
312
					'related_name' => $relationship->getReverseRelatedPluralName(),
313
					'const' => strtoupper(NameUtils::toSnakeCase($relationship->getRelatedPluralName()))
314
				]))
315
				->setType('PayloadInterface')
316
			);
317
318
			$methodNameGenerator = $this->factory->getRelationshipMethodNameGenerator();
319
			$trait->setMethod(PhpMethod::create('doAdd' . $relationship->getReverseRelatedPluralName())
320
				->setDescription('Interal mechanism to add ' . $relationship->getReverseRelatedPluralName() . ' to ' . $model->getPhpName())
321
				->addParameter(PhpParameter::create('model')
322
					->setType($model->getPhpName())
323
				)
324
				->addParameter(PhpParameter::create('data'))
325
				->setBody($this->twig->render('do-to-many-add.twig', [
326
					'foreign_class' => $foreign->getPhpName(),
327
					'method_name' => $methodNameGenerator->generateReverseMethodName($relationship)
328
				]))
329
				->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
330
			);
331
		}
332
	}
333
334
	protected function generateToManyRelationshipRemove(PhpTrait $trait, Relationship $relationship) {
335
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotValid');
336
337
		$model = $relationship->getModel();
338
		$foreign = $relationship->getForeign();
339
		$trait->addUseStatement($foreign->getNamespace() . '\\' . $foreign->getPhpName() . 'Query');
340
		$trait->setMethod(PhpMethod::create('remove' . $relationship->getRelatedPluralName())
341
			->setDescription('Removes ' . $relationship->getRelatedPluralName() . ' from ' . $model->getPhpName())
342
			->addParameter(PhpParameter::create('id'))
343
			->addParameter(PhpParameter::create('data'))
344
			->setBody($this->twig->render('to-many-remove.twig', [
345
				'class' => $model->getPhpName(),
346
				'related_name' => $relationship->getRelatedPluralName(),
347
				'const' => strtoupper(NameUtils::toSnakeCase($relationship->getRelatedPluralName()))
348
			]))
349
			->setType('PayloadInterface')
350
		);
351
352
		$methodNameGenerator = $this->factory->getRelationshipMethodNameGenerator();
353
		$trait->setMethod(PhpMethod::create('doRemove' . $relationship->getRelatedPluralName())
354
			->setDescription('Interal mechanism to remove ' . $relationship->getRelatedPluralName() . ' from ' . $model->getPhpName())
355
			->addParameter(PhpParameter::create('model')
356
				->setType($model->getPhpName())
357
				)
358
			->addParameter(PhpParameter::create('data'))
359
			->setBody($this->twig->render('do-to-many-remove.twig', [
360
				'foreign_class' => $foreign->getPhpName(),
361
				'method_name' => $methodNameGenerator->generateMethodName($relationship)
362
			]))
363
			->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
364
		);
365
366
		// reflexive remove method on many-to-many relationship
367
		if ($relationship->getType() == Relationship::MANY_TO_MANY && $relationship->isReflexive()) {
368
			$trait->setMethod(PhpMethod::create('remove' . $relationship->getReverseRelatedPluralName())
369
				->setDescription('Removes ' . $relationship->getReverseRelatedPluralName() . ' from ' . $model->getPhpName())
370
				->addParameter(PhpParameter::create('id'))
371
				->addParameter(PhpParameter::create('data'))
372
				->setBody($this->twig->render('to-many-remove.twig', [
373
					'class' => $model->getPhpName(),
374
					'related_name' => $relationship->getReverseRelatedPluralName(),
375
					'const' => strtoupper(NameUtils::toSnakeCase($relationship->getRelatedPluralName()))
376
				]))
377
				->setType('PayloadInterface')
378
			);
379
380
			$methodNameGenerator = $this->factory->getRelationshipMethodNameGenerator();
381
			$trait->setMethod(PhpMethod::create('doRemove' . $relationship->getReverseRelatedPluralName())
382
				->setDescription('Interal mechanism to remove ' . $relationship->getReverseRelatedPluralName() . ' from ' . $model->getPhpName())
383
				->addParameter(PhpParameter::create('model')
384
					->setType($model->getPhpName())
385
				)
386
				->addParameter(PhpParameter::create('data'))
387
				->setBody($this->twig->render('do-to-many-remove.twig', [
388
					'foreign_class' => $foreign->getPhpName(),
389
					'method_name' => $methodNameGenerator->generateMethodName($relationship)
390
				]))
391
				->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
392
			);
393
		}
394
	}
395
396
	protected function generateOneToManyRelationshipUpdate(PhpTrait $trait, OneToManyRelationship $relationship) {
397
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotValid');
398
		$trait->addUseStatement('keeko\\framework\\exceptions\\ErrorsException');
399
400
		$model = $relationship->getModel();
401
		$foreign = $relationship->getForeign();
402
		$trait->addUseStatement($foreign->getNamespace() . '\\' . $foreign->getPhpName() . 'Query');
403
		$trait->setMethod(PhpMethod::create('update' . $relationship->getRelatedPluralName())
404
			->setDescription('Updates ' . $relationship->getRelatedPluralName() . ' on ' . $model->getPhpName())
405
			->addParameter(PhpParameter::create('id'))
406
			->addParameter(PhpParameter::create('data'))
407
			->setBody($this->twig->render('one-to-many-update.twig', [
408
				'class' => $model->getPhpName(),
409
				'related_name' => $relationship->getRelatedPluralName(),
410
				'const' => strtoupper(NameUtils::toSnakeCase($relationship->getRelatedPluralName()))
411
			]))
412
			->setType('PayloadInterface')
413
		);
414
415
		$trait->setMethod(PhpMethod::create('doUpdate' . $relationship->getRelatedPluralName())
416
			->setDescription('Internal update mechanism of ' . $relationship->getRelatedPluralName() . ' on ' . $model->getPhpName())
417
			->addParameter(PhpParameter::create('model')
418
				->setType($model->getPhpName())
419
			)
420
			->addParameter(PhpParameter::create('data'))
421
			->setBody($this->twig->render('do-one-to-many-update.twig', [
422
				'related' => $relationship->getRelatedName(),
423
				'reverse_related' => $relationship->getReverseRelatedName(),
424
				'foreign_class' => $foreign->getPhpName()
425
			]))
426
			->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
427
		);
428
	}
429
430
	protected function generateManyToManyRelationshipUpdate(PhpTrait $trait, ManyToManyRelationship $relationship) {
431
		$trait->addUseStatement('keeko\\framework\\domain\\payload\\NotValid');
432
		$trait->addUseStatement('keeko\\framework\\exceptions\\ErrorsException');
433
434
		$model = $relationship->getModel();
435
		$foreign = $relationship->getForeign();
436
		$middle = $relationship->getMiddle();
437
		$trait->addUseStatement($middle->getNamespace() . '\\' . $middle->getPhpName() . 'Query');
438
		$trait->addUseStatement($foreign->getNamespace() . '\\' . $foreign->getPhpName() . 'Query');
439
		$trait->setMethod(PhpMethod::create('update' . $relationship->getRelatedPluralName())
440
			->setDescription('Updates ' . $relationship->getRelatedPluralName() . ' on ' . $model->getPhpName())
441
			->addParameter(PhpParameter::create('id'))
442
			->addParameter(PhpParameter::create('data'))
443
			->setBody($this->twig->render('many-to-many-update.twig', [
444
				'class' => $model->getPhpName(),
445
				'related_name' => $relationship->getRelatedPluralName(),
446
				'const' => strtoupper(NameUtils::toSnakeCase($relationship->getRelatedPluralName()))
447
			]))
448
			->setType('PayloadInterface')
449
		);
450
451
		$methodNameGenerator = $this->factory->getRelationshipMethodNameGenerator();
452
		$trait->setMethod(PhpMethod::create('doUpdate' . $relationship->getRelatedPluralName())
453
			->setDescription('Internal update mechanism of ' . $relationship->getRelatedPluralName() . ' on ' . $model->getPhpName())
454
			->addParameter(PhpParameter::create('model')
455
				->setType($model->getPhpName())
456
			)
457
			->addParameter(PhpParameter::create('data'))
458
			->setBody($this->twig->render('do-many-to-many-update.twig', [
459
				'method_name' => $methodNameGenerator->generateMethodName($relationship),
460
				'reverse_related' => $methodNameGenerator->generateReverseMethodName($relationship),
461
				'foreign_class' => $foreign->getPhpName(),
462
				'middle_class' => $middle->getPhpName(),
463
			]))
464
			->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
465
		);
466
467
		// reflexive update methods
468
		if ($relationship->isReflexive()) {
469
			$trait->setMethod(PhpMethod::create('update' . $relationship->getReverseRelatedPluralName())
470
				->setDescription('Updates ' . $relationship->getReverseRelatedPluralName() . ' on ' . $model->getPhpName())
471
				->addParameter(PhpParameter::create('id'))
472
				->addParameter(PhpParameter::create('data'))
473
				->setBody($this->twig->render('many-to-many-update.twig', [
474
					'class' => $model->getPhpName(),
475
					'related_name' => $relationship->getReverseRelatedName(),
476
					'const' => strtoupper(NameUtils::toSnakeCase($relationship->getRelatedPluralName()))
477
				]))
478
				->setType('PayloadInterface')
479
			);
480
481
			$methodNameGenerator = $this->factory->getRelationshipMethodNameGenerator();
482
			$trait->setMethod(PhpMethod::create('doUpdate' . $relationship->getReverseRelatedPluralName())
483
				->setDescription('Internal update mechanism of ' . $relationship->getReverseRelatedPluralName() . ' on ' . $model->getPhpName())
484
				->addParameter(PhpParameter::create('model')
485
					->setType($model->getPhpName())
486
				)
487
				->addParameter(PhpParameter::create('data'))
488
				->setBody($this->twig->render('do-many-to-many-update.twig', [
489
					'method_name' => $methodNameGenerator->generateReverseMethodName($relationship),
490
					'reverse_related' => $methodNameGenerator->generateMethodName($relationship),
491
					'foreign_class' => $foreign->getPhpName(),
492
					'middle_class' => $middle->getPhpName(),
493
				]))
494
				->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
495
			);
496
		}
497
	}
498
}
499