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

Property::setProperties()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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