Completed
Push — master ( d21e4b...25fd19 )
by Nikolas
03:46
created

ObjectField::inflate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
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\collections\Map;
9
use watoki\factory\Factory;
10
use watoki\factory\Injector;
11
use watoki\reflect\Property;
12
use watoki\reflect\PropertyReader;
13
use watoki\reflect\type\ClassType;
14
use watoki\reflect\TypeFactory;
15
16
class ObjectField implements WebField {
17
18
    /** @var TypeFactory */
19
    private $types;
20
21
    /** @var FieldRegistry */
22
    private $fields;
23
24
    /**
25
     * @param TypeFactory $types
26
     * @param FieldRegistry $fields
27
     */
28
    public function __construct(TypeFactory $types, FieldRegistry $fields) {
29
        $this->types = $types;
30
        $this->fields = $fields;
31
    }
32
33
    /**
34
     * @param Parameter $parameter
35
     * @return bool
36
     */
37
    public function handles(Parameter $parameter) {
38
        return $parameter->getType() instanceof ClassType;
39
    }
40
41
    /**
42
     * @param Parameter $parameter
43
     * @return array|\rtens\domin\delivery\web\Element[]
44
     */
45
    public function headElements(Parameter $parameter) {
46
        $reader = new PropertyReader($this->types, $this->getClass($parameter));
47
48
        $headElements = [];
49
        foreach ($reader->readInterface() as $property) {
50
            $param = $this->makePropertyParameter($parameter, $property);
51
            $headElements = array_merge($headElements, $this->getField($param)->headElements($param));
52
        }
53
        return $headElements;
54
    }
55
56
    /**
57
     * @param Parameter $parameter
58
     * @param \watoki\collections\Map $serialized
59
     * @return object
60
     */
61
    public function inflate(Parameter $parameter, $serialized) {
62
        return $this->createInstance($parameter, $this->inflateProperties($parameter, $serialized));
63
    }
64
65
    /**
66
     * @param Parameter $parameter
67
     * @param mixed $value
68
     * @return string
69
     */
70
    public function render(Parameter $parameter, $value) {
71
        return (string)new Element('div', ['class' => 'panel panel-default'], [
72
            new Element('div', ['class' => 'panel-heading clearfix'], [
73
                new Element('h3', ['class' => 'panel-title'], [
74
                    htmlentities((new \ReflectionClass($this->getClass($parameter)))->getShortName()),
75
                ])
76
            ]),
77
            new Element('div', ['class' => 'panel-body'], $this->renderPropertyFields($parameter, $value))
78
        ]);
79
    }
80
81
    private function createInstance(Parameter $parameter, $properties) {
82
        $injector = new Injector(new Factory());
83
        $instance = $injector->injectConstructor($this->getClass($parameter), $properties, function () {
84
            return false;
85
        });
86
87
        $reader = new PropertyReader($this->types, $this->getClass($parameter));
88
        foreach ($reader->readInterface() as $property) {
89
            if ($property->canSet()) {
90
                $property->set($instance, $properties[$property->name()]);
91
            }
92
        }
93
        return $instance;
94
    }
95
96
    private function inflateProperties(Parameter $parameter, Map $serialized) {
97
        $reader = new PropertyReader($this->types, $this->getClass($parameter));
98
99
        $properties = [];
100
        foreach ($reader->readInterface() as $property) {
101
            if ($serialized->has($property->name())) {
102
                $param = $this->makePropertyParameter($parameter, $property);
103
                $properties[$property->name()] = $this->inflateProperty($serialized[$property->name()], $param);
104
            }
105
        }
106
        return $properties;
107
    }
108
109
    private function inflateProperty($serialized, Parameter $parameter) {
110
        return $this->getField($parameter)->inflate($parameter, $serialized);
111
    }
112
113
    private function renderPropertyFields(Parameter $parameter, $object) {
114
        $reader = new PropertyReader($this->types, $this->getClass($parameter));
115
116
        $fields = [];
117
        foreach ($reader->readInterface($object) as $property) {
118
            if (!$property->canSet()) {
119
                continue;
120
            }
121
122
            $param = $this->makePropertyParameter($parameter, $property);
123
            $fields[] = $this->renderPropertyField($property, $param, $object);
124
        }
125
        return $fields;
126
    }
127
128
    private function renderPropertyField(Property $property, Parameter $param, $object) {
129
        return new Element('div', ['class' => 'form-group'], [
130
            new Element('label', [], [ucfirst($property->name()) . ($property->isRequired() ? '*' : '')]),
131
            $this->getField($param)->render($param, $object ? $property->get($object) : null)
132
        ]);
133
    }
134
135
    /**
136
     * @param Parameter $param
137
     * @return WebField
138
     */
139
    private function getField(Parameter $param) {
140
        return $this->fields->getField($param);
141
    }
142
143 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...
144
        $type = $parameter->getType();
145
        if (!($type instanceof ClassType)) {
146
            throw new \InvalidArgumentException("[$type] is not a ClassType");
147
        }
148
        return $type->getClass();
149
    }
150
151
    private function makePropertyParameter(Parameter $parameter, Property $property) {
152
        return new Parameter($parameter->getName() . '[' . $property->name() . ']', $property->type(), $property->isRequired());
153
    }
154
}