diff --git a/src/Configuration/Configuration.php b/src/Configuration/Configuration.php index b6b080ff..c1f72733 100644 --- a/src/Configuration/Configuration.php +++ b/src/Configuration/Configuration.php @@ -51,7 +51,7 @@ public function getName(): string */ public function create(Loader $config): void { - // By default, we do nothing here, but you can override this method in your configuration class + // By default, we do nothing here, but you can override this method in your configuration class // to set up your server or package as needed. } diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index 0fbc7a48..5b1498c0 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -290,16 +290,12 @@ private static function isComparisonOperator(mixed $comparator): bool return false; } - return in_array( - Str::upper($comparator), - [ + return in_array(Str::upper($comparator), [ '=', '>', '<', '>=', '=<', '<>', '!=', 'LIKE', 'NOT', 'IS NOT', "IN", "NOT IN", 'ILIKE', '&', '|', '<<', '>>', 'NOT LIKE', '&&', '@>', '<@', '?', '?|', '?&', '||', '-', '@?', '@@', '#-', 'IS DISTINCT FROM', 'IS NOT DISTINCT FROM', - ], - true - ); + ], true); } /** @@ -397,15 +393,14 @@ public function setTable(string $table): QueryBuilder * WHERE column IS NULL * * @param string $column - * @param string $boolean * @return QueryBuilder */ - public function whereNull(string $column, string $boolean = 'and'): QueryBuilder + public function whereNull(string $column): QueryBuilder { if (is_null($this->where)) { $this->where = $column . ' is null'; } else { - $this->where .= ' ' . $boolean . ' ' . $column . ' is null'; + $this->where .= ' and ' . $column . ' is null'; } return $this; @@ -416,16 +411,15 @@ public function whereNull(string $column, string $boolean = 'and'): QueryBuilder * * WHERE column NOT NULL * - * @param $column - * @param string $boolean + * @param string $column * @return QueryBuilder */ - public function whereNotNull($column, $boolean = 'and'): QueryBuilder + public function whereNotNull(string $column): QueryBuilder { if (is_null($this->where)) { $this->where = $column . ' is not null'; } else { - $this->where .= ' ' . $boolean . ' ' . $column . ' is not null'; + $this->where .= ' and ' . $column . ' is not null'; } return $this; @@ -440,7 +434,14 @@ public function whereNotNull($column, $boolean = 'and'): QueryBuilder */ public function whereNotBetween(string $column, array $range): QueryBuilder { - $this->whereBetween($column, $range, 'not'); + $range = (array) $range; + $between = implode(' and ', $range); + + if (is_null($this->where)) { + $this->where = $column . ' not between ' . $between; + } else { + $this->where .= ' and ' . $column . ' not between ' . $between; + } return $this; } @@ -452,32 +453,36 @@ public function whereNotBetween(string $column, array $range): QueryBuilder * * @param string $column * @param array $range - * @param string $boolean * @return QueryBuilder - * @throws QueryBuilderException */ - public function whereBetween(string $column, array $range, string $boolean = 'and'): QueryBuilder + public function whereBetween(string $column, array $range): QueryBuilder { - $range = (array)$range; + $range = (array) $range; $between = implode(' and ', $range); if (is_null($this->where)) { - if ($boolean == 'not') { - $this->where = $column . ' not between ' . $between; - } else { - $this->where = $column . ' between ' . $between; - } + $this->where = $column . ' between ' . $between; } else { - if ($boolean == 'not') { - $this->where .= ' and ' . $column . ' not between ' . $between; - } else { - $this->where .= ' ' . $boolean . ' ' . $column . ' between ' . $between; - } + $this->where .= ' and ' . $column . ' between ' . $between; } return $this; } + /** + * WHERE column NOT BETWEEN '' AND '' + * + * @param string $column + * @param mixed $value + * @return QueryBuilder + */ + public function whereDifferent(string $column, mixed $value): QueryBuilder + { + $this->where($column, '<>', $value); + + return $this; + } + /** * Where clause with <> comparison * @@ -488,7 +493,25 @@ public function whereBetween(string $column, array $range, string $boolean = 'an */ public function whereNotIn(string $column, array $range) { - $this->whereIn($column, $range, 'not'); + if ($range instanceof QueryBuilder) { + $range = "(" . $range->toSql() . ")"; + } + + if (is_array($range)) { + $range = (array)$range; + $this->where_data_binding = array_merge($this->where_data_binding, $range); + + $map = array_map(fn() => '?', $range); + $in = implode(', ', $map); + } else { + $in = (string) $range; + } + + if (is_null($this->where)) { + $this->where = $column . ' not in (' . $in . ')'; + } else { + $this->where .= ' and ' . $column . ' not in (' . $in . ')'; + } return $this; } @@ -498,11 +521,10 @@ public function whereNotIn(string $column, array $range) * * @param string $column * @param array $range - * @param string $boolean * @return QueryBuilder * @throws QueryBuilderException */ - public function whereIn(string $column, array $range, string $boolean = 'and'): QueryBuilder + public function whereIn(string $column, array $range): QueryBuilder { if ($range instanceof QueryBuilder) { $range = "(" . $range->toSql() . ")"; @@ -515,21 +537,13 @@ public function whereIn(string $column, array $range, string $boolean = 'and'): $map = array_map(fn() => '?', $range); $in = implode(', ', $map); } else { - $in = (string)$range; + $in = (string) $range; } if (is_null($this->where)) { - if ($boolean == 'not') { - $this->where = $column . ' not in (' . $in . ')'; - } else { - $this->where = $column . ' in (' . $in . ')'; - } + $this->where = $column . ' in (' . $in . ')'; } else { - if ($boolean == 'not') { - $this->where .= ' and ' . $column . ' not in (' . $in . ')'; - } else { - $this->where .= ' and ' . $column . ' in (' . $in . ')'; - } + $this->where .= ' and ' . $column . ' in (' . $in . ')'; } return $this; @@ -727,8 +741,8 @@ public function orOn(string $first, $comparator = '=', $second = null): QueryBui /** * Clause Group By * - * @param string $column - * @return QueryBuilder + * @param string $column + * @return QueryBuilder * @deprecated */ public function group($column) diff --git a/tests/Scheduler/ScheduleTest.php b/tests/Scheduler/ScheduleTest.php index a7259615..24b3a04f 100644 --- a/tests/Scheduler/ScheduleTest.php +++ b/tests/Scheduler/ScheduleTest.php @@ -223,10 +223,10 @@ public function test_is_due_every_minute() public function test_is_due_specific_time() { $this->schedule->dailyAt('10:30'); - + $dueTime = new DateTime('today 10:30'); $notDueTime = new DateTime('today 11:00'); - + $this->assertTrue($this->schedule->isDue($dueTime)); $this->assertFalse($this->schedule->isDue($notDueTime)); } @@ -284,10 +284,10 @@ public function test_fluent_api_chaining() public function test_is_due_hourly() { $this->schedule->hourly(); - + $dueTime = new DateTime('today 14:00'); $notDueTime = new DateTime('today 14:30'); - + $this->assertTrue($this->schedule->isDue($dueTime)); $this->assertFalse($this->schedule->isDue($notDueTime)); } @@ -295,10 +295,10 @@ public function test_is_due_hourly() public function test_is_due_with_step() { $this->schedule->everyFiveMinutes(); - + $dueTime = new DateTime('today 14:05'); $notDueTime = new DateTime('today 14:03'); - + $this->assertTrue($this->schedule->isDue($dueTime)); $this->assertFalse($this->schedule->isDue($notDueTime)); } diff --git a/tests/Scheduler/ScheduledEventTest.php b/tests/Scheduler/ScheduledEventTest.php index 07af122e..4f50cff3 100644 --- a/tests/Scheduler/ScheduledEventTest.php +++ b/tests/Scheduler/ScheduledEventTest.php @@ -287,7 +287,7 @@ public function test_throws_for_invalid_task_class() $this->expectExceptionMessage('Task class [NonExistentClass] does not exist'); // Create a mock that skips queue push - $event = new class(ScheduledEvent::TYPE_TASK, 'NonExistentClass') extends ScheduledEvent { + $event = new class (ScheduledEvent::TYPE_TASK, 'NonExistentClass') extends ScheduledEvent { protected function pushToQueue(\Bow\Queue\QueueTask $task): void { // Skip actual queue push in test @@ -303,7 +303,7 @@ public function test_throws_for_non_queue_task_instance() $this->expectExceptionMessage('Task must be an instance of'); // Create a mock that skips queue push - $event = new class(ScheduledEvent::TYPE_TASK, new \stdClass()) extends ScheduledEvent { + $event = new class (ScheduledEvent::TYPE_TASK, new \stdClass()) extends ScheduledEvent { protected function pushToQueue(\Bow\Queue\QueueTask $task): void { // Skip actual queue push in test diff --git a/tests/Scheduler/SchedulerCommandTest.php b/tests/Scheduler/SchedulerCommandTest.php index 63a0adb5..53b13f7e 100644 --- a/tests/Scheduler/SchedulerCommandTest.php +++ b/tests/Scheduler/SchedulerCommandTest.php @@ -353,7 +353,7 @@ public function test_display_result_shows_skipped_status() // Skipped status only occurs with overlap prevention when lock is already held // For this test, we'll just verify the displayResult method handles 'skipped' status // by checking the match expression in the code exists and works - + // Register an event that will be due $this->scheduler->call(fn() => 'test') ->everyMinute() @@ -387,7 +387,7 @@ public function test_list_shows_due_status_correctly() public function test_full_workflow_register_list_run() { $counter = 0; - + $this->scheduler->call(function () use (&$counter) { $counter++; return $counter; @@ -414,7 +414,7 @@ public function test_full_workflow_register_list_run() public function test_multiple_event_types_in_list() { - + $this->scheduler->call(fn() => 'closure')->everyMinute()->description('Closure event'); $this->scheduler->command('test:command')->hourly()->description('Command event'); $this->scheduler->exec('echo hello')->daily()->description('Exec event'); @@ -431,7 +431,7 @@ public function test_multiple_event_types_in_list() public function test_events_with_different_schedules() { - + $this->scheduler->call(fn() => null)->everyMinute(); $this->scheduler->call(fn() => null)->hourly(); $this->scheduler->call(fn() => null)->daily(); @@ -459,7 +459,7 @@ public function test_loads_routes_scheduler_file() $markerFile = TESTING_RESOURCE_BASE_DIRECTORY . '/scheduler_marker.txt'; $schedulerFile = $routesDir . '/scheduler.php'; - + file_put_contents($schedulerFile, '