Issues (12)

src/QueryBuilder.php (7 issues)

1
<?php
2
3
namespace Sfneal\Builders;
4
5
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
6
use Illuminate\Database\Eloquent\Collection;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Database\Query\Builder;
9
use Sfneal\Builders\Traits\CountAndPaginate;
10
11
class QueryBuilder extends EloquentBuilder
12
{
13
    use CountAndPaginate;
14
15
    /**
16
     * @var string Declare a custom MySQL select string to be used by selectRawJson()
17
     */
18
    protected $selectRawJson;
19
20
    /**
21
     * @var Model
22
     */
23
    protected $targetModel;
24
25
    /**
26
     * @var string Name of the User model's table
27
     */
28
    protected $tableName;
29
30
    /**
31
     * @var string Name of the User model's primary key
32
     */
33
    protected $primaryKeyName;
34
35
    /**
36
     * UserBuilder constructor.
37
     *
38
     * @param Builder $query
39
     */
40
    public function __construct(Builder $query)
41
    {
42
        $this->setTableAndKeyNames();
43
        parent::__construct($query);
44
    }
45
46
    /**
47
     * Set the $tableName & $primaryKeyName properties.
48
     *
49
     * @return void
50
     */
51
    private function setTableAndKeyNames(): void
52
    {
53
        // Only declare table & pk if the target model is declared
54
        if (isset($this->targetModel)) {
55
            $this->tableName = $this->targetModel::getTableName();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->targetModel::getTableName() can also be of type Illuminate\Database\Eloquent\Builder. However, the property $tableName is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
56
            $this->primaryKeyName = $this->targetModel::getPrimaryKeyName();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->targetModel::getPrimaryKeyName() can also be of type Illuminate\Database\Eloquent\Builder. However, the property $primaryKeyName is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
57
        }
58
    }
59
60
    /**
61
     * Retrieve a concatenation string that combines two columns in a table into a single column.
62
     *
63
     * @param string $column1
64
     * @param string $column2
65
     * @param string $delimiter
66
     * @return string
67
     */
68
    protected function concatColumns(string $column1, string $column2, string $delimiter = ' '): string
69
    {
70
        // Prepend table name if its been declared
71
        $column1 = (isset($this->tableName)) ? "{$this->tableName}.{$column1}" : $column1;
72
        $column2 = (isset($this->tableName)) ? "{$this->tableName}.{$column2}" : $column2;
73
74
        // Return concatenate the two columns using the delimiter
75
        return "concat({$column1}, '{$delimiter}', {$column2})";
76
    }
77
78
    /**
79
     * Retrieve a MySQL if statement that can be used within a query.
80
     *
81
     * @param string $condition
82
     * @param string $expr_true
83
     * @param string $expr_false
84
     * @return string
85
     */
86
    protected function ifStatement(string $condition, string $expr_true, string $expr_false): string
87
    {
88
        return "if({$condition}, {$expr_true}, {$expr_false})";
89
    }
90
91
    /**
92
     * Wildcard where like query to determine if any part of the value is found.
93
     *
94
     * @param string $column
95
     * @param $value
96
     * @param bool $leadingWildcard
97
     * @param bool $trailingWildcard
98
     * @return $this
99
     */
100
    public function whereLike(string $column, $value, bool $leadingWildcard = true, bool $trailingWildcard = true): self
101
    {
102
        $this->where(
103
            $column,
104
            'LIKE',
105
            ($leadingWildcard ? '%' : '').$value.($trailingWildcard ? '%' : '')
106
        );
107
108
        return $this;
109
    }
110
111
    /**
112
     * Wildcard or where like query to determine if any part of the value is found.
113
     *
114
     * @param string $column
115
     * @param $value
116
     * @param bool $leadingWildcard
117
     * @param bool $trailingWildcard
118
     * @return $this
119
     */
120
    public function orWhereLike(string $column, $value, bool $leadingWildcard = true, bool $trailingWildcard = true): self
121
    {
122
        $this->orWhere(
123
            $column,
124
            'LIKE',
125
            ($leadingWildcard ? '%' : '').$value.($trailingWildcard ? '%' : '')
126
        );
127
128
        return $this;
129
    }
130
131
    /**
132
     * Retrieve a flat, single-dimensional array of results without keys.
133
     *
134
     * @param string $column
135
     * @return array
136
     */
137
    public function getFlatArray(string $column): array
138
    {
139
        return array_map(function ($collection) use ($column) {
140
            return $collection[$column];
141
        }, $this->distinct()->get($column)->toArray());
0 ignored issues
show
The method distinct() does not exist on Sfneal\Builders\QueryBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

141
        }, $this->/** @scrutinizer ignore-call */ distinct()->get($column)->toArray());
Loading history...
142
    }
143
144
    /**
145
     * Retrieve raw query results formatted for Ajax select2 form inputs.
146
     *
147
     * @param string|null $raw
148
     * @return $this
149
     */
150
    public function selectRawJson(string $raw = null): self
151
    {
152
        $this->withoutGlobalScopes();
153
        $this->selectRaw($raw ?? $this->selectRawJson);
0 ignored issues
show
The method selectRaw() does not exist on Sfneal\Builders\QueryBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

153
        $this->/** @scrutinizer ignore-call */ 
154
               selectRaw($raw ?? $this->selectRawJson);
Loading history...
154
155
        return $this;
156
    }
157
158
    /**
159
     * Order query results by the 'created_at' column.
160
     *
161
     * @param string $direction
162
     * @return $this
163
     */
164
    public function orderByCreatedAt(string $direction = 'desc'): self
165
    {
166
        $this->orderBy('created_at', $direction);
0 ignored issues
show
The method orderBy() does not exist on Sfneal\Builders\QueryBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

166
        $this->/** @scrutinizer ignore-call */ 
167
               orderBy('created_at', $direction);
Loading history...
167
168
        return $this;
169
    }
170
171
    /**
172
     * Retrieve the 'next' Model in the database.
173
     *
174
     * @param int|null $model_id
175
     * @return QueryBuilder|Collection|Model|null
176
     */
177
    public function getNextModel(int $model_id = null)
178
    {
179
        return $this->find($this->getNextModelId($model_id));
180
    }
181
182
    /**
183
     * Retrieve the 'previous' Model in the database.
184
     *
185
     * @param int|null $model_id
186
     * @return QueryBuilder|Collection|Model|null
187
     */
188
    public function getPreviousModel(int $model_id = null)
189
    {
190
        return $this->find($this->getPreviousModelId($model_id));
191
    }
192
193
    /**
194
     * Retrieve the 'next' Model's ID.
195
     *
196
     * @param int|null $model_id
197
     * @return mixed
198
     */
199
    public function getNextModelId(int $model_id = null)
200
    {
201
        return $this
202
            ->where($this->model->getKeyName(), '>', $model_id ?? $this->model->getKey())
203
            ->min($this->model->getKeyName());
0 ignored issues
show
The method min() does not exist on Sfneal\Builders\QueryBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

203
            ->/** @scrutinizer ignore-call */ min($this->model->getKeyName());
Loading history...
204
    }
205
206
    /**
207
     * Retrieve the 'previous' Model's ID.
208
     *
209
     * @param int|null $model_id
210
     * @return mixed
211
     */
212
    public function getPreviousModelId(int $model_id = null)
213
    {
214
        return $this
215
            ->where($this->model->getKeyName(), '<', $model_id ?? $this->model->getKey())
216
            ->max($this->model->getKeyName());
0 ignored issues
show
The method max() does not exist on Sfneal\Builders\QueryBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

216
            ->/** @scrutinizer ignore-call */ max($this->model->getKeyName());
Loading history...
217
    }
218
}
219