Completed
Push — master ( 8a0039...4684b6 )
by Maik
03:19
created

OrmMapping::assignPropertyValue()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 6
Bugs 1 Features 0
Metric Value
c 6
b 1
f 0
dl 0
loc 17
ccs 11
cts 11
cp 1
rs 9.2
cc 4
eloc 10
nc 4
nop 5
crap 4
1
<?php
2
namespace Nkey\Caribu\Orm;
3
4
trait OrmMapping
5
{
6
    use OrmAnnotation;
7
8
    /**
9
     * Map a object from default class into specific
10
     *
11
     * @param stdClass $from
12
     *            The unmapped data as stdClass object
13
     * @param string $toClass
14
     *            The name of class to map data into
15
     *            
16
     * @return object The new created object of $toClass containing the mapped data
17
     *        
18
     * @throws OrmException
19
     * @throws PDOException
20
     */
21 26
    private static function map($from, $toClass, Orm $orm)
22
    {
23 26
        $result = self::mapAnnotated($from, $toClass);
24
        
25 26
        self::mapReferenced($from, $toClass, $result);
26 26
        if (self::isEager($toClass)) {
27 5
            self::injectMappedBy($toClass, $result, $orm);
28 4
        }
29
        
30 25
        return $result;
31
    }
32
33
    /**
34
     * Map a referenced object into current mapped object
35
     *
36
     * @param object $from
37
     *            The unmapped object as stdClass
38
     * @param string $toClass
39
     *            The name of class where the mapped data will be stored into
40
     * @param AbstractModel $result
41
     *            The mapped entity
42
     */
43 26
    private static function mapReferenced($from, $toClass, $result)
44
    {
45
        try {
46 26
            $rfToClass = new \ReflectionClass($toClass);
47
            
48 26
            foreach (get_object_vars($from) as $property => $value) {
49 26
                if (! strpos($property, '.')) {
50 26
                    continue;
51
                }
52
                
53 3
                list ($toProperty, $column) = explode('.', $property);
54
                
55 3
                if (! $rfToClass->hasProperty($toProperty)) {
56
                    continue;
57
                }
58
                
59 3
                $referencedClass = self::getAnnotatedPropertyType($toClass, $toProperty, $rfToClass->getNamespaceName());
60 3
                $rfReferenced = new \ReflectionClass($referencedClass);
61
                
62 3
                $findMethod = $rfReferenced->getMethod("find");
63 3
                $referencedObject = $findMethod->invoke(null, array(
64
                    $column => $value
65 3
                ));
66
                
67 3
                $propertySetter = $rfToClass->getMethod(sprintf("set%s", ucfirst($toProperty)));
68
                
69 3
                $propertySetter->invoke($result, $referencedObject);
70 26
            }
71 26
        } catch (\ReflectionException $exception) {
72
            throw OrmException::fromPrevious($exception);
73
        }
74 26
    }
75
76
    /**
77
     * Inject the mappedBy annotated properties
78
     *
79
     * @param string $toClass
80
     *            The class of entity
81
     * @param AbstractModel $object
82
     *            Prefilled entity
83
     *            
84
     * @throws OrmException
85
     * @throws PDOException
86
     */
87 5
    private static function injectMappedBy($toClass, &$object, Orm $orm)
88
    {
89
        try {
90 5
            $rfToClass = new \ReflectionClass($toClass);
91
            
92 5
            foreach ($rfToClass->getProperties() as $property) {
93 5
                if (null === ($parameters = self::getAnnotatedMappedByParameters($property->getDocComment()))) {
94 5
                    continue;
95
                }
96
                
97 5
                $mappedBy = self::parseMappedBy($parameters);
98
                
99 5
                $type = self::getAnnotatedType($property->getDocComment(), $rfToClass->getNamespaceName());
100
                
101 4
                if (null === $type) {
102
                    throw new OrmException("Can't use mappedBy without specific type for property {property}", array(
103
                        'property' => $property->getName()
104
                    ));
105
                }
106
                
107 4
                if (self::isPrimitive($type)) {
108
                    throw new OrmException("Primitive type can not be used in mappedBy for property {property}", array(
109
                        'property' => $property->getName()
110
                    ));
111
                }
112
                
113 4
                $getMethod = new \ReflectionMethod($toClass, sprintf("get%s", ucfirst($property->getName())));
114 4
                if ($getMethod->invoke($object)) {
115 2
                    continue;
116
                }
117
                
118 2
                $ownPrimaryKey = self::getPrimaryKey($toClass, $object, true);
119
                
120 2
                $otherTable = self::getTableName($type);
121 2
                $otherPrimaryKeyName = self::getPrimaryKeyCol($type);
122 2
                $ownPrimaryKeyName = self::getPrimaryKeyCol($toClass);
123
                
124 2
                $query = sprintf("SELECT %s.* FROM %s
125
                        JOIN %s ON %s.%s = %s.%s
126 2
                        WHERE %s.%s = :%s", $otherTable, $otherTable, $mappedBy['table'], $mappedBy['table'], $mappedBy['column'], $otherTable, $otherPrimaryKeyName, $mappedBy['table'], $mappedBy['inverseColumn'], $ownPrimaryKeyName);
127
                
128 2
                $statement = null;
129
                
130
                try {
131 2
                    $statement = $orm->startTX()->prepare($query);
132 2
                    $statement->bindValue(sprintf(":%s", $ownPrimaryKeyName), $ownPrimaryKey);
133
                    
134 2
                    $statement->execute();
135
                    
136 2
                    $result = $statement->fetch(\PDO::FETCH_OBJ);
137
                    
138 2
                    if (false == $result) {
139
                        throw new OrmException("No foreign entity found for {entity} using primary key {pk}", array(
140
                            'entity' => $toClass,
141
                            'pk' => $$ownPrimaryKey
142
                        ));
143
                    }
144
                    
145 2
                    $orm->commitTX();
146
                    
147 2
                    $setMethod = new \ReflectionMethod($toClass, sprintf("set%s", ucfirst($property->getName())));
148
                    
149 2
                    $setMethod->invoke($object, self::map($result, $type, $orm));
150 2
                } catch (\PDOException $exception) {
151
                    throw self::handleException($orm, $statement, $exception, "Mapping failed", - 1010);
152
                }
153 4
            }
154 5
        } catch (\ReflectionException $exception) {
155
            throw OrmException::fromPrevious($exception);
156
        }
157 4
    }
158
159
    /**
160
     * Map default class object into specific by annotation
161
     *
162
     * @param object $from
163
     *            The unmapped dataset
164
     * @param string $toClass
165
     *            The name of class where to map data in
166
     *            
167
     * @return AbstractModel The mapped data as entity
168
     *        
169
     * @throws OrmException
170
     */
171 26
    private static function mapAnnotated($from, $toClass)
172
    {
173
        try {
174 26
            $resultClass = new \ReflectionClass($toClass);
175
            
176 26
            $rf = new \ReflectionObject($from);
177
            
178 26
            $result = $resultClass->newInstanceWithoutConstructor();
179
            
180 26
            $properties = $rf->getProperties();
181 26
            foreach ($properties as $property) {
182
                // attached property by annotation mapping => map later
183 26
                if (strpos($property->getName(), '.')) {
184 3
                    continue;
185
                }
186
                
187 26
                list ($type, $value) = self::getAnnotatedPropertyValue($from, $toClass, $property, $rf->getNamespaceName());
188
                
189 26
                $result = self::assignPropertyValue($result, $resultClass, $property->getName(), $type, $value);
190 26
            }
191
            
192 26
            return $result;
193
        } catch (\ReflectionException $exception) {
194
            throw OrmException::fromPrevious($exception);
195
        }
196
    }
197
198
    /**
199
     * Assign the property value to result object via annotation
200
     *
201
     * @param object $result            
202
     * @param \ReflectionClass $resultClass            
203
     * @param string $propertyName            
204
     * @param string $type            
0 ignored issues
show
Bug introduced by
There is no parameter named $type. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
205
     * @param mixed $value            
206
     *
207
     * @return boolean Whether to continue assigning
208
     */
209 21
    private static function assignAnnotatedPropertyValue($result, $resultClassProperty, $resultClass, $propertyName, $value)
210
    {
211 21
        $docComments = $resultClassProperty->getDocComment();
212
        
213 21
        $type = self::getAnnotatedType($docComments, $resultClass->getNamespaceName());
214
        
215 21
        if (null === ($destinationProperty = self::getAnnotatedColumn($docComments)) || $destinationProperty !== $propertyName || null === $type) {
216 3
            return true;
217
        }
218
        
219 19
        if (! self::isPrimitive($type) && class_exists($type) && ! $value instanceof $type) {
220 1
            return false;
221
        }
222
        
223 18
        $method = sprintf("set%s", ucfirst($resultClassProperty->getName()));
224 18
        if ($resultClass->hasMethod($method)) {
225 18
            $rfMethod = new \ReflectionMethod($resultClass->name, $method);
226 18
            $rfMethod->invoke($result, $value);
227 18
            return false;
228
        }
229
        
230
        return true;
231
    }
232
233
    /**
234
     * Assign the property value to result object
235
     *
236
     * @param object $result            
237
     * @param \ReflectionClass $resultClass            
238
     * @param string $propertyName            
239
     * @param string $type            
240
     * @param mixed $value            
241
     *
242
     * @return object The assigned result object
243
     */
244 26
    private static function assignPropertyValue($result, \ReflectionClass $resultClass, $propertyName, $type, $value)
245
    {
246 26
        $method = sprintf("set%s", ucfirst($propertyName));
247
        
248 26
        if ($resultClass->hasMethod($method)) {
249 26
            $rfMethod = new \ReflectionMethod($resultClass->name, $method);
250 26
            $rfMethod->invoke($result, self::convertType($type, $value));
251 26
        } else {
252 21
            foreach ($resultClass->getProperties() as $resultClassProperty) {
253 21
                if (! self::assignAnnotatedPropertyValue($result, $resultClassProperty, $resultClass, $propertyName, $value)) {
254 19
                    break;
255
                }
256 21
            }
257
        }
258
        
259 26
        return $result;
260
    }
261
262
    /**
263
     * Parse the @mappedBy annotation
264
     *
265
     * @param string $mappedBy
266
     *            The mappedBy annotation string
267
     *            
268
     * @return array All parsed property attributes of the mappedBy string
269
     */
270 5
    private static function parseMappedBy($mappedBy)
271
    {
272 5
        $mappingOptions = array();
273 5
        foreach (explode(',', $mappedBy) as $mappingOption) {
274 5
            list ($option, $value) = preg_split('/=/', $mappingOption);
275 5
            $mappingOptions[$option] = $value;
276 5
        }
277
        
278 5
        return $mappingOptions;
279
    }
280
}
281