Passed
Push — master ( 0ea13e...268b05 )
by Anton
01:37
created

PivotedPromise::setConstrain()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Cycle\ORM\Relation\Pivoted;
10
11
use Cycle\ORM\Exception\ORMException;
12
use Cycle\ORM\Heap\Node;
13
use Cycle\ORM\Iterator;
14
use Cycle\ORM\ORMInterface;
15
use Cycle\ORM\Parser\RootNode;
16
use Cycle\ORM\Promise\PromiseInterface;
17
use Cycle\ORM\Relation;
18
use Cycle\ORM\Select;
19
use Cycle\ORM\Select\JoinableLoader;
20
use Cycle\ORM\Select\Loader\ManyToManyLoader;
21
22
/**
23
 * Promise use loader to configure query and it's scope.
24
 */
25
class PivotedPromise implements PromiseInterface
26
{
27
    /** @var ORMInterface */
28
    private $orm;
29
30
    /** @var string */
31
    private $target;
32
33
    /** @var array */
34
    private $relationSchema = [];
35
36
    /** @var mixed */
37
    private $innerKey;
38
39
    /** @var null|PivotedStorage */
40
    private $resolved;
41
42
    /**
43
     * @param ORMInterface $orm
44
     * @param string       $target
45
     * @param array        $relationSchema
46
     * @param mixed        $innerKey
47
     */
48
    public function __construct(ORMInterface $orm, string $target, array $relationSchema, $innerKey)
49
    {
50
        $this->orm = $orm;
51
        $this->target = $target;
52
        $this->relationSchema = $relationSchema;
53
        $this->innerKey = $innerKey;
54
    }
55
56
    /**
57
     * @inheritdoc
58
     */
59
    public function __loaded(): bool
60
    {
61
        return empty($this->orm);
62
    }
63
64
    /**
65
     * @inheritdoc
66
     */
67
    public function __role(): string
68
    {
69
        return $this->target;
70
    }
71
72
    /**
73
     * @inheritdoc
74
     */
75
    public function __scope(): array
76
    {
77
        return [
78
            $this->relationSchema[Relation::INNER_KEY] => $this->innerKey
79
        ];
80
    }
81
82
    /**
83
     * @return PivotedStorage
84
     */
85
    public function __resolve()
86
    {
87
        if (is_null($this->orm)) {
88
            return $this->resolved;
89
        }
90
91
        if (!$this->orm instanceof Select\SourceProviderInterface) {
0 ignored issues
show
introduced by
$this->orm is always a sub-type of Cycle\ORM\Select\SourceProviderInterface.
Loading history...
92
            throw new ORMException("PivotedPromise require ORM to implement SourceFactoryInterface");
93
        }
94
95
        $table = $this->orm->getSource($this->target)->getTable();
96
97
        // getting scoped query
98
        $root = new Select\RootLoader($this->orm, $this->target);
99
        $query = $root->buildQuery();
100
101
        // responsible for all the scoping
102
        $loader = new ManyToManyLoader($this->orm, $table, $this->target, $this->relationSchema);
103
104
        /** @var ManyToManyLoader $loader */
105
        $loader = $loader->withContext($loader, [
106
            'constrain' => $this->orm->getSource($this->target)->getConstrain(),
107
            'as'        => $table,
108
            'method'    => JoinableLoader::POSTLOAD
109
        ]);
110
111
        $query = $loader->configureQuery($query, [$this->innerKey]);
112
113
        // we are going to add pivot node into virtual root node to aggregate the results
114
        $root = new RootNode([$this->relationSchema[Relation::INNER_KEY]], $this->relationSchema[Relation::INNER_KEY]);
115
116
        $node = $loader->createNode();
117
        $root->linkNode('output', $node);
118
119
        // emulate presence of parent entity
120
        $root->parseRow(0, [$this->innerKey]);
121
122
        $iterator = $query->getIterator();
123
        foreach ($iterator as $row) {
124
            $node->parseRow(0, $row);
125
        }
126
        $iterator->close();
127
128
        $elements = [];
129
        $pivotData = new \SplObjectStorage();
130
        foreach (new Iterator($this->orm, $this->target, $root->getResult()[0]['output']) as $pivot => $entity) {
131
            $pivotData[$entity] = $this->orm->make(
132
                $this->relationSchema[Relation::THOUGH_ENTITY],
133
                $pivot,
134
                Node::MANAGED
135
            );
136
137
            $elements[] = $entity;
138
        }
139
140
        $this->resolved = new PivotedStorage($elements, $pivotData);
141
        $this->orm = null;
142
143
        return $this->resolved;
144
    }
145
}