Passed
Push — master ( 8bd912...d93388 )
by Alan
06:58 queued 02:20
created

src/Bridge/Doctrine/Common/PropertyHelperTrait.php (2 issues)

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