Completed
Push — feature/EVO-8123-rql-select-up... ( 4de408...d2ec4c )
by
unknown
64:31
created

SelectExclusionStrategy::createArrayByPath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 10
ccs 0
cts 8
cp 0
rs 9.4285
cc 2
eloc 7
nc 2
nop 1
crap 6
1
<?php
2
/**
3
 * Class for exclusion strategies.
4
 */
5
namespace Graviton\RestBundle\ExclusionStrategy;
6
7
use JMS\Serializer\Exclusion\ExclusionStrategyInterface;
8
use JMS\Serializer\Metadata\ClassMetadata;
9
use JMS\Serializer\Metadata\PropertyMetadata;
10
use JMS\Serializer\Context;
11
use Symfony\Component\HttpFoundation\RequestStack;
12
use Xiag\Rql\Parser\Query;
13
use Xiag\Rql\Parser\Node\SelectNode;
14
15
/**
16
 * In this Strategy we skip all properties on first level who are not selected if there is a select in rql.
17
 *
18
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
19
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
20
 * @link     http://swisscom.ch
21
 */
22
class SelectExclusionStrategy implements ExclusionStrategyInterface
23
{
24
    /**
25
     * @var RequestStack $requestStack
26
     */
27
    protected $requestStack;
28
29
    /**
30
     * @var Boolean $isSelect
31
     */
32
    protected $isSelect;
33
34
    /**
35
     * @var array $currentPath
36
     */
37
    protected $currentPath;
38
39
    /**
40
     * @var array for selected tree level
41
     */
42
    protected $selectTree = [];
43
44
    /**
45
     * SelectExclusionStrategy constructor.
46
     * Comstructor Injection of the global request_stack to access the selected Fields via Query-Object
47
     * @param RequestStack $requestStack the global request_stack
48
     */
49 4
    public function __construct(RequestStack $requestStack)
50
    {
51 4
        $this->requestStack = $requestStack;
52 4
        $this->createSelectionTreeFromRQL();
53 4
    }
54
55
    /**
56
     * Convert dot string to array.
57
     *
58
     * @param string $path string dotted array
59
     *
60
     * @return array
61
     */
62
    private function createArrayByPath($path)
63
    {
64
        $arr = [];
65
        $scope = &$arr;
66
        $keys = explode('.', $path);
67
        while ($key = array_shift($keys)) {
68
            $scope = &$scope[$key];
69
        }
70
        return $arr;
71
    }
72
73
    /**
74
     * Initializing $this->selectedFields and $this->isSelect
75
     * getting the fields that should be really serialized and setting the switch that there is actually a select
76
     * called once in the object, so shouldSkipProperty can use the information for every field
77
     * @return void
78
     */
79
    private function createSelectionTreeFromRQL()
80
    {
81
        $currentRequest = $this->requestStack->getCurrentRequest();
82 4
        $this->selectTree = [];
83
        $this->currentPath = [];
84 4
        $this->isSelect = false;
85 4
86 4
        /** @var SelectNode $select */
87 4
        if ($currentRequest
88
            && ($rqlQuery = $currentRequest->get('rqlQuery')) instanceof Query
89
            && $select = $rqlQuery->getSelect()
90 2
        ) {
91 4
            $this->isSelect = true;
92 4
            // Build simple selected field tree
93 2
            foreach ($select->getFields() as $field) {
94
                $field = str_replace('$', '', $field);
95
                $arr = $this->createArrayByPath($field);
96
                $this->selectTree = array_merge_recursive($this->selectTree, $arr);
97
            }
98
            $this->selectTree['id'] = true;
99
        }
100
    }
101
102
    /**
103 4
     * @InheritDoc: Whether the class should be skipped.
104
     * @param ClassMetadata $metadata         the ClassMetadata for the Class of the property to be serialized
105
     * @param Context       $navigatorContext the context for serialization
106
     * @return boolean
107
     */
108
    public function shouldSkipClass(ClassMetadata $metadata, Context $navigatorContext)
109
    {
110
        return false;
111
    }
112
113
    /**
114
     * @InheritDoc: Whether the property should be skipped.
115
     * Skipping properties who are not selected if there is a select in rql.
116
     * @param PropertyMetadata $property the property to be serialized
117
     * @param Context          $context  the context for serialization
118
     * @return boolean
119
     */
120
    public function shouldSkipProperty(PropertyMetadata $property, Context $context)
121
    {
122
        // nothing selected, default serialization
123
        if (! $this->isSelect) {
124
            return false;
125
        }
126
127
        // Level starts at 1, so -1 to have it level 0
128
        $depth = $context->getDepth()-1;
129
130
        // Here we build a level based array so we get them all
131
        $this->currentPath[$depth] = $property->name;
132
        $keyPath = [];
133
        foreach ($this->currentPath as $key => $path) {
134
            if ($key > $depth && array_key_exists($key, $this->currentPath)) {
135
                unset($this->currentPath[$key]);
136
            } else {
137
                $keyPath[] = $path;
138
            }
139
        }
140
141
        // check path and parent/son should be seen.
142
        $tree = $this->selectTree;
143
        foreach ($keyPath as $path) {
144
            if (empty($tree)) {
145
                break;
146
            }
147
            if (array_key_exists($path, $tree)) {
148
                $tree = $tree[$path];
149
            } else {
150
                return true;
151
            }
152
        }
153
        return false;
154
    }
155
}
156