Test Failed
Pull Request — master (#5)
by Alex
07:05
created

AbstractJoin::createJoinFilters()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 4
eloc 7
c 2
b 0
f 0
nc 3
nop 3
dl 0
loc 14
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arp\LaminasDoctrine\Query\Filter;
6
7
use Arp\LaminasDoctrine\Query\Exception\InvalidArgumentException;
8
use Arp\LaminasDoctrine\Query\Exception\QueryFilterException;
9
use Arp\LaminasDoctrine\Query\Exception\QueryFilterManagerException;
10
use Arp\LaminasDoctrine\Query\Metadata\MetadataInterface;
11
use Arp\LaminasDoctrine\Query\QueryBuilderInterface;
12
use Doctrine\ORM\Mapping\MappingException;
13
use Doctrine\ORM\Query\Expr\Andx as DoctrineAndX;
14
use Doctrine\ORM\Query\Expr\Base;
15
use Doctrine\ORM\Query\Expr\Composite;
16
use Doctrine\ORM\Query\Expr\Join;
17
use Doctrine\ORM\Query\Expr\Orx as DoctrineOrX;
18
19
/**
20
 * @author  Alex Patterson <[email protected]>
21
 * @package Arp\LaminasDoctrine\Query\Filter
22
 */
23
abstract class AbstractJoin extends AbstractFilter
24
{
25
    /**
26
     * @param QueryBuilderInterface      $queryBuilder
27
     * @param string                     $fieldName
28
     * @param string                     $alias
29
     * @param null|string|Composite|Base $condition
30
     * @param string                     $joinType
31
     * @param string|null                $indexBy
32
     */
33
    abstract protected function applyJoin(
34
        QueryBuilderInterface $queryBuilder,
35
        string $fieldName,
36
        string $alias,
37
        $condition = null,
38
        string $joinType = Join::WITH,
39
        ?string $indexBy = null
40
    ): void;
41
42
    /**
43
     * @param QueryBuilderInterface $queryBuilder
44
     * @param MetadataInterface     $metadata
45
     * @param array                 $criteria
46
     *
47
     * @throws InvalidArgumentException
48
     * @throws QueryFilterException
49
     */
50
    public function filter(QueryBuilderInterface $queryBuilder, MetadataInterface $metadata, array $criteria): void
51
    {
52
        $fieldName = $this->resolveFieldName(, , $criteria);
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected ',' on line 52 at column 45
Loading history...
Coding Style introduced by
Space found before comma in argument list
Loading history...
53
        $mapping = $this->getAssociationMapping($metadata, $fieldName);
54
55
        $alias = $criteria['alias'] ?? null;
56
        if (null === $alias) {
57
            throw new InvalidArgumentException(
58
                sprintf('The required \'alias\' criteria value is missing for filter \'%s\'', static::class)
59
            );
60
        }
61
62
        $conditions = $criteria['conditions'] ?? [];
63
        $condition = null;
64
65
        if (is_string($conditions)) {
66
            $condition = $conditions;
67
        } elseif (is_object($condition)) {
68
            $condition = (string)$conditions;
69
        } elseif (is_array($conditions) && !empty($conditions)) {
70
            $tempQueryBuilder = $queryBuilder->createQueryBuilder();
71
72
            $this->filterJoinCriteria(
73
                $tempQueryBuilder,
74
                $mapping['targetEntity'],
75
                ['filters' => $this->createJoinFilters($conditions, $alias, $criteria)]
76
            );
77
78
            $condition = $this->mergeJoinConditions($queryBuilder, $tempQueryBuilder);
79
        }
80
81
        $parentAlias = $criteria['parent_alias'] ?? 'entity';
82
        $this->applyJoin(
83
            $queryBuilder,
84
            $parentAlias . '.' . $fieldName,
85
            $alias,
86
            $condition,
87
            $criteria['join_type'] ?? Join::WITH,
88
            $criteria['index_by'] ?? null
89
        );
90
    }
91
92
    /**
93
     * @param MetadataInterface $metadata
94
     * @param string            $fieldName
95
     *
96
     * @return array
97
     *
98
     * @throws InvalidArgumentException
99
     */
100
    private function getAssociationMapping(MetadataInterface $metadata, string $fieldName): array
101
    {
102
        try {
103
            return $metadata->getAssociationFiledMapping($fieldName);
104
        } catch (MappingException $e) {
105
            throw new InvalidArgumentException(
106
                sprintf(
107
                    'Failed to load association field mapping for field \'%s::%s\' in filter \'%s\'',
108
                    $metadata->getName(),
109
                    $fieldName,
110
                    static::class
111
                )
112
            );
113
        }
114
    }
115
116
    /**
117
     * @param QueryBuilderInterface $qb
118
     * @param string                $targetEntity
119
     * @param array                 $criteria
120
     *
121
     * @throws QueryFilterException
122
     */
123
    private function filterJoinCriteria(QueryBuilderInterface $qb, string $targetEntity, array $criteria): void
124
    {
125
        try {
126
            $this->queryFilterManager->filter($qb, $targetEntity, $criteria);
127
        } catch (QueryFilterManagerException $e) {
128
            throw new QueryFilterException(
129
                sprintf(
130
                    'Failed to apply query filter \'%s\' conditions for target entity \'%s\': %s',
131
                    static::class,
132
                    $targetEntity,
133
                    $e->getMessage()
134
                ),
135
                $e->getCode(),
136
                $e
137
            );
138
        }
139
    }
140
141
    /**
142
     * @param array  $conditions
143
     * @param string $alias
144
     * @param array  $criteria
145
     *
146
     * @return array
147
     */
148
    private function createJoinFilters(array $conditions, string $alias, array $criteria): array
149
    {
150
        // Use the join alias as the default alias for conditions
151
        foreach ($conditions as $index => $condition) {
152
            if (is_array($condition) && empty($condition['alias'])) {
153
                $conditions[$index]['alias'] = $alias;
154
            }
155
        }
156
157
        return [
158
            [
159
                'name'       => AndX::class,
160
                'conditions' => $conditions,
161
                'where'      => $criteria['filters']['where'] ?? null,
162
            ],
163
        ];
164
    }
165
166
    /**
167
     * @param QueryBuilderInterface $queryBuilder
168
     * @param QueryBuilderInterface $qb
169
     *
170
     * @return DoctrineAndX|DoctrineOrX|null
171
     */
172
    private function mergeJoinConditions(QueryBuilderInterface $queryBuilder, QueryBuilderInterface $qb): ?Composite
173
    {
174
        $parts = $qb->getQueryParts();
175
176
        if (!isset($parts['where'])) {
177
            return null;
178
        }
179
180
        if ($parts['where'] instanceof DoctrineAndx) {
181
            $condition = $queryBuilder->expr()->andX();
182
        } elseif ($parts['where'] instanceof DoctrineOrX) {
183
            $condition = $queryBuilder->expr()->orX();
184
        } else {
185
            return null;
186
        }
187
188
        $condition->addMultiple($parts['where']->getParts());
189
        $queryBuilder->mergeParameters($qb);
190
191
        return $condition;
192
    }
193
}
194