Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

* Generated classes include scalar typehints and return type
* Nullable typehints are generated instead of a default `null` value
* A fluent interface isn't generated anymore
* A fluent interface isn't generated anymore by default unless you set the `fluentMutatorMethods` config flag to `true`
* Useless PHPDoc (`@param` and `@return` annotations when a typehint exist and mutator description) isn't generated anymore
* `DateTimeInterface` is used instead of `DateTime` when possible
* Add the ability to not generate mutator methods (useful when generating public properties)
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
},
"require-dev": {
"doctrine/orm": "^2.2",
"symfony/validator": "^2.7 || ^3.0 || ^4.0",
"symfony/filesystem": "^2.7 || ^3.0 || ^4.0"
"symfony/filesystem": "^2.7 || ^3.0 || ^4.0",
"symfony/validator": "^2.7 || ^3.0 || ^4.0"
},
"suggest": {
"doctrine/collections": "For Doctrine collections",
Expand Down
8 changes: 4 additions & 4 deletions src/AnnotationGenerator/PhpDocAnnotationGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public function generateSetterAnnotations(string $className, string $fieldName):
*/
public function generateAdderAnnotations(string $className, string $fieldName): array
{
if (!$this->isDocUseful($className, $fieldName)) {
if (!$this->isDocUseful($className, $fieldName, true)) {
return [];
}

Expand All @@ -125,16 +125,16 @@ public function generateAdderAnnotations(string $className, string $fieldName):
*/
public function generateRemoverAnnotations(string $className, string $fieldName): array
{
if (!$this->isDocUseful($className, $fieldName)) {
if (!$this->isDocUseful($className, $fieldName, true)) {
return [];
}

return [sprintf('@param %s $%s', $this->toPhpType($this->classes[$className]['fields'][$fieldName], true), $fieldName)];
}

private function isDocUseful(string $className, string $fieldName): bool
private function isDocUseful(string $className, string $fieldName, $adderOrRemover = false): bool
{
$typeHint = $this->classes[$className]['fields'][$fieldName]['typeHint'] ?? false;
$typeHint = $this->classes[$className]['fields'][$fieldName][$adderOrRemover ? 'adderRemoverTypeHint' : 'typeHint'] ?? false;

return false === $typeHint || 'array' === $typeHint;
}
Expand Down
19 changes: 14 additions & 5 deletions src/TypesGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ public function generate(array $config): void
foreach ($class['fields'] as &$field) {
$field['isEnum'] = isset($classes[$field['range']]) && $classes[$field['range']]['isEnum'];
$field['typeHint'] = $this->fieldToTypeHint($field, $classes) ?? false;

if ('array' === $field['typeHint']) {
$field['adderRemoverTypeHint'] = $this->fieldToAdderRemoverTypeHint($field, $classes) ?? false;
}
}
}

Expand Down Expand Up @@ -404,7 +408,7 @@ public function generate(array $config): void
}
}

if (!empty($interfaceMappings) && $config['doctrine']['resolveTargetEntityConfigPath']) {
if (!$interfaceMappings && $config['doctrine']['resolveTargetEntityConfigPath']) {
$file = $config['output'].'/'.$config['doctrine']['resolveTargetEntityConfigPath'];
$dir = dirname($file);
if (!file_exists($dir)) {
Expand Down Expand Up @@ -511,14 +515,19 @@ private function isDatatype(string $type): bool

private function fieldToTypeHint(array $field, array $classes): ?string
{
if ($field['isEnum']) {
return null;
}

if ($field['isArray']) {
return 'array';
}

return $this->fieldToAdderRemoverTypeHint($field, $classes);
}

private function fieldToAdderRemoverTypeHint(array $field, array $classes): ?string
{
if ($field['isEnum']) {
return 'string';
}

switch ($field['range']) {
case 'Boolean':
return 'bool';
Expand Down
1 change: 1 addition & 0 deletions src/TypesGeneratorConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ function ($rdfa) {
->scalarNode('author')->defaultFalse()->info('The value of the phpDoc\'s @author annotation')->example('Kévin Dunglas <dunglas@gmail.com>')->end()
->enumNode('fieldVisibility')->values(['private', 'protected', 'public'])->defaultValue('private')->cannotBeEmpty()->info('Visibility of entities fields')->end()
->booleanNode('mutatorMethods')->defaultTrue()->info('Set this flag to false to not generate getter, setter, adder and remover methods')->end()
->booleanNode('fluentMutatorMethods')->defaultFalse()->info('Set this flag to true to generate fluent setter, adder and remover methods')->end()
->arrayNode('types')
->beforeNormalization()
->always()
Expand Down
19 changes: 16 additions & 3 deletions templates/class.php.twig
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,21 @@ use {{ use }};
* {{ annotation }}
{% endfor %}
*/
public function add{{ field.name|ucfirst }}({% if field.typeHint and not field.isEnum %}{{ field.typeHint }} {% endif %}${{ field.name }}): void
public function add{{ field.name|ucfirst }}({% if field.typeHint and not field.isEnum %}{{ field.adderRemoverTypeHint }} {% endif %}${{ field.name }}): {% if config.fluentMutatorMethods %}self{% else %}void{% endif %}
{
$this->{{ field.name }}[] = {% if field.isEnum %}(string) {% endif %}${{ field.name }};
{% if config.fluentMutatorMethods %}

return $this;
{% endif %}
}

/**
{% for annotation in field.removerAnnotations %}
* {{ annotation }}
{% endfor %}
*/
public function remove{{ field.name|ucfirst }}({% if field.typeHint %}{{ field.typeHint }} {% endif %}${{ field.name }}): void
public function remove{{ field.name|ucfirst }}({% if field.typeHint %}{{ field.adderRemoverTypeHint }} {% endif %}${{ field.name }}): {% if config.fluentMutatorMethods %}self{% else %}void{% endif %}
{
{% if config.doctrine.useCollection and field.isArray and field.typeHint and not field.isEnum %}
$this->{{ field.name }}->removeElement({% if field.isEnum %}(string) {% endif %}${{ field.name }});
Expand All @@ -86,6 +90,10 @@ use {{ use }};
if (false !== $key) {
unset($this->{{ field.name }}[$key]);
}
{% endif %}
{% if config.fluentMutatorMethods %}

return $this;
{% endif %}
}
{% else %}
Expand All @@ -94,9 +102,14 @@ use {{ use }};
* {{ annotation }}
{% endfor %}
*/
public function set{{ field.name|ucfirst }}({% if field.typeHint %}{% if field.isNullable %}?{% endif %}{{ field.typeHint }} {% endif %}${{ field.name }}): void
public function set{{ field.name|ucfirst }}({% if field.typeHint %}{% if field.isNullable %}?{% endif %}{{ field.typeHint }} {% endif %}${{ field.name }}): {% if config.fluentMutatorMethods %}self{% else %}void{% endif %}
{
$this->{{ field.name }} = ${{ field.name }};

{% if config.fluentMutatorMethods %}

return $this;
{% endif %}
}
{% endif %}

Expand Down
3 changes: 3 additions & 0 deletions tests/Command/DumpConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ interface: AppBundle\Model # Example: Acme\Model
# Set this flag to false to not generate getter, setter, adder and remover methods
mutatorMethods: true

# Set this flag to true to generate fluent setter, adder and remover methods
fluentMutatorMethods: false

# Schema.org's types to use
types:

Expand Down
40 changes: 40 additions & 0 deletions tests/Command/GenerateTypesCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,44 @@ public function getArguments()
[__DIR__.'/../../build/mongodb/ecommerce/', __DIR__.'/../config/mongodb/ecommerce.yaml'],
];
}

public function testFluentMutators()
{
$outputDir = __DIR__.'/../../build/fluent-mutators';
$config = __DIR__.'/../config/fluent-mutators.yaml';

$this->fs->mkdir($outputDir);

$commandTester = new CommandTester(new GenerateTypesCommand());
$this->assertEquals(0, $commandTester->execute(['output' => $outputDir, 'config' => $config]));

$organization = file_get_contents($outputDir.'/AppBundle/Entity/Person.php');

$this->assertContains(<<<'PHP'
public function setUrl(?string $url): self
{
$this->url = $url;

return $this;
}
PHP
, $organization);

$this->assertContains(<<<'PHP'
public function addFriends(Person $friends): self
{
$this->friends[] = $friends;

return $this;
}

public function removeFriends(Person $friends): self
{
$this->friends->removeElement($friends);

return $this;
}
PHP
, $organization);
}
}
4 changes: 4 additions & 0 deletions tests/TypesGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ private function getConfig(): array
],
'generateId' => true,
'useInterface' => false,
'doctrine' => [
'useCollection' => true,
'resolveTargetEntityConfigPath' => null,
],
];
}

Expand Down
41 changes: 41 additions & 0 deletions tests/config/fluent-mutators.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
rdfa: [{ uri: tests/data/schema.rdfa, format: rdfa }]
relations: [tests/data/v1.owl]

fluentMutatorMethods: true
types:
Person:
properties:
name: ~
familyName: ~
givenName: ~
additionalName: ~
gender: ~
address: { range: PostalAddress }
# Custom range and custom ORM\Column content
birthDate: { range: DateTime, ormColumn: 'type="datetimetz", nullable=true, options={"comment" = "Birthdate with timezone."}' }
telephone: ~
email: ~
jobTitle: ~
# Default relation table name would be "person_organization" for all following fields, but we customize them
affiliation: ~
brand: { relationTableName: "person_brand"}
memberOf: { range: "Organization", cardinality: (1..*), relationTableName: "person_memberof"}
worksFor: { range: "Organization", cardinality: (0..*), relationTableName: "person_worksfor"}
# url field is a custom one without definition, it should render error
url: ~
friends: { range: "Person", cardinality: (0..*) }
PostalAddress:
# Disable the generation of the class hierarchy for this type
properties:
# Force the type of the addressCountry property to text
addressCountry: { range: "Text" }
addressLocality: ~
addressRegion: ~
postOfficeBoxNumber: ~
postalCode: ~
streetAddress: ~
Organization:
properties:
name: ~
# Custom property with custom ORM\Column content
adminCode: {range: Text, ormColumn: 'type="string", length=3, unique=true, nullable=false, options={"comment" = "A code for central administration."}' }