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

Property::__construct()   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 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
    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
        foreach ($classMetaData->fieldMappings as $property) {
40
            // identifiers are normally autogenerated values. (id)
41
            if (\in_array($property['fieldName'], $identifierFieldNames)) {
42
                continue;
43
            }
44
45
            // CREATE SCARLAR
46
            if (!$property['nullable']
47
                && true === ScalarType::isScalarType($property['type'])) {
48
                // Also we should check property name and as examble for things like company create faker()->company()
49
                // if there is an default property which is not null|''|false we can use it? $this->>defaultProperties
50
                $reflectionProperty = new ReflectionProperty($entity->getName(), $property['fieldName']);
51
                $propertyAnnotation = $reader->getPropertyAnnotations($reflectionProperty);
52
                $this->properties[$property['fieldName']] = $this->createScalarProperties($property['type'], $propertyAnnotation, $property['fieldName']);
53
            }
54
55
            // CREATE JSON
56
            if (!$property['nullable']
57
                && 'json' === $property['type']) {
58
                $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...
59
            }
60
61
            // CREATE DATETIME
62
            if (!$property['nullable']
63
                && 'datetime' === $property['type']) {
64
                $this->createDateTimeProperty($property['fieldName']);
65
            }
66
        }
67
68
        return $this->properties;
69
    }
70
71
    /**
72
     * creates with faker nice default values for Scalar Types.
73
     *
74
     * @return mixed <int|string|float|bool>
75
     *
76
     * @throws Exception
77
     */
78
    public function createScalarProperties(string $type, $propertyAnnotation, string $fieldName)
79
    {
80
        switch ($type) {
81
            case 'string':
82
                // check propertyAnnotation
83
                return $this->createStringPropertyFromAnnotationOrFieldName($propertyAnnotation, $fieldName);
84
            case 'integer':
85
                return faker()->randomNumber();
86
            case 'boolean':
87
                return true; // is a bool always true?
88
            case 'float':
89
                return faker()->randomFloat();
90
91
            default:
92
                throw new Exception('type not found: '.$type);
93
        }
94
    }
95
96
    public function createJsonProperty($propertyAnnotation, string $fieldName)
97
    {
98
        $this->properties[$fieldName] = $this->createJsonPropertyFromAnnotationOrFieldName($propertyAnnotation, $fieldName);
99
    }
100
101
    public function createDateTimeProperty(string $fieldName)
102
    {
103
        $this->properties[$fieldName] = faker()->dateTime()->format('Y-m-d');
104
    }
105
106
    public function createDateProperty(string $fieldName)
107
    {
108
        $this->properties[$fieldName] = faker()->date();
109
    }
110
111
    public function createStringPropertyFromAnnotationOrFieldName(array $propertyAnnotations, $fieldName): string
112
    {
113
        foreach ($propertyAnnotations as $key => $constraint) {
114
            $classname = \get_class($constraint);
115
            // Now we can look for keys contains to generate different string with faker..
116
            // Email, Url, Hostname, Uuid,
117
            switch ($classname) {
118
                    case 'Symfony\Component\Validator\Constraints\Email':
119
                        return faker()->email();
120
                    case 'Symfony\Component\Validator\Constraints\Url':
121
                        return faker()->url();
122
                }
123
        }
124
125
        // try to Generate string from fieldname
126
        // roles, company, email, password, url, firstName, name|lastName, title, address, city, country, isbn, ....
127
        if (\array_key_exists($fieldName, StringScalarDetectionList::SCALAR_DETECTION_LIST)) {
128
            return faker()->{StringScalarDetectionList::SCALAR_DETECTION_LIST[$fieldName]}();
129
        }
130
131
        return faker()->sentence();
132
    }
133
134
    /**
135
     * if we found some fieldName we know then we create it with faker.
136
     *
137
     * @return string[]
138
     */
139
    public function createJsonPropertyFromAnnotationOrFieldName(array $propertyAnnotations, $fieldName): array
140
    {
141
        // for field roles as examble return ['ROLE_USER']
142
        if (\array_key_exists($fieldName, JsonDetectionList::LIST)) {
143
            return JsonDetectionList::LIST[$fieldName];
144
        }
145
146
        return ['DEFAULT'];
147
    }
148
149
    /**
150
     * @TODO
151
     * We store defaults values from properties.
152
     * if there is an default property which is not null|''|false we can use it? $this->defaultProperties
153
     */
154
    public function getDefaultFromProperty(ReflectionClass $reflectionClass): void
155
    {
156
        $this->defaultProperties = $reflectionClass->getDefaultProperties();
157
    }
158
159
    /**
160
     * @TODO get relations which cant be NULL
161
     */
162
    public function getPropertiesFromDoctrineRelations()
163
    {
164
    }
165
166
    /**
167
     * @TODO
168
     * This should be only used via flag/argument .. make:ftg --use-properties-from-factory
169
     * A Factory should have all nesseccary fields or in other words we cant guarantee thats there are all needed fields
170
     * If using this flag its up to developer to have defaults filled there
171
     *
172
     * @TODO maybe we can add/move this extractor to foundry.
173
     */
174
    public function createPropertiesFromFactory()
175
    {
176
    }
177
178
    public function getProperties(): array
179
    {
180
        return $this->properties;
181
    }
182
183
    public function setProperties(array $properties): void
184
    {
185
        $this->properties = $properties;
186
    }
187
}
188