Completed
Push — master ( 854c26...66addc )
by Tom
15s
created

DoctrineObject::isNullable()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 10.8579

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 4
cts 7
cp 0.5714
rs 8.8333
c 0
b 0
f 0
cc 7
nc 6
nop 1
crap 10.8579
1
<?php
2
3
namespace DoctrineModule\Stdlib\Hydrator;
4
5
use DateTime;
6
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
7
use Doctrine\Common\Persistence\ObjectManager;
8
use Doctrine\Common\Inflector\Inflector;
9
use InvalidArgumentException;
10
use RuntimeException;
11
use Traversable;
12
use Zend\Stdlib\ArrayUtils;
13
use Zend\Hydrator\AbstractHydrator;
14
use Zend\Hydrator\Filter\FilterProviderInterface;
15
16
/**
17
 * This hydrator has been completely refactored for DoctrineModule 0.7.0. It provides an easy and powerful way
18
 * of extracting/hydrator objects in Doctrine, by handling most associations types.
19
 *
20
 * Starting from DoctrineModule 0.8.0, the hydrator can be used multiple times with different objects
21
 *
22
 * @license MIT
23
 * @link    http://www.doctrine-project.org/
24
 * @since   0.7.0
25
 * @author  Michael Gallego <[email protected]>
26
 */
27
class DoctrineObject extends AbstractHydrator
28
{
29
    /**
30
     * @var ObjectManager
31
     */
32
    protected $objectManager;
33
34
    /**
35
     * @var ClassMetadata
36
     */
37
    protected $metadata;
38
39
    /**
40
     * @var bool
41
     */
42
    protected $byValue = true;
43
44
    /**
45
     * @var string
46
     */
47
    protected $defaultByValueStrategy = __NAMESPACE__ . '\Strategy\AllowRemoveByValue';
48
49
    /**
50
     * @var string
51
     */
52
    protected $defaultByReferenceStrategy = __NAMESPACE__ . '\Strategy\AllowRemoveByReference';
53
54
55
    /**
56
     * Constructor
57
     *
58
     * @param ObjectManager $objectManager The ObjectManager to use
59
     * @param bool          $byValue       If set to true, hydrator will always use entity's public API
60
     */
61 73
    public function __construct(ObjectManager $objectManager, $byValue = true)
62
    {
63 73
        parent::__construct();
64
65 73
        $this->objectManager = $objectManager;
66 73
        $this->byValue       = (bool) $byValue;
67 73
    }
68
69
    /**
70
     * @return string
71
     */
72 14
    public function getDefaultByValueStrategy()
73
    {
74 14
        return $this->defaultByValueStrategy;
75
    }
76
77
    /**
78
     * @param string $defaultByValueStrategy
79
     * @return DoctrineObject
80
     */
81 1
    public function setDefaultByValueStrategy($defaultByValueStrategy)
82
    {
83 1
        $this->defaultByValueStrategy = $defaultByValueStrategy;
84 1
        return $this;
85
    }
86
87
    /**
88
     * @return string
89
     */
90 9
    public function getDefaultByReferenceStrategy()
91
    {
92 9
        return $this->defaultByReferenceStrategy;
93
    }
94
95
    /**
96
     * @param string $defaultByReferenceStrategy
97
     * @return DoctrineObject
98
     */
99 1
    public function setDefaultByReferenceStrategy($defaultByReferenceStrategy)
100
    {
101 1
        $this->defaultByReferenceStrategy = $defaultByReferenceStrategy;
102 1
        return $this;
103
    }
104
105
    /**
106
     * Extract values from an object
107
     *
108
     * @param  object $object
109
     * @return array
110
     */
111 17
    public function extract($object)
112
    {
113 17
        $this->prepare($object);
114
115 17
        if ($this->byValue) {
116 10
            return $this->extractByValue($object);
117
        }
118
119 7
        return $this->extractByReference($object);
120
    }
121
122
    /**
123
     * Hydrate $object with the provided $data.
124
     *
125
     * @param  array  $data
126
     * @param  object $object
127
     * @return object
128
     */
129 57
    public function hydrate(array $data, $object)
130
    {
131 57
        $this->prepare($object);
132
133 57
        if ($this->byValue) {
134 43
            return $this->hydrateByValue($data, $object);
135
        }
136
137 29
        return $this->hydrateByReference($data, $object);
138
    }
139
140
    /**
141
     * Prepare the hydrator by adding strategies to every collection valued associations
142
     *
143
     * @param  object $object
144
     * @return void
145
     */
146 73
    protected function prepare($object)
147
    {
148 73
        $this->metadata = $this->objectManager->getClassMetadata(get_class($object));
149 73
        $this->prepareStrategies();
150 73
    }
151
152
    /**
153
     * Prepare strategies before the hydrator is used
154
     *
155
     * @throws \InvalidArgumentException
156
     * @return void
157
     */
158 73
    protected function prepareStrategies()
159
    {
160 73
        $associations = $this->metadata->getAssociationNames();
161
162 73
        foreach ($associations as $association) {
163 37
            if ($this->metadata->isCollectionValuedAssociation($association)) {
164
                // Add a strategy if the association has none set by user
165 23
                if (! $this->hasStrategy($association)) {
166 21
                    if ($this->byValue) {
167 14
                        $strategyClassName = $this->getDefaultByValueStrategy();
168
                    } else {
169 9
                        $strategyClassName = $this->getDefaultByReferenceStrategy();
170
                    }
171
172 21
                    $this->addStrategy($association, new $strategyClassName());
173
                }
174
175 23
                $strategy = $this->getStrategy($association);
176
177 23
                if (! $strategy instanceof Strategy\AbstractCollectionStrategy) {
178
                    throw new InvalidArgumentException(
179
                        sprintf(
180
                            'Strategies used for collections valued associations must inherit from '
181
                            . 'Strategy\AbstractCollectionStrategy, %s given',
182
                            get_class($strategy)
183
                        )
184
                    );
185
                }
186
187 23
                $strategy->setCollectionName($association)
188 37
                         ->setClassMetadata($this->metadata);
189
            }
190
        }
191 73
    }
192
193
    /**
194
     * Extract values from an object using a by-value logic (this means that it uses the entity
195
     * API, in this case, getters)
196
     *
197
     * @param  object $object
198
     * @throws RuntimeException
199
     * @return array
200
     */
201 10
    protected function extractByValue($object)
202
    {
203 10
        $fieldNames = array_merge($this->metadata->getFieldNames(), $this->metadata->getAssociationNames());
204 10
        $methods    = get_class_methods($object);
205 10
        $filter     = $object instanceof FilterProviderInterface
206
            ? $object->getFilter()
207 10
            : $this->filterComposite;
208
209 10
        $data = [];
210 10
        foreach ($fieldNames as $fieldName) {
211 10
            if ($filter && ! $filter->filter($fieldName)) {
212 1
                continue;
213
            }
214
215 10
            $getter = 'get' . Inflector::classify($fieldName);
216 10
            $isser  = 'is' . Inflector::classify($fieldName);
217
218 10
            $dataFieldName = $this->computeExtractFieldName($fieldName);
219 10
            if (in_array($getter, $methods)) {
220 10
                $data[$dataFieldName] = $this->extractValue($fieldName, $object->$getter(), $object);
221 1
            } elseif (in_array($isser, $methods)) {
222 1
                $data[$dataFieldName] = $this->extractValue($fieldName, $object->$isser(), $object);
223
            } elseif (substr($fieldName, 0, 2) === 'is'
224
                && ctype_upper(substr($fieldName, 2, 1))
225
                && in_array($fieldName, $methods)) {
226 10
                $data[$dataFieldName] = $this->extractValue($fieldName, $object->$fieldName(), $object);
227
            }
228
229
            // Unknown fields are ignored
230
        }
231
232 10
        return $data;
233
    }
234
235
    /**
236
     * Extract values from an object using a by-reference logic (this means that values are
237
     * directly fetched without using the public API of the entity, in this case, getters)
238
     *
239
     * @param  object $object
240
     * @return array
241
     */
242 7
    protected function extractByReference($object)
243
    {
244 7
        $fieldNames = array_merge($this->metadata->getFieldNames(), $this->metadata->getAssociationNames());
245 7
        $refl       = $this->metadata->getReflectionClass();
246 7
        $filter     = $object instanceof FilterProviderInterface
247
            ? $object->getFilter()
248 7
            : $this->filterComposite;
249
250 7
        $data = [];
251 7
        foreach ($fieldNames as $fieldName) {
252 7
            if ($filter && ! $filter->filter($fieldName)) {
253 1
                continue;
254
            }
255 7
            $reflProperty = $refl->getProperty($fieldName);
256 7
            $reflProperty->setAccessible(true);
257
258 7
            $dataFieldName        = $this->computeExtractFieldName($fieldName);
259 7
            $data[$dataFieldName] = $this->extractValue($fieldName, $reflProperty->getValue($object), $object);
260
        }
261
262 7
        return $data;
263
    }
264
265
    /**
266
     * Converts a value for hydration
267
     * Apply strategies first, then the type conversions
268
     *
269
     * @inheritdoc
270
     */
271 54
    public function hydrateValue($name, $value, $data = null)
272
    {
273 54
        $value = parent::hydrateValue($name, $value, $data);
274
275 54
        if (is_null($value) && $this->isNullable($name)) {
276
            return null;
277
        }
278
279 54
        return $this->handleTypeConversions($value, $this->metadata->getTypeOfField($name));
280
    }
281
282
    /**
283
     * Hydrate the object using a by-value logic (this means that it uses the entity API, in this
284
     * case, setters)
285
     *
286
     * @param  array  $data
287
     * @param  object $object
288
     * @throws RuntimeException
289
     * @return object
290
     */
291 44
    protected function hydrateByValue(array $data, $object)
292
    {
293 44
        $tryObject = $this->tryConvertArrayToObject($data, $object);
294 44
        $metadata  = $this->metadata;
295
296 44
        if (is_object($tryObject)) {
297 41
            $object = $tryObject;
298
        }
299
300 44
        foreach ($data as $field => $value) {
301 42
            $field  = $this->computeHydrateFieldName($field);
302 42
            $setter = 'set' . Inflector::classify($field);
303
304 42
            if ($metadata->hasAssociation($field)) {
305 18
                $target = $metadata->getAssociationTargetClass($field);
306
307 18
                if ($metadata->isSingleValuedAssociation($field)) {
308 7
                    if (! is_callable([$object, $setter])) {
309
                        continue;
310
                    }
311
312 7
                    $value = $this->toOne($target, $this->hydrateValue($field, $value, $data));
313
314 7
                    if (null === $value
315 7
                        && ! current($metadata->getReflectionClass()->getMethod($setter)->getParameters())->allowsNull()
316
                    ) {
317 1
                        continue;
318
                    }
319
320 6
                    $object->$setter($value);
321 11
                } elseif ($metadata->isCollectionValuedAssociation($field)) {
322 17
                    $this->toMany($object, $field, $target, $value);
323
                }
324
            } else {
325 27
                if (! is_callable([$object, $setter])) {
326 1
                    continue;
327
                }
328
329 40
                $object->$setter($this->hydrateValue($field, $value, $data));
330
            }
331
        }
332
333 44
        return $object;
334
    }
335
336
    /**
337
     * Hydrate the object using a by-reference logic (this means that values are modified directly without
338
     * using the public API, in this case setters, and hence override any logic that could be done in those
339
     * setters)
340
     *
341
     * @param  array  $data
342
     * @param  object $object
343
     * @return object
344
     */
345 30
    protected function hydrateByReference(array $data, $object)
346
    {
347 30
        $tryObject = $this->tryConvertArrayToObject($data, $object);
348 30
        $metadata  = $this->metadata;
349 30
        $refl      = $metadata->getReflectionClass();
350
351 30
        if (is_object($tryObject)) {
352 29
            $object = $tryObject;
353
        }
354
355 30
        foreach ($data as $field => $value) {
356 28
            $field = $this->computeHydrateFieldName($field);
357
358
            // Ignore unknown fields
359 28
            if (! $refl->hasProperty($field)) {
360
                continue;
361
            }
362
363 28
            $reflProperty = $refl->getProperty($field);
364 28
            $reflProperty->setAccessible(true);
365
366 28
            if ($metadata->hasAssociation($field)) {
367 11
                $target = $metadata->getAssociationTargetClass($field);
368
369 11
                if ($metadata->isSingleValuedAssociation($field)) {
370 5
                    $value = $this->toOne($target, $this->hydrateValue($field, $value, $data));
371 5
                    $reflProperty->setValue($object, $value);
372 6
                } elseif ($metadata->isCollectionValuedAssociation($field)) {
373 11
                    $this->toMany($object, $field, $target, $value);
374
                }
375
            } else {
376 28
                $reflProperty->setValue($object, $this->hydrateValue($field, $value, $data));
377
            }
378
        }
379
380 30
        return $object;
381
    }
382
383
    /**
384
     * This function tries, given an array of data, to convert it to an object if the given array contains
385
     * an identifier for the object. This is useful in a context of updating existing entities, without ugly
386
     * tricks like setting manually the existing id directly into the entity
387
     *
388
     * @param  array  $data   The data that may contain identifiers keys
389
     * @param  object $object
390
     * @return object
391
     */
392 57
    protected function tryConvertArrayToObject($data, $object)
393
    {
394 57
        $metadata         = $this->metadata;
395 57
        $identifierNames  = $metadata->getIdentifierFieldNames($object);
0 ignored issues
show
Unused Code introduced by
The call to ClassMetadata::getIdentifierFieldNames() has too many arguments starting with $object.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
396 57
        $identifierValues = [];
397
398 57
        if (empty($identifierNames)) {
399 31
            return $object;
400
        }
401
402 26
        foreach ($identifierNames as $identifierName) {
403 26
            if (! isset($data[$identifierName])) {
404 22
                return $object;
405
            }
406
407 4
            $identifierValues[$identifierName] = $data[$identifierName];
408
        }
409
410 4
        return $this->find($identifierValues, $metadata->getName());
411
    }
412
413
    /**
414
     * Handle ToOne associations
415
     *
416
     * When $value is an array but is not the $target's identifiers, $value is
417
     * most likely an array of fieldset data. The identifiers will be determined
418
     * and a target instance will be initialized and then hydrated. The hydrated
419
     * target will be returned.
420
     *
421
     * @param  string $target
422
     * @param  mixed  $value
423
     * @return object
424
     */
425 12
    protected function toOne($target, $value)
426
    {
427 12
        $metadata = $this->objectManager->getClassMetadata($target);
428
429 12
        if (is_array($value) && array_keys($value) != $metadata->getIdentifier()) {
430
            // $value is most likely an array of fieldset data
431 1
            $identifiers = array_intersect_key(
432 1
                $value,
433 1
                array_flip($metadata->getIdentifier())
434
            );
435 1
            $object      = $this->find($identifiers, $target) ?: new $target;
436 1
            return $this->hydrate($value, $object);
437
        }
438
439 11
        return $this->find($value, $target);
440
    }
441
442
    /**
443
     * Handle ToMany associations. In proper Doctrine design, Collections should not be swapped, so
444
     * collections are always handled by reference. Internally, every collection is handled using specials
445
     * strategies that inherit from AbstractCollectionStrategy class, and that add or remove elements but without
446
     * changing the collection of the object
447
     *
448
     * @param  object $object
449
     * @param  mixed  $collectionName
450
     * @param  string $target
451
     * @param  mixed  $values
452
     *
453
     * @throws \InvalidArgumentException
454
     *
455
     * @return void
456
     */
457 17
    protected function toMany($object, $collectionName, $target, $values)
458
    {
459 17
        $metadata   = $this->objectManager->getClassMetadata(ltrim($target, '\\'));
460 17
        $identifier = $metadata->getIdentifier();
461
462 17
        if (! is_array($values) && ! $values instanceof Traversable) {
463
            $values = (array)$values;
464
        }
465
466 17
        $collection = [];
467
468
        // If the collection contains identifiers, fetch the objects from database
469 17
        foreach ($values as $value) {
470 17
            if ($value instanceof $target) {
471
                // assumes modifications have already taken place in object
472 7
                $collection[] = $value;
473 7
                continue;
474
            } elseif (empty($value)) {
475
                // assumes no id and retrieves new $target
476 1
                $collection[] = $this->find($value, $target);
477 1
                continue;
478
            }
479
480 9
            $find = [];
481 9
            if (is_array($identifier)) {
482 9
                foreach ($identifier as $field) {
483 9
                    switch (gettype($value)) {
484 9
                        case 'object':
485 1
                            $getter = 'get' . Inflector::classify($field);
486
487 1
                            if (is_callable([$value, $getter])) {
488
                                $find[$field] = $value->$getter();
489 1
                            } elseif (property_exists($value, $field)) {
490 1
                                $find[$field] = $value->$field;
491
                            }
492 1
                            break;
493 8
                        case 'array':
494 5
                            if (array_key_exists($field, $value) && $value[$field] != null) {
495 5
                                $find[$field] = $value[$field];
496 5
                                unset($value[$field]); // removed identifier from persistable data
497
                            }
498 5
                            break;
499
                        default:
500 3
                            $find[$field] = $value;
501 9
                            break;
502
                    }
503
                }
504
            }
505
506 9
            if (! empty($find) && $found = $this->find($find, $target)) {
507 9
                $collection[] = (is_array($value)) ? $this->hydrate($value, $found) : $found;
508
            } else {
509 9
                $collection[] = (is_array($value)) ? $this->hydrate($value, new $target) : new $target;
510
            }
511
        }
512
513 17
        $collection = array_filter(
514 17
            $collection,
515
            function ($item) {
516 17
                return null !== $item;
517 17
            }
518
        );
519
520
        // Set the object so that the strategy can extract the Collection from it
521
522
        /** @var \DoctrineModule\Stdlib\Hydrator\Strategy\AbstractCollectionStrategy $collectionStrategy */
523 17
        $collectionStrategy = $this->getStrategy($collectionName);
524 17
        $collectionStrategy->setObject($object);
525
526
        // We could directly call hydrate method from the strategy, but if people want to override
527
        // hydrateValue function, they can do it and do their own stuff
528 17
        $this->hydrateValue($collectionName, $collection, $values);
529 17
    }
530
531
    /**
532
     * Handle various type conversions that should be supported natively by Doctrine (like DateTime)
533
     * See Documentation of Doctrine Mapping Types for defaults
534
     *
535
     * @link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#doctrine-mapping-types
536
     * @param  mixed  $value
537
     * @param  string $typeOfField
538
     * @return DateTime
539
     */
540 54
    protected function handleTypeConversions($value, $typeOfField)
541
    {
542 54
        if (is_null($value)) {
543 6
            return null;
544
        }
545
546 48
        switch ($typeOfField) {
547 48
            case 'boolean':
548 1
                $value = (bool)$value;
549 1
                break;
550 47
            case 'string':
551 38
            case 'text':
552 37
            case 'bigint':
553 36
            case 'decimal':
554 16
                $value = (string)$value;
555 16
                break;
556 35
            case 'integer':
557 32
            case 'smallint':
558 5
                $value = (int)$value;
559 5
                break;
560 31
            case 'float':
561 1
                $value = (double)$value;
562 1
                break;
563 30
            case 'datetimetz':
564 29
            case 'datetime':
565 26
            case 'time':
566 25
            case 'date':
567 6
                if ($value === '') {
568 1
                    return null;
569
                }
570
571 5
                if ($value instanceof Datetime) {
572 4
                    return $value;
573
                }
574
575 5
                if (is_int($value)) {
576 5
                    $dateTime = new DateTime();
577 5
                    $dateTime->setTimestamp($value);
578 5
                    return $dateTime;
579
                }
580
581 4
                if (is_string($value)) {
582 4
                    return new DateTime($value);
583
                }
584
585
                break;
586
            default:
587 24
                break;
588
        }
589
590 42
        return $value;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $value; (boolean|string|integer|double|object|array) is incompatible with the return type documented by DoctrineModule\Stdlib\Hy...::handleTypeConversions of type DateTime|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
591
    }
592
593
    /**
594
     * Find an object by a given target class and identifier
595
     *
596
     * @param  mixed   $identifiers
597
     * @param  string  $targetClass
598
     *
599
     * @return object|null
600
     */
601 26
    protected function find($identifiers, $targetClass)
602
    {
603 26
        if ($identifiers instanceof $targetClass) {
604 2
            return $identifiers;
605
        }
606
607 24
        if ($this->isNullIdentifier($identifiers)) {
608 5
            return null;
609
        }
610
611 19
        return $this->objectManager->find($targetClass, $identifiers);
612
    }
613
614
    /**
615
     * Verifies if a provided identifier is to be considered null
616
     *
617
     * @param  mixed $identifier
618
     *
619
     * @return bool
620
     */
621 24
    private function isNullIdentifier($identifier)
622
    {
623 24
        if (null === $identifier) {
624 5
            return true;
625
        }
626
627 19
        if ($identifier instanceof Traversable || is_array($identifier)) {
628 16
            $nonNullIdentifiers = array_filter(
629 16
                ArrayUtils::iteratorToArray($identifier),
630
                function ($value) {
631 16
                    return null !== $value;
632 16
                }
633
            );
634
635 16
            return empty($nonNullIdentifiers);
636
        }
637
638 3
        return false;
639
    }
640
641
    /**
642
     * Check the field is nullable
643
     *
644
     * @param $name
645
     * @return bool
646
     */
647 6
    private function isNullable($name)
648
    {
649
        //TODO: need update after updating isNullable method of Doctrine\ORM\Mapping\ClassMetadata
650 6
        if ($this->metadata->hasField($name)) {
651
            return method_exists($this->metadata, 'isNullable') && $this->metadata->isNullable($name);
0 ignored issues
show
Bug introduced by
The method isNullable() does not seem to exist on object<Doctrine\Common\P...\Mapping\ClassMetadata>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
652 6
        } else if ($this->metadata->hasAssociation($name) && method_exists($this->metadata, 'getAssociationMapping')) {
653
            $mapping = $this->metadata->getAssociationMapping($name);
0 ignored issues
show
Bug introduced by
The method getAssociationMapping() does not seem to exist on object<Doctrine\Common\P...\Mapping\ClassMetadata>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
654
655
            return false !== $mapping && isset($mapping['nullable']) && $mapping['nullable'];
656
        }
657
658 6
        return false;
659
    }
660
661
    /**
662
     * Applies the naming strategy if there is one set
663
     *
664
     * @param string $field
665
     *
666
     * @return string
667
     */
668 55
    protected function computeHydrateFieldName($field)
669
    {
670 55
        if ($this->hasNamingStrategy()) {
671 2
            $field = $this->getNamingStrategy()->hydrate($field);
672
        }
673 55
        return $field;
674
    }
675
676
    /**
677
     * Applies the naming strategy if there is one set
678
     *
679
     * @param string $field
680
     *
681
     * @return string
682
     */
683 17
    protected function computeExtractFieldName($field)
684
    {
685 17
        if ($this->hasNamingStrategy()) {
686 2
            $field = $this->getNamingStrategy()->extract($field);
687
        }
688 17
        return $field;
689
    }
690
}
691