Completed
Pull Request — develop (#513)
by
unknown
05:30
created

getSelectedFieldsFromRQL()   C

Complexity

Conditions 8
Paths 9

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 22.916

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 31
rs 5.3846
ccs 10
cts 26
cp 0.3846
cc 8
eloc 19
nc 9
nop 0
crap 22.916

1 Method

Rating   Name   Duplication   Size   Complexity  
B SelectExclusionStrategy::createSelectionTreeFromRQL() 0 23 5
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 array  $arr  output array
59
     * @param string $path string dotet array
60
     *
61
     * @return void
62
     */
63
    private function createArrayByPath(&$arr, $path)
64
    {
65
        $keys = explode('.', $path);
66
        while ($key = array_shift($keys)) {
67
            $arr = &$arr[$key];
68
        }
69
    }
70
71
    /**
72
     * Initializing $this->selectedFields and $this->isSelect
73
     * getting the fields that should be really serialized and setting the switch that there is actually a select
74
     * called once in the object, so shouldSkipProperty can use the information for every field
75
     * @return void
76
     */
77 4
    private function createSelectionTreeFromRQL()
78
    {
79 4
        $currentRequest = $this->requestStack->getCurrentRequest();
80 4
        $this->selectTree = [];
81 4
        $this->currentPath = [];
82 4
        $this->isSelect = false;
83
84
        /** @var SelectNode $select */
85 2
        if ($currentRequest
86 4
            && ($rqlQuery = $currentRequest->get('rqlQuery')) instanceof Query
87 4
            && $select = $rqlQuery->getSelect()
88 2
        ) {
89
            $this->isSelect = true;
90
            // Build simple selected field tree
91
            foreach ($select->getFields() as $field) {
92
                $field = str_replace('$', '', $field);
93
                $arr = [];
94
                $this->createArrayByPath($arr, $field);
95
                $this->selectTree = array_merge_recursive($this->selectTree, $arr);
96
            }
97
            $this->selectTree['id'] = true;
98
        }
99 4
    }
100
101
    /**
102
     * @InheritDoc: Whether the class should be skipped.
103
     * @param ClassMetadata $metadata         the ClassMetadata for the Class of the property to be serialized
104
     * @param Context       $navigatorContext the context for serialization
105
     * @return boolean
106
     */
107
    public function shouldSkipClass(ClassMetadata $metadata, Context $navigatorContext)
108
    {
109
        return false;
110
    }
111
112
    /**
113
     * @InheritDoc: Whether the property should be skipped.
114
     * Skipping properties who are not selected if there is a select in rql.
115
     * @param PropertyMetadata $property the property to be serialized
116
     * @param Context          $context  the context for serialization
117
     * @return boolean
118
     */
119
    public function shouldSkipProperty(PropertyMetadata $property, Context $context)
120
    {
121
        // nothing selected, default serialization
122
        if (! $this->isSelect) {
123
            return false;
124
        }
125
126
        // Level starts at 1, so -1 to have it level 0
127
        $depth = $context->getDepth()-1;
128
129
        // Here we build a level based array so we get them all
130
        $this->currentPath[$depth] = $property->name;
131
        $keyPath = [];
132
        foreach ($this->currentPath as $key => $path) {
133
            if ($key > $depth && array_key_exists($key, $this->currentPath)) {
134
                unset($this->currentPath[$key]);
135
            } else {
136
                $keyPath[] = $path;
137
            }
138
        }
139
140
        // check path and parent/son should be seen.
141
        $tree = $this->selectTree;
142
        foreach ($keyPath as $path) {
143
            if (empty($tree)) {
144
                break;
145
            }
146
            if (array_key_exists($path, $tree)) {
147
                $tree = $tree[$path];
148
            } else {
149
                return true;
150
            }
151
        }
152
        return false;
153
    }
154
}
155