Passed
Pull Request — 2.4 (#2676)
by
unknown
04:36
created

PropertyHelperTrait::isPropertyEnabled()   C

Complexity

Conditions 13
Paths 84

Size

Total Lines 36
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 18
nc 84
nop 1
dl 0
loc 36
rs 6.6166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\Bridge\Doctrine\Common;
15
16
use ApiPlatform\Core\Exception\PropertyNotFoundException;
17
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
18
use Doctrine\Common\Persistence\ManagerRegistry;
19
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
20
use Doctrine\DBAL\Types\Type;
21
22
/**
23
 * Helper trait for getting information regarding a property using the resource metadata.
24
 *
25
 * @author Kévin Dunglas <[email protected]>
26
 * @author Théo FIDRY <[email protected]>
27
 * @author Alan Poulain <[email protected]>
28
 */
29
trait PropertyHelperTrait
30
{
31
    /** @var PropertyMetadataFactoryInterface|null */
32
    protected $propertyMetadataFactory;
33
34
    abstract protected function getManagerRegistry(): ManagerRegistry;
35
36
    /**
37
     * Determines whether the given property is enabled.
38
     */
39
    protected function isPropertyEnabled(string $property/*, string $resourceClass, array $context = []*/): bool
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
40
    {
41
        if (\func_num_args() < 3 && __CLASS__ !== \get_class($this) && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName()) {
42
            if (\func_num_args() < 2) {
43
                @trigger_error(sprintf('Method %s() will have a second `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', __FUNCTION__), E_USER_DEPRECATED);
44
            }
45
46
            @trigger_error(sprintf('Method %s() will have a third "$context" argument in API Platform 3.0. Not defining it is deprecated since API Platform 2.4.', __FUNCTION__), E_USER_DEPRECATED);
47
        }
48
49
        $resourceClass = 1 < \func_num_args() ? (string) func_get_arg(1) : null;
50
        $context = 2 < \func_num_args() ? (array) func_get_arg(2) : [];
51
52
        if (null !== $this->properties) {
53
            return \array_key_exists($property, $this->properties);
54
        }
55
56
        if (null !== $resourceClass && null !== $this->propertyMetadataFactory) {
57
            try {
58
                $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $property, $this->getFactoryOptions($context));
0 ignored issues
show
Bug introduced by
It seems like getFactoryOptions() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

58
                $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $property, $this->/** @scrutinizer ignore-call */ getFactoryOptions($context));
Loading history...
59
            } catch (PropertyNotFoundException $e) {
60
                return false;
61
            }
62
63
            // to ensure sanity, unreadable properties must still be explicitly enabled
64
            if (!$propertyMetadata->isReadable()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $propertyMetadata->isReadable() of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
65
                return false;
66
            }
67
        }
68
69
        // to ensure sanity, nested properties must still be explicitly enabled
70
        if (null === $resourceClass) {
71
            return !$this->isPropertyNested($property);
72
        }
73
74
        return !$this->isPropertyNested($property, $resourceClass);
75
    }
76
77
    /**
78
     * Determines whether the given property is mapped.
79
     */
80
    protected function isPropertyMapped(string $property, string $resourceClass, bool $allowAssociation = false): bool
81
    {
82
        if ($this->isPropertyNested($property, $resourceClass)) {
83
            $propertyParts = $this->splitPropertyParts($property, $resourceClass);
84
            $metadata = $this->getNestedMetadata($resourceClass, $propertyParts['associations']);
85
            $property = $propertyParts['field'];
86
        } else {
87
            $metadata = $this->getClassMetadata($resourceClass);
88
        }
89
90
        return $metadata->hasField($property) || ($allowAssociation && $metadata->hasAssociation($property));
91
    }
92
93
    /**
94
     * Determines whether the given property is nested.
95
     */
96
    protected function isPropertyNested(string $property/*, string $resourceClass*/): bool
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
97
    {
98
        if (\func_num_args() > 1) {
99
            $resourceClass = (string) func_get_arg(1);
100
        } else {
101
            if (__CLASS__ !== \get_class($this)) {
102
                $r = new \ReflectionMethod($this, __FUNCTION__);
103
                if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
104
                    @trigger_error(sprintf('Method %s() will have a second `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', __FUNCTION__), E_USER_DEPRECATED);
105
                }
106
            }
107
            $resourceClass = null;
108
        }
109
110
        $pos = strpos($property, '.');
111
        if (false === $pos) {
112
            return false;
113
        }
114
115
        return null !== $resourceClass && $this->getClassMetadata($resourceClass)->hasAssociation(substr($property, 0, $pos));
116
    }
117
118
    /**
119
     * Determines whether the given property is embedded.
120
     */
121
    protected function isPropertyEmbedded(string $property, string $resourceClass): bool
122
    {
123
        return false !== strpos($property, '.') && $this->getClassMetadata($resourceClass)->hasField($property);
124
    }
125
126
    /**
127
     * Splits the given property into parts.
128
     *
129
     * Returns an array with the following keys:
130
     *   - associations: array of associations according to nesting order
131
     *   - field: string holding the actual field (leaf node)
132
     */
133
    protected function splitPropertyParts(string $property/*, string $resourceClass*/): array
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
134
    {
135
        $resourceClass = null;
136
        $parts = explode('.', $property);
137
138
        if (\func_num_args() > 1) {
139
            $resourceClass = func_get_arg(1);
140
        } elseif (__CLASS__ !== \get_class($this)) {
141
            $r = new \ReflectionMethod($this, __FUNCTION__);
142
            if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
143
                @trigger_error(sprintf('Method %s() will have a second `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', __FUNCTION__), E_USER_DEPRECATED);
144
            }
145
        }
146
147
        if (null === $resourceClass) {
148
            return [
149
                'associations' => \array_slice($parts, 0, -1),
150
                'field' => end($parts),
151
            ];
152
        }
153
154
        $metadata = $this->getClassMetadata($resourceClass);
155
        $slice = 0;
156
157
        foreach ($parts as $part) {
158
            if ($metadata->hasAssociation($part)) {
159
                $metadata = $this->getClassMetadata($metadata->getAssociationTargetClass($part));
160
                ++$slice;
161
            }
162
        }
163
164
        if (\count($parts) === $slice) {
165
            --$slice;
166
        }
167
168
        return [
169
            'associations' => \array_slice($parts, 0, $slice),
170
            'field' => implode('.', \array_slice($parts, $slice)),
171
        ];
172
    }
173
174
    /**
175
     * Gets the Doctrine Type of a given property/resourceClass.
176
     *
177
     * @return Type|string|null
178
     */
179
    protected function getDoctrineFieldType(string $property, string $resourceClass)
180
    {
181
        $propertyParts = $this->splitPropertyParts($property, $resourceClass);
182
        $metadata = $this->getNestedMetadata($resourceClass, $propertyParts['associations']);
183
184
        return $metadata->getTypeOfField($propertyParts['field']);
185
    }
186
187
    /**
188
     * Gets nested class metadata for the given resource.
189
     *
190
     * @param string[] $associations
191
     */
192
    protected function getNestedMetadata(string $resourceClass, array $associations): ClassMetadata
193
    {
194
        $metadata = $this->getClassMetadata($resourceClass);
195
196
        foreach ($associations as $association) {
197
            if ($metadata->hasAssociation($association)) {
198
                $associationClass = $metadata->getAssociationTargetClass($association);
199
200
                $metadata = $this->getClassMetadata($associationClass);
201
            }
202
        }
203
204
        return $metadata;
205
    }
206
207
    /**
208
     * Gets class metadata for the given resource.
209
     */
210
    protected function getClassMetadata(string $resourceClass): ClassMetadata
211
    {
212
        return $this
213
            ->getManagerRegistry()
214
            ->getManagerForClass($resourceClass)
215
            ->getClassMetadata($resourceClass);
216
    }
217
}
218