Completed
Branch feature/pre-split (60f5c0)
by Anton
03:19
created

DocumentInstantiator::normalizeFields()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 12
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 2
nop 1
dl 12
loc 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Spiral Framework, Core Components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\ODM\Entities;
8
9
use Spiral\ODM\CompositableInterface;
10
use Spiral\ODM\Document;
11
use Spiral\ODM\DocumentEntity;
12
use Spiral\ODM\Exceptions\DefinitionException;
13
use Spiral\ODM\Exceptions\InstantionException;
14
use Spiral\ODM\InstantiatorInterface;
15
use Spiral\ODM\ODMInterface;
16
17
/**
18
 * Provides ability to construct Document and DocumentEntities with inheritance support.
19
 */
20
class DocumentInstantiator implements InstantiatorInterface
21
{
22
    /**
23
     * @invisible
24
     * @var ODMInterface
25
     */
26
    private $odm;
27
28
    /**
29
     * Primary instantiation class.
30
     *
31
     * @var string
32
     */
33
    private $class = '';
34
35
    /**
36
     * Normalized schema delivered by DocumentSchema.
37
     *
38
     * @var array
39
     */
40
    private $schema = [];
41
42
    /**
43
     * @param ODMInterface $odm
44
     * @param string       $class
45
     * @param array        $schema
46
     */
47
    public function __construct(ODMInterface $odm, string $class, array $schema)
48
    {
49
        $this->odm = $odm;
50
        $this->class = $class;
51
        $this->schema = $schema;
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     *
57
     * @return CompositableInterface|DocumentEntity|Document
58
     *
59
     * @throws InstantionException
60
     */
61
    public function instantiate($fields, bool $filter = true): CompositableInterface
62
    {
63
        $class = $this->defineClass($fields);
64
65
        if ($class !== $this->class) {
66
            //We have to dedicate class creation to external instantiator (possibly children class)
67
            return $this->odm->instantiate($class, $fields, $filter);
68
        }
69
70
        if (!is_array($fields)) {
71
            $fields = iterator_to_array($fields);
72
        }
73
74
        //Now we can construct needed class, in this case we are following DocumentEntity declaration
75
        if (!$filter) {
76
            //No need to filter values, passing directly in constructor
77
            return new $class($fields, $this->schema, $this->odm);
78
        }
79
80
        /*
81
         * Filtering entity
82
         */
83
84
        $entity = new $class($fields, $this->schema, $this->odm);
85
        if (!$entity instanceof CompositableInterface) {
86
            throw new InstantionException(
87
                "Unable to set filtered values for '{$class}', must be instance of CompositableInterface"
88
            );
89
        }
90
91
        //Must pass value thought all needed filters
92
        $entity->stateValue($fields);
93
94
        return $entity;
95
    }
96
97
    /**
98
     * Define document class using it's fieldset and definition.
99
     *
100
     * @param \ArrayAccess|array $fields
101
     *
102
     * @return string
103
     *
104
     * @throws DefinitionException
105
     */
106
    protected function defineClass($fields)
107
    {
108
        //Rule to define class instance
109
        $definition = $this->schema[DocumentEntity::SH_INSTANTIATION];
110
111
        if (is_string($definition)) {
112
            //Document has no variations
113
            return $definition;
114
        }
115
116
        if (!is_array($fields)) {
117
            //Unable to resolve for non array set, using same class as given
118
            return $this->class;
119
        }
120
121
        $defined = $this->class;
122
        foreach ($definition as $field => $child) {
123
            if (array_key_exists($field, $fields)) {
124
                //Apparently this is child
125
                $defined = $child;
126
                break;
127
            }
128
        }
129
130
        return $defined;
131
    }
132
}