Passed
Pull Request — master (#2144)
by Alan
03:40
created

PropertyHelperTrait::isPropertyEmbedded()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
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 Doctrine\Common\Persistence\Mapping\ClassMetadata;
17
use Doctrine\DBAL\Types\Type;
18
19
/**
20
 * Helper trait for getting information regarding a property using the resource metadata.
21
 *
22
 * @author Kévin Dunglas <[email protected]>
23
 * @author Théo FIDRY <[email protected]>
24
 * @author Alan Poulain <[email protected]>
25
 */
26
trait PropertyHelperTrait
27
{
28
    protected $managerRegistry;
29
30
    /**
31
     * Determines whether the given property is mapped.
32
     */
33
    protected function isPropertyMapped(string $property, string $resourceClass, bool $allowAssociation = false): bool
34
    {
35
        if ($this->isPropertyNested($property, $resourceClass)) {
36
            $propertyParts = $this->splitPropertyParts($property, $resourceClass);
37
            $metadata = $this->getNestedMetadata($resourceClass, $propertyParts['associations']);
38
            $property = $propertyParts['field'];
39
        } else {
40
            $metadata = $this->getClassMetadata($resourceClass);
41
        }
42
43
        return $metadata->hasField($property) || ($allowAssociation && $metadata->hasAssociation($property));
44
    }
45
46
    /**
47
     * Determines whether the given property is nested.
48
     */
49
    protected function isPropertyNested(string $property/*, string $resourceClass*/): bool
50
    {
51
        if (\func_num_args() > 1) {
52
            $resourceClass = (string) func_get_arg(1);
53
        } else {
54
            if (__CLASS__ !== \get_class($this)) {
55
                $r = new \ReflectionMethod($this, __FUNCTION__);
56
                if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
57
                    @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);
58
                }
59
            }
60
            $resourceClass = null;
61
        }
62
63
        $pos = strpos($property, '.');
64
        if (false === $pos) {
65
            return false;
66
        }
67
68
        return null !== $resourceClass && $this->getClassMetadata($resourceClass)->hasAssociation(substr($property, 0, $pos));
69
    }
70
71
    /**
72
     * Determines whether the given property is embedded.
73
     */
74
    protected function isPropertyEmbedded(string $property, string $resourceClass): bool
75
    {
76
        return false !== strpos($property, '.') && $this->getClassMetadata($resourceClass)->hasField($property);
77
    }
78
79
    /**
80
     * Splits the given property into parts.
81
     *
82
     * Returns an array with the following keys:
83
     *   - associations: array of associations according to nesting order
84
     *   - field: string holding the actual field (leaf node)
85
     */
86
    protected function splitPropertyParts(string $property/*, string $resourceClass*/): array
87
    {
88
        $resourceClass = null;
89
        $parts = explode('.', $property);
90
91
        if (\func_num_args() > 1) {
92
            $resourceClass = func_get_arg(1);
93
        } else {
94
            if (__CLASS__ !== \get_class($this)) {
95
                $r = new \ReflectionMethod($this, __FUNCTION__);
96
                if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
97
                    @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);
98
                }
99
            }
100
        }
101
102
        if (null === $resourceClass) {
103
            return [
104
                'associations' => \array_slice($parts, 0, -1),
105
                'field' => end($parts),
106
            ];
107
        }
108
109
        $metadata = $this->getClassMetadata($resourceClass);
110
        $slice = 0;
111
112
        foreach ($parts as $part) {
113
            if ($metadata->hasAssociation($part)) {
114
                $metadata = $this->getClassMetadata($metadata->getAssociationTargetClass($part));
115
                ++$slice;
116
            }
117
        }
118
119
        if (\count($parts) === $slice) {
120
            --$slice;
121
        }
122
123
        return [
124
            'associations' => \array_slice($parts, 0, $slice),
125
            'field' => implode('.', \array_slice($parts, $slice)),
126
        ];
127
    }
128
129
    /**
130
     * Gets the Doctrine Type of a given property/resourceClass.
131
     *
132
     * @return Type|string|null
133
     */
134
    protected function getDoctrineFieldType(string $property, string $resourceClass)
135
    {
136
        $propertyParts = $this->splitPropertyParts($property, $resourceClass);
137
        $metadata = $this->getNestedMetadata($resourceClass, $propertyParts['associations']);
138
139
        return $metadata->getTypeOfField($propertyParts['field']);
140
    }
141
142
    /**
143
     * Gets nested class metadata for the given resource.
144
     *
145
     * @param string[] $associations
146
     */
147
    protected function getNestedMetadata(string $resourceClass, array $associations): ClassMetadata
148
    {
149
        $metadata = $this->getClassMetadata($resourceClass);
150
151
        foreach ($associations as $association) {
152
            if ($metadata->hasAssociation($association)) {
153
                $associationClass = $metadata->getAssociationTargetClass($association);
154
155
                $metadata = $this->getClassMetadata($associationClass);
156
            }
157
        }
158
159
        return $metadata;
160
    }
161
162
    /**
163
     * Gets class metadata for the given resource.
164
     */
165
    protected function getClassMetadata(string $resourceClass): ClassMetadata
166
    {
167
        return $this
168
            ->managerRegistry
169
            ->getManagerForClass($resourceClass)
170
            ->getClassMetadata($resourceClass);
171
    }
172
}
173