Issues (65)

src/Grammar/Predicate.php (1 issue)

1
<?php
2
3
namespace HexMakina\Crudites\Grammar;
4
5
/**
6
 * An abstract class representing a SQL predicate.
7
 * Instantiate and __toString().
8
 *
9
 * @package HexMakina\Crudites\Grammar\Query
10
 */
11
class Predicate extends Grammar
12
{
13
    protected $left;
14
    protected ?string $operator = null;
15
    protected $right = null;
16
17
    protected ?string $bind_label = null;
18
19
    protected array $bindings = [];
20
21
    /**
22
     * Predicate constructor.
23
     * The constructor takes a left side expression, an operator, and a right side expression.
24
     * The left and right side expressions can be either strings or arrays.
25
     * If an array is provided, the first element is the table name and the second element is the column name.
26
     * The constructor also adds backticks to the column names.
27
     * 
28
     *
29
     * @param mixed $left, left side expression
30
     * @param string $operator, operator to use
31
     * @param mixed $right, right side expression
32
     */
33
    public function __construct($left, string $operator = null, $right = null)
34
    {
35
        $this->left = $left;
36
        $this->operator = $operator;
37
        $this->right = $right;
38
    }
39
40
    /**
41
     * Converts the predicate to a string.
42
     *
43
     * @return string The string representation of the predicate.
44
     */
45
    public function __toString()
46
    {
47
        $left = is_array($this->left) ? self::identifier($this->left) : ($this->left ?? '');
48
        $right = is_array($this->right) ? self::identifier($this->right) : ($this->right ?? '');
49
        return trim(sprintf('%s %s %s', $left, $this->operator ?? '', $right));
50
    }
51
52
    /**
53
     * Gets the bindings for the predicate.
54
     *
55
     * @return array The bindings for the predicate.
56
     */
57
    public function bindings(): array
58
    {
59
        return $this->bindings ?? [];
60
    }
61
62
63
    public function withValue($value, string $bind_label = null): self
64
    {
65
        $this->bindings[$this->bindLabel($bind_label)] = $value;
66
        $this->right = ':' . $this->bindLabel($bind_label);
67
        return $this;
68
    }
69
70
    public function withValues(array $values, string $bind_label)
71
    {
72
        if (empty($values)) {
73
            throw new \InvalidArgumentException('PREDICATE_VALUES_ARE_EMPTY');
74
        }
75
76
        
77
        foreach ($values as $index => $val) {
78
            $this->bindings[sprintf('%s_%d', $bind_label, $index)] = $val;
79
        }
80
81
        $this->operator = 'IN';
82
        $this->right = '(:' . implode(',:', array_keys($this->bindings)) . ')';
83
84
        return $this;
85
    }
86
87
    public function isNotEmpty(): self
88
    {
89
        $res = is_array($this->left) ? self::identifier($this->left) : $this->left;
90
        $this->left = sprintf("(%s IS NOT NULL AND %s <> '')", $res, $res);
91
        $this->operator = null;
92
        $this->right = null;
93
94
        return $this;
95
    }
96
97
    public function isEmpty(): self
98
    {
99
        $res = is_array($this->left) ? self::identifier($this->left) : $this->left;
100
        $this->left = sprintf("(%s IS NULL OR %s = '')", $res, $res);
101
        $this->operator = null;
102
        $this->right = null;
103
104
        return $this;
105
    }
106
107
108
    /**
109
     * Gets the binding label for the predicate.
110
     *
111
     * @return string The binding label for the predicate.
112
     */
113
    private function bindLabel(string $bind_label = null): string
114
    {
115
        // setter
116
        if ($bind_label !== null) {
117
            $this->bind_label = $bind_label;
118
            return $this->bind_label;
119
        }
120
        // getter
121
        else if ($this->bind_label !== null)
122
            return $this->bind_label;
123
124
        // generator
125
        else if (is_array($this->left)) {
126
            $this->bind_label = implode('_', $this->left);
127
            return $this->bind_label;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->bind_label returns the type null which is incompatible with the type-hinted return string.
Loading history...
128
        }
129
130
        throw new \InvalidArgumentException('PREDICATE_REQUIRES_A_BIND_LABEL');
131
    }
132
}
133