Completed
Push — master ( 1de9b7...830752 )
by Kristof
38:46 queued 24:09
created

AbstractDoctrineORMAdminListConfigurator.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
namespace Kunstmaan\AdminListBundle\AdminList\Configurator;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Doctrine\ORM\Query;
7
use Doctrine\ORM\QueryBuilder;
8
use Kunstmaan\AdminBundle\Helper\Security\Acl\AclHelper;
9
use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\PermissionDefinition;
10
use Kunstmaan\AdminListBundle\AdminList\Filter;
11
use Kunstmaan\AdminListBundle\AdminList\FilterType\ORM\AbstractORMFilterType;
12
use Kunstmaan\AdminListBundle\AdminList\SortableInterface;
13
use Pagerfanta\Adapter\DoctrineORMAdapter;
14
use Pagerfanta\Pagerfanta;
15
use Traversable;
16
17
/**
18
 * An abstract admin list configurator that can be used with the orm query builder
19
 */
20
abstract class AbstractDoctrineORMAdminListConfigurator extends AbstractAdminListConfigurator
21
{
22
    /**
23
     * @var EntityManagerInterface
24
     */
25
    protected $em;
26
27
    /**
28
     * @var Query
29
     */
30
    private $query = null;
31
32
    /**
33
     * @var Pagerfanta
34
     */
35
    private $pagerfanta = null;
36
37
    /**
38
     * @var PermissionDefinition
39
     */
40
    private $permissionDef = null;
41
42
    /**
43
     * @var AclHelper
44
     */
45
    protected $aclHelper = null;
46
47
    /**
48
     * AbstractDoctrineORMAdminListConfigurator constructor.
49
     *
50
     * @param EntityManagerInterface $em
51
     * @param AclHelper|null         $aclHelper
52
     */
53
    public function __construct(EntityManagerInterface $em, AclHelper $aclHelper = null)
54
    {
55
        $this->em = $em;
56
        $this->aclHelper = $aclHelper;
57
    }
58
59
    /**
60
     * Return the url to edit the given $item
61
     *
62
     * @param object $item
63
     *
64
     * @return array
65
     */
66 View Code Duplication
    public function getEditUrlFor($item)
67
    {
68
        $params = array('id' => $item->getId());
69
        $params = array_merge($params, $this->getExtraParameters());
70
71
        return array(
72
            'path' => $this->getPathByConvention($this::SUFFIX_EDIT),
73
            'params' => $params,
74
        );
75
    }
76
77
    /**
78
     * Get the delete url for the given $item
79
     *
80
     * @param object $item
81
     *
82
     * @return array
83
     */
84 View Code Duplication
    public function getDeleteUrlFor($item)
85
    {
86
        $params = array('id' => $item->getId());
87
        $params = array_merge($params, $this->getExtraParameters());
88
89
        return array(
90
            'path' => $this->getPathByConvention($this::SUFFIX_DELETE),
91
            'params' => $params,
92
        );
93
    }
94
95
    /**
96
     * @return Pagerfanta
97
     */
98 View Code Duplication
    public function getPagerfanta()
99
    {
100
        if (is_null($this->pagerfanta)) {
101
            $adapter = new DoctrineORMAdapter($this->getQuery());
102
            $this->pagerfanta = new Pagerfanta($adapter);
103
            $this->pagerfanta->setNormalizeOutOfRangePages(true);
104
            $this->pagerfanta->setMaxPerPage($this->getLimit());
105
            $this->pagerfanta->setCurrentPage($this->getPage());
106
        }
107
108
        return $this->pagerfanta;
109
    }
110
111
    /**
112
     * @param QueryBuilder $queryBuilder
113
     */
114
    public function adaptQueryBuilder(QueryBuilder $queryBuilder)
115
    {
116
        $queryBuilder->where('1=1');
117
    }
118
119
    /**
120
     * @return int
121
     */
122
    public function getCount()
123
    {
124
        return $this->getPagerfanta()->getNbResults();
125
    }
126
127
    /**
128
     * @return array|Traversable
129
     */
130
    public function getItems()
131
    {
132
        return $this->getPagerfanta()->getCurrentPageResults();
133
    }
134
135
    /**
136
     * Return an iterator for all items that matches the current filtering
137
     *
138
     * @return \Iterator
139
     */
140
    public function getIterator()
141
    {
142
        return $this->getQuery()->iterate();
143
    }
144
145
    /**
146
     * @return Query|null
147
     */
148
    public function getQuery()
149
    {
150
        if (is_null($this->query)) {
151
            $queryBuilder = $this->getQueryBuilder();
152
            $this->adaptQueryBuilder($queryBuilder);
153
154
            // Apply filters
155
            $filters = $this->getFilterBuilder()->getCurrentFilters();
156
            /* @var Filter $filter */
157
            foreach ($filters as $filter) {
158
                /* @var AbstractORMFilterType $type */
159
                $type = $filter->getType();
160
                $type->setQueryBuilder($queryBuilder);
161
                $filter->apply();
162
            }
163
164
            // Apply sorting
165
            if (!empty($this->orderBy)) {
166
                $orderBy = $this->orderBy;
167
                if ($this->getEntityManager()->getClassMetadata($this->getRepositoryName())->hasAssociation($this->orderBy)) {
168
                    $queryBuilder->leftJoin('b.' . $orderBy, 'A' . $orderBy);
169
                    $orderBy = 'A' . $orderBy . '.id';
170
                } elseif (!strpos($orderBy, '.')) {
171
                    $orderBy = 'b.' . $orderBy;
172
                }
173
                $queryBuilder->orderBy($orderBy, ($this->orderDirection == 'DESC' ? 'DESC' : 'ASC'));
174
            }
175
176
            // Apply other changes
177
            $this->finishQueryBuilder($queryBuilder);
178
179
            // Apply ACL restrictions (if applicable)
180
            if (!is_null($this->permissionDef) && !is_null($this->aclHelper)) {
181
                $this->query = $this->aclHelper->apply($queryBuilder, $this->permissionDef);
182
            } else {
183
                $this->query = $queryBuilder->getQuery();
184
            }
185
        }
186
187
        return $this->query;
188
    }
189
190
    /**
191
     * @param QueryBuilder $queryBuilder
192
     */
193
    protected function finishQueryBuilder(QueryBuilder $queryBuilder)
194
    {
195
        if ($this instanceof SortableInterface) {
196
            $queryBuilder->addOrderBy('b.' . $this->getSortableField());
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class Kunstmaan\AdminListBundl...RMAdminListConfigurator as the method getSortableField() does only exist in the following sub-classes of Kunstmaan\AdminListBundl...RMAdminListConfigurator: Kunstmaan\AdminListBundl...inList\Configurator\ORM. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
197
        }
198
    }
199
200
    /**
201
     * @return QueryBuilder
202
     */
203
    protected function getQueryBuilder()
204
    {
205
        $queryBuilder = $this->em
206
            ->getRepository($this->getRepositoryName())
207
            ->createQueryBuilder('b');
208
209
        return $queryBuilder;
210
    }
211
212
    /**
213
     * Get current permission definition.
214
     *
215
     * @return PermissionDefinition|null
216
     */
217
    public function getPermissionDefinition()
218
    {
219
        return $this->permissionDef;
220
    }
221
222
    /**
223
     * Set permission definition.
224
     *
225
     * @param PermissionDefinition $permissionDef
226
     *
227
     * @return AbstractDoctrineORMAdminListConfigurator
228
     */
229
    public function setPermissionDefinition(PermissionDefinition $permissionDef)
230
    {
231
        $this->permissionDef = $permissionDef;
232
233
        return $this;
234
    }
235
236
    /**
237
     * @param EntityManagerInterface $em
238
     *
239
     * @return AbstractDoctrineORMAdminListConfigurator
240
     */
241
    public function setEntityManager(EntityManagerInterface $em)
242
    {
243
        $this->em = $em;
244
245
        return $this;
246
    }
247
248
    /**
249
     * @return EntityManagerInterface
250
     */
251
    public function getEntityManager()
252
    {
253
        return $this->em;
254
    }
255
}
256