Passed
Pull Request — master (#173)
by
unknown
02:52
created

Property::createPropertiesFromFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 0
c 1
b 0
f 0
dl 0
loc 2
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Zenstruck\Foundry\Bundle\Extractor;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Exception;
7
use ReflectionClass;
8
use ReflectionProperty;
9
use Zenstruck\Foundry\Bundle\Extractor\Type\JsonDetectionList;
10
use function Zenstruck\Foundry\faker;
11
12
class Property
13
{
14
    /**
15
     * @var array
16
     */
17
    private $properties = [];
18
19
    /**
20
     * @var array
21
     */
22
    private $defaultProperties = [];
23
24
    /**
25
     * @var EntityManagerInterface
26
     */
27
    private $em;
28
29
    public function __construct(EntityManagerInterface $em)
30
    {
31
        $this->em = $em;
32
    }
33
34
    /**
35
     * @TODO Support for defaults is missing
36
     * @TODO Support for relations is missing
37
     *
38
     * We only want create Defaults for non NULL probs
39
     * Dont create Default for field id
40
     * Based on fieldNames or Constraints or Types
41
     */
42
    public function getScalarPropertiesFromDoctrineFieldMappings(ReflectionClass $entity): array
43
    {
44
        $reader = new \Doctrine\Common\Annotations\AnnotationReader();
45
46
        $classMetaData = $this->em->getClassMetadata($entity->getName());
47
        $this->getDefaultFromProperty($entity);
48
        $identifierFieldNames = $classMetaData->getIdentifierFieldNames();
49
50
        foreach ($classMetaData->fieldMappings as $property) {
51
            // identifiers are normally autogenerated values. (id)
52
            if (\in_array($property['fieldName'], $identifierFieldNames)) {
53
                continue;
54
            }
55
56
            // CREATE SCARLAR
57
            if (!$property['nullable']
58
                && true === ScalarType::isScalarType($property['type'])) {
59
                // Also we should check property name and as examble for things like company create faker()->company()
60
                // if there is an default property which is not null|''|false we can use it? $this->>defaultProperties
61
                $reflectionProperty = new ReflectionProperty($entity->getName(), $property['fieldName']);
62
                $propertyAnnotation = $reader->getPropertyAnnotations($reflectionProperty);
63
                $this->properties[$property['fieldName']] = $this->createScalarProperties($property['type'], $propertyAnnotation, $property['fieldName']);
64
            }
65
66
            // CREATE JSON
67
            if (!$property['nullable']
68
                && 'json' === $property['type']) {
69
                $this->createJsonProperty($propertyAnnotation, $property['fieldName']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $propertyAnnotation does not seem to be defined for all execution paths leading up to this point.
Loading history...
70
            }
71
72
            // CREATE DATETIME
73
            if (!$property['nullable']
74
                && 'datetime' === $property['type']) {
75
                $this->createDateTimeProperty($property['fieldName']);
76
            }
77
        }
78
79
        return $this->properties;
80
    }
81
82
    /**
83
     * creates with faker nice default values for Scalar Types.
84
     *
85
     * @return mixed <int|string|float|bool>
86
     *
87
     * @throws Exception
88
     */
89
    public function createScalarProperties(string $type, $propertyAnnotation, string $fieldName)
90
    {
91
        switch ($type) {
92
            case 'string':
93
                // check propertyAnnotation
94
                return $this->createStringPropertyFromAnnotationOrFieldName($propertyAnnotation, $fieldName);
95
            case 'integer':
96
                return faker()->randomNumber();
97
            case 'boolean':
98
                return true; // is a bool always true?
99
            case 'float':
100
                return faker()->randomFloat();
101
102
            default:
103
                throw new Exception('type not found: '.$type);
104
        }
105
    }
106
107
    public function createJsonProperty($propertyAnnotation, string $fieldName)
108
    {
109
        $this->properties[$fieldName] = $this->createJsonPropertyFromAnnotationOrFieldName($propertyAnnotation, $fieldName);
110
    }
111
112
    public function createDateTimeProperty(string $fieldName)
113
    {
114
        $this->properties[$fieldName] = faker()->dateTime()->format('Y-m-d');
115
    }
116
117
    public function createDateProperty(string $fieldName)
118
    {
119
        $this->properties[$fieldName] = faker()->date();
120
    }
121
122
    public function createStringPropertyFromAnnotationOrFieldName(array $propertyAnnotations, $fieldName): string
123
    {
124
        foreach ($propertyAnnotations as $key => $constraint) {
125
            $classname = \get_class($constraint);
126
            // Now we can look for keys contains to generate different string with faker..
127
            // Email, Url, Hostname, Uuid,
128
            switch ($classname) {
129
                    case 'Symfony\Component\Validator\Constraints\Email':
130
                        return faker()->email();
131
                    case 'Symfony\Component\Validator\Constraints\Url':
132
                        return faker()->url();
133
                }
134
        }
135
136
        // try to Generate string from fieldname
137
        // roles, company, email, password, url, firstName, name|lastName, title, address, city, country, isbn, ....
138
        if (\array_key_exists($fieldName, StringScalarDetectionList::SCALAR_DETECTION_LIST)) {
139
            return faker()->{StringScalarDetectionList::SCALAR_DETECTION_LIST[$fieldName]}();
140
        }
141
142
        return faker()->sentence();
143
    }
144
145
    /**
146
     * if we found some fieldName we know then we create it with faker.
147
     *
148
     * @return string[]
149
     */
150
    public function createJsonPropertyFromAnnotationOrFieldName(array $propertyAnnotations, $fieldName): array
151
    {
152
        // for field roles as examble return ['ROLE_USER']
153
        if (\array_key_exists($fieldName, JsonDetectionList::LIST)) {
154
            return JsonDetectionList::LIST[$fieldName];
155
        }
156
157
        return ['DEFAULT'];
158
    }
159
160
    /**
161
     * @TODO
162
     * We store defaults values from properties.
163
     * if there is an default property which is not null|''|false we can use it? $this->defaultProperties
164
     */
165
    public function getDefaultFromProperty(ReflectionClass $reflectionClass): void
166
    {
167
        $this->defaultProperties = $reflectionClass->getDefaultProperties();
168
    }
169
170
    /**
171
     * @TODO get relations which cant be NULL
172
     */
173
    public function getPropertiesFromDoctrineRelations()
174
    {
175
    }
176
177
    /**
178
     * @TODO
179
     * This should be only used via flag/argument .. make:ftg --use-properties-from-factory
180
     * A Factory should have all nesseccary fields or in other words we cant guarantee thats there are all needed fields
181
     * If using this flag its up to developer to have defaults filled there
182
     *
183
     * @TODO maybe we can add/move this extractor to foundry.
184
     */
185
    public function createPropertiesFromFactory()
186
    {
187
    }
188
189
    public function getProperties(): array
190
    {
191
        return $this->properties;
192
    }
193
194
    public function setProperties(array $properties): void
195
    {
196
        $this->properties = $properties;
197
    }
198
}
199