Completed
Push — master ( ca7ebd...ffd42a )
by Nikolas
03:40
created

ObjectField::getProperties()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 9
rs 9.6666
cc 3
eloc 5
nc 3
nop 2
1
<?php
2
namespace rtens\domin\delivery\web\fields;
3
4
use rtens\domin\delivery\FieldRegistry;
5
use rtens\domin\delivery\web\Element;
6
use rtens\domin\delivery\web\WebField;
7
use rtens\domin\Parameter;
8
use watoki\factory\Factory;
9
use watoki\factory\Injector;
10
use watoki\reflect\Property;
11
use watoki\reflect\PropertyReader;
12
use watoki\reflect\type\ClassType;
13
use watoki\reflect\TypeFactory;
14
15
class ObjectField implements WebField {
16
17
    /** @var TypeFactory */
18
    private $types;
19
20
    /** @var FieldRegistry */
21
    private $fields;
22
23
    /**
24
     * @param TypeFactory $types
25
     * @param FieldRegistry $fields
26
     */
27
    public function __construct(TypeFactory $types, FieldRegistry $fields) {
28
        $this->types = $types;
29
        $this->fields = $fields;
30
    }
31
32
    /**
33
     * @param Parameter $parameter
34
     * @return bool
35
     */
36
    public function handles(Parameter $parameter) {
37
        return $parameter->getType() instanceof ClassType;
38
    }
39
40
    /**
41
     * @param Parameter $parameter
42
     * @return array|\rtens\domin\delivery\web\Element[]
43
     */
44
    public function headElements(Parameter $parameter) {
45
        $headElements = [];
46
        foreach ($this->getProperties($parameter) as $property) {
47
            $param = $this->makePropertyParameter($parameter, $property);
48
            $headElements = array_merge($headElements, $this->getField($param)->headElements($param));
49
        }
50
        return $headElements;
51
    }
52
53
    /**
54
     * @param Parameter $parameter
55
     * @param array $serialized
56
     * @return object
57
     */
58
    public function inflate(Parameter $parameter, $serialized) {
59
        return $this->createInstance($parameter, $this->inflateProperties($parameter, $serialized));
60
    }
61
62
    /**
63
     * @param Parameter $parameter
64
     * @param mixed $value
65
     * @return string
66
     */
67
    public function render(Parameter $parameter, $value) {
68
        return (string)new Element('div', ['class' => 'panel panel-default'], [
69
            new Element('div', ['class' => 'panel-heading clearfix'], [
70
                new Element('h3', ['class' => 'panel-title'], [
71
                    htmlentities((new \ReflectionClass($this->getClass($parameter)))->getShortName()),
72
                ])
73
            ]),
74
            new Element('div', ['class' => 'panel-body'], $this->renderPropertyFields($parameter, $value))
75
        ]);
76
    }
77
78
    private function createInstance(Parameter $parameter, $properties) {
79
        $injector = new Injector(new Factory());
80
        $instance = $injector->injectConstructor($this->getClass($parameter), $properties, function () {
81
            return false;
82
        });
83
84
        foreach ($this->getProperties($parameter) as $property) {
85
            $property->set($instance, $properties[$property->name()]);
86
        }
87
        return $instance;
88
    }
89
90
    private function inflateProperties(Parameter $parameter, array $serialized) {
91
        $properties = [];
92
        foreach ($this->getProperties($parameter) as $property) {
93
            if (array_key_exists($property->name(), $serialized)) {
94
                $param = $this->makePropertyParameter($parameter, $property);
95
                $properties[$property->name()] = $this->inflateProperty($serialized[$property->name()], $param);
96
            }
97
        }
98
        return $properties;
99
    }
100
101
    private function inflateProperty($serialized, Parameter $parameter) {
102
        return $this->getField($parameter)->inflate($parameter, $serialized);
103
    }
104
105
    private function renderPropertyFields(Parameter $parameter, $object) {
106
        $fields = [];
107
        foreach ($this->getProperties($parameter, $object) as $property) {
108
            $param = $this->makePropertyParameter($parameter, $property);
109
            $fields[] = $this->renderPropertyField($property, $param, $object);
110
        }
111
        return $fields;
112
    }
113
114
    private function renderPropertyField(Property $property, Parameter $param, $object) {
115
        return new Element('div', ['class' => 'form-group'], [
116
            new Element('label', [], [ucfirst($property->name()) . ($property->isRequired() ? '*' : '')]),
117
            $this->getField($param)->render($param, $object ? $property->get($object) : null)
118
        ]);
119
    }
120
121
    /**
122
     * @param Parameter $param
123
     * @return WebField
124
     */
125
    private function getField(Parameter $param) {
126
        return $this->fields->getField($param);
127
    }
128
129 View Code Duplication
    private function getClass(Parameter $parameter) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130
        $type = $parameter->getType();
131
        if (!($type instanceof ClassType)) {
132
            throw new \InvalidArgumentException("[$type] is not a ClassType");
133
        }
134
        return $type->getClass();
135
    }
136
137
    private function makePropertyParameter(Parameter $parameter, Property $property) {
138
        return new Parameter($parameter->getName() . '[' . $property->name() . ']', $property->type(), $property->isRequired());
139
    }
140
141
    private function getProperties(Parameter $parameter, $object = null) {
142
        $reader = new PropertyReader($this->types, $this->getClass($parameter));
143
144
        foreach ($reader->readInterface($object) as $property) {
145
            if ($property->canSet()) {
146
                yield $property;
147
            }
148
        }
149
    }
150
}