LaravelFreelancerNL /
laravel-arangodb
| 1 | <?php |
||||
| 2 | |||||
| 3 | namespace LaravelFreelancerNL\Aranguent\Query; |
||||
| 4 | |||||
| 5 | use Exception; |
||||
| 6 | use Illuminate\Database\ConnectionInterface as IlluminateConnectionInterface; |
||||
| 7 | use Illuminate\Database\Query\Builder as IlluminateQueryBuilder; |
||||
| 8 | use Illuminate\Database\Query\Expression; |
||||
| 9 | use Illuminate\Database\Query\Grammars\Grammar as IlluminateQueryGrammar; |
||||
| 10 | use Illuminate\Database\Query\Processors\Processor as IlluminateProcessor; |
||||
| 11 | use LaravelFreelancerNL\Aranguent\Connection; |
||||
| 12 | use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsGroups; |
||||
| 13 | use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsSearches; |
||||
| 14 | use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsInserts; |
||||
| 15 | use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsJoins; |
||||
| 16 | use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsSelects; |
||||
| 17 | use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsSubqueries; |
||||
| 18 | use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsUpdates; |
||||
| 19 | use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsWheres; |
||||
| 20 | use LaravelFreelancerNL\Aranguent\Query\Concerns\ConvertsIdToKey; |
||||
| 21 | use LaravelFreelancerNL\Aranguent\Query\Concerns\HandlesAliases; |
||||
| 22 | use LaravelFreelancerNL\Aranguent\Query\Concerns\HandlesBindings; |
||||
| 23 | use LaravelFreelancerNL\Aranguent\Query\Enums\VariablePosition; |
||||
| 24 | use LaravelFreelancerNL\FluentAQL\QueryBuilder as AQB; |
||||
| 25 | use phpDocumentor\Reflection\Types\Boolean; |
||||
| 26 | |||||
| 27 | class Builder extends IlluminateQueryBuilder |
||||
| 28 | { |
||||
| 29 | use BuildsGroups; |
||||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||||
| 30 | use BuildsInserts; |
||||
| 31 | use BuildsJoins; |
||||
| 32 | use BuildsSearches; |
||||
| 33 | use BuildsSelects; |
||||
| 34 | use BuildsSubqueries; |
||||
| 35 | use BuildsUpdates; |
||||
| 36 | use BuildsWheres; |
||||
| 37 | use ConvertsIdToKey; |
||||
| 38 | use HandlesAliases; |
||||
| 39 | use HandlesBindings; |
||||
| 40 | public AQB $aqb; |
||||
| 41 | |||||
| 42 | /** |
||||
| 43 | * The current query value bindings. |
||||
| 44 | * |
||||
| 45 | * @var array<mixed> |
||||
| 46 | */ |
||||
| 47 | public $bindings = [ |
||||
| 48 | 'preIterationVariables' => [], |
||||
| 49 | 'from' => [], |
||||
| 50 | 'fromOptions' => [], |
||||
| 51 | 'search' => [], |
||||
| 52 | 'join' => [], |
||||
| 53 | 'postIterationVariables' => [], |
||||
| 54 | 'where' => [], |
||||
| 55 | 'groupBy' => [], |
||||
| 56 | 'having' => [], |
||||
| 57 | 'order' => [], |
||||
| 58 | 'union' => [], |
||||
| 59 | 'unionOrder' => [], |
||||
| 60 | 'select' => [], |
||||
| 61 | 'insert' => [], |
||||
| 62 | 'update' => [], |
||||
| 63 | 'upsert' => [], |
||||
| 64 | ]; |
||||
| 65 | |||||
| 66 | |||||
| 67 | /** |
||||
| 68 | * @var Connection |
||||
| 69 | */ |
||||
| 70 | public $connection; |
||||
| 71 | |||||
| 72 | /** |
||||
| 73 | * @var IlluminateQueryGrammar |
||||
| 74 | */ |
||||
| 75 | public $grammar; |
||||
| 76 | |||||
| 77 | /** |
||||
| 78 | * 'from' options. |
||||
| 79 | * |
||||
| 80 | * @var array<mixed> |
||||
| 81 | */ |
||||
| 82 | public $fromOptions = []; |
||||
| 83 | |||||
| 84 | /** |
||||
| 85 | * The current query value bindings. |
||||
| 86 | * |
||||
| 87 | * @var null|array<mixed> |
||||
| 88 | */ |
||||
| 89 | public ?array $search = null; |
||||
| 90 | |||||
| 91 | /** |
||||
| 92 | * The query variables that should be set before traversals (for/joins). |
||||
| 93 | * |
||||
| 94 | * @var array<mixed> |
||||
| 95 | */ |
||||
| 96 | public $preIterationVariables = []; |
||||
| 97 | |||||
| 98 | /** |
||||
| 99 | * The query variables that should be set after traversals (for/joins). |
||||
| 100 | * |
||||
| 101 | * @var array<mixed> |
||||
| 102 | */ |
||||
| 103 | public $postIterationVariables = []; |
||||
| 104 | |||||
| 105 | /** |
||||
| 106 | * ID of the query |
||||
| 107 | * Used as prefix for automatically generated bindings. |
||||
| 108 | * |
||||
| 109 | * @var int |
||||
| 110 | */ |
||||
| 111 | protected $queryId = 1; |
||||
| 112 | |||||
| 113 | /** |
||||
| 114 | * @override |
||||
| 115 | * Create a new query builder instance. |
||||
| 116 | */ |
||||
| 117 | 395 | public function __construct( |
|||
| 118 | IlluminateConnectionInterface $connection, |
||||
| 119 | ?IlluminateQueryGrammar $grammar = null, |
||||
| 120 | ?IlluminateProcessor $processor = null, |
||||
| 121 | ?AQB $aqb = null, |
||||
| 122 | ) { |
||||
| 123 | assert($processor instanceof IlluminateProcessor); |
||||
| 124 | |||||
| 125 | 395 | parent::__construct($connection, $grammar, $processor); |
|||
| 126 | |||||
| 127 | 395 | if (!$aqb instanceof AQB) { |
|||
| 128 | 395 | $aqb = new AQB(); |
|||
| 129 | } |
||||
| 130 | 395 | $this->aqb = $aqb; |
|||
| 131 | |||||
| 132 | 395 | $this->queryId = spl_object_id($this); |
|||
| 133 | } |
||||
| 134 | |||||
| 135 | 39 | public function getQueryId(): int |
|||
| 136 | { |
||||
| 137 | 39 | return $this->queryId; |
|||
| 138 | } |
||||
| 139 | |||||
| 140 | /** |
||||
| 141 | * Get the current query value bindings in a flattened array. |
||||
| 142 | * |
||||
| 143 | * @return array<mixed> |
||||
| 144 | */ |
||||
| 145 | 356 | public function getBindings() |
|||
| 146 | { |
||||
| 147 | 356 | $extractedBindings = []; |
|||
| 148 | 356 | foreach ($this->bindings as $typeBinds) { |
|||
| 149 | 356 | foreach ($typeBinds as $key => $value) { |
|||
| 150 | 270 | $extractedBindings[$key] = $value; |
|||
| 151 | } |
||||
| 152 | } |
||||
| 153 | |||||
| 154 | 356 | return $extractedBindings; |
|||
| 155 | } |
||||
| 156 | |||||
| 157 | /** |
||||
| 158 | * Run a pagination count query. |
||||
| 159 | * |
||||
| 160 | * @param array<mixed> $columns |
||||
| 161 | * @return array<mixed> |
||||
| 162 | */ |
||||
| 163 | 3 | protected function runPaginationCountQuery($columns = ['*']) |
|||
| 164 | { |
||||
| 165 | 3 | $without = $this->unions ? ['orders', 'limit', 'offset'] : ['columns', 'orders', 'limit', 'offset']; |
|||
| 166 | |||||
| 167 | 3 | $closeResults = $this->cloneWithout($without) |
|||
| 168 | 3 | ->cloneWithoutBindings($this->unions ? ['order'] : ['select', 'order']) |
|||
| 169 | 3 | ->setAggregate('count', $this->withoutSelectAliases($columns)) |
|||
|
0 ignored issues
–
show
$this->withoutSelectAliases($columns) of type array<mixed,Illuminate\C...uery\Expression|string> is incompatible with the type array<mixed,Illuminate\C...uery\Expression|string> expected by parameter $columns of Illuminate\Database\Query\Builder::setAggregate().
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 170 | 3 | ->get()->all(); |
|||
| 171 | |||||
| 172 | 3 | return $closeResults; |
|||
| 173 | } |
||||
| 174 | |||||
| 175 | /** |
||||
| 176 | * Delete records from the database. |
||||
| 177 | * |
||||
| 178 | * @param mixed $id |
||||
| 179 | * @return int |
||||
| 180 | */ |
||||
| 181 | 35 | public function delete($id = null) |
|||
| 182 | { |
||||
| 183 | 35 | $table = (string) $this->grammar->getValue($this->from); |
|||
| 184 | |||||
| 185 | // If an ID is passed to the method, we will set the where clause to check the |
||||
| 186 | // ID to let developers to simply and quickly remove a single row from this |
||||
| 187 | // database without manually specifying the "where" clauses on the query. |
||||
| 188 | 35 | if (!is_null($id)) { |
|||
| 189 | 1 | $this->where($table . '._key', '=', $id); |
|||
| 190 | } |
||||
| 191 | |||||
| 192 | 35 | $this->applyBeforeQueryCallbacks(); |
|||
| 193 | |||||
| 194 | 35 | return $this->connection->delete( |
|||
| 195 | 35 | $this->grammar->compileDelete($this), |
|||
| 196 | 35 | $this->cleanBindings( |
|||
| 197 | 35 | $this->grammar->prepareBindingsForDelete($this->bindings), |
|||
| 198 | 35 | ), |
|||
| 199 | 35 | ); |
|||
| 200 | } |
||||
| 201 | |||||
| 202 | /** |
||||
| 203 | * Determine if any rows exist for the current query. |
||||
| 204 | * |
||||
| 205 | * @return bool |
||||
| 206 | */ |
||||
| 207 | 18 | public function exists() |
|||
| 208 | { |
||||
| 209 | 18 | $this->applyBeforeQueryCallbacks(); |
|||
| 210 | |||||
| 211 | 18 | $results = $this->connection->select( |
|||
| 212 | 18 | $this->grammar->compileExists($this), |
|||
| 213 | 18 | $this->getBindings(), |
|||
| 214 | 18 | !$this->useWritePdo, |
|||
| 215 | 18 | ); |
|||
| 216 | |||||
| 217 | // If the results have rows, we will get the row and see if the exists column is a |
||||
| 218 | // boolean true. If there are no results for this query we will return false as |
||||
| 219 | // there are no rows for this query at all, and we can return that info here. |
||||
| 220 | 18 | if (isset($results[0])) { |
|||
| 221 | 18 | $results = (array) $results[0]; |
|||
| 222 | |||||
| 223 | 18 | return (bool) $results['exists']; |
|||
| 224 | } |
||||
| 225 | |||||
| 226 | return false; |
||||
| 227 | } |
||||
| 228 | |||||
| 229 | |||||
| 230 | /** |
||||
| 231 | * Execute an aggregate function on the database. |
||||
| 232 | * |
||||
| 233 | * @param string $function |
||||
| 234 | * @param array<mixed> $columns |
||||
| 235 | * @return mixed |
||||
| 236 | */ |
||||
| 237 | 79 | public function aggregate($function, $columns = ['*']) |
|||
| 238 | { |
||||
| 239 | 79 | $results = $this->cloneWithout($this->unions ? [] : ['columns']) |
|||
| 240 | 79 | ->setAggregate($function, $columns) |
|||
| 241 | 79 | ->get($columns); |
|||
| 242 | |||||
| 243 | 79 | if (!$results->isEmpty()) { |
|||
| 244 | 79 | return ($results->first())->aggregate; |
|||
| 245 | } |
||||
| 246 | |||||
| 247 | return false; |
||||
| 248 | } |
||||
| 249 | |||||
| 250 | /** |
||||
| 251 | * Set a variable |
||||
| 252 | * @param string $variable |
||||
| 253 | * @param IlluminateQueryBuilder|Expression|array<mixed>|Int|Float|String|Boolean $value |
||||
| 254 | * @param string|VariablePosition $variablePosition |
||||
| 255 | * @return Builder |
||||
| 256 | */ |
||||
| 257 | 7 | public function set( |
|||
| 258 | string $variable, |
||||
| 259 | IlluminateQueryBuilder|Expression|array|Boolean|Int|Float|String $value, |
||||
| 260 | VariablePosition|string $variablePosition = VariablePosition::preIterations, |
||||
| 261 | ): Builder { |
||||
| 262 | 7 | if (is_string($variablePosition)) { |
|||
| 263 | 3 | $variablePosition = VariablePosition::tryFrom($variablePosition) ?? VariablePosition::preIterations; |
|||
| 264 | } |
||||
| 265 | 7 | if ($value instanceof Expression) { |
|||
|
0 ignored issues
–
show
|
|||||
| 266 | 5 | $this->{$variablePosition->value}[$variable] = $value->getValue($this->grammar); |
|||
| 267 | |||||
| 268 | 5 | return $this; |
|||
| 269 | } |
||||
| 270 | |||||
| 271 | 3 | if ($value instanceof Builder) { |
|||
|
0 ignored issues
–
show
|
|||||
| 272 | |||||
| 273 | 1 | [$subquery] = $this->createSub($value); |
|||
| 274 | |||||
| 275 | 1 | $this->{$variablePosition->value}[$variable] = $subquery; |
|||
| 276 | |||||
| 277 | 1 | return $this; |
|||
| 278 | } |
||||
| 279 | 2 | $this->{$variablePosition->value}[$variable] = $this->bindValue($value, $variablePosition->value); |
|||
| 280 | |||||
| 281 | 2 | return $this; |
|||
| 282 | } |
||||
| 283 | |||||
| 284 | 336 | public function isVariable(string $value): bool |
|||
| 285 | { |
||||
| 286 | if ( |
||||
| 287 | 336 | key_exists($value, $this->preIterationVariables) |
|||
| 288 | 336 | || key_exists($value, $this->postIterationVariables) |
|||
| 289 | ) { |
||||
| 290 | 1 | return true; |
|||
| 291 | } |
||||
| 292 | |||||
| 293 | 336 | return false; |
|||
| 294 | } |
||||
| 295 | |||||
| 296 | /** |
||||
| 297 | * @param mixed $value |
||||
| 298 | * @param array<mixed> $variables |
||||
| 299 | * @return bool |
||||
| 300 | */ |
||||
| 301 | 364 | public function isReference(mixed $value, array $variables = []): bool |
|||
| 302 | { |
||||
| 303 | 364 | if (!is_string($value) || empty($value)) { |
|||
| 304 | 177 | return false; |
|||
| 305 | } |
||||
| 306 | |||||
| 307 | 356 | if (empty($variables)) { |
|||
| 308 | 356 | $variables = array_merge( |
|||
| 309 | 356 | array_keys($this->preIterationVariables), |
|||
| 310 | 356 | array_keys($this->postIterationVariables), |
|||
| 311 | 356 | $this->tableAliases, |
|||
| 312 | 356 | ); |
|||
| 313 | } |
||||
| 314 | |||||
| 315 | 356 | $variablesRegex = implode('|', $variables); |
|||
| 316 | |||||
| 317 | 356 | return (bool) preg_match( |
|||
| 318 | 356 | '/^\`?(' |
|||
| 319 | 356 | . $variablesRegex |
|||
| 320 | 356 | . '|CURRENT|NEW|OLD)\`?(\[\`.+\`\]|\[[\d\w\*]*\])*(\.(\`.+\`|@?[\d\w]*)(\[\`.+\`\]|\[[\d\w\*]*\])*)*$/', |
|||
| 321 | 356 | $value, |
|||
| 322 | 356 | ); |
|||
| 323 | } |
||||
| 324 | |||||
| 325 | |||||
| 326 | /** |
||||
| 327 | * Get the database connection instance. |
||||
| 328 | * |
||||
| 329 | * @return Connection |
||||
| 330 | */ |
||||
| 331 | 207 | public function getConnection() |
|||
| 332 | { |
||||
| 333 | 207 | return $this->connection; |
|||
| 334 | } |
||||
| 335 | |||||
| 336 | /** |
||||
| 337 | * Prepend the database name if the given query is on another database. |
||||
| 338 | * |
||||
| 339 | * @param mixed $query |
||||
| 340 | * @return mixed |
||||
| 341 | * @throws Exception |
||||
| 342 | */ |
||||
| 343 | protected function prependDatabaseNameIfCrossDatabaseQuery($query) |
||||
| 344 | { |
||||
| 345 | if ($query->getConnection()->getDatabaseName() !== |
||||
| 346 | $this->getConnection()->getDatabaseName()) { |
||||
| 347 | $databaseName = $query->getConnection()->getDatabaseName(); |
||||
| 348 | |||||
| 349 | if (!str_starts_with($query->from, $databaseName) && !str_contains($query->from, '.')) { |
||||
| 350 | throw new Exception(message: 'ArangoDB does not support cross database queries.'); |
||||
| 351 | } |
||||
| 352 | } |
||||
| 353 | |||||
| 354 | return $query; |
||||
| 355 | } |
||||
| 356 | |||||
| 357 | /** |
||||
| 358 | * Get the AQL representation of the query. |
||||
| 359 | */ |
||||
| 360 | 3 | public function toAql(): string |
|||
| 361 | { |
||||
| 362 | 3 | return $this->toSql(); |
|||
| 363 | } |
||||
| 364 | } |
||||
| 365 |