Failed Conditions
Push — master ( 389cea...9cc91e )
by Adrien
13:06
created

NativeIn   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 53
Duplicated Lines 0 %

Test Coverage

Coverage 81.82%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 6
eloc 23
c 1
b 0
f 0
dl 0
loc 53
ccs 18
cts 22
cp 0.8182
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getSql() 0 16 4
A dql() 0 5 1
A parse() 0 14 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ecodev\Felix\ORM\Query;
6
7
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
8
use Doctrine\ORM\Query\AST\Literal;
9
use Doctrine\ORM\Query\AST\Node;
10
use Doctrine\ORM\Query\Lexer;
11
use Doctrine\ORM\Query\Parser;
12
use Doctrine\ORM\Query\SqlWalker;
13
use Exception;
14
15
/**
16
 * A custom DQL function to be able to use `IN` clause but with native SQL sub-queries.
17
 *
18
 * This is especially useful when we want to benefit from DQL builder, paginator,
19
 * automatic ACL filter etc., but still have to have some advanced conditions in sub-queries.
20
 *
21
 * DQL must not be handwritten, but instead `self::dql()` should be used
22
 */
23
class NativeIn extends FunctionNode
24
{
25
    private string|Node $field;
26
27
    private Literal $nativeQuery;
28
29
    private Literal $isNot;
30
31
    /**
32
     * Generate DQL `IN` clause with a native sub-query.
33
     *
34
     * @param string $field DQL for the field
35
     * @param string $nativeSql native SQL sub-query
36
     */
37
    public static function dql(string $field, string $nativeSql, bool $isNot = false): string
38
    {
39
        $quotedNativeSql = "'" . str_replace("'", "''", $nativeSql) . "'";
40
41
        return 'NATIVE_IN(' . $field . ',   ' . $quotedNativeSql . ', ' . (int) $isNot . ') = TRUE';
42
    }
43
44 7
    public function parse(Parser $parser): void
45
    {
46 7
        $parser->match(Lexer::T_IDENTIFIER);
47 7
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
48
49 7
        $this->field = $parser->ArithmeticPrimary();
50 7
        $parser->match(Lexer::T_COMMA);
51
52 7
        $this->nativeQuery = $parser->Literal();
53 7
        $parser->match(Lexer::T_COMMA);
54
55 7
        $this->isNot = $parser->Literal();
56
57 7
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
58
    }
59
60 7
    public function getSql(SqlWalker $sqlWalker): string
61
    {
62 7
        $field = is_string($this->field) ? $sqlWalker->walkResultVariable($this->field) : $this->field->dispatch($sqlWalker);
63 7
        $nativeSql = $this->nativeQuery->dispatch($sqlWalker);
64 7
        $nativeSql = preg_replace("~^'(.*)'$~", '\\1', $nativeSql);
65 7
        if ($nativeSql === null) {
66
            throw new Exception('Error while unquoting native SQL');
67
        }
68
69 7
        $unquotedNativeSql = str_replace(["\\'", '\\n'], ["'", "\n"], $nativeSql);
70
71 7
        $isNot = $this->isNot->dispatch($sqlWalker);
72
73 7
        $sql = $field . ($isNot ? ' NOT' : '') . ' IN (' . $unquotedNativeSql . ')';
74
75 7
        return $sql;
76
    }
77
}
78