Completed
Branch feature/pre-split (6ce9be)
by Anton
03:09
created

DocumentInstantiator::instantiate()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 28
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
c 0
b 0
f 0
cc 4
eloc 13
nc 4
nop 2
rs 8.5806
1
<?php
2
/**
3
 * 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
        //Now we can construct needed class, in this case we are following DocumentEntity declaration
71
        if (!$filter) {
72
            //No need to filter values, passing directly in constructor
73
            return new $class($fields, $this->schema, $this->odm);
74
        } else {
75
            $entity = new $class($fields, $this->schema, $this->odm);
76
77
            if (!$entity instanceof CompositableInterface) {
78
                throw new InstantionException(
79
                    "Unable to set filtered values for {$class}, must be instance of CompositableInterface"
80
                );
81
            }
82
83
            //Must pass value thought all needed filters
84
            $entity->stateValue($fields);
85
86
            return $entity;
87
        }
88
    }
89
90
    /**
91
     * Define document class using it's fieldset and definition.
92
     *
93
     * @param \ArrayAccess|array $fields
94
     *
95
     * @return string
96
     *
97
     * @throws DefinitionException
98
     */
99
    protected function defineClass($fields)
100
    {
101
        //Rule to define class instance
102
        $definition = $this->schema[DocumentEntity::SH_INSTANTIATION];
103
104
        if (is_string($definition)) {
105
            //Document has no variations
106
            return $definition;
107
        }
108
109
        if (!is_array($fields)) {
110
            //Unable to resolve for non array set, using same class as given
111
            return $this->class;
112
        }
113
114
        $defined = $this->class;
115
        foreach ($definition as $field => $child) {
116
            if (array_key_exists($field, $fields)) {
117
                //Apparently this is child
118
                $defined = $child;
119
                break;
120
            }
121
        }
122
123
        return $defined;
124
    }
125
}