Completed
Push — master ( 46c3a0...a94e83 )
by Grégoire
13s queued 11s
created

src/Form/ChoiceList/ModelChoiceLoader.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Form\ChoiceList;
15
16
use Doctrine\Common\Util\ClassUtils;
17
use Sonata\AdminBundle\Model\ModelManagerInterface;
18
use Sonata\Doctrine\Adapter\AdapterInterface;
19
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
20
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
21
use Symfony\Component\Form\Exception\RuntimeException;
22
use Symfony\Component\PropertyAccess\PropertyAccess;
23
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
24
use Symfony\Component\PropertyAccess\PropertyPath;
25
26
/**
27
 * @final since sonata-project/admin-bundle 3.52
28
 *
29
 * @author Thomas Rabaix <[email protected]>
30
 */
31
class ModelChoiceLoader implements ChoiceLoaderInterface
32
{
33
    public $identifier;
34
35
    /**
36
     * @var \Sonata\AdminBundle\Model\ModelManagerInterface
37
     */
38
    private $modelManager;
39
40
    /**
41
     * @var string
42
     */
43
    private $class;
44
45
    private $property;
46
47
    private $query;
48
49
    private $choices;
50
51
    /**
52
     * @var PropertyPath
53
     */
54
    private $propertyPath;
55
56
    /**
57
     * @var PropertyAccessorInterface
58
     */
59
    private $propertyAccessor;
60
61
    private $choiceList;
62
63
    /**
64
     * @param string      $class
65
     * @param string|null $property
66
     * @param mixed|null  $query
67
     * @param array       $choices
68
     */
69
    public function __construct(
70
        ModelManagerInterface $modelManager,
71
        $class,
72
        $property = null,
73
        $query = null,
74
        $choices = [],
75
        PropertyAccessorInterface $propertyAccessor = null
76
    ) {
77
        $this->modelManager = $modelManager;
78
        $this->class = $class;
79
        $this->property = $property;
80
        $this->query = $query;
81
        $this->choices = $choices;
82
83
        $this->identifier = $this->modelManager->getIdentifierFieldNames($this->class);
84
85
        // The property option defines, which property (path) is used for
86
        // displaying entities as strings
87
        if ($property) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $property of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
88
            $this->propertyPath = new PropertyPath($property);
89
            $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
90
        }
91
    }
92
93
    public function loadChoiceList($value = null)
94
    {
95
        if (!$this->choiceList) {
96
            if ($this->query) {
97
                $entities = $this->modelManager->executeQuery($this->query);
98
            } elseif (\is_array($this->choices) && \count($this->choices) > 0) {
99
                $entities = $this->choices;
100
            } else {
101
                $entities = $this->modelManager->findBy($this->class);
102
            }
103
104
            $choices = [];
105
            foreach ($entities as $key => $entity) {
106
                if ($this->propertyPath) {
107
                    // If the property option was given, use it
108
                    $valueObject = $this->propertyAccessor->getValue($entity, $this->propertyPath);
109
                } else {
110
                    // Otherwise expect a __toString() method in the entity
111
                    try {
112
                        $valueObject = (string) $entity;
113
                    } catch (\Exception $e) {
114
                        throw new RuntimeException(sprintf('Unable to convert the entity "%s" to string, provide "property" option or implement "__toString()" method in your entity.', ClassUtils::getClass($entity)), 0, $e);
115
                    }
116
                }
117
118
                $id = implode(AdapterInterface::ID_SEPARATOR, $this->getIdentifierValues($entity));
119
120
                if (!\array_key_exists($valueObject, $choices)) {
121
                    $choices[$valueObject] = [];
122
                }
123
124
                $choices[$valueObject][] = $id;
125
            }
126
127
            $finalChoices = [];
128
            foreach ($choices as $valueObject => $idx) {
129
                if (\count($idx) > 1) { // avoid issue with identical values ...
130
                    foreach ($idx as $id) {
131
                        $finalChoices[sprintf('%s (id: %s)', $valueObject, $id)] = $id;
132
                    }
133
                } else {
134
                    $finalChoices[$valueObject] = current($idx);
135
                }
136
            }
137
138
            $this->choiceList = new ArrayChoiceList($finalChoices, $value);
139
        }
140
141
        return $this->choiceList;
142
    }
143
144
    public function loadChoicesForValues(array $values, $value = null)
145
    {
146
        return $this->loadChoiceList($value)->getChoicesForValues($values);
147
    }
148
149
    public function loadValuesForChoices(array $choices, $value = null)
150
    {
151
        return $this->loadChoiceList($value)->getValuesForChoices($choices);
152
    }
153
154
    /**
155
     * @param object $entity
156
     */
157
    private function getIdentifierValues($entity): array
158
    {
159
        try {
160
            return $this->modelManager->getIdentifierValues($entity);
161
        } catch (\Exception $e) {
162
            throw new \InvalidArgumentException(sprintf('Unable to retrieve the identifier values for entity %s', ClassUtils::getClass($entity)), 0, $e);
163
        }
164
    }
165
}
166