diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bc1f72a6726..432ee0620dff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,56 @@ # Release Notes for 11.x -## [Unreleased](https://github.com/laravel/framework/compare/v11.38.2...11.x) +## [Unreleased](https://github.com/laravel/framework/compare/v11.40.0...11.x) + +## [v11.40.0](https://github.com/laravel/framework/compare/v11.39.1...v11.40.0) - 2025-01-24 + +* draft: fix: Don't release lock for ShouldBeUniqueUntilProcessing Job that gets released by [@mathiasgrimm](https://github.com/mathiasgrimm) in https://github.com/laravel/framework/pull/54261 +* [11.x] Add Laravel Pint by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/53835 +* Add self to HasCollection type param in Model by [@thena-seer-sfg](https://github.com/thena-seer-sfg) in https://github.com/laravel/framework/pull/54311 +* [11.x] Add pending attributes by [@tontonsb](https://github.com/tontonsb) in https://github.com/laravel/framework/pull/53720 +* fix: `schedule:test` on commands using runInBackground by [@dallyger](https://github.com/dallyger) in https://github.com/laravel/framework/pull/54321 +* [11.x] Helper methods to dump responses of the Laravel HTTP client by [@morrislaptop](https://github.com/morrislaptop) in https://github.com/laravel/framework/pull/54317 +* Add support for cursor editor in ResolvesDumpSource by [@tuxfamily](https://github.com/tuxfamily) in https://github.com/laravel/framework/pull/54318 +* [11.x] Add Customizable Date Validation Rule with Flexible Date Constraints by [@michaelnabil230](https://github.com/michaelnabil230) in https://github.com/laravel/framework/pull/53465 +* [11.x] start syncing StyleCI rules to Pint by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/54326 +* [11.x] apply our new Pint rule to the `/tests` directory by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/54325 +* fix(Collection::pop()): count < 1 by [@artumi-richard](https://github.com/artumi-richard) in https://github.com/laravel/framework/pull/54340 +* Patch CVE-2025-22145 in nesbot/carbon package by [@dennis-koster](https://github.com/dennis-koster) in https://github.com/laravel/framework/pull/54335 +* [11.x] Prevent unintended serialization and compression by [@JeppeKnockaert](https://github.com/JeppeKnockaert) in https://github.com/laravel/framework/pull/54337 +* [11.x] Pass collection of models to `whereMorphedTo` / `whereNotMorphedTo` by [@gdebrauwer](https://github.com/gdebrauwer) in https://github.com/laravel/framework/pull/54324 + +## [v11.39.1](https://github.com/laravel/framework/compare/v11.39.0...v11.39.1) - 2025-01-22 + +* fix: collapseWithKeys on empty collection by [@benatoff](https://github.com/benatoff) in https://github.com/laravel/framework/pull/54290 +* fix(broadcaster): incorrect channel matching because of dot in pattern by [@021-projects](https://github.com/021-projects) in https://github.com/laravel/framework/pull/54303 +* [11.x] Use constructor property promotion for database query condition expression by [@shaedrich](https://github.com/shaedrich) in https://github.com/laravel/framework/pull/54302 +* [11.x] Add IncrementOrCreate method to Eloquent by [@carloeusebi](https://github.com/carloeusebi) in https://github.com/laravel/framework/pull/54300 +* [11.x] Add additional test cases for Arr helper to enhance coverage by [@mrvipchien](https://github.com/mrvipchien) in https://github.com/laravel/framework/pull/54298 +* Bump vite from 5.2.14 to 5.4.12 in /src/Illuminate/Foundation/resources/exceptions/renderer by [@dependabot](https://github.com/dependabot) in https://github.com/laravel/framework/pull/54296 +* [11.x] Fix unique jobs that have a uniqueVia method by [@DougSisk](https://github.com/DougSisk) in https://github.com/laravel/framework/pull/54294 + +## [v11.39.0](https://github.com/laravel/framework/compare/v11.38.2...v11.39.0) - 2025-01-21 + +* [11.x] Replace duplicate `ValidatedInput` functions with `InteractsWithData` trait by [@stevebauman](https://github.com/stevebauman) in https://github.com/laravel/framework/pull/54208 +* [11.x] Improve `Email` validation rule custom translation messages by [@SanderMuller](https://github.com/SanderMuller) in https://github.com/laravel/framework/pull/54202 +* [11.x] Fix deprecation warnings in `optimize:clear` and `optimize` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/54197 +* [11.x] Add support for phpredis backoff and max retry config options by [@TheLevti](https://github.com/TheLevti) in https://github.com/laravel/framework/pull/54191 +* Introduces UseFactory attribute by [@christopherarter](https://github.com/christopherarter) in https://github.com/laravel/framework/pull/54065 +* [11.x] Set class-string generic on `UseFactory` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/54215 +* [11.x] switch LazyCollection::make() for new LazyCollection() by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/54216 +* support style file name hashes with query strings in manifest by [@newapx](https://github.com/newapx) in https://github.com/laravel/framework/pull/54219 +* [11.x] Solidify `Rule::email()` tests by [@SanderMuller](https://github.com/SanderMuller) in https://github.com/laravel/framework/pull/54226 +* [11.x] Fix line-ending mismatch in CliDumperTest::testArray and CliDumperTest::testObject by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/54222 +* Add a report/log option to filesystem exceptions without throwing by [@lotharthesavior](https://github.com/lotharthesavior) in https://github.com/laravel/framework/pull/54212 +* [11.x] Fix Cache component to be aware of phpredis serialization and compression settings by [@TheLevti](https://github.com/TheLevti) in https://github.com/laravel/framework/pull/54221 +* [11.x] fix: Forcing DB Session driver to always use the write connection by [@mathiasgrimm](https://github.com/mathiasgrimm) in https://github.com/laravel/framework/pull/54231 +* [11.x] Fix line-ending mismatch in `BladeComponentTagCompilerTest` under `Illuminate\Tests\View\Blade` by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/54233 +* [11.x] Fix job not logged in failed_jobs table if timeout occurs within database transaction by [@decaylala](https://github.com/decaylala) in https://github.com/laravel/framework/pull/54173 +* [11.x] Fix unique job lock is not released on model not found exception, lock gets stuck. by [@zackAJ](https://github.com/zackAJ) in https://github.com/laravel/framework/pull/54000 +* [11.x] Fix line-ending mismatch on Windows test by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/54236 +* Added support in DB::prohibitDestructiveCommands to preventing destructive Rollback… by [@hexathos](https://github.com/hexathos) in https://github.com/laravel/framework/pull/54238 +* [11.x] Add applyAfterQueryCallbacks Support to Non-Mutator Cases in pluck Method by [@batinmustu](https://github.com/batinmustu) in https://github.com/laravel/framework/pull/54268 +* [11.x] `addPath()` Allow adding new path for translation loader. by [@selcukcukur](https://github.com/selcukcukur) in https://github.com/laravel/framework/pull/54277 ## [v11.38.2](https://github.com/laravel/framework/compare/v11.38.1...v11.38.2) - 2025-01-15 diff --git a/composer.json b/composer.json index f39a7a8229a3..39ea6589a7f7 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "league/flysystem-local": "^3.25.1", "league/uri": "^7.5.1", "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.72.2|^3.4", + "nesbot/carbon": "^2.72.6|^3.8.4", "nunomaduro/termwind": "^2.0", "psr/container": "^1.1.1|^2.0.1", "psr/log": "^1.0|^2.0|^3.0", @@ -104,6 +104,7 @@ "fakerphp/faker": "^1.24", "guzzlehttp/promises": "^2.0.3", "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", "league/flysystem-aws-s3-v3": "^3.25.1", "league/flysystem-ftp": "^3.25.1", "league/flysystem-path-prefixing": "^3.25.1", diff --git a/pint.json b/pint.json new file mode 100644 index 000000000000..c36f1f8282e2 --- /dev/null +++ b/pint.json @@ -0,0 +1,211 @@ +{ + "preset": "empty", + "rules": { + "align_multiline_comment": true, + "array_indentation": true, + "array_syntax": { + "syntax": "short" + }, + "binary_operator_spaces": { + "default": "single_space" + }, + "blank_line_after_namespace": true, + "blank_line_after_opening_tag": true, + "blank_line_before_statement": { + "statements": [ + "return" + ] + }, + "blank_line_between_import_groups": true, + "blank_lines_before_namespace": true, + "braces_position": { + "control_structures_opening_brace": "same_line", + "functions_opening_brace": "next_line_unless_newline_at_signature_end", + "anonymous_functions_opening_brace": "same_line", + "classes_opening_brace": "next_line_unless_newline_at_signature_end", + "anonymous_classes_opening_brace": "next_line_unless_newline_at_signature_end", + "allow_single_line_empty_anonymous_classes": false, + "allow_single_line_anonymous_functions": false + }, + "cast_spaces": true, + "class_definition": true, + "class_reference_name_casing": true, + "clean_namespace": true, + "compact_nullable_type_declaration": true, + "concat_space": true, + "constant_case": { + "case": "lower" + }, + "control_structure_braces": true, + "declare_equal_normalize": true, + "elseif": true, + "encoding": true, + "full_opening_tag": true, + "function_declaration": true, + "heredoc_to_nowdoc": true, + "include": true, + "increment_style": { + "style": "post" + }, + "indentation_type": true, + "integer_literal_case": true, + "lambda_not_used_import": true, + "line_ending": true, + "list_syntax": { + "syntax": "short" + }, + "lowercase_cast": true, + "lowercase_keywords": true, + "lowercase_static_reference": true, + "magic_constant_casing": true, + "magic_method_casing": true, + "method_argument_space": { + "on_multiline": "ignore" + }, + "method_chaining_indentation": true, + "multiline_whitespace_before_semicolons": { + "strategy": "no_multi_line" + }, + "native_function_casing": true, + "native_type_declaration_casing": true, + "no_alternative_syntax": true, + "no_binary_string": true, + "no_blank_lines_after_class_opening": true, + "no_blank_lines_after_phpdoc": true, + "no_closing_tag": true, + "no_empty_phpdoc": true, + "no_empty_statement": true, + "no_extra_blank_lines": { + "tokens": [ + "extra", + "throw", + "use" + ] + }, + "no_leading_import_slash": true, + "no_leading_namespace_whitespace": true, + "no_mixed_echo_print": { + "use": "echo" + }, + "no_multiline_whitespace_around_double_arrow": true, + "no_short_bool_cast": true, + "no_singleline_whitespace_before_semicolons": true, + "no_space_around_double_colon": true, + "no_spaces_around_offset": { + "positions": [ + "inside", + "outside" + ] + }, + "no_spaces_after_function_name": true, + "no_trailing_comma_in_singleline": true, + "no_trailing_whitespace": true, + "no_trailing_whitespace_in_comment": true, + "no_unneeded_braces": true, + "no_unneeded_control_parentheses": true, + "no_unneeded_import_alias": true, + "no_unset_cast": true, + "no_unused_imports": true, + "no_useless_return": true, + "no_whitespace_before_comma_in_array": true, + "no_whitespace_in_blank_line": true, + "normalize_index_brace": true, + "not_operator_with_successor_space": true, + "nullable_type_declaration_for_default_null_value": true, + "object_operator_without_whitespace": true, + "ordered_imports": { + "sort_algorithm": "alpha", + "imports_order": [ + "const", + "class", + "function" + ] + }, + "phpdoc_align": { + "align": "left", + "spacing": { + "param": 2 + } + }, + "phpdoc_indent": true, + "phpdoc_inline_tag_normalizer": true, + "phpdoc_no_access": true, + "phpdoc_no_package": true, + "phpdoc_no_useless_inheritdoc": true, + "phpdoc_order": { + "order": [ + "param", + "return", + "throws" + ] + }, + "phpdoc_return_self_reference": true, + "phpdoc_scalar": true, + "phpdoc_separation": { + "groups": [ + [ + "deprecated", + "link", + "see", + "since" + ], + [ + "author", + "copyright", + "license" + ], + [ + "category", + "package", + "subpackage" + ], + [ + "property", + "property-read", + "property-write" + ], + [ + "param", + "return" + ] + ] + }, + "phpdoc_single_line_var_spacing": true, + "phpdoc_summary": true, + "phpdoc_trim": true, + "phpdoc_types": true, + "phpdoc_var_without_name": true, + "return_type_declaration": { + "space_before": "none" + }, + "short_scalar_cast": true, + "single_blank_line_at_eof": true, + "single_class_element_per_statement": true, + "single_import_per_statement": true, + "single_line_after_imports": true, + "single_line_comment_style": true, + "single_quote": true, + "space_after_semicolon": true, + "spaces_inside_parentheses": true, + "standardize_not_equals": true, + "switch_case_semicolon_to_colon": true, + "switch_case_space": true, + "switch_continue_to_break": true, + "ternary_operator_spaces": true, + "trailing_comma_in_multiline": true, + "trim_array_spaces": true, + "type_declaration_spaces": true, + "types_spaces": true, + "unary_operator_spaces": true, + "visibility_required": { + "elements": [ + "method", + "property" + ] + }, + "whitespace_after_comma_in_array": true + }, + "notPath": [ + "tests/Foundation/fixtures/bad-syntax-strategy.php" + ] +} diff --git a/src/Illuminate/Auth/DatabaseUserProvider.php b/src/Illuminate/Auth/DatabaseUserProvider.php index aaaafd8a8b45..def86b346a55 100755 --- a/src/Illuminate/Auth/DatabaseUserProvider.php +++ b/src/Illuminate/Auth/DatabaseUserProvider.php @@ -87,8 +87,8 @@ public function retrieveByToken($identifier, #[\SensitiveParameter] $token) public function updateRememberToken(UserContract $user, #[\SensitiveParameter] $token) { $this->connection->table($this->table) - ->where($user->getAuthIdentifierName(), $user->getAuthIdentifier()) - ->update([$user->getRememberTokenName() => $token]); + ->where($user->getAuthIdentifierName(), $user->getAuthIdentifier()) + ->update([$user->getRememberTokenName() => $token]); } /** diff --git a/src/Illuminate/Auth/EloquentUserProvider.php b/src/Illuminate/Auth/EloquentUserProvider.php index 77c8fef712b1..dc7a21ecac5d 100755 --- a/src/Illuminate/Auth/EloquentUserProvider.php +++ b/src/Illuminate/Auth/EloquentUserProvider.php @@ -55,8 +55,8 @@ public function retrieveById($identifier) $model = $this->createModel(); return $this->newModelQuery($model) - ->where($model->getAuthIdentifierName(), $identifier) - ->first(); + ->where($model->getAuthIdentifierName(), $identifier) + ->first(); } /** diff --git a/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php b/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php index 5abccd59bafb..eb21f2c0662f 100644 --- a/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php +++ b/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php @@ -372,6 +372,8 @@ protected function retrieveChannelOptions($channel) */ protected function channelNameMatchesPattern($channel, $pattern) { + $pattern = str_replace('.', '\.', $pattern); + return preg_match('/^'.preg_replace('/\{(.*?)\}/', '([^\.]+)', $pattern).'$/', $channel); } diff --git a/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php b/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php index 2799de29a8fa..962d814183a8 100644 --- a/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php +++ b/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php @@ -135,7 +135,7 @@ protected function decodePusherResponse($request, $response) } return response()->json(json_decode($response, true)) - ->withCallback($request->callback); + ->withCallback($request->callback); } /** diff --git a/src/Illuminate/Bus/Batch.php b/src/Illuminate/Bus/Batch.php index d551abb4e19e..bcfb898cb940 100644 --- a/src/Illuminate/Bus/Batch.php +++ b/src/Illuminate/Bus/Batch.php @@ -171,9 +171,9 @@ public function add($jobs) return with($this->prepareBatchedChain($job), function ($chain) { return $chain->first() - ->allOnQueue($this->options['queue'] ?? null) - ->allOnConnection($this->options['connection'] ?? null) - ->chain($chain->slice(1)->values()->all()); + ->allOnQueue($this->options['queue'] ?? null) + ->allOnConnection($this->options['connection'] ?? null) + ->chain($chain->slice(1)->values()->all()); }); } else { $job->withBatchId($this->id); diff --git a/src/Illuminate/Bus/DatabaseBatchRepository.php b/src/Illuminate/Bus/DatabaseBatchRepository.php index 2e0c7c68a9e6..31bd39878c57 100644 --- a/src/Illuminate/Bus/DatabaseBatchRepository.php +++ b/src/Illuminate/Bus/DatabaseBatchRepository.php @@ -58,14 +58,14 @@ public function __construct(BatchFactory $factory, Connection $connection, strin public function get($limit = 50, $before = null) { return $this->connection->table($this->table) - ->orderByDesc('id') - ->take($limit) - ->when($before, fn ($q) => $q->where('id', '<', $before)) - ->get() - ->map(function ($batch) { - return $this->toBatch($batch); - }) - ->all(); + ->orderByDesc('id') + ->take($limit) + ->when($before, fn ($q) => $q->where('id', '<', $before)) + ->get() + ->map(function ($batch) { + return $this->toBatch($batch); + }) + ->all(); } /** @@ -77,9 +77,9 @@ public function get($limit = 50, $before = null) public function find(string $batchId) { $batch = $this->connection->table($this->table) - ->useWritePdo() - ->where('id', $batchId) - ->first(); + ->useWritePdo() + ->where('id', $batchId) + ->first(); if ($batch) { return $this->toBatch($batch); @@ -185,8 +185,8 @@ protected function updateAtomicValues(string $batchId, Closure $callback) { return $this->connection->transaction(function () use ($batchId, $callback) { $batch = $this->connection->table($this->table)->where('id', $batchId) - ->lockForUpdate() - ->first(); + ->lockForUpdate() + ->first(); return is_null($batch) ? [] : tap($callback($batch), function ($values) use ($batchId) { $this->connection->table($this->table)->where('id', $batchId)->update($values); diff --git a/src/Illuminate/Cache/DatabaseLock.php b/src/Illuminate/Cache/DatabaseLock.php index 715d141c0d45..506696fdbd16 100644 --- a/src/Illuminate/Cache/DatabaseLock.php +++ b/src/Illuminate/Cache/DatabaseLock.php @@ -112,9 +112,9 @@ public function release() { if ($this->isOwnedByCurrentProcess()) { $this->connection->table($this->table) - ->where('key', $this->name) - ->where('owner', $this->owner) - ->delete(); + ->where('key', $this->name) + ->where('owner', $this->owner) + ->delete(); return true; } @@ -130,8 +130,8 @@ public function release() public function forceRelease() { $this->connection->table($this->table) - ->where('key', $this->name) - ->delete(); + ->where('key', $this->name) + ->delete(); } /** diff --git a/src/Illuminate/Cache/DatabaseStore.php b/src/Illuminate/Cache/DatabaseStore.php index 15ca30b322b7..56564c2988e3 100755 --- a/src/Illuminate/Cache/DatabaseStore.php +++ b/src/Illuminate/Cache/DatabaseStore.php @@ -263,7 +263,7 @@ protected function incrementOrDecrement($key, $value, Closure $callback) $prefixed = $this->prefix.$key; $cache = $this->table()->where('key', $prefixed) - ->lockForUpdate()->first(); + ->lockForUpdate()->first(); // If there is no value in the cache, we will return false here. Otherwise the // value will be decrypted and we will proceed with this function to either diff --git a/src/Illuminate/Cache/MemcachedStore.php b/src/Illuminate/Cache/MemcachedStore.php index 88198d9222bf..5197c2df71f5 100755 --- a/src/Illuminate/Cache/MemcachedStore.php +++ b/src/Illuminate/Cache/MemcachedStore.php @@ -45,7 +45,7 @@ public function __construct($memcached, $prefix = '') $this->memcached = $memcached; $this->onVersionThree = (new ReflectionMethod('Memcached', 'getMulti')) - ->getNumberOfParameters() == 2; + ->getNumberOfParameters() == 2; } /** diff --git a/src/Illuminate/Cache/RedisStore.php b/src/Illuminate/Cache/RedisStore.php index d84c9a505965..ebdd0febbd82 100755 --- a/src/Illuminate/Cache/RedisStore.php +++ b/src/Illuminate/Cache/RedisStore.php @@ -432,6 +432,10 @@ public function setPrefix($prefix) protected function pack($value, $connection) { if ($connection instanceof PhpRedisConnection) { + if ($this->shouldBeStoredWithoutSerialization($value)) { + return $value; + } + if ($connection->serialized()) { return $connection->pack([$value])[0]; } @@ -452,7 +456,18 @@ protected function pack($value, $connection) */ protected function serialize($value) { - return is_numeric($value) && ! in_array($value, [INF, -INF]) && ! is_nan($value) ? $value : serialize($value); + return $this->shouldBeStoredWithoutSerialization($value) ? $value : serialize($value); + } + + /** + * Determine if the given value should be stored as plain value. + * + * @param mixed $value + * @return bool + */ + protected function shouldBeStoredWithoutSerialization($value): bool + { + return is_numeric($value) && ! in_array($value, [INF, -INF]) && ! is_nan($value); } /** diff --git a/src/Illuminate/Cache/Repository.php b/src/Illuminate/Cache/Repository.php index a01f4aedb209..a5f1df9db137 100755 --- a/src/Illuminate/Cache/Repository.php +++ b/src/Illuminate/Cache/Repository.php @@ -638,7 +638,7 @@ protected function getSeconds($ttl) * * @return string|null */ - protected function getName() + public function getName() { return $this->config['store'] ?? null; } diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 71b432cef89c..01e1114ed84b 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -148,6 +148,10 @@ public function collapse() */ public function collapseWithKeys() { + if (! $this->items) { + return new static; + } + $results = []; foreach ($this->items as $key => $values) { @@ -982,6 +986,10 @@ public function select($keys) */ public function pop($count = 1) { + if ($count < 1) { + return new static; + } + if ($count === 1) { return array_pop($this->items); } diff --git a/src/Illuminate/Console/Scheduling/ManagesFrequencies.php b/src/Illuminate/Console/Scheduling/ManagesFrequencies.php index 105334e01b43..619d852e4817 100644 --- a/src/Illuminate/Console/Scheduling/ManagesFrequencies.php +++ b/src/Illuminate/Console/Scheduling/ManagesFrequencies.php @@ -391,7 +391,7 @@ protected function hourBasedSchedule($minutes, $hours) $hours = is_array($hours) ? implode(',', $hours) : $hours; return $this->spliceIntoPosition(1, $minutes) - ->spliceIntoPosition(2, $hours); + ->spliceIntoPosition(2, $hours); } /** @@ -492,8 +492,8 @@ public function sundays() public function weekly() { return $this->spliceIntoPosition(1, 0) - ->spliceIntoPosition(2, 0) - ->spliceIntoPosition(5, 0); + ->spliceIntoPosition(2, 0) + ->spliceIntoPosition(5, 0); } /** @@ -518,8 +518,8 @@ public function weeklyOn($dayOfWeek, $time = '0:0') public function monthly() { return $this->spliceIntoPosition(1, 0) - ->spliceIntoPosition(2, 0) - ->spliceIntoPosition(3, 1); + ->spliceIntoPosition(2, 0) + ->spliceIntoPosition(3, 1); } /** @@ -574,9 +574,9 @@ public function lastDayOfMonth($time = '0:0') public function quarterly() { return $this->spliceIntoPosition(1, 0) - ->spliceIntoPosition(2, 0) - ->spliceIntoPosition(3, 1) - ->spliceIntoPosition(4, '1-12/3'); + ->spliceIntoPosition(2, 0) + ->spliceIntoPosition(3, 1) + ->spliceIntoPosition(4, '1-12/3'); } /** @@ -591,7 +591,7 @@ public function quarterlyOn($dayOfQuarter = 1, $time = '0:0') $this->dailyAt($time); return $this->spliceIntoPosition(3, $dayOfQuarter) - ->spliceIntoPosition(4, '1-12/3'); + ->spliceIntoPosition(4, '1-12/3'); } /** @@ -602,9 +602,9 @@ public function quarterlyOn($dayOfQuarter = 1, $time = '0:0') public function yearly() { return $this->spliceIntoPosition(1, 0) - ->spliceIntoPosition(2, 0) - ->spliceIntoPosition(3, 1) - ->spliceIntoPosition(4, 1); + ->spliceIntoPosition(2, 0) + ->spliceIntoPosition(3, 1) + ->spliceIntoPosition(4, 1); } /** @@ -620,7 +620,7 @@ public function yearlyOn($month = 1, $dayOfMonth = 1, $time = '0:0') $this->dailyAt($time); return $this->spliceIntoPosition(3, $dayOfMonth) - ->spliceIntoPosition(4, $month); + ->spliceIntoPosition(4, $month); } /** diff --git a/src/Illuminate/Console/Scheduling/ScheduleTestCommand.php b/src/Illuminate/Console/Scheduling/ScheduleTestCommand.php index 5e902a05b13e..0da0f7d04aa0 100644 --- a/src/Illuminate/Console/Scheduling/ScheduleTestCommand.php +++ b/src/Illuminate/Console/Scheduling/ScheduleTestCommand.php @@ -76,9 +76,11 @@ public function handle(Schedule $schedule) $description = sprintf( 'Running [%s]%s', $command, - $event->runInBackground ? ' in background' : '', + $event->runInBackground ? ' normally in background' : '', ); + $event->runInBackground = false; + $this->components->task($description, fn () => $event->run($this->laravel)); if (! $event instanceof CallbackEvent) { diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index 692dfdee2f67..64f2eb25bf2b 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -467,7 +467,7 @@ public function cursor($query, $bindings = [], $useReadPdo = true) // mode and prepare the bindings for the query. Once that's done we will be // ready to execute the query against the database and return the cursor. $statement = $this->prepared($this->getPdoForSelect($useReadPdo) - ->prepare($query)); + ->prepare($query)); $this->bindValues( $statement, $this->prepareBindings($bindings) diff --git a/src/Illuminate/Database/Connectors/PostgresConnector.php b/src/Illuminate/Database/Connectors/PostgresConnector.php index ef806dd9ab2f..31d2ff4732ca 100755 --- a/src/Illuminate/Database/Connectors/PostgresConnector.php +++ b/src/Illuminate/Database/Connectors/PostgresConnector.php @@ -50,65 +50,6 @@ public function connect(array $config) return $connection; } - /** - * Set the connection transaction isolation level. - * - * @param \PDO $connection - * @param array $config - * @return void - */ - protected function configureIsolationLevel($connection, array $config) - { - if (isset($config['isolation_level'])) { - $connection->prepare("set session characteristics as transaction isolation level {$config['isolation_level']}")->execute(); - } - } - - /** - * Set the timezone on the connection. - * - * @param \PDO $connection - * @param array $config - * @return void - */ - protected function configureTimezone($connection, array $config) - { - if (isset($config['timezone'])) { - $timezone = $config['timezone']; - - $connection->prepare("set time zone '{$timezone}'")->execute(); - } - } - - /** - * Set the "search_path" on the database connection. - * - * @param \PDO $connection - * @param array $config - * @return void - */ - protected function configureSearchPath($connection, $config) - { - if (isset($config['search_path']) || isset($config['schema'])) { - $searchPath = $this->quoteSearchPath( - $this->parseSearchPath($config['search_path'] ?? $config['schema']) - ); - - $connection->prepare("set search_path to {$searchPath}")->execute(); - } - } - - /** - * Format the search path for the DSN. - * - * @param array $searchPath - * @return string - */ - protected function quoteSearchPath($searchPath) - { - return count($searchPath) === 1 ? '"'.$searchPath[0].'"' : '"'.implode('", "', $searchPath).'"'; - } - /** * Create a DSN string from a configuration. * @@ -171,6 +112,65 @@ protected function addSslOptions($dsn, array $config) return $dsn; } + /** + * Set the connection transaction isolation level. + * + * @param \PDO $connection + * @param array $config + * @return void + */ + protected function configureIsolationLevel($connection, array $config) + { + if (isset($config['isolation_level'])) { + $connection->prepare("set session characteristics as transaction isolation level {$config['isolation_level']}")->execute(); + } + } + + /** + * Set the timezone on the connection. + * + * @param \PDO $connection + * @param array $config + * @return void + */ + protected function configureTimezone($connection, array $config) + { + if (isset($config['timezone'])) { + $timezone = $config['timezone']; + + $connection->prepare("set time zone '{$timezone}'")->execute(); + } + } + + /** + * Set the "search_path" on the database connection. + * + * @param \PDO $connection + * @param array $config + * @return void + */ + protected function configureSearchPath($connection, $config) + { + if (isset($config['search_path']) || isset($config['schema'])) { + $searchPath = $this->quoteSearchPath( + $this->parseSearchPath($config['search_path'] ?? $config['schema']) + ); + + $connection->prepare("set search_path to {$searchPath}")->execute(); + } + } + + /** + * Format the search path for the DSN. + * + * @param array $searchPath + * @return string + */ + protected function quoteSearchPath($searchPath) + { + return count($searchPath) === 1 ? '"'.$searchPath[0].'"' : '"'.implode('", "', $searchPath).'"'; + } + /** * Configure the synchronous_commit setting. * @@ -180,10 +180,8 @@ protected function addSslOptions($dsn, array $config) */ protected function configureSynchronousCommit($connection, array $config) { - if (! isset($config['synchronous_commit'])) { - return; + if (isset($config['synchronous_commit'])) { + $connection->prepare("set synchronous_commit to '{$config['synchronous_commit']}'")->execute(); } - - $connection->prepare("set synchronous_commit to '{$config['synchronous_commit']}'")->execute(); } } diff --git a/src/Illuminate/Database/Console/DumpCommand.php b/src/Illuminate/Database/Console/DumpCommand.php index 64148e90f9ac..b27d6c66a93c 100644 --- a/src/Illuminate/Database/Console/DumpCommand.php +++ b/src/Illuminate/Database/Console/DumpCommand.php @@ -77,10 +77,10 @@ protected function schemaState(Connection $connection) $migrationTable = is_array($migrations) ? ($migrations['table'] ?? 'migrations') : $migrations; return $connection->getSchemaState() - ->withMigrationTable($migrationTable) - ->handleOutputUsing(function ($type, $buffer) { - $this->output->write($buffer); - }); + ->withMigrationTable($migrationTable) + ->handleOutputUsing(function ($type, $buffer) { + $this->output->write($buffer); + }); } /** diff --git a/src/Illuminate/Database/Console/Migrations/RollbackCommand.php b/src/Illuminate/Database/Console/Migrations/RollbackCommand.php index 0a88ec5a1a06..8846a5e376cf 100755 --- a/src/Illuminate/Database/Console/Migrations/RollbackCommand.php +++ b/src/Illuminate/Database/Console/Migrations/RollbackCommand.php @@ -2,7 +2,9 @@ namespace Illuminate\Database\Console\Migrations; +use Illuminate\Console\Command; use Illuminate\Console\ConfirmableTrait; +use Illuminate\Console\Prohibitable; use Illuminate\Database\Migrations\Migrator; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputOption; @@ -10,7 +12,7 @@ #[AsCommand('migrate:rollback')] class RollbackCommand extends BaseCommand { - use ConfirmableTrait; + use ConfirmableTrait, Prohibitable; /** * The console command name. @@ -53,8 +55,9 @@ public function __construct(Migrator $migrator) */ public function handle() { - if (! $this->confirmToProceed()) { - return 1; + if ($this->isProhibited() || + ! $this->confirmToProceed()) { + return Command::FAILURE; } $this->migrator->usingConnection($this->option('database'), function () { diff --git a/src/Illuminate/Database/Console/Migrations/StatusCommand.php b/src/Illuminate/Database/Console/Migrations/StatusCommand.php index 191983288739..378c4a720d26 100644 --- a/src/Illuminate/Database/Console/Migrations/StatusCommand.php +++ b/src/Illuminate/Database/Console/Migrations/StatusCommand.php @@ -101,19 +101,19 @@ public function handle() protected function getStatusFor(array $ran, array $batches) { return (new Collection($this->getAllMigrationFiles())) - ->map(function ($migration) use ($ran, $batches) { - $migrationName = $this->migrator->getMigrationName($migration); + ->map(function ($migration) use ($ran, $batches) { + $migrationName = $this->migrator->getMigrationName($migration); - $status = in_array($migrationName, $ran) - ? 'Ran' - : 'Pending'; + $status = in_array($migrationName, $ran) + ? 'Ran' + : 'Pending'; - if (in_array($migrationName, $ran)) { - $status = '['.$batches[$migrationName].'] '.$status; - } + if (in_array($migrationName, $ran)) { + $status = '['.$batches[$migrationName].'] '.$status; + } - return [$migrationName, $status]; - }); + return [$migrationName, $status]; + }); } /** diff --git a/src/Illuminate/Database/Console/Seeds/SeedCommand.php b/src/Illuminate/Database/Console/Seeds/SeedCommand.php index 0d4dbd5ad4fe..4ce2b0213129 100644 --- a/src/Illuminate/Database/Console/Seeds/SeedCommand.php +++ b/src/Illuminate/Database/Console/Seeds/SeedCommand.php @@ -96,8 +96,8 @@ protected function getSeeder() } return $this->laravel->make($class) - ->setContainer($this->laravel) - ->setCommand($this); + ->setContainer($this->laravel) + ->setCommand($this); } /** diff --git a/src/Illuminate/Database/Console/WipeCommand.php b/src/Illuminate/Database/Console/WipeCommand.php index f756dd121de0..754c9eea8414 100644 --- a/src/Illuminate/Database/Console/WipeCommand.php +++ b/src/Illuminate/Database/Console/WipeCommand.php @@ -69,8 +69,8 @@ public function handle() protected function dropAllTables($database) { $this->laravel['db']->connection($database) - ->getSchemaBuilder() - ->dropAllTables(); + ->getSchemaBuilder() + ->dropAllTables(); } /** @@ -82,8 +82,8 @@ protected function dropAllTables($database) protected function dropAllViews($database) { $this->laravel['db']->connection($database) - ->getSchemaBuilder() - ->dropAllViews(); + ->getSchemaBuilder() + ->dropAllViews(); } /** @@ -95,8 +95,8 @@ protected function dropAllViews($database) protected function dropAllTypes($database) { $this->laravel['db']->connection($database) - ->getSchemaBuilder() - ->dropAllTypes(); + ->getSchemaBuilder() + ->dropAllTypes(); } /** diff --git a/src/Illuminate/Database/DatabaseManager.php b/src/Illuminate/Database/DatabaseManager.php index 6f8867c4e51d..34ba2cc01bd9 100755 --- a/src/Illuminate/Database/DatabaseManager.php +++ b/src/Illuminate/Database/DatabaseManager.php @@ -228,7 +228,7 @@ protected function configuration($name) } return (new ConfigurationUrlParser) - ->parseConfiguration($config); + ->parseConfiguration($config); } /** @@ -374,8 +374,8 @@ protected function refreshPdoConnections($name) ); return $this->connections[$name] - ->setPdo($fresh->getRawPdo()) - ->setReadPdo($fresh->getRawReadPdo()); + ->setPdo($fresh->getRawPdo()) + ->setReadPdo($fresh->getRawReadPdo()); } /** diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index 9a4642d3feae..e12ec14a2591 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -53,6 +53,13 @@ class Builder implements BuilderContract */ protected $model; + /** + * The attributes that should be added to new models created by this builder. + * + * @var array + */ + public $pendingAttributes = []; + /** * The relationships that should be eager loaded. * @@ -622,6 +629,25 @@ public function updateOrCreate(array $attributes, array $values = []) }); } + /** + * Create a record matching the attributes, or increment the existing record. + * + * @param array $attributes + * @param string $column + * @param int|float $default + * @param int|float $step + * @param array $extra + * @return TModel + */ + public function incrementOrCreate(array $attributes, string $column = 'count', $default = 1, $step = 1, array $extra = []) + { + return tap($this->firstOrCreate($attributes, [$column => $default]), function ($instance) use ($column, $step, $extra) { + if (! $instance->wasRecentlyCreated) { + $instance->increment($column, $step, $extra); + } + }); + } + /** * Execute the query and get the first result or throw an exception. * @@ -950,7 +976,7 @@ public function pluck($column, $key = null) if (! $this->model->hasAnyGetMutator($column) && ! $this->model->hasCast($column) && ! in_array($column, $this->model->getDates())) { - return $results; + return $this->applyAfterQueryCallbacks($results); } return $this->applyAfterQueryCallbacks( @@ -1603,6 +1629,8 @@ public function withOnly($relations) */ public function newModelInstance($attributes = []) { + $attributes = array_merge($this->pendingAttributes, $attributes); + return $this->model->newInstance($attributes)->setConnection( $this->query->getConnection()->getName() ); @@ -1763,6 +1791,30 @@ protected function addNestedWiths($name, $results) return $results; } + /** + * Specify attributes that should be added to any new models created by this builder. + * + * The given key / value pairs will also be added as where conditions to the query. + * + * @param \Illuminate\Contracts\Database\Query\Expression|array|string $attributes + * @param mixed $value + * @return $this + */ + public function withAttributes(Expression|array|string $attributes, $value = null) + { + if (! is_array($attributes)) { + $attributes = [$attributes => $value]; + } + + foreach ($attributes as $column => $value) { + $this->where($this->qualifyColumn($column), $value); + } + + $this->pendingAttributes = array_merge($this->pendingAttributes, $attributes); + + return $this; + } + /** * Apply query-time casts to the model instance. * diff --git a/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php index a81337729c7f..67229b4a0332 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php @@ -220,8 +220,8 @@ protected function isGuardableColumn($key) if (! isset(static::$guardableColumns[get_class($this)])) { $columns = $this->getConnection() - ->getSchemaBuilder() - ->getColumnListing($this->getTable()); + ->getSchemaBuilder() + ->getColumnListing($this->getTable()); if (empty($columns)) { return true; diff --git a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php index 56d116454621..e064689a8f54 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php @@ -257,7 +257,7 @@ public function hasMorph($relation, $types, $operator = '>=', $count = 1, $boole } $query->where($this->qualifyColumn($relation->getMorphType()), '=', (new $type)->getMorphClass()) - ->whereHas($belongsTo, $callback, $operator, $count); + ->whereHas($belongsTo, $callback, $operator, $count); }); } }, null, null, $boolean); @@ -536,7 +536,7 @@ public function orWhereMorphDoesntHaveRelation($relation, $types, $column, $oper * Add a morph-to relationship condition to the query. * * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param \Illuminate\Database\Eloquent\Model|string|null $model + * @param \Illuminate\Database\Eloquent\Model|iterable|string|null $model * @return $this */ public function whereMorphedTo($relation, $model, $boolean = 'and') @@ -559,9 +559,19 @@ public function whereMorphedTo($relation, $model, $boolean = 'and') return $this->where($relation->qualifyColumn($relation->getMorphType()), $model, null, $boolean); } - return $this->where(function ($query) use ($relation, $model) { - $query->where($relation->qualifyColumn($relation->getMorphType()), $model->getMorphClass()) - ->where($relation->qualifyColumn($relation->getForeignKeyName()), $model->getKey()); + $models = BaseCollection::wrap($model); + + if ($models->isEmpty()) { + throw new InvalidArgumentException('Collection given to whereMorphedTo method may not be empty.'); + } + + return $this->where(function ($query) use ($relation, $models) { + $models->groupBy(fn ($model) => $model->getMorphClass())->each(function ($models) use ($query, $relation) { + $query->orWhere(function ($query) use ($relation, $models) { + $query->where($relation->qualifyColumn($relation->getMorphType()), $models->first()->getMorphClass()) + ->whereIn($relation->qualifyColumn($relation->getForeignKeyName()), $models->map->getKey()); + }); + }); }, null, null, $boolean); } @@ -569,7 +579,7 @@ public function whereMorphedTo($relation, $model, $boolean = 'and') * Add a not morph-to relationship condition to the query. * * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param \Illuminate\Database\Eloquent\Model|string $model + * @param \Illuminate\Database\Eloquent\Model|iterable|string $model * @return $this */ public function whereNotMorphedTo($relation, $model, $boolean = 'and') @@ -588,9 +598,19 @@ public function whereNotMorphedTo($relation, $model, $boolean = 'and') return $this->whereNot($relation->qualifyColumn($relation->getMorphType()), '<=>', $model, $boolean); } - return $this->whereNot(function ($query) use ($relation, $model) { - $query->where($relation->qualifyColumn($relation->getMorphType()), '<=>', $model->getMorphClass()) - ->where($relation->qualifyColumn($relation->getForeignKeyName()), '<=>', $model->getKey()); + $models = BaseCollection::wrap($model); + + if ($models->isEmpty()) { + throw new InvalidArgumentException('Collection given to whereNotMorphedTo method may not be empty.'); + } + + return $this->whereNot(function ($query) use ($relation, $models) { + $models->groupBy(fn ($model) => $model->getMorphClass())->each(function ($models) use ($query, $relation) { + $query->orWhere(function ($query) use ($relation, $models) { + $query->where($relation->qualifyColumn($relation->getMorphType()), '<=>', $models->first()->getMorphClass()) + ->whereNotIn($relation->qualifyColumn($relation->getForeignKeyName()), $models->map->getKey()); + }); + }); }, null, null, $boolean); } @@ -598,7 +618,7 @@ public function whereNotMorphedTo($relation, $model, $boolean = 'and') * Add a morph-to relationship condition to the query with an "or where" clause. * * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param \Illuminate\Database\Eloquent\Model|string|null $model + * @param \Illuminate\Database\Eloquent\Model|iterable|string|null $model * @return $this */ public function orWhereMorphedTo($relation, $model) @@ -610,7 +630,7 @@ public function orWhereMorphedTo($relation, $model) * Add a not morph-to relationship condition to the query with an "or where" clause. * * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param \Illuminate\Database\Eloquent\Model|string $model + * @param \Illuminate\Database\Eloquent\Model|iterable|string $model * @return $this */ public function orWhereNotMorphedTo($relation, $model) diff --git a/src/Illuminate/Database/Eloquent/Factories/HasFactory.php b/src/Illuminate/Database/Eloquent/Factories/HasFactory.php index 2078d9f025f3..ca37657def04 100644 --- a/src/Illuminate/Database/Eloquent/Factories/HasFactory.php +++ b/src/Illuminate/Database/Eloquent/Factories/HasFactory.php @@ -21,8 +21,8 @@ public static function factory($count = null, $state = []) $factory = static::newFactory() ?? Factory::factoryForModel(static::class); return $factory - ->count(is_numeric($count) ? $count : null) - ->state(is_callable($count) || is_array($count) ? $count : $state); + ->count(is_numeric($count) ? $count : null) + ->state(is_callable($count) || is_array($count) ? $count : $state); } /** diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 5d19f7f17f83..08e3c6488b6e 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -38,7 +38,7 @@ abstract class Model implements Arrayable, ArrayAccess, CanBeEscapedWhenCastToSt Concerns\GuardsAttributes, Concerns\PreventsCircularRecursion, ForwardsCalls; - /** @use HasCollection<\Illuminate\Database\Eloquent\Collection> */ + /** @use HasCollection<\Illuminate\Database\Eloquent\Collection> */ use HasCollection; /** diff --git a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php index 500dcc20842a..a51d4e16fc71 100755 --- a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php @@ -1351,6 +1351,8 @@ public function saveManyQuietly($models, array $pivotAttributes = []) */ public function create(array $attributes = [], array $joining = [], $touch = true) { + $attributes = array_merge($this->getQuery()->pendingAttributes, $attributes); + $instance = $this->related->newInstance($attributes); // Once we save the related model, we need to attach it to the base model via diff --git a/src/Illuminate/Database/Eloquent/Relations/Concerns/ComparesRelatedModels.php b/src/Illuminate/Database/Eloquent/Relations/Concerns/ComparesRelatedModels.php index ca06698875e8..3dccf1310765 100644 --- a/src/Illuminate/Database/Eloquent/Relations/Concerns/ComparesRelatedModels.php +++ b/src/Illuminate/Database/Eloquent/Relations/Concerns/ComparesRelatedModels.php @@ -22,8 +22,8 @@ public function is($model) if ($match && $this instanceof SupportsPartialRelations && $this->isOneOfMany()) { return $this->query - ->whereKey($model->getKey()) - ->exists(); + ->whereKey($model->getKey()) + ->exists(); } return $match; diff --git a/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php b/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php index 034a7d7c8630..f09de9451eff 100644 --- a/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php +++ b/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php @@ -91,7 +91,7 @@ public function sync($ids, $detaching = true) // in this joining table. We'll spin through the given IDs, checking to see // if they exist in the array of current ones, and if not we will insert. $current = $this->getCurrentlyAttachedPivots() - ->pluck($this->relatedPivotKey)->all(); + ->pluck($this->relatedPivotKey)->all(); $records = $this->formatRecordsList($this->parseIds($ids)); @@ -240,9 +240,9 @@ public function updateExistingPivot($id, array $attributes, $touch = true) protected function updateExistingPivotUsingCustomClass($id, array $attributes, $touch) { $pivot = $this->getCurrentlyAttachedPivots() - ->where($this->foreignPivotKey, $this->parent->{$this->parentKey}) - ->where($this->relatedPivotKey, $this->parseId($id)) - ->first(); + ->where($this->foreignPivotKey, $this->parent->{$this->parentKey}) + ->where($this->relatedPivotKey, $this->parseId($id)) + ->first(); $updated = $pivot ? $pivot->fill($attributes)->isDirty() : false; diff --git a/src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php b/src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php index 0ba60ccc9cf7..de94098dcfeb 100755 --- a/src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php @@ -447,6 +447,12 @@ protected function setForeignAttributesForCreate(Model $model) { $model->setAttribute($this->getForeignKeyName(), $this->getParentKey()); + foreach ($this->getQuery()->pendingAttributes as $key => $value) { + if (! $model->hasAttribute($key)) { + $model->setAttribute($key, $value); + } + } + $this->applyInverseRelationToModel($model); } diff --git a/src/Illuminate/Database/Eloquent/Relations/MorphOneOrMany.php b/src/Illuminate/Database/Eloquent/Relations/MorphOneOrMany.php index 3478f73859d4..44531957d5b7 100755 --- a/src/Illuminate/Database/Eloquent/Relations/MorphOneOrMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/MorphOneOrMany.php @@ -96,6 +96,12 @@ protected function setForeignAttributesForCreate(Model $model) $model->{$this->getMorphType()} = $this->morphClass; + foreach ($this->getQuery()->pendingAttributes as $key => $value) { + if (! $model->hasAttribute($key)) { + $model->setAttribute($key, $value); + } + } + $this->applyInverseRelationToModel($model); } diff --git a/src/Illuminate/Database/Eloquent/Relations/MorphPivot.php b/src/Illuminate/Database/Eloquent/Relations/MorphPivot.php index 6a3f395e73a6..566e198c9bea 100644 --- a/src/Illuminate/Database/Eloquent/Relations/MorphPivot.php +++ b/src/Illuminate/Database/Eloquent/Relations/MorphPivot.php @@ -148,9 +148,9 @@ public function newQueryForRestoration($ids) $segments = explode(':', $ids); return $this->newQueryWithoutScopes() - ->where($segments[0], $segments[1]) - ->where($segments[2], $segments[3]) - ->where($segments[4], $segments[5]); + ->where($segments[0], $segments[1]) + ->where($segments[2], $segments[3]) + ->where($segments[4], $segments[5]); } /** @@ -174,8 +174,8 @@ protected function newQueryForCollectionRestoration(array $ids) $query->orWhere(function ($query) use ($segments) { return $query->where($segments[0], $segments[1]) - ->where($segments[2], $segments[3]) - ->where($segments[4], $segments[5]); + ->where($segments[2], $segments[3]) + ->where($segments[4], $segments[5]); }); } diff --git a/src/Illuminate/Database/Eloquent/Relations/MorphTo.php b/src/Illuminate/Database/Eloquent/Relations/MorphTo.php index e743e93ea379..cc42984552a1 100644 --- a/src/Illuminate/Database/Eloquent/Relations/MorphTo.php +++ b/src/Illuminate/Database/Eloquent/Relations/MorphTo.php @@ -146,14 +146,14 @@ protected function getResultsByType($type) $ownerKey = $this->ownerKey ?? $instance->getKeyName(); $query = $this->replayMacros($instance->newQuery()) - ->mergeConstraintsFrom($this->getQuery()) - ->with(array_merge( - $this->getQuery()->getEagerLoads(), - (array) ($this->morphableEagerLoads[get_class($instance)] ?? []) - )) - ->withCount( - (array) ($this->morphableEagerLoadCounts[get_class($instance)] ?? []) - ); + ->mergeConstraintsFrom($this->getQuery()) + ->with(array_merge( + $this->getQuery()->getEagerLoads(), + (array) ($this->morphableEagerLoads[get_class($instance)] ?? []) + )) + ->withCount( + (array) ($this->morphableEagerLoadCounts[get_class($instance)] ?? []) + ); if ($callback = ($this->morphableConstraints[get_class($instance)] ?? null)) { $callback($query); diff --git a/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php b/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php index 9cb7445374d3..157202bccf21 100644 --- a/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php @@ -129,7 +129,7 @@ protected function getCurrentlyAttachedPivots() return parent::getCurrentlyAttachedPivots()->map(function ($record) { return $record instanceof MorphPivot ? $record->setMorphType($this->morphType) - ->setMorphClass($this->morphClass) + ->setMorphClass($this->morphClass) : $record; }); } @@ -161,9 +161,9 @@ public function newPivot(array $attributes = [], $exists = false) : MorphPivot::fromAttributes($this->parent, $attributes, $this->table, $exists); $pivot->setPivotKeys($this->foreignPivotKey, $this->relatedPivotKey) - ->setRelatedModel($this->related) - ->setMorphType($this->morphType) - ->setMorphClass($this->morphClass); + ->setRelatedModel($this->related) + ->setMorphType($this->morphType) + ->setMorphClass($this->morphClass); return $pivot; } diff --git a/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php b/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php index cf020d64db06..c5d5252854f1 100755 --- a/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php +++ b/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php @@ -48,9 +48,9 @@ public function __construct(Resolver $resolver, $table) public function getRan() { return $this->table() - ->orderBy('batch', 'asc') - ->orderBy('migration', 'asc') - ->pluck('migration')->all(); + ->orderBy('batch', 'asc') + ->orderBy('migration', 'asc') + ->pluck('migration')->all(); } /** @@ -64,8 +64,8 @@ public function getMigrations($steps) $query = $this->table()->where('batch', '>=', '1'); return $query->orderBy('batch', 'desc') - ->orderBy('migration', 'desc') - ->take($steps)->get()->all(); + ->orderBy('migration', 'desc') + ->take($steps)->get()->all(); } /** @@ -103,9 +103,9 @@ public function getLast() public function getMigrationBatches() { return $this->table() - ->orderBy('batch', 'asc') - ->orderBy('migration', 'asc') - ->pluck('batch', 'migration')->all(); + ->orderBy('batch', 'asc') + ->orderBy('migration', 'asc') + ->pluck('batch', 'migration')->all(); } /** diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index f166b28bbbfe..dd762d1dda88 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -2820,7 +2820,7 @@ public function forPageBeforeId($perPage = 15, $lastId = 0, $column = 'id') } return $this->orderBy($column, 'desc') - ->limit($perPage); + ->limit($perPage); } /** @@ -2840,7 +2840,7 @@ public function forPageAfterId($perPage = 15, $lastId = 0, $column = 'id') } return $this->orderBy($column, 'asc') - ->limit($perPage); + ->limit($perPage); } /** @@ -2873,10 +2873,10 @@ public function reorder($column = null, $direction = 'asc') protected function removeExistingOrdersFor($column) { return (new Collection($this->orders)) - ->reject(function ($order) use ($column) { - return isset($order['column']) - ? $order['column'] === $column : false; - })->values()->all(); + ->reject(function ($order) use ($column) { + return isset($order['column']) + ? $order['column'] === $column : false; + })->values()->all(); } /** @@ -3307,9 +3307,9 @@ protected function runPaginationCountQuery($columns = ['*']) $without = $this->unions ? ['unionOrders', 'unionLimit', 'unionOffset'] : ['columns', 'orders', 'limit', 'offset']; return $this->cloneWithout($without) - ->cloneWithoutBindings($this->unions ? ['unionOrder'] : ['select', 'order']) - ->setAggregate('count', $this->withoutSelectAliases($columns)) - ->get()->all(); + ->cloneWithoutBindings($this->unions ? ['unionOrder'] : ['select', 'order']) + ->setAggregate('count', $this->withoutSelectAliases($columns)) + ->get()->all(); } /** @@ -3320,7 +3320,7 @@ protected function runPaginationCountQuery($columns = ['*']) protected function cloneForPaginationCount() { return $this->cloneWithout(['orders', 'limit', 'offset']) - ->cloneWithoutBindings(['order']); + ->cloneWithoutBindings(['order']); } /** @@ -3628,9 +3628,9 @@ public function average($column) public function aggregate($function, $columns = ['*']) { $results = $this->cloneWithout($this->unions || $this->havings ? [] : ['columns']) - ->cloneWithoutBindings($this->unions || $this->havings ? [] : ['select']) - ->setAggregate($function, $columns) - ->get($columns); + ->cloneWithoutBindings($this->unions || $this->havings ? [] : ['select']) + ->setAggregate($function, $columns) + ->get($columns); if (! $results->isEmpty()) { return array_change_key_case((array) $results[0])['aggregate']; @@ -4243,12 +4243,12 @@ public function mergeBindings(self $query) public function cleanBindings(array $bindings) { return (new Collection($bindings)) - ->reject(function ($binding) { - return $binding instanceof ExpressionContract; - }) - ->map([$this, 'castBinding']) - ->values() - ->all(); + ->reject(function ($binding) { + return $binding instanceof ExpressionContract; + }) + ->map([$this, 'castBinding']) + ->values() + ->all(); } /** diff --git a/src/Illuminate/Database/Query/Expression.php b/src/Illuminate/Database/Query/Expression.php index 7233e11dc091..1da00d5e9bc1 100755 --- a/src/Illuminate/Database/Query/Expression.php +++ b/src/Illuminate/Database/Query/Expression.php @@ -5,31 +5,27 @@ use Illuminate\Contracts\Database\Query\Expression as ExpressionContract; use Illuminate\Database\Grammar; +/** + * @template TValue of string|int|float + */ class Expression implements ExpressionContract { - /** - * The value of the expression. - * - * @var string|int|float - */ - protected $value; - /** * Create a new raw query expression. * - * @param string|int|float $value + * @param TValue $value * @return void */ - public function __construct($value) - { - $this->value = $value; + public function __construct( + protected $value + ) { } /** * Get the value of the expression. * * @param \Illuminate\Database\Grammar $grammar - * @return string|int|float + * @return TValue */ public function getValue(Grammar $grammar) { diff --git a/src/Illuminate/Filesystem/FilesystemManager.php b/src/Illuminate/Filesystem/FilesystemManager.php index dfdab7c6e7f8..ed139a81eb41 100644 --- a/src/Illuminate/Filesystem/FilesystemManager.php +++ b/src/Illuminate/Filesystem/FilesystemManager.php @@ -324,6 +324,10 @@ protected function createFlysystem(FlysystemAdapter $adapter, array $config) $adapter = new PathPrefixedAdapter($adapter, $config['prefix']); } + if (str_contains($config['endpoint'] ?? '', 'r2.cloudflarestorage.com')) { + $config['retain_visibility'] = false; + } + return new Flysystem($adapter, Arr::only($config, [ 'directory_visibility', 'disable_asserts', diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index c3c78a0c5148..cd91699f9f7e 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -45,7 +45,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '11.38.2'; + const VERSION = '11.40.0'; /** * The base path for the Laravel installation. @@ -840,12 +840,12 @@ public function registered($callback) public function registerConfiguredProviders() { $providers = (new Collection($this->make('config')->get('app.providers'))) - ->partition(fn ($provider) => str_starts_with($provider, 'Illuminate\\')); + ->partition(fn ($provider) => str_starts_with($provider, 'Illuminate\\')); $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]); (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath())) - ->load($providers->collapse()->toArray()); + ->load($providers->collapse()->toArray()); $this->fireAppCallbacks($this->registeredCallbacks); } diff --git a/src/Illuminate/Foundation/Bus/PendingDispatch.php b/src/Illuminate/Foundation/Bus/PendingDispatch.php index 4dcdf09702f6..ae96e63d05da 100644 --- a/src/Illuminate/Foundation/Bus/PendingDispatch.php +++ b/src/Illuminate/Foundation/Bus/PendingDispatch.php @@ -178,7 +178,7 @@ protected function shouldDispatch() } return (new UniqueLock(Container::getInstance()->make(Cache::class))) - ->acquire($this->job); + ->acquire($this->job); } /** diff --git a/src/Illuminate/Foundation/Concerns/ResolvesDumpSource.php b/src/Illuminate/Foundation/Concerns/ResolvesDumpSource.php index 382d651b6056..318ec6c57f3b 100644 --- a/src/Illuminate/Foundation/Concerns/ResolvesDumpSource.php +++ b/src/Illuminate/Foundation/Concerns/ResolvesDumpSource.php @@ -13,6 +13,7 @@ trait ResolvesDumpSource */ protected $editorHrefs = [ 'atom' => 'atom://core/open/file?filename={file}&line={line}', + 'cursor' => 'cursor://file/{file}:{line}', 'emacs' => 'emacs://open?url=file://{file}&line={line}', 'idea' => 'idea://open?file={file}&line={line}', 'macvim' => 'mvim://open/?url=file://{file}&line={line}', diff --git a/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php b/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php index a1f7228830bd..93a8d6cf365a 100644 --- a/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php +++ b/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php @@ -200,7 +200,7 @@ protected function installNodeDependencies() } $command = Process::command(implode(' && ', $commands)) - ->path(base_path()); + ->path(base_path()); if (! windows_os()) { $command->tty(true); diff --git a/src/Illuminate/Foundation/Console/Kernel.php b/src/Illuminate/Foundation/Console/Kernel.php index 165855b80132..0ec8e312e254 100644 --- a/src/Illuminate/Foundation/Console/Kernel.php +++ b/src/Illuminate/Foundation/Console/Kernel.php @@ -538,8 +538,8 @@ protected function getArtisan() { if (is_null($this->artisan)) { $this->artisan = (new Artisan($this->app, $this->events, $this->app->version())) - ->resolveCommands($this->commands) - ->setContainerCommandLoader(); + ->resolveCommands($this->commands) + ->setContainerCommandLoader(); if ($this->symfonyDispatcher instanceof EventDispatcher) { $this->artisan->setDispatcher($this->symfonyDispatcher); diff --git a/src/Illuminate/Foundation/Console/RouteListCommand.php b/src/Illuminate/Foundation/Console/RouteListCommand.php index 569469e3ce55..104bff78af28 100644 --- a/src/Illuminate/Foundation/Console/RouteListCommand.php +++ b/src/Illuminate/Foundation/Console/RouteListCommand.php @@ -223,7 +223,7 @@ protected function isVendorRoute(Route $route) { if ($route->action['uses'] instanceof Closure) { $path = (new ReflectionFunction($route->action['uses'])) - ->getFileName(); + ->getFileName(); } elseif (is_string($route->action['uses']) && str_contains($route->action['uses'], 'SerializableClosure')) { return false; @@ -233,7 +233,7 @@ protected function isVendorRoute(Route $route) } $path = (new ReflectionClass($route->getControllerClass())) - ->getFileName(); + ->getFileName(); } else { return false; } diff --git a/src/Illuminate/Foundation/Exceptions/Handler.php b/src/Illuminate/Foundation/Exceptions/Handler.php index 5fb5f539110e..2f381d1ef6b7 100644 --- a/src/Illuminate/Foundation/Exceptions/Handler.php +++ b/src/Illuminate/Foundation/Exceptions/Handler.php @@ -483,10 +483,10 @@ public function stopIgnoring(array|string $exceptions) $exceptions = Arr::wrap($exceptions); $this->dontReport = (new Collection($this->dontReport)) - ->reject(fn ($ignored) => in_array($ignored, $exceptions))->values()->all(); + ->reject(fn ($ignored) => in_array($ignored, $exceptions))->values()->all(); $this->internalDontReport = (new Collection($this->internalDontReport)) - ->reject(fn ($ignored) => in_array($ignored, $exceptions))->values()->all(); + ->reject(fn ($ignored) => in_array($ignored, $exceptions))->values()->all(); return $this; } @@ -748,8 +748,8 @@ protected function convertValidationExceptionToResponse(ValidationException $e, protected function invalid($request, ValidationException $exception) { return redirect($exception->redirectTo ?? url()->previous()) - ->withInput(Arr::except($request->input(), $this->dontFlash)) - ->withErrors($exception->errors(), $request->input('_error_bag', $exception->errorBag)); + ->withInput(Arr::except($request->input(), $this->dontFlash)) + ->withErrors($exception->errors(), $request->input('_error_bag', $exception->errorBag)); } /** diff --git a/src/Illuminate/Foundation/Http/FormRequest.php b/src/Illuminate/Foundation/Http/FormRequest.php index 87a1e2048555..edff3035af98 100644 --- a/src/Illuminate/Foundation/Http/FormRequest.php +++ b/src/Illuminate/Foundation/Http/FormRequest.php @@ -166,8 +166,8 @@ protected function failedValidation(Validator $validator) $exception = $validator->getException(); throw (new $exception($validator)) - ->errorBag($this->errorBag) - ->redirectTo($this->getRedirectUrl()); + ->errorBag($this->errorBag) + ->redirectTo($this->getRedirectUrl()); } /** diff --git a/src/Illuminate/Foundation/Http/Kernel.php b/src/Illuminate/Foundation/Http/Kernel.php index 90c9fb010f4d..02c0f3fdbd95 100644 --- a/src/Illuminate/Foundation/Http/Kernel.php +++ b/src/Illuminate/Foundation/Http/Kernel.php @@ -171,9 +171,9 @@ protected function sendRequestThroughRouter($request) $this->bootstrap(); return (new Pipeline($this->app)) - ->send($request) - ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) - ->then($this->dispatchToRouter()); + ->send($request) + ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) + ->then($this->dispatchToRouter()); } /** diff --git a/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php b/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php index e993d714179d..fca2cc61057f 100644 --- a/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php @@ -239,7 +239,7 @@ protected function registerExceptionTracking() $this->app->make('events')->listen(MessageLogged::class, function ($event) { if (isset($event->context['exception'])) { $this->app->make(LoggedExceptionCollection::class) - ->push($event->context['exception']); + ->push($event->context['exception']); } }); } diff --git a/src/Illuminate/Foundation/Support/Providers/EventServiceProvider.php b/src/Illuminate/Foundation/Support/Providers/EventServiceProvider.php index 37e35477411d..788d6ed54b4b 100644 --- a/src/Illuminate/Foundation/Support/Providers/EventServiceProvider.php +++ b/src/Illuminate/Foundation/Support/Providers/EventServiceProvider.php @@ -146,18 +146,18 @@ public function shouldDiscoverEvents() public function discoverEvents() { return (new Collection($this->discoverEventsWithin())) - ->flatMap(function ($directory) { - return glob($directory, GLOB_ONLYDIR); - }) - ->reject(function ($directory) { - return ! is_dir($directory); - }) - ->reduce(function ($discovered, $directory) { - return array_merge_recursive( - $discovered, - DiscoverEvents::within($directory, $this->eventDiscoveryBasePath()) - ); - }, []); + ->flatMap(function ($directory) { + return glob($directory, GLOB_ONLYDIR); + }) + ->reject(function ($directory) { + return ! is_dir($directory); + }) + ->reduce(function ($discovered, $directory) { + return array_merge_recursive( + $discovered, + DiscoverEvents::within($directory, $this->eventDiscoveryBasePath()) + ); + }, []); } /** diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/package-lock.json b/src/Illuminate/Foundation/resources/exceptions/renderer/package-lock.json index 37c0dc03e8bc..5ffbe5243951 100644 --- a/src/Illuminate/Foundation/resources/exceptions/renderer/package-lock.json +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/package-lock.json @@ -11,7 +11,7 @@ "postcss": "^8.4.38", "tailwindcss": "^3.4.3", "tippy.js": "^6.3.7", - "vite": "^5.2.14", + "vite": "^5.4.12", "vite-require": "^0.2.3" } }, @@ -27,12 +27,13 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "aix" @@ -42,12 +43,13 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -57,12 +59,13 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -72,12 +75,13 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -87,12 +91,13 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -102,12 +107,13 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -117,12 +123,13 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -132,12 +139,13 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -147,12 +155,13 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -162,12 +171,13 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -177,12 +187,13 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -192,12 +203,13 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -207,12 +219,13 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -222,12 +235,13 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -237,12 +251,13 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -252,12 +267,13 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -267,12 +283,13 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -282,12 +299,13 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -297,12 +315,13 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -312,12 +331,13 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "sunos" @@ -327,12 +347,13 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -342,12 +363,13 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -357,12 +379,13 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1023,10 +1046,11 @@ "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==" }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -1034,29 +1058,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escalade": { @@ -1462,9 +1486,10 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -1494,9 +1519,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "funding": [ { "type": "opencollective", @@ -1511,10 +1536,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -1794,9 +1820,10 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -2079,13 +2106,14 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/vite": { - "version": "5.2.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.14.tgz", - "integrity": "sha512-TFQLuwWLPms+NBNlh0D9LZQ+HXW471COABxw/9TEUBrjuHMo9BrYBPrN/SYAwIuVL+rLerycxiLT41t4f5MZpA==", + "version": "5.4.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.12.tgz", + "integrity": "sha512-KwUaKB27TvWwDJr1GjjWthLMATbGEbeWYZIbGZ5qFIsgPP3vWzLu4cVooqhm5/Z2SPDUMjyPVjTztm5tYKwQxA==", + "license": "MIT", "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -2104,6 +2132,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -2121,6 +2150,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/package.json b/src/Illuminate/Foundation/resources/exceptions/renderer/package.json index af1daa2d9496..588c6aed9d86 100644 --- a/src/Illuminate/Foundation/resources/exceptions/renderer/package.json +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/package.json @@ -12,7 +12,7 @@ "postcss": "^8.4.38", "tailwindcss": "^3.4.3", "tippy.js": "^6.3.7", - "vite": "^5.2.14", + "vite": "^5.4.12", "vite-require": "^0.2.3" } } diff --git a/src/Illuminate/Http/Client/PendingRequest.php b/src/Illuminate/Http/Client/PendingRequest.php index 6ece4cc79dd6..839c6e6debd5 100644 --- a/src/Illuminate/Http/Client/PendingRequest.php +++ b/src/Illuminate/Http/Client/PendingRequest.php @@ -1326,10 +1326,10 @@ public function buildStubHandler() return function ($handler) { return function ($request, $options) use ($handler) { $response = ($this->stubCallbacks ?? new Collection) - ->map - ->__invoke((new Request($request))->withData($options['laravel_data']), $options) - ->filter() - ->first(); + ->map + ->__invoke((new Request($request))->withData($options['laravel_data']), $options) + ->filter() + ->first(); if (is_null($response)) { if ($this->preventStrayRequests) { diff --git a/src/Illuminate/Http/Client/Response.php b/src/Illuminate/Http/Client/Response.php index 491eecf081fd..ac51d9c7ade8 100644 --- a/src/Illuminate/Http/Client/Response.php +++ b/src/Illuminate/Http/Client/Response.php @@ -396,6 +396,68 @@ public function throwIfServerError() return $this->serverError() ? $this->throw() : $this; } + /** + * Dump the content from the response. + * + * @param string|null $key + * @return $this + */ + public function dump($key = null) + { + $content = $this->body(); + + $json = json_decode($content); + + if (json_last_error() === JSON_ERROR_NONE) { + $content = $json; + } + + if (! is_null($key)) { + dump(data_get($content, $key)); + } else { + dump($content); + } + + return $this; + } + + /** + * Dump the content from the response and end the script. + * + * @param string|null $key + * @return never + */ + public function dd($key = null) + { + $this->dump($key); + + exit(1); + } + + /** + * Dump the headers from the response. + * + * @return $this + */ + public function dumpHeaders() + { + dump($this->headers()); + + return $this; + } + + /** + * Dump the headers from the response and end the script. + * + * @return never + */ + public function ddHeaders() + { + $this->dumpHeaders(); + + exit(1); + } + /** * Determine if the given offset exists. * diff --git a/src/Illuminate/Http/RedirectResponse.php b/src/Illuminate/Http/RedirectResponse.php index 5c506ba6b00b..6e4a9cde5b1f 100755 --- a/src/Illuminate/Http/RedirectResponse.php +++ b/src/Illuminate/Http/RedirectResponse.php @@ -169,7 +169,7 @@ protected function parseErrors($provider) public function withFragment($fragment) { return $this->withoutFragment() - ->setTargetUrl($this->getTargetUrl().'#'.Str::after($fragment, '#')); + ->setTargetUrl($this->getTargetUrl().'#'.Str::after($fragment, '#')); } /** diff --git a/src/Illuminate/Http/Resources/CollectsResources.php b/src/Illuminate/Http/Resources/CollectsResources.php index fd06cdcce8f6..c1bad66733c8 100644 --- a/src/Illuminate/Http/Resources/CollectsResources.php +++ b/src/Illuminate/Http/Resources/CollectsResources.php @@ -80,8 +80,8 @@ public function jsonOptions() } return (new ReflectionClass($collects)) - ->newInstanceWithoutConstructor() - ->jsonOptions(); + ->newInstanceWithoutConstructor() + ->jsonOptions(); } /** diff --git a/src/Illuminate/Mail/Mailable.php b/src/Illuminate/Mail/Mailable.php index 6c1681a489c2..0d976ec70135 100644 --- a/src/Illuminate/Mail/Mailable.php +++ b/src/Illuminate/Mail/Mailable.php @@ -207,12 +207,12 @@ public function send($mailer) return $mailer->send($this->buildView(), $this->buildViewData(), function ($message) { $this->buildFrom($message) - ->buildRecipients($message) - ->buildSubject($message) - ->buildTags($message) - ->buildMetadata($message) - ->runCallbacks($message) - ->buildAttachments($message); + ->buildRecipients($message) + ->buildSubject($message) + ->buildTags($message) + ->buildMetadata($message) + ->runCallbacks($message) + ->buildAttachments($message); }); }); } @@ -256,10 +256,10 @@ public function later($delay, Queue $queue) protected function newQueuedJob() { return Container::getInstance()->make(SendQueuedMailable::class, ['mailable' => $this]) - ->through(array_merge( - method_exists($this, 'middleware') ? $this->middleware() : [], - $this->middleware ?? [] - )); + ->through(array_merge( + method_exists($this, 'middleware') ? $this->middleware() : [], + $this->middleware ?? [] + )); } /** @@ -992,9 +992,9 @@ public function attach($file, array $options = []) } $this->attachments = (new Collection($this->attachments)) - ->push(compact('file', 'options')) - ->unique('file') - ->all(); + ->push(compact('file', 'options')) + ->unique('file') + ->all(); return $this; } @@ -1074,8 +1074,8 @@ private function hasEnvelopeAttachment($attachment, $options = []) $attachments = $this->attachments(); return (new Collection(is_object($attachments) ? [$attachments] : $attachments)) - ->map(fn ($attached) => $attached instanceof Attachable ? $attached->toMailAttachment() : $attached) - ->contains(fn ($attached) => $attached->isEquivalent($attachment, $options)); + ->map(fn ($attached) => $attached instanceof Attachable ? $attached->toMailAttachment() : $attached) + ->contains(fn ($attached) => $attached->isEquivalent($attachment, $options)); } /** diff --git a/src/Illuminate/Mail/Transport/SesTransport.php b/src/Illuminate/Mail/Transport/SesTransport.php index 2ab1af6d7eab..daa3be18991e 100644 --- a/src/Illuminate/Mail/Transport/SesTransport.php +++ b/src/Illuminate/Mail/Transport/SesTransport.php @@ -68,10 +68,10 @@ protected function doSend(SentMessage $message): void $options, [ 'Source' => $message->getEnvelope()->getSender()->toString(), 'Destinations' => (new Collection($message->getEnvelope()->getRecipients())) - ->map - ->toString() - ->values() - ->all(), + ->map + ->toString() + ->values() + ->all(), 'RawMessage' => [ 'Data' => $message->toString(), ], diff --git a/src/Illuminate/Mail/Transport/SesV2Transport.php b/src/Illuminate/Mail/Transport/SesV2Transport.php index 3bb2985365b3..ab47b44b3ea8 100644 --- a/src/Illuminate/Mail/Transport/SesV2Transport.php +++ b/src/Illuminate/Mail/Transport/SesV2Transport.php @@ -69,10 +69,10 @@ protected function doSend(SentMessage $message): void 'Source' => $message->getEnvelope()->getSender()->toString(), 'Destination' => [ 'ToAddresses' => (new Collection($message->getEnvelope()->getRecipients())) - ->map - ->toString() - ->values() - ->all(), + ->map + ->toString() + ->values() + ->all(), ], 'Content' => [ 'Raw' => [ diff --git a/src/Illuminate/Notifications/Channels/BroadcastChannel.php b/src/Illuminate/Notifications/Channels/BroadcastChannel.php index e1010afc9e19..14739d8eb9d8 100644 --- a/src/Illuminate/Notifications/Channels/BroadcastChannel.php +++ b/src/Illuminate/Notifications/Channels/BroadcastChannel.php @@ -45,7 +45,7 @@ public function send($notifiable, Notification $notification) if ($message instanceof BroadcastMessage) { $event->onConnection($message->connection) - ->onQueue($message->queue); + ->onQueue($message->queue); } return $this->events->dispatch($event); diff --git a/src/Illuminate/Notifications/Messages/MailMessage.php b/src/Illuminate/Notifications/Messages/MailMessage.php index f01ef667ef16..a80117a50178 100644 --- a/src/Illuminate/Notifications/Messages/MailMessage.php +++ b/src/Illuminate/Notifications/Messages/MailMessage.php @@ -404,7 +404,7 @@ public function render() $markdown = Container::getInstance()->make(Markdown::class); return $markdown->theme($this->theme ?: $markdown->getTheme()) - ->render($this->markdown, $this->data()); + ->render($this->markdown, $this->data()); } /** diff --git a/src/Illuminate/Notifications/NotificationSender.php b/src/Illuminate/Notifications/NotificationSender.php index 983f8b41476f..cea407f70b9a 100644 --- a/src/Illuminate/Notifications/NotificationSender.php +++ b/src/Illuminate/Notifications/NotificationSender.php @@ -228,10 +228,10 @@ protected function queueNotification($notifiables, $notification) $this->bus->dispatch( (new SendQueuedNotifications($notifiable, $notification, [$channel])) - ->onConnection($connection) - ->onQueue($queue) - ->delay(is_array($delay) ? ($delay[$channel] ?? null) : $delay) - ->through($middleware) + ->onConnection($connection) + ->onQueue($queue) + ->delay(is_array($delay) ? ($delay[$channel] ?? null) : $delay) + ->through($middleware) ); } } diff --git a/src/Illuminate/Process/PendingProcess.php b/src/Illuminate/Process/PendingProcess.php index 668385460ac2..7161dd5bfb8a 100644 --- a/src/Illuminate/Process/PendingProcess.php +++ b/src/Illuminate/Process/PendingProcess.php @@ -351,7 +351,7 @@ public function withFakeHandlers(array $fakeHandlers) protected function fakeFor(string $command) { return (new Collection($this->fakeHandlers)) - ->first(fn ($handler, $pattern) => $pattern === '*' || Str::is($pattern, $command)); + ->first(fn ($handler, $pattern) => $pattern === '*' || Str::is($pattern, $command)); } /** diff --git a/src/Illuminate/Process/Pipe.php b/src/Illuminate/Process/Pipe.php index 1803c7d251ce..06e7e16598e8 100644 --- a/src/Illuminate/Process/Pipe.php +++ b/src/Illuminate/Process/Pipe.php @@ -69,22 +69,22 @@ public function run(?callable $output = null) call_user_func($this->callback, $this); return (new Collection($this->pendingProcesses)) - ->reduce(function ($previousProcessResult, $pendingProcess, $key) use ($output) { - if (! $pendingProcess instanceof PendingProcess) { - throw new InvalidArgumentException('Process pipe must only contain pending processes.'); - } + ->reduce(function ($previousProcessResult, $pendingProcess, $key) use ($output) { + if (! $pendingProcess instanceof PendingProcess) { + throw new InvalidArgumentException('Process pipe must only contain pending processes.'); + } - if ($previousProcessResult && $previousProcessResult->failed()) { - return $previousProcessResult; - } + if ($previousProcessResult && $previousProcessResult->failed()) { + return $previousProcessResult; + } - return $pendingProcess->when( - $previousProcessResult, - fn () => $pendingProcess->input($previousProcessResult->output()) - )->run(output: $output ? function ($type, $buffer) use ($key, $output) { - $output($type, $buffer, $key); - } : null); - }); + return $pendingProcess->when( + $previousProcessResult, + fn () => $pendingProcess->input($previousProcessResult->output()) + )->run(output: $output ? function ($type, $buffer) use ($key, $output) { + $output($type, $buffer, $key); + } : null); + }); } /** diff --git a/src/Illuminate/Process/Pool.php b/src/Illuminate/Process/Pool.php index e5271377d201..1a98a8541a57 100644 --- a/src/Illuminate/Process/Pool.php +++ b/src/Illuminate/Process/Pool.php @@ -79,7 +79,7 @@ public function start(?callable $output = null) $output($type, $buffer, $key); } : null)]; }) - ->all() + ->all() ); } diff --git a/src/Illuminate/Queue/CallQueuedHandler.php b/src/Illuminate/Queue/CallQueuedHandler.php index 4bd5aa30feb2..12f49b495152 100644 --- a/src/Illuminate/Queue/CallQueuedHandler.php +++ b/src/Illuminate/Queue/CallQueuedHandler.php @@ -66,10 +66,6 @@ public function call(Job $job, array $data) return $this->handleModelNotFound($job, $e); } - if ($command instanceof ShouldBeUniqueUntilProcessing) { - $this->ensureUniqueJobLockIsReleased($command); - } - $this->dispatchThroughMiddleware($job, $command); if (! $job->isReleased() && ! $command instanceof ShouldBeUniqueUntilProcessing) { @@ -121,12 +117,16 @@ protected function dispatchThroughMiddleware(Job $job, $command) } return (new Pipeline($this->container))->send($command) - ->through(array_merge(method_exists($command, 'middleware') ? $command->middleware() : [], $command->middleware ?? [])) - ->then(function ($command) use ($job) { - return $this->dispatcher->dispatchNow( - $command, $this->resolveHandler($job, $command) - ); - }); + ->through(array_merge(method_exists($command, 'middleware') ? $command->middleware() : [], $command->middleware ?? [])) + ->then(function ($command) use ($job) { + if ($command instanceof ShouldBeUniqueUntilProcessing) { + $this->ensureUniqueJobLockIsReleased($command); + } + + return $this->dispatcher->dispatchNow( + $command, $this->resolveHandler($job, $command) + ); + }); } /** diff --git a/src/Illuminate/Queue/DatabaseQueue.php b/src/Illuminate/Queue/DatabaseQueue.php index 213f99563484..41533f084217 100644 --- a/src/Illuminate/Queue/DatabaseQueue.php +++ b/src/Illuminate/Queue/DatabaseQueue.php @@ -76,8 +76,8 @@ public function __construct( public function size($queue = null) { return $this->database->table($this->table) - ->where('queue', $this->getQueue($queue)) - ->count(); + ->where('queue', $this->getQueue($queue)) + ->count(); } /** @@ -239,14 +239,14 @@ public function pop($queue = null) protected function getNextAvailableJob($queue) { $job = $this->database->table($this->table) - ->lock($this->getLockForPopping()) - ->where('queue', $this->getQueue($queue)) - ->where(function ($query) { - $this->isAvailable($query); - $this->isReservedButExpired($query); - }) - ->orderBy('id', 'asc') - ->first(); + ->lock($this->getLockForPopping()) + ->where('queue', $this->getQueue($queue)) + ->where(function ($query) { + $this->isAvailable($query); + $this->isReservedButExpired($query); + }) + ->orderBy('id', 'asc') + ->first(); return $job ? new DatabaseJobRecord((object) $job) : null; } @@ -293,7 +293,7 @@ protected function isAvailable($query) { $query->where(function ($query) { $query->whereNull('reserved_at') - ->where('available_at', '<=', $this->currentTime()); + ->where('available_at', '<=', $this->currentTime()); }); } @@ -392,8 +392,8 @@ public function deleteAndRelease($queue, $job, $delay) public function clear($queue) { return $this->database->table($this->table) - ->where('queue', $this->getQueue($queue)) - ->delete(); + ->where('queue', $this->getQueue($queue)) + ->delete(); } /** diff --git a/src/Illuminate/Queue/Failed/DynamoDbFailedJobProvider.php b/src/Illuminate/Queue/Failed/DynamoDbFailedJobProvider.php index 6d0bae90d031..c76a55ca8b1c 100644 --- a/src/Illuminate/Queue/Failed/DynamoDbFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/DynamoDbFailedJobProvider.php @@ -72,7 +72,7 @@ public function log($connection, $queue, $payload, $exception) 'payload' => ['S' => $payload], 'exception' => ['S' => (string) $exception], 'failed_at' => ['N' => (string) $failedAt->getTimestamp()], - 'expires_at' => ['N' => (string) $failedAt->addDays(3)->getTimestamp()], + 'expires_at' => ['N' => (string) $failedAt->addDays(7)->getTimestamp()], ], ]); diff --git a/src/Illuminate/Queue/QueueManager.php b/src/Illuminate/Queue/QueueManager.php index a64e43345c9e..399b66bc8729 100755 --- a/src/Illuminate/Queue/QueueManager.php +++ b/src/Illuminate/Queue/QueueManager.php @@ -160,8 +160,8 @@ protected function resolve($name) } return $this->getConnector($config['driver']) - ->connect($config) - ->setConnectionName($name); + ->connect($config) + ->setConnectionName($name); } /** diff --git a/src/Illuminate/Routing/CreatesRegularExpressionRouteConstraints.php b/src/Illuminate/Routing/CreatesRegularExpressionRouteConstraints.php index 64e497986634..c17374cef7c6 100644 --- a/src/Illuminate/Routing/CreatesRegularExpressionRouteConstraints.php +++ b/src/Illuminate/Routing/CreatesRegularExpressionRouteConstraints.php @@ -90,7 +90,7 @@ public function whereIn($parameters, array $values) protected function assignExpressionToParameters($parameters, $expression) { return $this->where(Collection::wrap($parameters) - ->mapWithKeys(fn ($parameter) => [$parameter => $expression]) - ->all()); + ->mapWithKeys(fn ($parameter) => [$parameter => $expression]) + ->all()); } } diff --git a/src/Illuminate/Routing/Route.php b/src/Illuminate/Routing/Route.php index c710312d1202..050d3c39b13c 100755 --- a/src/Illuminate/Routing/Route.php +++ b/src/Illuminate/Routing/Route.php @@ -378,7 +378,7 @@ public function bind(Request $request) $this->compileRoute(); $this->parameters = (new RouteParameterBinder($this)) - ->parameters($request); + ->parameters($request); $this->originalParameters = $this->parameters; diff --git a/src/Illuminate/Routing/Router.php b/src/Illuminate/Routing/Router.php index 3665d910aa34..cea49ce721f9 100644 --- a/src/Illuminate/Routing/Router.php +++ b/src/Illuminate/Routing/Router.php @@ -259,8 +259,8 @@ public function fallback($action) public function redirect($uri, $destination, $status = 302) { return $this->any($uri, '\Illuminate\Routing\RedirectController') - ->defaults('destination', $destination) - ->defaults('status', $status); + ->defaults('destination', $destination) + ->defaults('status', $status); } /** @@ -288,12 +288,12 @@ public function permanentRedirect($uri, $destination) public function view($uri, $view, $data = [], $status = 200, array $headers = []) { return $this->match(['GET', 'HEAD'], $uri, '\Illuminate\Routing\ViewController') - ->setDefaults([ - 'view' => $view, - 'data' => $data, - 'status' => is_array($status) ? 200 : $status, - 'headers' => is_array($status) ? $status : $headers, - ]); + ->setDefaults([ + 'view' => $view, + 'data' => $data, + 'status' => is_array($status) ? 200 : $status, + 'headers' => is_array($status) ? $status : $headers, + ]); } /** @@ -669,8 +669,8 @@ protected function prependGroupController($class) public function newRoute($methods, $uri, $action) { return (new Route($methods, $uri, $action)) - ->setRouter($this) - ->setContainer($this->container); + ->setRouter($this) + ->setContainer($this->container); } /** @@ -802,11 +802,11 @@ protected function runRouteWithinStack(Route $route, Request $request) $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route); return (new Pipeline($this->container)) - ->send($request) - ->through($middleware) - ->then(fn ($request) => $this->prepareResponse( - $request, $route->run() - )); + ->send($request) + ->through($middleware) + ->then(fn ($request) => $this->prepareResponse( + $request, $route->run() + )); } /** diff --git a/src/Illuminate/Session/DatabaseSessionHandler.php b/src/Illuminate/Session/DatabaseSessionHandler.php index f4c1e9441323..132f1e347246 100644 --- a/src/Illuminate/Session/DatabaseSessionHandler.php +++ b/src/Illuminate/Session/DatabaseSessionHandler.php @@ -192,7 +192,7 @@ protected function getDefaultPayload($data) return tap($payload, function (&$payload) { $this->addUserInformation($payload) - ->addRequestInformation($payload); + ->addRequestInformation($payload); }); } diff --git a/src/Illuminate/Session/FileSessionHandler.php b/src/Illuminate/Session/FileSessionHandler.php index 08b2d80bd72d..82fe2245384b 100644 --- a/src/Illuminate/Session/FileSessionHandler.php +++ b/src/Illuminate/Session/FileSessionHandler.php @@ -112,10 +112,10 @@ public function destroy($sessionId): bool public function gc($lifetime): int { $files = Finder::create() - ->in($this->path) - ->files() - ->ignoreDotFiles(true) - ->date('<= now - '.$lifetime.' seconds'); + ->in($this->path) + ->files() + ->ignoreDotFiles(true) + ->date('<= now - '.$lifetime.' seconds'); $deletedSessions = 0; diff --git a/src/Illuminate/Session/Middleware/StartSession.php b/src/Illuminate/Session/Middleware/StartSession.php index c6310984673f..c1ffbb84c3df 100644 --- a/src/Illuminate/Session/Middleware/StartSession.php +++ b/src/Illuminate/Session/Middleware/StartSession.php @@ -83,8 +83,8 @@ protected function handleRequestWhileBlocking(Request $request, $session, Closur : $this->manager->defaultRouteBlockLockSeconds(); $lock = $this->cache($this->manager->blockDriver()) - ->lock('session:'.$session->getId(), $lockFor) - ->betweenBlockedAttemptsSleepFor(50); + ->lock('session:'.$session->getId(), $lockFor) + ->betweenBlockedAttemptsSleepFor(50); try { $lock->block( diff --git a/src/Illuminate/Support/Composer.php b/src/Illuminate/Support/Composer.php index 856d7728d227..860c886faf06 100644 --- a/src/Illuminate/Support/Composer.php +++ b/src/Illuminate/Support/Composer.php @@ -69,9 +69,9 @@ public function requirePackages(array $packages, bool $dev = false, Closure|Outp 'require', ...$packages, ])) - ->when($dev, function ($command) { - $command->push('--dev'); - })->all(); + ->when($dev, function ($command) { + $command->push('--dev'); + })->all(); return 0 === $this->getProcess($command, ['COMPOSER_MEMORY_LIMIT' => '-1']) ->run( @@ -98,9 +98,9 @@ public function removePackages(array $packages, bool $dev = false, Closure|Outpu 'remove', ...$packages, ])) - ->when($dev, function ($command) { - $command->push('--dev'); - })->all(); + ->when($dev, function ($command) { + $command->push('--dev'); + })->all(); return 0 === $this->getProcess($command, ['COMPOSER_MEMORY_LIMIT' => '-1']) ->run( diff --git a/src/Illuminate/Support/DefaultProviders.php b/src/Illuminate/Support/DefaultProviders.php index 5c836c67f9b4..791e86072b75 100644 --- a/src/Illuminate/Support/DefaultProviders.php +++ b/src/Illuminate/Support/DefaultProviders.php @@ -86,9 +86,9 @@ public function replace(array $replacements) public function except(array $providers) { return new static((new Collection($this->providers)) - ->reject(fn ($p) => in_array($p, $providers)) - ->values() - ->toArray()); + ->reject(fn ($p) => in_array($p, $providers)) + ->values() + ->toArray()); } /** diff --git a/src/Illuminate/Support/Facades/Bus.php b/src/Illuminate/Support/Facades/Bus.php index e3155df5ae8a..337108f31d85 100644 --- a/src/Illuminate/Support/Facades/Bus.php +++ b/src/Illuminate/Support/Facades/Bus.php @@ -85,7 +85,7 @@ public static function dispatchChain($jobs) $jobs = is_array($jobs) ? $jobs : func_get_args(); return (new PendingChain(array_shift($jobs), $jobs)) - ->dispatch(); + ->dispatch(); } /** diff --git a/src/Illuminate/Support/Facades/Cache.php b/src/Illuminate/Support/Facades/Cache.php index f9c4b74cd4c0..1463306365ca 100755 --- a/src/Illuminate/Support/Facades/Cache.php +++ b/src/Illuminate/Support/Facades/Cache.php @@ -38,6 +38,7 @@ * @method static bool deleteMultiple(iterable $keys) * @method static bool clear() * @method static \Illuminate\Cache\TaggedCache tags(array|mixed $names) + * @method static string|null getName() * @method static bool supportsTags() * @method static int|null getDefaultCacheTime() * @method static \Illuminate\Cache\Repository setDefaultCacheTime(int|null $seconds) diff --git a/src/Illuminate/Support/Facades/DB.php b/src/Illuminate/Support/Facades/DB.php index cc282903ec79..c2d4249727d2 100644 --- a/src/Illuminate/Support/Facades/DB.php +++ b/src/Illuminate/Support/Facades/DB.php @@ -5,6 +5,7 @@ use Illuminate\Database\Console\Migrations\FreshCommand; use Illuminate\Database\Console\Migrations\RefreshCommand; use Illuminate\Database\Console\Migrations\ResetCommand; +use Illuminate\Database\Console\Migrations\RollbackCommand; use Illuminate\Database\Console\WipeCommand; /** @@ -132,6 +133,7 @@ public static function prohibitDestructiveCommands(bool $prohibit = true) FreshCommand::prohibit($prohibit); RefreshCommand::prohibit($prohibit); ResetCommand::prohibit($prohibit); + RollbackCommand::prohibit($prohibit); WipeCommand::prohibit($prohibit); } diff --git a/src/Illuminate/Support/Testing/Fakes/EventFake.php b/src/Illuminate/Support/Testing/Fakes/EventFake.php index 4334e5ccf5aa..7f226a786faf 100644 --- a/src/Illuminate/Support/Testing/Fakes/EventFake.php +++ b/src/Illuminate/Support/Testing/Fakes/EventFake.php @@ -87,7 +87,7 @@ public function assertListening($expectedEvent, $expectedListener) { foreach ($this->dispatcher->getListeners($expectedEvent) as $listenerClosure) { $actualListener = (new ReflectionFunction($listenerClosure)) - ->getStaticVariables()['listener']; + ->getStaticVariables()['listener']; $normalizedListener = $expectedListener; diff --git a/src/Illuminate/Testing/Constraints/NotSoftDeletedInDatabase.php b/src/Illuminate/Testing/Constraints/NotSoftDeletedInDatabase.php index 87cef8b6d02d..665a50588caf 100644 --- a/src/Illuminate/Testing/Constraints/NotSoftDeletedInDatabase.php +++ b/src/Illuminate/Testing/Constraints/NotSoftDeletedInDatabase.php @@ -59,9 +59,9 @@ public function __construct(Connection $database, array $data, string $deletedAt public function matches($table): bool { return $this->database->table($table) - ->where($this->data) - ->whereNull($this->deletedAtColumn) - ->exists(); + ->where($this->data) + ->whereNull($this->deletedAtColumn) + ->exists(); } /** diff --git a/src/Illuminate/Testing/Constraints/SoftDeletedInDatabase.php b/src/Illuminate/Testing/Constraints/SoftDeletedInDatabase.php index 0d14f83b6c67..c764d5f39c4e 100644 --- a/src/Illuminate/Testing/Constraints/SoftDeletedInDatabase.php +++ b/src/Illuminate/Testing/Constraints/SoftDeletedInDatabase.php @@ -61,9 +61,9 @@ public function __construct(Connection $database, array $data, string $deletedAt public function matches($table): bool { return $this->database->table($table) - ->where($this->data) - ->whereNotNull($this->deletedAtColumn) - ->exists(); + ->where($this->data) + ->whereNotNull($this->deletedAtColumn) + ->exists(); } /** diff --git a/src/Illuminate/Testing/PendingCommand.php b/src/Illuminate/Testing/PendingCommand.php index 0fc154612cb8..062d2a17738c 100644 --- a/src/Illuminate/Testing/PendingCommand.php +++ b/src/Illuminate/Testing/PendingCommand.php @@ -443,8 +443,8 @@ protected function mockConsoleOutput() private function createABufferedOutputMock() { $mock = Mockery::mock(BufferedOutput::class.'[doWrite]') - ->shouldAllowMockingProtectedMethods() - ->shouldIgnoreMissing(); + ->shouldAllowMockingProtectedMethods() + ->shouldIgnoreMissing(); if ($this->test->expectsOutput === false) { $mock->shouldReceive('doWrite')->never(); @@ -491,12 +491,12 @@ private function createABufferedOutputMock() foreach ($this->test->unexpectedOutputSubstrings as $text => $displayed) { $mock->shouldReceive('doWrite') - ->atLeast() - ->times(0) - ->withArgs(fn ($output) => str_contains($output, $text)) - ->andReturnUsing(function () use ($text) { - $this->test->unexpectedOutputSubstrings[$text] = true; - }); + ->atLeast() + ->times(0) + ->withArgs(fn ($output) => str_contains($output, $text)) + ->andReturnUsing(function () use ($text) { + $this->test->unexpectedOutputSubstrings[$text] = true; + }); } return $mock; diff --git a/src/Illuminate/Translation/FileLoader.php b/src/Illuminate/Translation/FileLoader.php index b324e7aaecc0..65c4c4abc323 100755 --- a/src/Illuminate/Translation/FileLoader.php +++ b/src/Illuminate/Translation/FileLoader.php @@ -182,6 +182,17 @@ public function namespaces() return $this->hints; } + /** + * Add a new path to the loader. + * + * @param string $path + * @return void + */ + public function addPath($path) + { + $this->paths[] = $path; + } + /** * Add a new JSON path to the loader. * diff --git a/src/Illuminate/Validation/Rule.php b/src/Illuminate/Validation/Rule.php index b94f40ca3372..c9d9a2bad92e 100644 --- a/src/Illuminate/Validation/Rule.php +++ b/src/Illuminate/Validation/Rule.php @@ -6,6 +6,7 @@ use Illuminate\Support\Traits\Macroable; use Illuminate\Validation\Rules\ArrayRule; use Illuminate\Validation\Rules\Can; +use Illuminate\Validation\Rules\Date; use Illuminate\Validation\Rules\Dimensions; use Illuminate\Validation\Rules\Email; use Illuminate\Validation\Rules\Enum; @@ -170,6 +171,16 @@ public static function prohibitedIf($callback) return new ProhibitedIf($callback); } + /** + * Get a date rule builder instance. + * + * @return \Illuminate\Validation\Rules\Date + */ + public static function date() + { + return new Date; + } + /** * Get an email rule builder instance. * diff --git a/src/Illuminate/Validation/Rules/Date.php b/src/Illuminate/Validation/Rules/Date.php new file mode 100644 index 000000000000..6b6140fbd8f8 --- /dev/null +++ b/src/Illuminate/Validation/Rules/Date.php @@ -0,0 +1,135 @@ +addRule('date_format:'.$format); + } + + /** + * Ensure the date is before today. + */ + public function beforeToday(): static + { + return $this->before('today'); + } + + /** + * Ensure the date is after today. + */ + public function afterToday(): static + { + return $this->after('today'); + } + + /** + * Ensure the date is before or equal to today. + */ + public function todayOrBefore(): static + { + return $this->beforeOrEqual('today'); + } + + /** + * Ensure the date is after or equal to today. + */ + public function todayOrAfter(): static + { + return $this->afterOrEqual('today'); + } + + /** + * Ensure the date is before the given date or date field. + */ + public function before(DateTimeInterface|string $date): static + { + return $this->addRule('before:'.$this->formatDate($date)); + } + + /** + * Ensure the date is after the given date or date field. + */ + public function after(DateTimeInterface|string $date): static + { + return $this->addRule('after:'.$this->formatDate($date)); + } + + /** + * Ensure the date is on or before the specified date or date field. + */ + public function beforeOrEqual(DateTimeInterface|string $date): static + { + return $this->addRule('before_or_equal:'.$this->formatDate($date)); + } + + /** + * Ensure the date is on or after the given date or date field. + */ + public function afterOrEqual(DateTimeInterface|string $date): static + { + return $this->addRule('after_or_equal:'.$this->formatDate($date)); + } + + /** + * Ensure the date is between two dates or date fields. + */ + public function between(DateTimeInterface|string $from, DateTimeInterface|string $to): static + { + return $this->after($from)->before($to); + } + + /** + * Ensure the date is between or equal to two dates or date fields. + */ + public function betweenOrEqual(DateTimeInterface|string $from, DateTimeInterface|string $to): static + { + return $this->afterOrEqual($from)->beforeOrEqual($to); + } + + /** + * Add custom rules to the validation rules array. + */ + protected function addRule(array|string $rules): static + { + $this->constraints = array_merge($this->constraints, Arr::wrap($rules)); + + return $this; + } + + /** + * Format the date for the validation rule. + */ + protected function formatDate(DateTimeInterface|string $date): string + { + return $date instanceof DateTimeInterface + ? $date->format('Y-m-d') + : $date; + } + + /** + * Convert the rule to a validation string. + */ + public function __toString(): string + { + return implode('|', $this->constraints); + } +} diff --git a/src/Illuminate/Validation/Validator.php b/src/Illuminate/Validation/Validator.php index dff9a156fc19..cdba5179f5c2 100755 --- a/src/Illuminate/Validation/Validator.php +++ b/src/Illuminate/Validation/Validator.php @@ -1225,7 +1225,7 @@ public function addRules($rules) // of the explicit rules needed for the given data. For example the rule // names.* would get expanded to names.0, names.1, etc. for this data. $response = (new ValidationRuleParser($this->data)) - ->explode(ValidationRuleParser::filterConditionalRules($rules, $this->data)); + ->explode(ValidationRuleParser::filterConditionalRules($rules, $this->data)); $this->rules = array_merge_recursive( $this->rules, $response->rules diff --git a/src/Illuminate/View/Compilers/BladeCompiler.php b/src/Illuminate/View/Compilers/BladeCompiler.php index d7b79bfd3b4b..ba8a339145c2 100644 --- a/src/Illuminate/View/Compilers/BladeCompiler.php +++ b/src/Illuminate/View/Compilers/BladeCompiler.php @@ -331,8 +331,8 @@ public function render() }; $view = Container::getInstance() - ->make(ViewFactory::class) - ->make($component->resolveView(), $data); + ->make(ViewFactory::class) + ->make($component->resolveView(), $data); return tap($view->render(), function () use ($view, $deleteCachedView) { if ($deleteCachedView) { @@ -821,8 +821,8 @@ public function anonymousComponentPath(string $path, ?string $prefix = null) ]; Container::getInstance() - ->make(ViewFactory::class) - ->addNamespace($prefixHash, $path); + ->make(ViewFactory::class) + ->addNamespace($prefixHash, $path); } /** @@ -837,9 +837,9 @@ public function anonymousComponentNamespace(string $directory, ?string $prefix = $prefix ??= $directory; $this->anonymousComponentNamespaces[$prefix] = (new Stringable($directory)) - ->replace('/', '.') - ->trim('. ') - ->toString(); + ->replace('/', '.') + ->trim('. ') + ->toString(); } /** diff --git a/src/Illuminate/View/Compilers/ComponentTagCompiler.php b/src/Illuminate/View/Compilers/ComponentTagCompiler.php index 3de2755f2a7f..357bcd241f58 100644 --- a/src/Illuminate/View/Compilers/ComponentTagCompiler.php +++ b/src/Illuminate/View/Compilers/ComponentTagCompiler.php @@ -421,8 +421,8 @@ public function findClassByComponent(string $component) public function guessClassName(string $component) { $namespace = Container::getInstance() - ->make(Application::class) - ->getNamespace(); + ->make(Application::class) + ->getNamespace(); $class = $this->formatClassName($component); @@ -787,12 +787,12 @@ protected function escapeSingleQuotesOutsideOfPhpBlocks(string $value) protected function attributesToString(array $attributes, $escapeBound = true) { return (new Collection($attributes)) - ->map(function (string $value, string $attribute) use ($escapeBound) { - return $escapeBound && isset($this->boundAttributes[$attribute]) && $value !== 'true' && ! is_numeric($value) - ? "'{$attribute}' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute({$value})" - : "'{$attribute}' => {$value}"; - }) - ->implode(','); + ->map(function (string $value, string $attribute) use ($escapeBound) { + return $escapeBound && isset($this->boundAttributes[$attribute]) && $value !== 'true' && ! is_numeric($value) + ? "'{$attribute}' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute({$value})" + : "'{$attribute}' => {$value}"; + }) + ->implode(','); } /** diff --git a/src/Illuminate/View/ComponentAttributeBag.php b/src/Illuminate/View/ComponentAttributeBag.php index befc983b3d36..780d93deb51d 100644 --- a/src/Illuminate/View/ComponentAttributeBag.php +++ b/src/Illuminate/View/ComponentAttributeBag.php @@ -274,12 +274,12 @@ public function merge(array $attributeDefaults = [], $escape = true) }, $attributeDefaults); [$appendableAttributes, $nonAppendableAttributes] = (new Collection($this->attributes)) - ->partition(function ($value, $key) use ($attributeDefaults) { - return $key === 'class' || $key === 'style' || ( - isset($attributeDefaults[$key]) && - $attributeDefaults[$key] instanceof AppendableAttributeValue - ); - }); + ->partition(function ($value, $key) use ($attributeDefaults) { + return $key === 'class' || $key === 'style' || ( + isset($attributeDefaults[$key]) && + $attributeDefaults[$key] instanceof AppendableAttributeValue + ); + }); $attributes = $appendableAttributes->mapWithKeys(function ($value, $key) use ($attributeDefaults, $escape) { $defaultsValue = isset($attributeDefaults[$key]) && $attributeDefaults[$key] instanceof AppendableAttributeValue diff --git a/tests/Broadcasting/AblyBroadcasterTest.php b/tests/Broadcasting/AblyBroadcasterTest.php index 4cf4458ae824..ea30f005069d 100644 --- a/tests/Broadcasting/AblyBroadcasterTest.php +++ b/tests/Broadcasting/AblyBroadcasterTest.php @@ -41,7 +41,7 @@ public function testAuthCallValidAuthenticationResponseWithPrivateChannelWhenCal }); $this->broadcaster->shouldReceive('validAuthenticationResponse') - ->once(); + ->once(); $this->broadcaster->auth( $this->getMockRequestWithUserForChannel('private-test') @@ -82,7 +82,7 @@ public function testAuthCallValidAuthenticationResponseWithPresenceChannelWhenCa }); $this->broadcaster->shouldReceive('validAuthenticationResponse') - ->once(); + ->once(); $this->broadcaster->auth( $this->getMockRequestWithUserForChannel('presence-test') @@ -125,17 +125,17 @@ protected function getMockRequestWithUserForChannel($channel) $request->shouldReceive('all')->andReturn(['channel_name' => $channel, 'socket_id' => 'abcd.1234']); $request->shouldReceive('input') - ->with('callback', false) - ->andReturn(false); + ->with('callback', false) + ->andReturn(false); $user = m::mock('User'); $user->shouldReceive('getAuthIdentifierForBroadcasting') - ->andReturn(42); + ->andReturn(42); $user->shouldReceive('getAuthIdentifier') - ->andReturn(42); + ->andReturn(42); $request->shouldReceive('user') - ->andReturn($user); + ->andReturn($user); return $request; } @@ -150,7 +150,7 @@ protected function getMockRequestWithoutUserForChannel($channel) $request->shouldReceive('all')->andReturn(['channel_name' => $channel]); $request->shouldReceive('user') - ->andReturn(null); + ->andReturn(null); return $request; } diff --git a/tests/Broadcasting/BroadcasterTest.php b/tests/Broadcasting/BroadcasterTest.php index 91e64d4d4323..57312935ccdb 100644 --- a/tests/Broadcasting/BroadcasterTest.php +++ b/tests/Broadcasting/BroadcasterTest.php @@ -206,9 +206,9 @@ public function testRetrieveUserWithoutGuard() $request = m::mock(Request::class); $request->shouldReceive('user') - ->once() - ->withNoArgs() - ->andReturn(new DummyUser); + ->once() + ->withNoArgs() + ->andReturn(new DummyUser); $this->assertInstanceOf( DummyUser::class, @@ -224,9 +224,9 @@ public function testRetrieveUserWithOneGuardUsingAStringForSpecifyingGuard() $request = m::mock(Request::class); $request->shouldReceive('user') - ->once() - ->with('myguard') - ->andReturn(new DummyUser); + ->once() + ->with('myguard') + ->andReturn(new DummyUser); $this->assertInstanceOf( DummyUser::class, @@ -245,14 +245,14 @@ public function testRetrieveUserWithMultipleGuardsAndRespectGuardsOrder() $request = m::mock(Request::class); $request->shouldReceive('user') - ->once() - ->with('myguard1') - ->andReturn(null); + ->once() + ->with('myguard1') + ->andReturn(null); $request->shouldReceive('user') - ->twice() - ->with('myguard2') - ->andReturn(new DummyUser) - ->ordered('user'); + ->twice() + ->with('myguard2') + ->andReturn(new DummyUser) + ->ordered('user'); $this->assertInstanceOf( DummyUser::class, @@ -273,11 +273,11 @@ public function testRetrieveUserDontUseDefaultGuardWhenOneGuardSpecified() $request = m::mock(Request::class); $request->shouldReceive('user') - ->once() - ->with('myguard') - ->andReturn(null); + ->once() + ->with('myguard') + ->andReturn(null); $request->shouldNotReceive('user') - ->withNoArgs(); + ->withNoArgs(); $this->broadcaster->retrieveUser($request, 'somechannel'); } @@ -290,15 +290,15 @@ public function testRetrieveUserDontUseDefaultGuardWhenMultipleGuardsSpecified() $request = m::mock(Request::class); $request->shouldReceive('user') - ->once() - ->with('myguard1') - ->andReturn(null); + ->once() + ->with('myguard1') + ->andReturn(null); $request->shouldReceive('user') - ->once() - ->with('myguard2') - ->andReturn(null); + ->once() + ->with('myguard2') + ->andReturn(null); $request->shouldNotReceive('user') - ->withNoArgs(); + ->withNoArgs(); $this->broadcaster->retrieveUser($request, 'somechannel'); } diff --git a/tests/Broadcasting/PusherBroadcasterTest.php b/tests/Broadcasting/PusherBroadcasterTest.php index d00b86d87629..9c5f3673a340 100644 --- a/tests/Broadcasting/PusherBroadcasterTest.php +++ b/tests/Broadcasting/PusherBroadcasterTest.php @@ -32,7 +32,7 @@ public function testAuthCallValidAuthenticationResponseWithPrivateChannelWhenCal }); $this->broadcaster->shouldReceive('validAuthenticationResponse') - ->once(); + ->once(); $this->broadcaster->auth( $this->getMockRequestWithUserForChannel('private-test') @@ -73,7 +73,7 @@ public function testAuthCallValidAuthenticationResponseWithPresenceChannelWhenCa }); $this->broadcaster->shouldReceive('validAuthenticationResponse') - ->once(); + ->once(); $this->broadcaster->auth( $this->getMockRequestWithUserForChannel('presence-test') @@ -115,8 +115,8 @@ public function testValidAuthenticationResponseCallPusherSocketAuthMethodWithPri ]; $this->pusher->shouldReceive('socket_auth') - ->once() - ->andReturn(json_encode($data)); + ->once() + ->andReturn(json_encode($data)); $this->assertEquals( $data, @@ -137,8 +137,8 @@ public function testValidAuthenticationResponseCallPusherPresenceAuthMethodWithP ]; $this->pusher->shouldReceive('presence_auth') - ->once() - ->andReturn(json_encode($data)); + ->once() + ->andReturn(json_encode($data)); $this->assertEquals( $data, @@ -181,17 +181,17 @@ protected function getMockRequestWithUserForChannel($channel) $request->shouldReceive('all')->andReturn(['channel_name' => $channel, 'socket_id' => 'abcd.1234']); $request->shouldReceive('input') - ->with('callback', false) - ->andReturn(false); + ->with('callback', false) + ->andReturn(false); $user = m::mock('User'); $user->shouldReceive('getAuthIdentifierForBroadcasting') - ->andReturn(42); + ->andReturn(42); $user->shouldReceive('getAuthIdentifier') - ->andReturn(42); + ->andReturn(42); $request->shouldReceive('user') - ->andReturn($user); + ->andReturn($user); return $request; } @@ -206,7 +206,7 @@ protected function getMockRequestWithoutUserForChannel($channel) $request->shouldReceive('all')->andReturn(['channel_name' => $channel]); $request->shouldReceive('user') - ->andReturn(null); + ->andReturn(null); return $request; } diff --git a/tests/Broadcasting/RedisBroadcasterTest.php b/tests/Broadcasting/RedisBroadcasterTest.php index 7422c5547818..87960ba034ff 100644 --- a/tests/Broadcasting/RedisBroadcasterTest.php +++ b/tests/Broadcasting/RedisBroadcasterTest.php @@ -41,7 +41,7 @@ public function testAuthCallValidAuthenticationResponseWithPrivateChannelWhenCal }); $this->broadcaster->shouldReceive('validAuthenticationResponse') - ->once(); + ->once(); $this->broadcaster->auth( $this->getMockRequestWithUserForChannel('private-test') @@ -82,7 +82,7 @@ public function testAuthCallValidAuthenticationResponseWithPresenceChannelWhenCa }); $this->broadcaster->shouldReceive('validAuthenticationResponse') - ->once(); + ->once(); $this->broadcaster->auth( $this->getMockRequestWithUserForChannel('presence-test') @@ -172,12 +172,12 @@ protected function getMockRequestWithUserForChannel($channel) $user = m::mock('User'); $user->shouldReceive('getAuthIdentifierForBroadcasting') - ->andReturn(42); + ->andReturn(42); $user->shouldReceive('getAuthIdentifier') - ->andReturn(42); + ->andReturn(42); $request->shouldReceive('user') - ->andReturn($user); + ->andReturn($user); return $request; } @@ -192,7 +192,7 @@ protected function getMockRequestWithoutUserForChannel($channel) $request->shouldReceive('all')->andReturn(['channel_name' => $channel]); $request->shouldReceive('user') - ->andReturn(null); + ->andReturn(null); return $request; } diff --git a/tests/Broadcasting/UsePusherChannelsNamesTest.php b/tests/Broadcasting/UsePusherChannelsNamesTest.php index 122e83e8c3f4..d1ea01ed727e 100644 --- a/tests/Broadcasting/UsePusherChannelsNamesTest.php +++ b/tests/Broadcasting/UsePusherChannelsNamesTest.php @@ -30,6 +30,19 @@ public function testChannelNameNormalizationSpecialCase() ); } + public function testChannelNamePatternMatching() + { + $broadcaster = new FakeBroadcasterUsingPusherChannelsNames; + + $this->assertEquals( + 0, + $broadcaster->testChannelNameMatchesPattern( + 'TestChannel', + 'Test.{id}' + ) + ); + } + #[DataProvider('channelsProvider')] public function testIsGuardedChannel($requestChannelName, $_, $guarded) { @@ -103,4 +116,9 @@ public function broadcast(array $channels, $event, array $payload = []) { // } + + public function testChannelNameMatchesPattern($channel, $pattern) + { + return $this->channelNameMatchesPattern($channel, $pattern); + } } diff --git a/tests/Bus/BusBatchTest.php b/tests/Bus/BusBatchTest.php index 3fe602a38656..855ce91bb2b3 100644 --- a/tests/Bus/BusBatchTest.php +++ b/tests/Bus/BusBatchTest.php @@ -101,8 +101,8 @@ public function test_jobs_can_be_added_to_the_batch() }; $queue->shouldReceive('connection')->once() - ->with('test-connection') - ->andReturn($connection = m::mock(stdClass::class)); + ->with('test-connection') + ->andReturn($connection = m::mock(stdClass::class)); $connection->shouldReceive('bulk')->once()->with(m::on(function ($args) use ($job, $secondJob) { return @@ -191,8 +191,8 @@ public function test_successful_jobs_can_be_recorded() }; $queue->shouldReceive('connection')->once() - ->with('test-connection') - ->andReturn($connection = m::mock(stdClass::class)); + ->with('test-connection') + ->andReturn($connection = m::mock(stdClass::class)); $connection->shouldReceive('bulk')->once(); @@ -231,8 +231,8 @@ public function test_failed_jobs_can_be_recorded_while_not_allowing_failures() }; $queue->shouldReceive('connection')->once() - ->with('test-connection') - ->andReturn($connection = m::mock(stdClass::class)); + ->with('test-connection') + ->andReturn($connection = m::mock(stdClass::class)); $connection->shouldReceive('bulk')->once(); @@ -273,8 +273,8 @@ public function test_failed_jobs_can_be_recorded_while_allowing_failures() }; $queue->shouldReceive('connection')->once() - ->with('test-connection') - ->andReturn($connection = m::mock(stdClass::class)); + ->with('test-connection') + ->andReturn($connection = m::mock(stdClass::class)); $connection->shouldReceive('bulk')->once(); @@ -472,26 +472,26 @@ protected function createTestBatch($queue, $allowFailures = false) $repository = new DatabaseBatchRepository(new BatchFactory($queue), DB::connection(), 'job_batches'); $pendingBatch = (new PendingBatch(new Container, collect())) - ->progress(function (Batch $batch) { - $_SERVER['__progress.batch'] = $batch; - $_SERVER['__progress.count']++; - }) - ->then(function (Batch $batch) { - $_SERVER['__then.batch'] = $batch; - $_SERVER['__then.count']++; - }) - ->catch(function (Batch $batch, $e) { - $_SERVER['__catch.batch'] = $batch; - $_SERVER['__catch.exception'] = $e; - $_SERVER['__catch.count']++; - }) - ->finally(function (Batch $batch) { - $_SERVER['__finally.batch'] = $batch; - $_SERVER['__finally.count']++; - }) - ->allowFailures($allowFailures) - ->onConnection('test-connection') - ->onQueue('test-queue'); + ->progress(function (Batch $batch) { + $_SERVER['__progress.batch'] = $batch; + $_SERVER['__progress.count']++; + }) + ->then(function (Batch $batch) { + $_SERVER['__then.batch'] = $batch; + $_SERVER['__then.count']++; + }) + ->catch(function (Batch $batch, $e) { + $_SERVER['__catch.batch'] = $batch; + $_SERVER['__catch.exception'] = $e; + $_SERVER['__catch.count']++; + }) + ->finally(function (Batch $batch) { + $_SERVER['__finally.batch'] = $batch; + $_SERVER['__finally.count']++; + }) + ->allowFailures($allowFailures) + ->onConnection('test-connection') + ->onQueue('test-queue'); return $repository->store($pendingBatch); } diff --git a/tests/Console/View/ComponentsTest.php b/tests/Console/View/ComponentsTest.php index f04e9f684a9b..ef710e51effd 100644 --- a/tests/Console/View/ComponentsTest.php +++ b/tests/Console/View/ComponentsTest.php @@ -75,17 +75,17 @@ public function testConfirm() $output = m::mock(OutputStyle::class); $output->shouldReceive('confirm') - ->with('Question?', false) - ->once() - ->andReturnTrue(); + ->with('Question?', false) + ->once() + ->andReturnTrue(); $result = with(new Components\Confirm($output))->render('Question?'); $this->assertTrue($result); $output->shouldReceive('confirm') - ->with('Question?', true) - ->once() - ->andReturnTrue(); + ->with('Question?', true) + ->once() + ->andReturnTrue(); $result = with(new Components\Confirm($output))->render('Question?', true); $this->assertTrue($result); @@ -96,9 +96,9 @@ public function testChoice() $output = m::mock(OutputStyle::class); $output->shouldReceive('askQuestion') - ->with(m::type(ChoiceQuestion::class)) - ->once() - ->andReturn('a'); + ->with(m::type(ChoiceQuestion::class)) + ->once() + ->andReturn('a'); $result = with(new Components\Choice($output))->render('Question?', ['a', 'b']); $this->assertSame('a', $result); diff --git a/tests/Container/ContainerTest.php b/tests/Container/ContainerTest.php index 53cf9b6713ac..7c99b0cbea21 100755 --- a/tests/Container/ContainerTest.php +++ b/tests/Container/ContainerTest.php @@ -516,13 +516,13 @@ public function testContainerGetFactory() public function testMakeWithMethodIsAnAliasForMakeMethod() { $mock = $this->getMockBuilder(Container::class) - ->onlyMethods(['make']) - ->getMock(); + ->onlyMethods(['make']) + ->getMock(); $mock->expects($this->once()) - ->method('make') - ->with(ContainerDefaultValueStub::class, ['default' => 'laurence']) - ->willReturn(new stdClass); + ->method('make') + ->with(ContainerDefaultValueStub::class, ['default' => 'laurence']) + ->willReturn(new stdClass); $result = $mock->makeWith(ContainerDefaultValueStub::class, ['default' => 'laurence']); @@ -672,8 +672,8 @@ public function testMethodLevelContextualBinding() $container->bind(IContainerContractStub::class, ContainerImplementationStubTwo::class); $container->when(ContainerContextualBindingCallTarget::class) - ->needs(IContainerContractStub::class) - ->give(ContainerImplementationStub::class); + ->needs(IContainerContractStub::class) + ->give(ContainerImplementationStub::class); $result = $container->call([new ContainerContextualBindingCallTarget, 'work']); diff --git a/tests/Database/DatabaseEloquentBelongsToManyWithAttributesTest.php b/tests/Database/DatabaseEloquentBelongsToManyWithAttributesTest.php new file mode 100755 index 000000000000..618616ed14ce --- /dev/null +++ b/tests/Database/DatabaseEloquentBelongsToManyWithAttributesTest.php @@ -0,0 +1,263 @@ +addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + $db->bootEloquent(); + $db->setAsGlobal(); + $this->createSchema(); + } + + public function testCreatesWithAttributesAndPivotValues(): void + { + $post = ManyToManyWithAttributesPost::create(); + $tag = $post->metaTags()->create(['name' => 'long article']); + + $this->assertSame('long article', $tag->name); + $this->assertTrue($tag->visible); + + $pivot = DB::table('with_attributes_pivot')->first(); + $this->assertSame('meta', $pivot->type); + $this->assertSame($post->id, $pivot->post_id); + $this->assertSame($tag->id, $pivot->tag_id); + } + + public function testQueriesWithAttributesAndPivotValues(): void + { + $post = new ManyToManyWithAttributesPost(['id' => 2]); + $wheres = $post->metaTags()->toBase()->wheres; + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_tags.visible', + 'operator' => '=', + 'value' => true, + 'boolean' => 'and', + ], $wheres); + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_pivot.type', + 'operator' => '=', + 'value' => 'meta', + 'boolean' => 'and', + ], $wheres); + } + + public function testMorphToManyWithAttributes(): void + { + $post = new ManyToManyWithAttributesPost(['id' => 2]); + $wheres = $post->morphedTags()->toBase()->wheres; + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_tags.visible', + 'operator' => '=', + 'value' => true, + 'boolean' => 'and', + ], $wheres); + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_taggables.type', + 'operator' => '=', + 'value' => 'meta', + 'boolean' => 'and', + ], $wheres); + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_taggables.taggable_type', + 'operator' => '=', + 'value' => ManyToManyWithAttributesPost::class, + 'boolean' => 'and', + ], $wheres); + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_taggables.taggable_id', + 'operator' => '=', + 'value' => 2, + 'boolean' => 'and', + ], $wheres); + + $tag = $post->morphedTags()->create(['name' => 'new tag']); + + $this->assertTrue($tag->visible); + $this->assertSame('new tag', $tag->name); + $this->assertSame($tag->id, $post->morphedTags()->first()->id); + } + + public function testMorphedByManyWithAttributes(): void + { + $tag = new ManyToManyWithAttributesTag(['id' => 4]); + $wheres = $tag->morphedPosts()->toBase()->wheres; + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_posts.title', + 'operator' => '=', + 'value' => 'Title!', + 'boolean' => 'and', + ], $wheres); + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_taggables.type', + 'operator' => '=', + 'value' => 'meta', + 'boolean' => 'and', + ], $wheres); + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_taggables.taggable_type', + 'operator' => '=', + 'value' => ManyToManyWithAttributesPost::class, + 'boolean' => 'and', + ], $wheres); + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_taggables.tag_id', + 'operator' => '=', + 'value' => 4, + 'boolean' => 'and', + ], $wheres); + + $post = $tag->morphedPosts()->create(); + $this->assertSame('Title!', $post->title); + $this->assertSame($post->id, $tag->morphedPosts()->first()->id); + } + + protected function createSchema() + { + $this->schema()->create('with_attributes_posts', function ($table) { + $table->increments('id'); + $table->string('title')->nullable(); + $table->timestamps(); + }); + + $this->schema()->create('with_attributes_tags', function ($table) { + $table->increments('id'); + $table->string('name'); + $table->boolean('visible')->nullable(); + $table->timestamps(); + }); + + $this->schema()->create('with_attributes_pivot', function ($table) { + $table->integer('post_id'); + $table->integer('tag_id'); + $table->string('type'); + }); + + $this->schema()->create('with_attributes_taggables', function ($table) { + $table->integer('tag_id'); + $table->integer('taggable_id'); + $table->string('taggable_type'); + $table->string('type'); + }); + } + + /** + * Tear down the database schema. + * + * @return void + */ + protected function tearDown(): void + { + $this->schema()->drop('with_attributes_posts'); + $this->schema()->drop('with_attributes_tags'); + $this->schema()->drop('with_attributes_pivot'); + } + + /** + * Get a database connection instance. + * + * @return \Illuminate\Database\Connection + */ + protected function connection($connection = 'default') + { + return Model::getConnectionResolver()->connection($connection); + } + + /** + * Get a schema builder instance. + * + * @return \Illuminate\Database\Schema\Builder + */ + protected function schema($connection = 'default') + { + return $this->connection($connection)->getSchemaBuilder(); + } +} + +class ManyToManyWithAttributesPost extends Model +{ + protected $guarded = []; + protected $table = 'with_attributes_posts'; + + public function tags(): BelongsToMany + { + return $this->belongsToMany( + ManyToManyWithAttributesTag::class, + 'with_attributes_pivot', + 'tag_id', + 'post_id', + ); + } + + public function metaTags(): BelongsToMany + { + return $this->tags() + ->withAttributes('visible', true) + ->withPivotValue('type', 'meta'); + } + + public function morphedTags(): MorphToMany + { + return $this + ->morphToMany( + ManyToManyWithAttributesTag::class, + 'taggable', + 'with_attributes_taggables', + relatedPivotKey: 'tag_id' + ) + ->withAttributes('visible', true) + ->withPivotValue('type', 'meta'); + } +} + +class ManyToManyWithAttributesTag extends Model +{ + protected $guarded = []; + protected $table = 'with_attributes_tags'; + + public function morphedPosts(): MorphToMany + { + return $this + ->morphedByMany( + ManyToManyWithAttributesPost::class, + 'taggable', + 'with_attributes_taggables', + 'tag_id', + ) + ->withAttributes('title', 'Title!') + ->withPivotValue('type', 'meta'); + } +} diff --git a/tests/Database/DatabaseEloquentBuilderCreateOrFirstTest.php b/tests/Database/DatabaseEloquentBuilderCreateOrFirstTest.php index ea09aa234b91..b629194b6ea6 100755 --- a/tests/Database/DatabaseEloquentBuilderCreateOrFirstTest.php +++ b/tests/Database/DatabaseEloquentBuilderCreateOrFirstTest.php @@ -300,6 +300,175 @@ public function testUpdateOrCreateMethodUpdatesRecordCreatedJustNow(): void ], $result->toArray()); } + public function testIncrementOrCreateMethodIncrementsExistingRecord(): void + { + $model = new EloquentBuilderCreateOrFirstTestModel(); + $this->mockConnectionForModel($model, 'SQLite'); + $model->getConnection()->shouldReceive('transactionLevel')->andReturn(0); + $model->getConnection()->shouldReceive('getName')->andReturn('sqlite'); + + $model->getConnection() + ->expects('select') + ->with('select * from "table" where ("attr" = ?) limit 1', ['foo'], true) + ->andReturn([[ + 'id' => 123, + 'attr' => 'foo', + 'count' => 1, + 'created_at' => '2023-01-01 00:00:00', + 'updated_at' => '2023-01-01 00:00:00', + ]]); + + $model->getConnection() + ->expects('raw') + ->with('"count" + 1') + ->andReturn('2'); + + $model->getConnection() + ->expects('update') + ->with( + 'update "table" set "count" = ?, "updated_at" = ? where "id" = ?', + ['2', '2023-01-01 00:00:00', 123], + ) + ->andReturn(1); + + $result = $model->newQuery()->incrementOrCreate(['attr' => 'foo'], 'count'); + $this->assertFalse($result->wasRecentlyCreated); + $this->assertEquals([ + 'id' => 123, + 'attr' => 'foo', + 'count' => 2, + 'created_at' => '2023-01-01T00:00:00.000000Z', + 'updated_at' => '2023-01-01T00:00:00.000000Z', + ], $result->toArray()); + } + + public function testIncrementOrCreateMethodCreatesNewRecord(): void + { + $model = new EloquentBuilderCreateOrFirstTestModel(); + $this->mockConnectionForModel($model, 'SQLite', [123]); + $model->getConnection()->shouldReceive('transactionLevel')->andReturn(0); + $model->getConnection()->shouldReceive('getName')->andReturn('sqlite'); + + $model->getConnection() + ->expects('select') + ->with('select * from "table" where ("attr" = ?) limit 1', ['foo'], true) + ->andReturn([]); + + $model->getConnection()->expects('insert')->with( + 'insert into "table" ("attr", "count", "updated_at", "created_at") values (?, ?, ?, ?)', + ['foo', '1', '2023-01-01 00:00:00', '2023-01-01 00:00:00'], + )->andReturnTrue(); + + $result = $model->newQuery()->incrementOrCreate(['attr' => 'foo']); + $this->assertTrue($result->wasRecentlyCreated); + $this->assertEquals([ + 'id' => 123, + 'attr' => 'foo', + 'count' => 1, + 'created_at' => '2023-01-01T00:00:00.000000Z', + 'updated_at' => '2023-01-01T00:00:00.000000Z', + ], $result->toArray()); + } + + public function testIncrementOrCreateMethodIncrementParametersArePassed(): void + { + $model = new EloquentBuilderCreateOrFirstTestModel(); + $this->mockConnectionForModel($model, 'SQLite'); + $model->getConnection()->shouldReceive('transactionLevel')->andReturn(0); + $model->getConnection()->shouldReceive('getName')->andReturn('sqlite'); + + $model->getConnection() + ->expects('select') + ->with('select * from "table" where ("attr" = ?) limit 1', ['foo'], true) + ->andReturn([[ + 'id' => 123, + 'attr' => 'foo', + 'val' => 'bar', + 'count' => 1, + 'created_at' => '2023-01-01 00:00:00', + 'updated_at' => '2023-01-01 00:00:00', + ]]); + + $model->getConnection() + ->expects('raw') + ->with('"count" + 2') + ->andReturn('3'); + + $model->getConnection() + ->expects('update') + ->with( + 'update "table" set "count" = ?, "val" = ?, "updated_at" = ? where "id" = ?', + ['3', 'baz', '2023-01-01 00:00:00', 123], + ) + ->andReturn(1); + + $result = $model->newQuery()->incrementOrCreate(['attr' => 'foo'], step: 2, extra: ['val' => 'baz']); + $this->assertFalse($result->wasRecentlyCreated); + $this->assertEquals([ + 'id' => 123, + 'attr' => 'foo', + 'count' => 3, + 'val' => 'baz', + 'created_at' => '2023-01-01T00:00:00.000000Z', + 'updated_at' => '2023-01-01T00:00:00.000000Z', + ], $result->toArray()); + } + + public function testIncrementOrCreateMethodRetrievesRecordCreatedJustNow(): void + { + $model = new EloquentBuilderCreateOrFirstTestModel(); + $this->mockConnectionForModel($model, 'SQLite'); + $model->getConnection()->shouldReceive('transactionLevel')->andReturn(0); + $model->getConnection()->shouldReceive('getName')->andReturn('sqlite'); + + $model->getConnection() + ->expects('select') + ->with('select * from "table" where ("attr" = ?) limit 1', ['foo'], true) + ->andReturn([]); + + $sql = 'insert into "table" ("attr", "count", "updated_at", "created_at") values (?, ?, ?, ?)'; + $bindings = ['foo', '1', '2023-01-01 00:00:00', '2023-01-01 00:00:00']; + + $model->getConnection() + ->expects('insert') + ->with($sql, $bindings) + ->andThrow(new UniqueConstraintViolationException('sqlite', $sql, $bindings, new Exception())); + + $model->getConnection() + ->expects('select') + ->with('select * from "table" where ("attr" = ?) limit 1', ['foo'], false) + ->andReturn([[ + 'id' => 123, + 'attr' => 'foo', + 'count' => 1, + 'created_at' => '2023-01-01 00:00:00', + 'updated_at' => '2023-01-01 00:00:00', + ]]); + + $model->getConnection() + ->expects('raw') + ->with('"count" + 1') + ->andReturn('2'); + + $model->getConnection() + ->expects('update') + ->with( + 'update "table" set "count" = ?, "updated_at" = ? where "id" = ?', + ['2', '2023-01-01 00:00:00', 123], + ) + ->andReturn(1); + + $result = $model->newQuery()->incrementOrCreate(['attr' => 'foo']); + $this->assertFalse($result->wasRecentlyCreated); + $this->assertEquals([ + 'id' => 123, + 'attr' => 'foo', + 'count' => 2, + 'created_at' => '2023-01-01T00:00:00.000000Z', + 'updated_at' => '2023-01-01T00:00:00.000000Z', + ], $result->toArray()); + } + protected function mockConnectionForModel(Model $model, string $database, array $lastInsertIds = []): void { $grammarClass = 'Illuminate\Database\Query\Grammars\\'.$database.'Grammar'; diff --git a/tests/Database/DatabaseEloquentBuilderTest.php b/tests/Database/DatabaseEloquentBuilderTest.php index 6ce852dd3f71..a84cd851eb0e 100755 --- a/tests/Database/DatabaseEloquentBuilderTest.php +++ b/tests/Database/DatabaseEloquentBuilderTest.php @@ -1774,10 +1774,47 @@ public function testWhereMorphedTo() $builder = $model->whereMorphedTo('morph', $relatedModel); - $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where ("eloquent_builder_test_model_parent_stubs"."morph_type" = ? and "eloquent_builder_test_model_parent_stubs"."morph_id" = ?)', $builder->toSql()); + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where (("eloquent_builder_test_model_parent_stubs"."morph_type" = ? and "eloquent_builder_test_model_parent_stubs"."morph_id" in (?)))', $builder->toSql()); $this->assertEquals([$relatedModel->getMorphClass(), $relatedModel->getKey()], $builder->getBindings()); } + public function testWhereMorphedToCollection() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $firstRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $firstRelatedModel->id = 1; + + $secondRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $secondRelatedModel->id = 2; + + $builder = $model->whereMorphedTo('morph', new Collection([$firstRelatedModel, $secondRelatedModel])); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where (("eloquent_builder_test_model_parent_stubs"."morph_type" = ? and "eloquent_builder_test_model_parent_stubs"."morph_id" in (?, ?)))', $builder->toSql()); + $this->assertEquals([$firstRelatedModel->getMorphClass(), $firstRelatedModel->getKey(), $secondRelatedModel->getKey()], $builder->getBindings()); + } + + public function testWhereMorphedToCollectionWithDifferentModels() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $firstRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $firstRelatedModel->id = 1; + + $secondRelatedModel = new EloquentBuilderTestModelFarRelatedStub; + $secondRelatedModel->id = 2; + + $thirdRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $thirdRelatedModel->id = 3; + + $builder = $model->whereMorphedTo('morph', [$firstRelatedModel, $secondRelatedModel, $thirdRelatedModel]); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where (("eloquent_builder_test_model_parent_stubs"."morph_type" = ? and "eloquent_builder_test_model_parent_stubs"."morph_id" in (?, ?)) or ("eloquent_builder_test_model_parent_stubs"."morph_type" = ? and "eloquent_builder_test_model_parent_stubs"."morph_id" in (?)))', $builder->toSql()); + $this->assertEquals([$firstRelatedModel->getMorphClass(), $firstRelatedModel->getKey(), $thirdRelatedModel->getKey(), $secondRelatedModel->getMorphClass(), $secondRelatedModel->id], $builder->getBindings()); + } + public function testWhereMorphedToNull() { $model = new EloquentBuilderTestModelParentStub; @@ -1797,10 +1834,47 @@ public function testWhereNotMorphedTo() $builder = $model->whereNotMorphedTo('morph', $relatedModel); - $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where not ("eloquent_builder_test_model_parent_stubs"."morph_type" <=> ? and "eloquent_builder_test_model_parent_stubs"."morph_id" <=> ?)', $builder->toSql()); + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where not (("eloquent_builder_test_model_parent_stubs"."morph_type" <=> ? and "eloquent_builder_test_model_parent_stubs"."morph_id" not in (?)))', $builder->toSql()); $this->assertEquals([$relatedModel->getMorphClass(), $relatedModel->getKey()], $builder->getBindings()); } + public function testWhereNotMorphedToCollection() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $firstRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $firstRelatedModel->id = 1; + + $secondRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $secondRelatedModel->id = 2; + + $builder = $model->whereNotMorphedTo('morph', new Collection([$firstRelatedModel, $secondRelatedModel])); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where not (("eloquent_builder_test_model_parent_stubs"."morph_type" <=> ? and "eloquent_builder_test_model_parent_stubs"."morph_id" not in (?, ?)))', $builder->toSql()); + $this->assertEquals([$firstRelatedModel->getMorphClass(), $firstRelatedModel->getKey(), $secondRelatedModel->getKey()], $builder->getBindings()); + } + + public function testWhereNotMorphedToCollectionWithDifferentModels() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $firstRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $firstRelatedModel->id = 1; + + $secondRelatedModel = new EloquentBuilderTestModelFarRelatedStub; + $secondRelatedModel->id = 2; + + $thirdRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $thirdRelatedModel->id = 3; + + $builder = $model->whereNotMorphedTo('morph', [$firstRelatedModel, $secondRelatedModel, $thirdRelatedModel]); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where not (("eloquent_builder_test_model_parent_stubs"."morph_type" <=> ? and "eloquent_builder_test_model_parent_stubs"."morph_id" not in (?, ?)) or ("eloquent_builder_test_model_parent_stubs"."morph_type" <=> ? and "eloquent_builder_test_model_parent_stubs"."morph_id" not in (?)))', $builder->toSql()); + $this->assertEquals([$firstRelatedModel->getMorphClass(), $firstRelatedModel->getKey(), $thirdRelatedModel->getKey(), $secondRelatedModel->getMorphClass(), $secondRelatedModel->id], $builder->getBindings()); + } + public function testOrWhereMorphedTo() { $model = new EloquentBuilderTestModelParentStub; @@ -1811,10 +1885,47 @@ public function testOrWhereMorphedTo() $builder = $model->where('bar', 'baz')->orWhereMorphedTo('morph', $relatedModel); - $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "bar" = ? or ("eloquent_builder_test_model_parent_stubs"."morph_type" = ? and "eloquent_builder_test_model_parent_stubs"."morph_id" = ?)', $builder->toSql()); + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "bar" = ? or (("eloquent_builder_test_model_parent_stubs"."morph_type" = ? and "eloquent_builder_test_model_parent_stubs"."morph_id" in (?)))', $builder->toSql()); $this->assertEquals(['baz', $relatedModel->getMorphClass(), $relatedModel->getKey()], $builder->getBindings()); } + public function testOrWhereMorphedToCollection() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $firstRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $firstRelatedModel->id = 1; + + $secondRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $secondRelatedModel->id = 2; + + $builder = $model->where('bar', 'baz')->orWhereMorphedTo('morph', new Collection([$firstRelatedModel, $secondRelatedModel])); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "bar" = ? or (("eloquent_builder_test_model_parent_stubs"."morph_type" = ? and "eloquent_builder_test_model_parent_stubs"."morph_id" in (?, ?)))', $builder->toSql()); + $this->assertEquals(['baz', $firstRelatedModel->getMorphClass(), $firstRelatedModel->getKey(), $secondRelatedModel->getKey()], $builder->getBindings()); + } + + public function testOrWhereMorphedToCollectionWithDifferentModels() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $firstRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $firstRelatedModel->id = 1; + + $secondRelatedModel = new EloquentBuilderTestModelFarRelatedStub; + $secondRelatedModel->id = 2; + + $thirdRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $thirdRelatedModel->id = 3; + + $builder = $model->where('bar', 'baz')->orWhereMorphedTo('morph', [$firstRelatedModel, $secondRelatedModel, $thirdRelatedModel]); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "bar" = ? or (("eloquent_builder_test_model_parent_stubs"."morph_type" = ? and "eloquent_builder_test_model_parent_stubs"."morph_id" in (?, ?)) or ("eloquent_builder_test_model_parent_stubs"."morph_type" = ? and "eloquent_builder_test_model_parent_stubs"."morph_id" in (?)))', $builder->toSql()); + $this->assertEquals(['baz', $firstRelatedModel->getMorphClass(), $firstRelatedModel->getKey(), $thirdRelatedModel->getKey(), $secondRelatedModel->getMorphClass(), $secondRelatedModel->id], $builder->getBindings()); + } + public function testOrWhereMorphedToNull() { $model = new EloquentBuilderTestModelParentStub; @@ -1836,10 +1947,47 @@ public function testOrWhereNotMorphedTo() $builder = $model->where('bar', 'baz')->orWhereNotMorphedTo('morph', $relatedModel); - $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "bar" = ? or not ("eloquent_builder_test_model_parent_stubs"."morph_type" <=> ? and "eloquent_builder_test_model_parent_stubs"."morph_id" <=> ?)', $builder->toSql()); + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "bar" = ? or not (("eloquent_builder_test_model_parent_stubs"."morph_type" <=> ? and "eloquent_builder_test_model_parent_stubs"."morph_id" not in (?)))', $builder->toSql()); $this->assertEquals(['baz', $relatedModel->getMorphClass(), $relatedModel->getKey()], $builder->getBindings()); } + public function testOrWhereNotMorphedToCollection() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $firstRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $firstRelatedModel->id = 1; + + $secondRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $secondRelatedModel->id = 2; + + $builder = $model->where('bar', 'baz')->orWhereNotMorphedTo('morph', new Collection([$firstRelatedModel, $secondRelatedModel])); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "bar" = ? or not (("eloquent_builder_test_model_parent_stubs"."morph_type" <=> ? and "eloquent_builder_test_model_parent_stubs"."morph_id" not in (?, ?)))', $builder->toSql()); + $this->assertEquals(['baz', $firstRelatedModel->getMorphClass(), $firstRelatedModel->getKey(), $secondRelatedModel->getKey()], $builder->getBindings()); + } + + public function testOrWhereNotMorphedToCollectionWithDifferentModels() + { + $model = new EloquentBuilderTestModelParentStub; + $this->mockConnectionForModel($model, ''); + + $firstRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $firstRelatedModel->id = 1; + + $secondRelatedModel = new EloquentBuilderTestModelFarRelatedStub; + $secondRelatedModel->id = 2; + + $thirdRelatedModel = new EloquentBuilderTestModelCloseRelatedStub; + $thirdRelatedModel->id = 3; + + $builder = $model->where('bar', 'baz')->orWhereNotMorphedTo('morph', [$firstRelatedModel, $secondRelatedModel, $thirdRelatedModel]); + + $this->assertSame('select * from "eloquent_builder_test_model_parent_stubs" where "bar" = ? or not (("eloquent_builder_test_model_parent_stubs"."morph_type" <=> ? and "eloquent_builder_test_model_parent_stubs"."morph_id" not in (?, ?)) or ("eloquent_builder_test_model_parent_stubs"."morph_type" <=> ? and "eloquent_builder_test_model_parent_stubs"."morph_id" not in (?)))', $builder->toSql()); + $this->assertEquals(['baz', $firstRelatedModel->getMorphClass(), $firstRelatedModel->getKey(), $thirdRelatedModel->getKey(), $secondRelatedModel->getMorphClass(), $secondRelatedModel->id], $builder->getBindings()); + } + public function testWhereMorphedToClass() { $model = new EloquentBuilderTestModelParentStub; diff --git a/tests/Database/DatabaseEloquentHasManyThroughIntegrationTest.php b/tests/Database/DatabaseEloquentHasManyThroughIntegrationTest.php index 2b5535c5c808..7d5b184372db 100644 --- a/tests/Database/DatabaseEloquentHasManyThroughIntegrationTest.php +++ b/tests/Database/DatabaseEloquentHasManyThroughIntegrationTest.php @@ -136,11 +136,11 @@ public function testWithWhereHasOnARelationWithCustomIntermediateAndLocalKey() public function testFindMethod() { HasManyThroughTestCountry::create(['id' => 1, 'name' => 'United States of America', 'shortname' => 'us']) - ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) - ->posts()->createMany([ - ['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com'], - ['id' => 2, 'title' => 'Another title', 'body' => 'Another body', 'email' => 'taylorotwell@gmail.com'], - ]); + ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) + ->posts()->createMany([ + ['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com'], + ['id' => 2, 'title' => 'Another title', 'body' => 'Another body', 'email' => 'taylorotwell@gmail.com'], + ]); $country = HasManyThroughTestCountry::first(); $post = $country->posts()->find(1); @@ -155,11 +155,11 @@ public function testFindMethod() public function testFindManyMethod() { HasManyThroughTestCountry::create(['id' => 1, 'name' => 'United States of America', 'shortname' => 'us']) - ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) - ->posts()->createMany([ - ['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com'], - ['id' => 2, 'title' => 'Another title', 'body' => 'Another body', 'email' => 'taylorotwell@gmail.com'], - ]); + ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) + ->posts()->createMany([ + ['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com'], + ['id' => 2, 'title' => 'Another title', 'body' => 'Another body', 'email' => 'taylorotwell@gmail.com'], + ]); $country = HasManyThroughTestCountry::first(); @@ -184,7 +184,7 @@ public function testFindOrFailThrowsAnException() $this->expectExceptionMessage('No query results for model [Illuminate\Tests\Database\HasManyThroughTestPost] 1'); HasManyThroughTestCountry::create(['id' => 1, 'name' => 'United States of America', 'shortname' => 'us']) - ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']); + ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']); HasManyThroughTestCountry::first()->posts()->findOrFail(1); } @@ -195,8 +195,8 @@ public function testFindOrFailWithManyThrowsAnException() $this->expectExceptionMessage('No query results for model [Illuminate\Tests\Database\HasManyThroughTestPost] 1, 2'); HasManyThroughTestCountry::create(['id' => 1, 'name' => 'United States of America', 'shortname' => 'us']) - ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) - ->posts()->create(['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com']); + ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) + ->posts()->create(['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com']); HasManyThroughTestCountry::first()->posts()->findOrFail([1, 2]); } @@ -207,8 +207,8 @@ public function testFindOrFailWithManyUsingCollectionThrowsAnException() $this->expectExceptionMessage('No query results for model [Illuminate\Tests\Database\HasManyThroughTestPost] 1, 2'); HasManyThroughTestCountry::create(['id' => 1, 'name' => 'United States of America', 'shortname' => 'us']) - ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) - ->posts()->create(['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com']); + ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) + ->posts()->create(['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com']); HasManyThroughTestCountry::first()->posts()->findOrFail(new Collection([1, 2])); } @@ -216,8 +216,8 @@ public function testFindOrFailWithManyUsingCollectionThrowsAnException() public function testFindOrMethod() { HasManyThroughTestCountry::create(['id' => 1, 'name' => 'United States of America', 'shortname' => 'us']) - ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) - ->posts()->create(['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com']); + ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) + ->posts()->create(['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com']); $result = HasManyThroughTestCountry::first()->posts()->findOr(1, fn () => 'callback result'); $this->assertInstanceOf(HasManyThroughTestPost::class, $result); @@ -236,11 +236,11 @@ public function testFindOrMethod() public function testFindOrMethodWithMany() { HasManyThroughTestCountry::create(['id' => 1, 'name' => 'United States of America', 'shortname' => 'us']) - ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) - ->posts()->createMany([ - ['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com'], - ['id' => 2, 'title' => 'Another title', 'body' => 'Another body', 'email' => 'taylorotwell@gmail.com'], - ]); + ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) + ->posts()->createMany([ + ['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com'], + ['id' => 2, 'title' => 'Another title', 'body' => 'Another body', 'email' => 'taylorotwell@gmail.com'], + ]); $result = HasManyThroughTestCountry::first()->posts()->findOr([1, 2], fn () => 'callback result'); $this->assertInstanceOf(Collection::class, $result); @@ -263,11 +263,11 @@ public function testFindOrMethodWithMany() public function testFindOrMethodWithManyUsingCollection() { HasManyThroughTestCountry::create(['id' => 1, 'name' => 'United States of America', 'shortname' => 'us']) - ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) - ->posts()->createMany([ - ['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com'], - ['id' => 2, 'title' => 'Another title', 'body' => 'Another body', 'email' => 'taylorotwell@gmail.com'], - ]); + ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) + ->posts()->createMany([ + ['id' => 1, 'title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com'], + ['id' => 2, 'title' => 'Another title', 'body' => 'Another body', 'email' => 'taylorotwell@gmail.com'], + ]); $result = HasManyThroughTestCountry::first()->posts()->findOr(new Collection([1, 2]), fn () => 'callback result'); $this->assertInstanceOf(Collection::class, $result); @@ -500,11 +500,11 @@ public function testEagerLoadingLoadsRelatedModelsCorrectly() protected function seedData() { HasManyThroughTestCountry::create(['id' => 1, 'name' => 'United States of America', 'shortname' => 'us']) - ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) - ->posts()->createMany([ - ['title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com'], - ['title' => 'Another title', 'body' => 'Another body', 'email' => 'taylorotwell@gmail.com'], - ]); + ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com', 'country_short' => 'us']) + ->posts()->createMany([ + ['title' => 'A title', 'body' => 'A body', 'email' => 'taylorotwell@gmail.com'], + ['title' => 'Another title', 'body' => 'Another body', 'email' => 'taylorotwell@gmail.com'], + ]); } protected function seedDataExtended() @@ -533,11 +533,11 @@ protected function seedDataExtended() protected function seedDefaultData() { HasManyThroughDefaultTestCountry::create(['id' => 1, 'name' => 'United States of America']) - ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com']) - ->posts()->createMany([ - ['title' => 'A title', 'body' => 'A body'], - ['title' => 'Another title', 'body' => 'Another body'], - ]); + ->users()->create(['id' => 1, 'email' => 'taylorotwell@gmail.com']) + ->posts()->createMany([ + ['title' => 'A title', 'body' => 'A body'], + ['title' => 'Another title', 'body' => 'Another body'], + ]); } /** diff --git a/tests/Database/DatabaseEloquentHasOneOrManyWithAttributesTest.php b/tests/Database/DatabaseEloquentHasOneOrManyWithAttributesTest.php new file mode 100755 index 000000000000..80f1e677eb37 --- /dev/null +++ b/tests/Database/DatabaseEloquentHasOneOrManyWithAttributesTest.php @@ -0,0 +1,275 @@ +addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + $db->bootEloquent(); + $db->setAsGlobal(); + } + + public function testHasManyAddsAttributes(): void + { + $parentId = 123; + $key = 'a key'; + $value = 'the value'; + + $parent = new RelatedWithAttributesModel; + $parent->id = $parentId; + + $relationship = $parent + ->hasMany(RelatedWithAttributesModel::class, 'parent_id') + ->withAttributes([$key => $value]); + + $relatedModel = $relationship->make(); + + $this->assertSame($parentId, $relatedModel->parent_id); + $this->assertSame($value, $relatedModel->$key); + } + + public function testHasOneAddsAttributes(): void + { + $parentId = 123; + $key = 'a key'; + $value = 'the value'; + + $parent = new RelatedWithAttributesModel; + $parent->id = $parentId; + + $relationship = $parent + ->hasOne(RelatedWithAttributesModel::class, 'parent_id') + ->withAttributes([$key => $value]); + + $relatedModel = $relationship->make(); + + $this->assertSame($parentId, $relatedModel->parent_id); + $this->assertSame($value, $relatedModel->$key); + } + + public function testMorphManyAddsAttributes(): void + { + $parentId = 123; + $key = 'a key'; + $value = 'the value'; + + $parent = new RelatedWithAttributesModel; + $parent->id = $parentId; + + $relationship = $parent + ->morphMany(RelatedWithAttributesModel::class, 'relatable') + ->withAttributes([$key => $value]); + + $relatedModel = $relationship->make(); + + $this->assertSame($parentId, $relatedModel->relatable_id); + $this->assertSame($parent::class, $relatedModel->relatable_type); + $this->assertSame($value, $relatedModel->$key); + } + + public function testMorphOneAddsAttributes(): void + { + $parentId = 123; + $key = 'a key'; + $value = 'the value'; + + $parent = new RelatedWithAttributesModel; + $parent->id = $parentId; + + $relationship = $parent + ->morphOne(RelatedWithAttributesModel::class, 'relatable') + ->withAttributes([$key => $value]); + + $relatedModel = $relationship->make(); + + $this->assertSame($parentId, $relatedModel->relatable_id); + $this->assertSame($parent::class, $relatedModel->relatable_type); + $this->assertSame($value, $relatedModel->$key); + } + + public function testWithAttributesCanBeOverriden(): void + { + $key = 'a key'; + $defaultValue = 'a value'; + $value = 'the value'; + + $parent = new RelatedWithAttributesModel; + + $relationship = $parent + ->hasMany(RelatedWithAttributesModel::class, 'relatable') + ->withAttributes([$key => $defaultValue]); + + $relatedModel = $relationship->make([$key => $value]); + + $this->assertSame($value, $relatedModel->$key); + } + + public function testQueryingDoesNotBreakWither(): void + { + $parentId = 123; + $key = 'a key'; + $value = 'the value'; + + $parent = new RelatedWithAttributesModel; + $parent->id = $parentId; + + $relationship = $parent + ->hasMany(RelatedWithAttributesModel::class, 'parent_id') + ->where($key, $value) + ->withAttributes([$key => $value]); + + $relatedModel = $relationship->make(); + + $this->assertSame($parentId, $relatedModel->parent_id); + $this->assertSame($value, $relatedModel->$key); + } + + public function testAttributesCanBeAppended(): void + { + $parent = new RelatedWithAttributesModel; + + $relationship = $parent + ->hasMany(RelatedWithAttributesModel::class, 'parent_id') + ->withAttributes(['a' => 'A']) + ->withAttributes(['b' => 'B']) + ->withAttributes(['a' => 'AA']); + + $relatedModel = $relationship->make([ + 'b' => 'BB', + 'c' => 'C', + ]); + + $this->assertSame('AA', $relatedModel->a); + $this->assertSame('BB', $relatedModel->b); + $this->assertSame('C', $relatedModel->c); + } + + public function testSingleAttributeApi(): void + { + $parent = new RelatedWithAttributesModel; + $key = 'attr'; + $value = 'Value'; + + $relationship = $parent + ->hasMany(RelatedWithAttributesModel::class, 'parent_id') + ->withAttributes($key, $value); + + $relatedModel = $relationship->make(); + + $this->assertSame($value, $relatedModel->$key); + } + + public function testWheresAreSet(): void + { + $parentId = 123; + $key = 'a key'; + $value = 'the value'; + + $parent = new RelatedWithAttributesModel; + $parent->id = $parentId; + + $relationship = $parent + ->hasMany(RelatedWithAttributesModel::class, 'parent_id') + ->withAttributes([$key => $value]); + + $wheres = $relationship->toBase()->wheres; + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'related_with_attributes_models.'.$key, + 'operator' => '=', + 'value' => $value, + 'boolean' => 'and', + ], $wheres); + + // Ensure this doesn't break the default where either. + $this->assertContains([ + 'type' => 'Basic', + 'column' => $parent->qualifyColumn('parent_id'), + 'operator' => '=', + 'value' => $parentId, + 'boolean' => 'and', + ], $wheres); + } + + public function testNullValueIsAccepted(): void + { + $parentId = 123; + $key = 'a key'; + + $parent = new RelatedWithAttributesModel; + $parent->id = $parentId; + + $relationship = $parent + ->hasMany(RelatedWithAttributesModel::class, 'parent_id') + ->withAttributes([$key => null]); + + $wheres = $relationship->toBase()->wheres; + $relatedModel = $relationship->make(); + + $this->assertNull($relatedModel->$key); + + $this->assertContains([ + 'type' => 'Null', + 'column' => 'related_with_attributes_models.'.$key, + 'boolean' => 'and', + ], $wheres); + } + + public function testOneKeepsAttributesFromHasMany(): void + { + $parentId = 123; + $key = 'a key'; + $value = 'the value'; + + $parent = new RelatedWithAttributesModel; + $parent->id = $parentId; + + $relationship = $parent + ->hasMany(RelatedWithAttributesModel::class, 'parent_id') + ->withAttributes([$key => $value]) + ->one(); + + $relatedModel = $relationship->make(); + + $this->assertSame($parentId, $relatedModel->parent_id); + $this->assertSame($value, $relatedModel->$key); + } + + public function testOneKeepsAttributesFromMorphMany(): void + { + $parentId = 123; + $key = 'a key'; + $value = 'the value'; + + $parent = new RelatedWithAttributesModel; + $parent->id = $parentId; + + $relationship = $parent + ->morphMany(RelatedWithAttributesModel::class, 'relatable') + ->withAttributes([$key => $value]) + ->one(); + + $relatedModel = $relationship->make(); + + $this->assertSame($parentId, $relatedModel->relatable_id); + $this->assertSame($parent::class, $relatedModel->relatable_type); + $this->assertSame($value, $relatedModel->$key); + } +} + +class RelatedWithAttributesModel extends Model +{ + protected $guarded = []; +} diff --git a/tests/Database/DatabaseEloquentIntegrationTest.php b/tests/Database/DatabaseEloquentIntegrationTest.php index 26541e631c3b..cc01fefa4bc6 100644 --- a/tests/Database/DatabaseEloquentIntegrationTest.php +++ b/tests/Database/DatabaseEloquentIntegrationTest.php @@ -1175,7 +1175,7 @@ public function testHasWithNonWhereBindings() $user = EloquentTestUser::create(['id' => 1, 'email' => 'taylorotwell@gmail.com']); $user->posts()->create(['name' => 'Post 2']) - ->photos()->create(['name' => 'photo.jpg']); + ->photos()->create(['name' => 'photo.jpg']); $query = EloquentTestUser::has('postWithPhotos'); @@ -2322,7 +2322,7 @@ class EloquentTestUserWithCustomFriendPivot extends EloquentTestUser public function friends() { return $this->belongsToMany(EloquentTestUser::class, 'friends', 'user_id', 'friend_id') - ->using(EloquentTestFriendPivot::class)->withPivot('user_id', 'friend_id', 'friend_level_id'); + ->using(EloquentTestFriendPivot::class)->withPivot('user_id', 'friend_id', 'friend_level_id'); } } diff --git a/tests/Database/DatabaseEloquentWithAttributesTest.php b/tests/Database/DatabaseEloquentWithAttributesTest.php new file mode 100755 index 000000000000..85b11d7991f3 --- /dev/null +++ b/tests/Database/DatabaseEloquentWithAttributesTest.php @@ -0,0 +1,59 @@ +addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + $db->bootEloquent(); + $db->setAsGlobal(); + } + + public function testAddsAttributes(): void + { + $key = 'a key'; + $value = 'the value'; + + $query = WithAttributesModel::query() + ->withAttributes([$key => $value]); + + $model = $query->make(); + + $this->assertSame($value, $model->$key); + } + + public function testAddsWheres(): void + { + $key = 'a key'; + $value = 'the value'; + + $query = WithAttributesModel::query() + ->withAttributes([$key => $value]); + + $wheres = $query->toBase()->wheres; + + $this->assertContains([ + 'type' => 'Basic', + 'column' => 'with_attributes_models.'.$key, + 'operator' => '=', + 'value' => $value, + 'boolean' => 'and', + ], $wheres); + } +} + +class WithAttributesModel extends Model +{ + protected $guarded = []; +} diff --git a/tests/Database/DatabaseMigrationMakeCommandTest.php b/tests/Database/DatabaseMigrationMakeCommandTest.php index 2ac5dde1b453..79eb535074a5 100755 --- a/tests/Database/DatabaseMigrationMakeCommandTest.php +++ b/tests/Database/DatabaseMigrationMakeCommandTest.php @@ -28,8 +28,8 @@ public function testBasicCreateDumpsAutoload() $app->useDatabasePath(__DIR__); $command->setLaravel($app); $creator->shouldReceive('create')->once() - ->with('create_foo', __DIR__.DIRECTORY_SEPARATOR.'migrations', 'foo', true) - ->andReturn(__DIR__.'/migrations/2021_04_23_110457_create_foo.php'); + ->with('create_foo', __DIR__.DIRECTORY_SEPARATOR.'migrations', 'foo', true) + ->andReturn(__DIR__.'/migrations/2021_04_23_110457_create_foo.php'); $this->runCommand($command, ['name' => 'create_foo']); } @@ -44,8 +44,8 @@ public function testBasicCreateGivesCreatorProperArguments() $app->useDatabasePath(__DIR__); $command->setLaravel($app); $creator->shouldReceive('create')->once() - ->with('create_foo', __DIR__.DIRECTORY_SEPARATOR.'migrations', 'foo', true) - ->andReturn(__DIR__.'/migrations/2021_04_23_110457_create_foo.php'); + ->with('create_foo', __DIR__.DIRECTORY_SEPARATOR.'migrations', 'foo', true) + ->andReturn(__DIR__.'/migrations/2021_04_23_110457_create_foo.php'); $this->runCommand($command, ['name' => 'create_foo']); } @@ -60,8 +60,8 @@ public function testBasicCreateGivesCreatorProperArgumentsWhenNameIsStudlyCase() $app->useDatabasePath(__DIR__); $command->setLaravel($app); $creator->shouldReceive('create')->once() - ->with('create_foo', __DIR__.DIRECTORY_SEPARATOR.'migrations', 'foo', true) - ->andReturn(__DIR__.'/migrations/2021_04_23_110457_create_foo.php'); + ->with('create_foo', __DIR__.DIRECTORY_SEPARATOR.'migrations', 'foo', true) + ->andReturn(__DIR__.'/migrations/2021_04_23_110457_create_foo.php'); $this->runCommand($command, ['name' => 'CreateFoo']); } @@ -76,8 +76,8 @@ public function testBasicCreateGivesCreatorProperArgumentsWhenTableIsSet() $app->useDatabasePath(__DIR__); $command->setLaravel($app); $creator->shouldReceive('create')->once() - ->with('create_foo', __DIR__.DIRECTORY_SEPARATOR.'migrations', 'users', true) - ->andReturn(__DIR__.'/migrations/2021_04_23_110457_create_foo.php'); + ->with('create_foo', __DIR__.DIRECTORY_SEPARATOR.'migrations', 'users', true) + ->andReturn(__DIR__.'/migrations/2021_04_23_110457_create_foo.php'); $this->runCommand($command, ['name' => 'create_foo', '--create' => 'users']); } @@ -92,8 +92,8 @@ public function testBasicCreateGivesCreatorProperArgumentsWhenCreateTablePattern $app->useDatabasePath(__DIR__); $command->setLaravel($app); $creator->shouldReceive('create')->once() - ->with('create_users_table', __DIR__.DIRECTORY_SEPARATOR.'migrations', 'users', true) - ->andReturn(__DIR__.'/migrations/2021_04_23_110457_create_users_table.php'); + ->with('create_users_table', __DIR__.DIRECTORY_SEPARATOR.'migrations', 'users', true) + ->andReturn(__DIR__.'/migrations/2021_04_23_110457_create_users_table.php'); $this->runCommand($command, ['name' => 'create_users_table']); } @@ -108,8 +108,8 @@ public function testCanSpecifyPathToCreateMigrationsIn() $command->setLaravel($app); $app->setBasePath('/home/laravel'); $creator->shouldReceive('create')->once() - ->with('create_foo', '/home/laravel/vendor/laravel-package/migrations', 'users', true) - ->andReturn('/home/laravel/vendor/laravel-package/migrations/2021_04_23_110457_create_foo.php'); + ->with('create_foo', '/home/laravel/vendor/laravel-package/migrations', 'users', true) + ->andReturn('/home/laravel/vendor/laravel-package/migrations/2021_04_23_110457_create_foo.php'); $this->runCommand($command, ['name' => 'create_foo', '--path' => 'vendor/laravel-package/migrations', '--create' => 'users']); } diff --git a/tests/Database/DatabaseMySqlSchemaStateTest.php b/tests/Database/DatabaseMySqlSchemaStateTest.php index 7d797f9287d4..3a9c7db3896c 100644 --- a/tests/Database/DatabaseMySqlSchemaStateTest.php +++ b/tests/Database/DatabaseMySqlSchemaStateTest.php @@ -99,9 +99,9 @@ public function testExecuteDumpProcessForDepth() $mockVariables = []; $schemaState = $this->getMockBuilder(MySqlSchemaState::class) - ->disableOriginalConstructor() - ->onlyMethods(['makeProcess']) - ->getMock(); + ->disableOriginalConstructor() + ->onlyMethods(['makeProcess']) + ->getMock(); $schemaState->method('makeProcess')->willReturn($mockProcess); diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 096b82450d25..a8caf61f6016 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -3578,9 +3578,9 @@ public function testSubqueriesBindings() $builder = $this->getBuilder()->select('*')->from('users')->where('email', '=', function ($q) { $q->select(new Raw('max(id)')) - ->from('users')->where('email', '=', 'bar') - ->orderByRaw('email like ?', '%.com') - ->groupBy('id')->having('id', '=', 4); + ->from('users')->where('email', '=', 'bar') + ->orderByRaw('email like ?', '%.com') + ->groupBy('id')->having('id', '=', 4); })->orWhere('id', '=', 'foo')->groupBy('id')->having('id', '=', 5); $this->assertEquals([0 => 'bar', 1 => 4, 2 => '%.com', 3 => 'foo', 4 => 5], $builder->getBindings()); } @@ -4075,9 +4075,9 @@ public function testUpdateFromMethodWithJoinsOnPostgres() $result = $builder->from('users') ->join('orders', function ($join) { $join->on('users.id', '=', 'orders.user_id') - ->where('users.id', '=', 1); + ->where('users.id', '=', 1); })->where('name', 'baz') - ->updateFrom(['email' => 'foo', 'name' => 'bar']); + ->updateFrom(['email' => 'foo', 'name' => 'bar']); $this->assertEquals(1, $result); } @@ -4427,11 +4427,11 @@ public function testMySqlUpdateWrappingJson() $connection = $this->createMock(ConnectionInterface::class); $connection->expects($this->once()) - ->method('update') - ->with( - 'update `users` set `name` = json_set(`name`, \'$."first_name"\', ?), `name` = json_set(`name`, \'$."last_name"\', ?) where `active` = ?', - ['John', 'Doe', 1] - ); + ->method('update') + ->with( + 'update `users` set `name` = json_set(`name`, \'$."first_name"\', ?), `name` = json_set(`name`, \'$."last_name"\', ?) where `active` = ?', + ['John', 'Doe', 1] + ); $builder = new Builder($connection, $grammar, $processor); @@ -4445,11 +4445,11 @@ public function testMySqlUpdateWrappingNestedJson() $connection = $this->createMock(ConnectionInterface::class); $connection->expects($this->once()) - ->method('update') - ->with( - 'update `users` set `meta` = json_set(`meta`, \'$."name"."first_name"\', ?), `meta` = json_set(`meta`, \'$."name"."last_name"\', ?) where `active` = ?', - ['John', 'Doe', 1] - ); + ->method('update') + ->with( + 'update `users` set `meta` = json_set(`meta`, \'$."name"."first_name"\', ?), `meta` = json_set(`meta`, \'$."name"."last_name"\', ?) where `active` = ?', + ['John', 'Doe', 1] + ); $builder = new Builder($connection, $grammar, $processor); @@ -4463,16 +4463,16 @@ public function testMySqlUpdateWrappingJsonArray() $connection = $this->createMock(ConnectionInterface::class); $connection->expects($this->once()) - ->method('update') - ->with( - 'update `users` set `options` = ?, `meta` = json_set(`meta`, \'$."tags"\', cast(? as json)), `group_id` = 45, `created_at` = ? where `active` = ?', - [ - json_encode(['2fa' => false, 'presets' => ['laravel', 'vue']]), - json_encode(['white', 'large']), - new DateTime('2019-08-06'), - 1, - ] - ); + ->method('update') + ->with( + 'update `users` set `options` = ?, `meta` = json_set(`meta`, \'$."tags"\', cast(? as json)), `group_id` = 45, `created_at` = ? where `active` = ?', + [ + json_encode(['2fa' => false, 'presets' => ['laravel', 'vue']]), + json_encode(['white', 'large']), + new DateTime('2019-08-06'), + 1, + ] + ); $builder = new Builder($connection, $grammar, $processor); $builder->from('users')->where('active', 1)->update([ @@ -4490,14 +4490,14 @@ public function testMySqlUpdateWrappingJsonPathArrayIndex() $connection = $this->createMock(ConnectionInterface::class); $connection->expects($this->once()) - ->method('update') - ->with( - 'update `users` set `options` = json_set(`options`, \'$[1]."2fa"\', false), `meta` = json_set(`meta`, \'$."tags"[0][2]\', ?) where `active` = ?', - [ - 'large', - 1, - ] - ); + ->method('update') + ->with( + 'update `users` set `options` = json_set(`options`, \'$[1]."2fa"\', false), `meta` = json_set(`meta`, \'$."tags"[0][2]\', ?) where `active` = ?', + [ + 'large', + 1, + ] + ); $builder = new Builder($connection, $grammar, $processor); $builder->from('users')->where('active', 1)->update([ @@ -4513,11 +4513,11 @@ public function testMySqlUpdateWithJsonPreparesBindingsCorrectly() $connection = m::mock(ConnectionInterface::class); $connection->shouldReceive('update') - ->once() - ->with( - 'update `users` set `options` = json_set(`options`, \'$."enable"\', false), `updated_at` = ? where `id` = ?', - ['2015-05-26 22:02:06', 0] - ); + ->once() + ->with( + 'update `users` set `options` = json_set(`options`, \'$."enable"\', false), `updated_at` = ? where `id` = ?', + ['2015-05-26 22:02:06', 0] + ); $builder = new Builder($connection, $grammar, $processor); $builder->from('users')->where('id', '=', 0)->update(['options->enable' => false, 'updated_at' => '2015-05-26 22:02:06']); diff --git a/tests/Foundation/Console/RouteListCommandTest.php b/tests/Foundation/Console/RouteListCommandTest.php index 6033aadc2940..a1a93fa155ee 100644 --- a/tests/Foundation/Console/RouteListCommandTest.php +++ b/tests/Foundation/Console/RouteListCommandTest.php @@ -57,7 +57,7 @@ protected function setUp(): void $router->get('/sub-example', function () { return 'Hello World'; })->domain('sub') - ->middleware('exampleMiddleware'); + ->middleware('exampleMiddleware'); $router->get('/example-group', function () { return 'Hello Group'; diff --git a/tests/Foundation/FoundationFormRequestTest.php b/tests/Foundation/FoundationFormRequestTest.php index 9dd164585b5d..8827b8f92f66 100644 --- a/tests/Foundation/FoundationFormRequestTest.php +++ b/tests/Foundation/FoundationFormRequestTest.php @@ -272,7 +272,7 @@ protected function createRequest($payload = [], $class = FoundationTestFormReque $request = $class::create('/', 'GET', $payload); return $request->setRedirector($this->createMockRedirector($request)) - ->setContainer($container); + ->setContainer($container); } /** @@ -284,7 +284,7 @@ protected function createRequest($payload = [], $class = FoundationTestFormReque protected function createValidationFactory($container) { $translator = m::mock(Translator::class)->shouldReceive('get') - ->zeroOrMoreTimes()->andReturn('error')->getMock(); + ->zeroOrMoreTimes()->andReturn('error')->getMock(); return new ValidationFactory($translator, $container); } @@ -300,13 +300,13 @@ protected function createMockRedirector($request) $redirector = $this->mocks['redirector'] = m::mock(Redirector::class); $redirector->shouldReceive('getUrlGenerator')->zeroOrMoreTimes() - ->andReturn($generator = $this->createMockUrlGenerator()); + ->andReturn($generator = $this->createMockUrlGenerator()); $redirector->shouldReceive('to')->zeroOrMoreTimes() - ->andReturn($this->createMockRedirectResponse()); + ->andReturn($this->createMockRedirectResponse()); $generator->shouldReceive('previous')->zeroOrMoreTimes() - ->andReturn('previous'); + ->andReturn('previous'); return $redirector; } diff --git a/tests/Http/HttpClientTest.php b/tests/Http/HttpClientTest.php index 64745bf49987..77570b385254 100644 --- a/tests/Http/HttpClientTest.php +++ b/tests/Http/HttpClientTest.php @@ -727,7 +727,7 @@ public function testFilesCanBeAttached() $this->factory->fake(); $this->factory->attach('foo', 'data', 'file.txt', ['X-Test-Header' => 'foo']) - ->post('http://foo.com/file'); + ->post('http://foo.com/file'); $this->factory->assertSent(function (Request $request) { return $request->url() === 'http://foo.com/file' && @@ -1606,6 +1606,63 @@ public function testCanDump() VarDumper::setHandler(null); } + public function testResponseCanDump() + { + $dumped = []; + + VarDumper::setHandler(function ($value) use (&$dumped) { + $dumped[] = $value; + }); + + $this->factory->fake([ + '200.com' => $this->factory::response('hello', 200), + ]); + + $this->factory->get('http://200.com')->dump(); + + $this->assertSame('hello', $dumped[0]); + + VarDumper::setHandler(null); + } + + public function testResponseCanDumpWithKey() + { + $dumped = []; + + VarDumper::setHandler(function ($value) use (&$dumped) { + $dumped[] = $value; + }); + + $this->factory->fake([ + '200.com' => $this->factory::response(['hello' => 'world'], 200), + ]); + + $this->factory->get('http://200.com')->dump('hello'); + + $this->assertSame('world', $dumped[0]); + + VarDumper::setHandler(null); + } + + public function testResponseCanDumpHeaders() + { + $dumped = []; + + VarDumper::setHandler(function ($value) use (&$dumped) { + $dumped[] = $value; + }); + + $this->factory->fake([ + '200.com' => $this->factory::response('hello', 200, ['hello' => 'world']), + ]); + + $this->factory->get('http://200.com')->dumpHeaders(); + + $this->assertSame(['hello' => ['world']], $dumped[0]); + + VarDumper::setHandler(null); + } + public function testResponseSequenceIsMacroable() { ResponseSequence::macro('customMethod', function () { diff --git a/tests/Integration/Cache/RedisStoreTest.php b/tests/Integration/Cache/RedisStoreTest.php index 8df8b4ee3f9c..25ee5ccbe721 100644 --- a/tests/Integration/Cache/RedisStoreTest.php +++ b/tests/Integration/Cache/RedisStoreTest.php @@ -249,4 +249,20 @@ public function testPutManyCallsPutWhenClustered() 'fizz' => 'buz', ], 10); } + + public function testIncrementWithSerializationEnabled() + { + /** @var \Illuminate\Cache\RedisStore $store */ + $store = Cache::store('redis'); + /** @var \Redis $client */ + $client = $store->connection()->client(); + $client->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP); + + $store->flush(); + $store->add('foo', 1, 10); + $this->assertEquals(1, $store->get('foo')); + + $store->increment('foo'); + $this->assertEquals(2, $store->get('foo')); + } } diff --git a/tests/Integration/Database/EloquentCursorPaginateTest.php b/tests/Integration/Database/EloquentCursorPaginateTest.php index 54480f90bf76..fb6d83b899b4 100644 --- a/tests/Integration/Database/EloquentCursorPaginateTest.php +++ b/tests/Integration/Database/EloquentCursorPaginateTest.php @@ -164,7 +164,7 @@ public function testPaginationWithMultipleWhereClauses() $this->assertCount( 1, $anotherQuery->cursorPaginate(5, ['*'], 'cursor', new Cursor(['id' => 3])) - ->items() + ->items() ); } @@ -239,7 +239,7 @@ public function testPaginationWithAliasedOrderBy() $this->assertCount( 4, $anotherQuery->cursorPaginate(10, ['*'], 'cursor', new Cursor(['user_id' => 2])) - ->items() + ->items() ); } diff --git a/tests/Integration/Database/EloquentPivotSerializationTest.php b/tests/Integration/Database/EloquentPivotSerializationTest.php index f308072f6004..52fc6a3dc985 100644 --- a/tests/Integration/Database/EloquentPivotSerializationTest.php +++ b/tests/Integration/Database/EloquentPivotSerializationTest.php @@ -164,7 +164,7 @@ public function collaborators() public function tags() { return $this->morphToMany(PivotSerializationTestTag::class, 'taggable', 'taggables', 'taggable_id', 'tag_id') - ->using(PivotSerializationTestTagAttachment::class); + ->using(PivotSerializationTestTagAttachment::class); } } @@ -175,7 +175,7 @@ class PivotSerializationTestTag extends Model public function projects() { return $this->morphedByMany(PivotSerializationTestProject::class, 'taggable', 'taggables', 'tag_id', 'taggable_id') - ->using(PivotSerializationTestTagAttachment::class); + ->using(PivotSerializationTestTagAttachment::class); } } diff --git a/tests/Integration/Database/EloquentPivotTest.php b/tests/Integration/Database/EloquentPivotTest.php index 2757992f6927..a45ca0fe78f7 100644 --- a/tests/Integration/Database/EloquentPivotTest.php +++ b/tests/Integration/Database/EloquentPivotTest.php @@ -91,17 +91,17 @@ class PivotTestUser extends Model public function activeSubscriptions() { return $this->belongsToMany(PivotTestProject::class, 'subscriptions', 'user_id', 'project_id') - ->withPivotValue('status', 'active') - ->withPivot('status') - ->using(PivotTestSubscription::class); + ->withPivotValue('status', 'active') + ->withPivot('status') + ->using(PivotTestSubscription::class); } public function inactiveSubscriptions() { return $this->belongsToMany(PivotTestProject::class, 'subscriptions', 'user_id', 'project_id') - ->withPivotValue('status', 'inactive') - ->withPivot('status') - ->using(PivotTestSubscription::class); + ->withPivotValue('status', 'inactive') + ->withPivot('status') + ->using(PivotTestSubscription::class); } } @@ -114,7 +114,7 @@ public function collaborators() return $this->belongsToMany( PivotTestUser::class, 'collaborators', 'project_id', 'user_id' )->withPivot('permissions') - ->using(PivotTestCollaborator::class); + ->using(PivotTestCollaborator::class); } public function contributors() diff --git a/tests/Integration/Queue/UniqueJobTest.php b/tests/Integration/Queue/UniqueJobTest.php index 82e567282a6f..5ec58efdec2b 100644 --- a/tests/Integration/Queue/UniqueJobTest.php +++ b/tests/Integration/Queue/UniqueJobTest.php @@ -4,6 +4,7 @@ use Exception; use Illuminate\Bus\Queueable; +use Illuminate\Container\Container; use Illuminate\Contracts\Cache\Repository as Cache; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing; @@ -51,6 +52,14 @@ public function testUniqueJobsAreNotDispatched() ); } + public function testUniqueJobWithViaDispatched() + { + Bus::fake(); + + UniqueViaJob::dispatch(); + Bus::assertDispatched(UniqueViaJob::class); + } + public function testLockIsReleasedForSuccessfulJobs() { UniqueTestJob::$handled = false; @@ -222,3 +231,11 @@ public function __construct(public User $user) { } } + +class UniqueViaJob extends UniqueTestJob +{ + public function uniqueVia(): Cache + { + return Container::getInstance()->make(Cache::class); + } +} diff --git a/tests/Integration/Queue/UniqueUntilProcessingJobTest.php b/tests/Integration/Queue/UniqueUntilProcessingJobTest.php new file mode 100644 index 000000000000..79ce03e65694 --- /dev/null +++ b/tests/Integration/Queue/UniqueUntilProcessingJobTest.php @@ -0,0 +1,89 @@ +set('queue.default', 'database'); + $app['config']->set('cache.default', 'database'); + $this->driver = 'database'; + } + + public function testShouldBeUniqueUntilProcessingReleasesLockWhenJobIsReleasedByAMiddleware() + { + // Job that does not release and gets processed + UniqueTestJobThatDoesNotRelease::dispatch(); + $lockKey = DB::table('cache_locks')->orderBy('id')->first()->key; + $this->assertNotNull($lockKey); + $this->runQueueWorkerCommand(['--once' => true]); + $this->assertFalse(UniqueTestJobThatDoesNotRelease::$released); + $lockKey = DB::table('cache_locks')->first()->key ?? null; + $this->assertNull($lockKey); + $this->assertDatabaseCount('jobs', 0); + + // Job that releases and does not get processed + UniqueUntilProcessingJobThatReleases::dispatch(); + $lockKey = DB::table('cache_locks')->first()->key; + $this->assertNotNull($lockKey); + $this->runQueueWorkerCommand(['--once' => true]); + $this->assertFalse(UniqueUntilProcessingJobThatReleases::$handled); + $this->assertTrue(UniqueUntilProcessingJobThatReleases::$released); + $lockKey = DB::table('cache_locks')->orderBy('id')->first()->key ?? null; + $this->assertNotNull($lockKey); + + UniqueUntilProcessingJobThatReleases::dispatch(); + $this->assertDatabaseCount('jobs', 1); + } +} + +class UniqueTestJobThatDoesNotRelease implements ShouldQueue, ShouldBeUniqueUntilProcessing +{ + use InteractsWithQueue, Queueable, Dispatchable; + + public static $handled = false; + public static $released = false; + + public function __construct() + { + static::$handled = false; + static::$released = false; + } + + public function handle() + { + static::$handled = true; + } +} + +class UniqueUntilProcessingJobThatReleases extends UniqueTestJobThatDoesNotRelease +{ + public function middleware() + { + return [ + function ($job) { + static::$released = true; + + return $job->release(30); + }, + ]; + } + + public function uniqueId() + { + return 100; + } +} diff --git a/tests/Integration/Testing/ArtisanCommandTest.php b/tests/Integration/Testing/ArtisanCommandTest.php index 08cd327e0582..fc26269e227f 100644 --- a/tests/Integration/Testing/ArtisanCommandTest.php +++ b/tests/Integration/Testing/ArtisanCommandTest.php @@ -73,24 +73,24 @@ public function test_console_command_that_fails() public function test_console_command_that_passes_with_output() { $this->artisan('survey') - ->expectsQuestion('What is your name?', 'Taylor Otwell') - ->expectsQuestion('Which language do you prefer?', 'PHP') - ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') - ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.') - ->assertExitCode(0); + ->expectsQuestion('What is your name?', 'Taylor Otwell') + ->expectsQuestion('Which language do you prefer?', 'PHP') + ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') + ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.') + ->assertExitCode(0); } public function test_console_command_that_passes_with_repeating_output() { $this->artisan('slim') - ->expectsQuestion('Who?', 'Taylor') - ->expectsQuestion('What?', 'Taylor') - ->expectsQuestion('Huh?', 'Taylor') - ->expectsOutput('Taylor') - ->doesntExpectOutput('Otwell') - ->expectsOutput('Taylor') - ->expectsOutput('Taylor') - ->assertExitCode(0); + ->expectsQuestion('Who?', 'Taylor') + ->expectsQuestion('What?', 'Taylor') + ->expectsQuestion('Huh?', 'Taylor') + ->expectsOutput('Taylor') + ->doesntExpectOutput('Otwell') + ->expectsOutput('Taylor') + ->expectsOutput('Taylor') + ->assertExitCode(0); } public function test_console_command_that_fails_from_unexpected_output() @@ -99,10 +99,10 @@ public function test_console_command_that_fails_from_unexpected_output() $this->expectExceptionMessage('Output "Your name is Taylor Otwell and you prefer PHP." was printed.'); $this->artisan('survey') - ->expectsQuestion('What is your name?', 'Taylor Otwell') - ->expectsQuestion('Which language do you prefer?', 'PHP') - ->doesntExpectOutput('Your name is Taylor Otwell and you prefer PHP.') - ->assertExitCode(0); + ->expectsQuestion('What is your name?', 'Taylor Otwell') + ->expectsQuestion('Which language do you prefer?', 'PHP') + ->doesntExpectOutput('Your name is Taylor Otwell and you prefer PHP.') + ->assertExitCode(0); } public function test_console_command_that_fails_from_unexpected_output_substring() @@ -111,8 +111,8 @@ public function test_console_command_that_fails_from_unexpected_output_substring $this->expectExceptionMessage('Output "Taylor Otwell" was printed.'); $this->artisan('contains') - ->doesntExpectOutputToContain('Taylor Otwell') - ->assertExitCode(0); + ->doesntExpectOutputToContain('Taylor Otwell') + ->assertExitCode(0); } public function test_console_command_that_fails_from_missing_output() @@ -122,10 +122,10 @@ public function test_console_command_that_fails_from_missing_output() $this->ignoringMockOnceExceptions(function () { $this->artisan('survey') - ->expectsQuestion('What is your name?', 'Taylor Otwell') - ->expectsQuestion('Which language do you prefer?', 'Ruby') - ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') - ->assertExitCode(0); + ->expectsQuestion('What is your name?', 'Taylor Otwell') + ->expectsQuestion('Which language do you prefer?', 'Ruby') + ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') + ->assertExitCode(0); }); } @@ -135,9 +135,9 @@ public function test_console_command_that_fails_from_exit_code_mismatch() $this->expectExceptionMessage('Expected status code 1 but received 0.'); $this->artisan('survey') - ->expectsQuestion('What is your name?', 'Taylor Otwell') - ->expectsQuestion('Which language do you prefer?', 'PHP') - ->assertExitCode(1); + ->expectsQuestion('What is your name?', 'Taylor Otwell') + ->expectsQuestion('Which language do you prefer?', 'PHP') + ->assertExitCode(1); } public function test_console_command_that_fails_from_unordered_output() @@ -146,21 +146,21 @@ public function test_console_command_that_fails_from_unordered_output() $this->ignoringMockOnceExceptions(function () { $this->artisan('slim') - ->expectsQuestion('Who?', 'Taylor') - ->expectsQuestion('What?', 'Danger') - ->expectsQuestion('Huh?', 'Otwell') - ->expectsOutput('Taylor') - ->expectsOutput('Otwell') - ->expectsOutput('Danger') - ->assertExitCode(0); + ->expectsQuestion('Who?', 'Taylor') + ->expectsQuestion('What?', 'Danger') + ->expectsQuestion('Huh?', 'Otwell') + ->expectsOutput('Taylor') + ->expectsOutput('Otwell') + ->expectsOutput('Danger') + ->assertExitCode(0); }); } public function test_console_command_that_passes_if_the_output_contains() { $this->artisan('contains') - ->expectsOutputToContain('Taylor Otwell') - ->assertExitCode(0); + ->expectsOutputToContain('Taylor Otwell') + ->assertExitCode(0); } public function test_console_command_that_passes_if_outputs_something() @@ -270,8 +270,8 @@ public function test_console_command_that_fails_if_the_output_does_not_contain() $this->ignoringMockOnceExceptions(function () { $this->artisan('contains') - ->expectsOutputToContain('Otwell Taylor') - ->assertExitCode(0); + ->expectsOutputToContain('Otwell Taylor') + ->assertExitCode(0); }); } diff --git a/tests/Mail/MailMailableDataTest.php b/tests/Mail/MailMailableDataTest.php index 01737705b033..282aa2f125e0 100644 --- a/tests/Mail/MailMailableDataTest.php +++ b/tests/Mail/MailMailableDataTest.php @@ -24,7 +24,7 @@ public function testMailableDataIsNotLost() $mailable = new MailableStub; $mailable->build(function ($m) use ($testData) { $m->view('view', $testData) - ->text('text-view'); + ->text('text-view'); }); $this->assertSame($testData, $mailable->buildViewData()); } diff --git a/tests/Mail/MailMailableTest.php b/tests/Mail/MailMailableTest.php index 4d461319da77..9de9abc40f97 100644 --- a/tests/Mail/MailMailableTest.php +++ b/tests/Mail/MailMailableTest.php @@ -1209,7 +1209,7 @@ class WelcomeMailableStub extends Mailable public function build() { $this->with('first_name', 'Taylor') - ->withLastName('Otwell'); + ->withLastName('Otwell'); } } diff --git a/tests/Notifications/NotificationMailMessageTest.php b/tests/Notifications/NotificationMailMessageTest.php index 7a59cfd8890d..b901f0aab297 100644 --- a/tests/Notifications/NotificationMailMessageTest.php +++ b/tests/Notifications/NotificationMailMessageTest.php @@ -75,7 +75,7 @@ public function testCcIsSetCorrectly() $message = new MailMessage; $message->cc('test@example.com') - ->cc('test@example.com', 'Test'); + ->cc('test@example.com', 'Test'); $this->assertSame([['test@example.com', null], ['test@example.com', 'Test']], $message->cc); @@ -94,7 +94,7 @@ public function testBccIsSetCorrectly() $message = new MailMessage; $message->bcc('test@example.com') - ->bcc('test@example.com', 'Test'); + ->bcc('test@example.com', 'Test'); $this->assertSame([['test@example.com', null], ['test@example.com', 'Test']], $message->bcc); @@ -113,7 +113,7 @@ public function testReplyToIsSetCorrectly() $message = new MailMessage; $message->replyTo('test@example.com') - ->replyTo('test@example.com', 'Test'); + ->replyTo('test@example.com', 'Test'); $this->assertSame([['test@example.com', null], ['test@example.com', 'Test']], $message->replyTo); diff --git a/tests/Pipeline/PipelineTest.php b/tests/Pipeline/PipelineTest.php index 6e155f01f49e..1d392aab5b94 100644 --- a/tests/Pipeline/PipelineTest.php +++ b/tests/Pipeline/PipelineTest.php @@ -20,11 +20,11 @@ public function testPipelineBasicUsage() }; $result = (new Pipeline(new Container)) - ->send('foo') - ->through([PipelineTestPipeOne::class, $pipeTwo]) - ->then(function ($piped) { - return $piped; - }); + ->send('foo') + ->through([PipelineTestPipeOne::class, $pipeTwo]) + ->then(function ($piped) { + return $piped; + }); $this->assertSame('foo', $result); $this->assertSame('foo', $_SERVER['__test.pipe.one']); @@ -235,9 +235,9 @@ public function testPipelineThrowsExceptionOnResolveWithoutContainer() public function testPipelineThenReturnMethodRunsPipelineThenReturnsPassable() { $result = (new Pipeline(new Container)) - ->send('foo') - ->through([PipelineTestPipeOne::class]) - ->thenReturn(); + ->send('foo') + ->through([PipelineTestPipeOne::class]) + ->thenReturn(); $this->assertSame('foo', $result); $this->assertSame('foo', $_SERVER['__test.pipe.one']); diff --git a/tests/Process/ProcessTest.php b/tests/Process/ProcessTest.php index 8b51dad64f9a..16ed54aa934d 100644 --- a/tests/Process/ProcessTest.php +++ b/tests/Process/ProcessTest.php @@ -319,8 +319,8 @@ public function testProcessFakeSequences() $factory->fake([ 'ls *' => $factory->sequence() - ->push('ls command 1') - ->push('ls command 2'), + ->push('ls command 1') + ->push('ls command 2'), 'cat *' => 'cat command', ]); @@ -340,9 +340,9 @@ public function testProcessFakeSequencesCanReturnEmptyResultsWhenSequenceIsEmpty $factory->fake([ 'ls *' => $factory->sequence() - ->push('ls command 1') - ->push('ls command 2') - ->dontFailWhenEmpty(), + ->push('ls command 1') + ->push('ls command 2') + ->dontFailWhenEmpty(), ]); $result = $factory->run('ls -la'); @@ -363,8 +363,8 @@ public function testProcessFakeSequencesCanThrowWhenSequenceIsEmpty() $factory->fake([ 'ls *' => $factory->sequence() - ->push('ls command 1') - ->push('ls command 2'), + ->push('ls command 1') + ->push('ls command 2'), ]); $result = $factory->run('ls -la'); @@ -716,10 +716,10 @@ public function testFakeInvokedProcessOutputWithLatestOutput() $factory->fake(function () use ($factory) { return $factory->describe() - ->output('ONE') - ->output('TWO') - ->output('THREE') - ->runsFor(iterations: 3); + ->output('ONE') + ->output('TWO') + ->output('THREE') + ->runsFor(iterations: 3); }); $process = $factory->start('echo "ONE"; sleep 1; echo "TWO"; sleep 1; echo "THREE"; sleep 1;'); diff --git a/tests/Queue/DynamoDbFailedJobProviderTest.php b/tests/Queue/DynamoDbFailedJobProviderTest.php index dcd0cfe5f9d1..84baa1015a6e 100644 --- a/tests/Queue/DynamoDbFailedJobProviderTest.php +++ b/tests/Queue/DynamoDbFailedJobProviderTest.php @@ -43,7 +43,7 @@ public function testCanProperlyLogFailedJob() 'payload' => ['S' => json_encode(['uuid' => (string) $uuid])], 'exception' => ['S' => (string) $exception], 'failed_at' => ['N' => (string) $now->getTimestamp()], - 'expires_at' => ['N' => (string) $now->addDays(3)->getTimestamp()], + 'expires_at' => ['N' => (string) $now->addDays(7)->getTimestamp()], ], ]); diff --git a/tests/Routing/RouteRegistrarTest.php b/tests/Routing/RouteRegistrarTest.php index 465f92cd26f8..83ff3392459c 100644 --- a/tests/Routing/RouteRegistrarTest.php +++ b/tests/Routing/RouteRegistrarTest.php @@ -246,7 +246,7 @@ public function testCanRegisterRouteWithArrayAndClosureUsesAction() public function testCanRegisterRouteWithControllerAction() { $this->router->middleware('controller-middleware') - ->get('users', RouteRegistrarControllerStub::class.'@index'); + ->get('users', RouteRegistrarControllerStub::class.'@index'); $this->seeResponse('controller', Request::create('users', 'GET')); $this->seeMiddleware('controller-middleware'); @@ -255,7 +255,7 @@ public function testCanRegisterRouteWithControllerAction() public function testCanRegisterRouteWithControllerActionArray() { $this->router->middleware('controller-middleware') - ->get('users', [RouteRegistrarControllerStub::class, 'index']); + ->get('users', [RouteRegistrarControllerStub::class, 'index']); $this->seeResponse('controller', Request::create('users', 'GET')); $this->seeMiddleware('controller-middleware'); @@ -519,7 +519,7 @@ public function testRegisteringNonApprovedAttributesThrows() public function testCanRegisterResource() { $this->router->middleware('resource-middleware') - ->resource('users', RouteRegistrarControllerStub::class); + ->resource('users', RouteRegistrarControllerStub::class); $this->seeResponse('deleted', Request::create('users/1', 'DELETE')); $this->seeMiddleware('resource-middleware'); @@ -611,8 +611,8 @@ public function testCanRegisterResourceWithMissingOption() public function testCanAccessRegisteredResourceRoutesAsRouteCollection() { $resource = $this->router->middleware('resource-middleware') - ->resource('users', RouteRegistrarControllerStub::class) - ->register(); + ->resource('users', RouteRegistrarControllerStub::class) + ->register(); $this->assertCount(7, $resource->getRoutes()); @@ -628,7 +628,7 @@ public function testCanAccessRegisteredResourceRoutesAsRouteCollection() public function testCanLimitMethodsOnRegisteredResource() { $this->router->resource('users', RouteRegistrarControllerStub::class) - ->only('index', 'show', 'destroy'); + ->only('index', 'show', 'destroy'); $this->assertCount(3, $this->router->getRoutes()); @@ -640,7 +640,7 @@ public function testCanLimitMethodsOnRegisteredResource() public function testCanExcludeMethodsOnRegisteredResource() { $this->router->resource('users', RouteRegistrarControllerStub::class) - ->except(['index', 'create', 'store', 'show', 'edit']); + ->except(['index', 'create', 'store', 'show', 'edit']); $this->assertCount(2, $this->router->getRoutes()); @@ -651,8 +651,8 @@ public function testCanExcludeMethodsOnRegisteredResource() public function testCanLimitAndExcludeMethodsOnRegisteredResource() { $this->router->resource('users', RouteRegistrarControllerStub::class) - ->only('index', 'show', 'destroy') - ->except('destroy'); + ->only('index', 'show', 'destroy') + ->except('destroy'); $this->assertCount(2, $this->router->getRoutes()); @@ -700,7 +700,7 @@ public function testCanSetScopedOptionOnRegisteredResource() public function testCanExcludeMethodsOnRegisteredApiResource() { $this->router->apiResource('users', RouteRegistrarControllerStub::class) - ->except(['index', 'show', 'store']); + ->except(['index', 'show', 'store']); $this->assertCount(2, $this->router->getRoutes()); @@ -812,18 +812,18 @@ public function testUserCanRegisterApiResourceWithOnlyOption() public function testCanNameRoutesOnRegisteredResource() { $this->router->resource('comments', RouteRegistrarControllerStub::class) - ->only('create', 'store')->names('reply'); + ->only('create', 'store')->names('reply'); $this->router->resource('users', RouteRegistrarControllerStub::class) - ->only('create', 'store')->names([ - 'create' => 'user.build', - 'store' => 'user.save', - ]); + ->only('create', 'store')->names([ + 'create' => 'user.build', + 'store' => 'user.save', + ]); $this->router->resource('posts', RouteRegistrarControllerStub::class) - ->only('create', 'destroy') - ->name('create', 'posts.make') - ->name('destroy', 'posts.remove'); + ->only('create', 'destroy') + ->name('create', 'posts.make') + ->name('destroy', 'posts.remove'); $this->assertTrue($this->router->getRoutes()->hasNamedRoute('reply.create')); $this->assertTrue($this->router->getRoutes()->hasNamedRoute('reply.store')); @@ -836,10 +836,10 @@ public function testCanNameRoutesOnRegisteredResource() public function testCanOverrideParametersOnRegisteredResource() { $this->router->resource('users', RouteRegistrarControllerStub::class) - ->parameters(['users' => 'admin_user']); + ->parameters(['users' => 'admin_user']); $this->router->resource('posts', RouteRegistrarControllerStub::class) - ->parameter('posts', 'topic'); + ->parameter('posts', 'topic'); $this->assertStringContainsString('admin_user', $this->router->getRoutes()->getByName('users.show')->uri); $this->assertStringContainsString('topic', $this->router->getRoutes()->getByName('posts.show')->uri); @@ -848,7 +848,7 @@ public function testCanOverrideParametersOnRegisteredResource() public function testCanSetMiddlewareOnRegisteredResource() { $this->router->resource('users', RouteRegistrarControllerStub::class) - ->middleware(RouteRegistrarMiddlewareStub::class); + ->middleware(RouteRegistrarMiddlewareStub::class); $this->seeMiddleware(RouteRegistrarMiddlewareStub::class); } @@ -856,10 +856,10 @@ public function testCanSetMiddlewareOnRegisteredResource() public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredResource() { $this->router->resource('users', RouteRegistrarControllerStub::class) - ->middleware('default') - ->middlewareFor('index', RouteRegistrarMiddlewareStub::class) - ->middlewareFor(['create', 'store'], 'one') - ->middlewareFor(['edit'], ['one', 'two']); + ->middleware('default') + ->middlewareFor('index', RouteRegistrarMiddlewareStub::class) + ->middlewareFor(['create', 'store'], 'one') + ->middlewareFor(['edit'], ['one', 'two']); $this->assertEquals($this->router->getRoutes()->getByName('users.index')->gatherMiddleware(), ['default', RouteRegistrarMiddlewareStub::class]); $this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['default', 'one']); @@ -870,10 +870,10 @@ public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredResource() $this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->gatherMiddleware(), ['default']); $this->router->resource('users', RouteRegistrarControllerStub::class) - ->middlewareFor('index', RouteRegistrarMiddlewareStub::class) - ->middlewareFor(['create', 'store'], 'one') - ->middlewareFor(['edit'], ['one', 'two']) - ->middleware('default'); + ->middlewareFor('index', RouteRegistrarMiddlewareStub::class) + ->middlewareFor(['create', 'store'], 'one') + ->middlewareFor(['edit'], ['one', 'two']) + ->middleware('default'); $this->assertEquals($this->router->getRoutes()->getByName('users.index')->gatherMiddleware(), [RouteRegistrarMiddlewareStub::class, 'default']); $this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['one', 'default']); @@ -887,9 +887,9 @@ public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredResource() public function testResourceWithoutMiddlewareRegistration() { $this->router->resource('users', RouteRegistrarControllerStub::class) - ->only('index') - ->middleware(['one', 'two']) - ->withoutMiddleware('one'); + ->only('index') + ->middleware(['one', 'two']) + ->withoutMiddleware('one'); $this->seeResponse('controller', Request::create('users', 'GET')); @@ -899,10 +899,10 @@ public function testResourceWithoutMiddlewareRegistration() public function testCanSetExcludedMiddlewareForSpecifiedMethodsOnRegisteredResource() { $this->router->resource('users', RouteRegistrarControllerStub::class) - ->withoutMiddleware('one') - ->withoutMiddlewareFor('index', 'two') - ->withoutMiddlewareFor(['create', 'store'], 'three') - ->withoutMiddlewareFor(['edit'], ['four', 'five']); + ->withoutMiddleware('one') + ->withoutMiddlewareFor('index', 'two') + ->withoutMiddlewareFor(['create', 'store'], 'three') + ->withoutMiddlewareFor(['edit'], ['four', 'five']); $this->assertEquals($this->router->getRoutes()->getByName('users.index')->excludedMiddleware(), ['one', 'two']); $this->assertEquals($this->router->getRoutes()->getByName('users.create')->excludedMiddleware(), ['one', 'three']); @@ -924,9 +924,9 @@ public function __toString() }; $this->router->resource('users', RouteRegistrarControllerStub::class) - ->only('index') - ->middleware([$one, 'two']) - ->withoutMiddleware('one'); + ->only('index') + ->middleware([$one, 'two']) + ->withoutMiddleware('one'); $this->seeResponse('controller', Request::create('users', 'GET')); @@ -942,7 +942,7 @@ public function testResourceWheres() ]; $this->router->resource('users', RouteRegistrarControllerStub::class) - ->where($wheres); + ->where($wheres); /** @var \Illuminate\Routing\Route $route */ foreach ($this->router->getRoutes() as $route) { @@ -1398,12 +1398,12 @@ public function testApiSingletonCanIncludeAnySingletonMethods() public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredSingletonResource() { $this->router->singleton('users', RouteRegistrarControllerStub::class) - ->creatable() - ->destroyable() - ->middleware('default') - ->middlewareFor('show', RouteRegistrarMiddlewareStub::class) - ->middlewareFor(['create', 'store'], 'one') - ->middlewareFor(['edit'], ['one', 'two']); + ->creatable() + ->destroyable() + ->middleware('default') + ->middlewareFor('show', RouteRegistrarMiddlewareStub::class) + ->middlewareFor(['create', 'store'], 'one') + ->middlewareFor(['edit'], ['one', 'two']); $this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['default', 'one']); $this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['default', 'one']); @@ -1413,12 +1413,12 @@ public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredSingletonReso $this->assertEquals($this->router->getRoutes()->getByName('users.destroy')->gatherMiddleware(), ['default']); $this->router->singleton('users', RouteRegistrarControllerStub::class) - ->creatable() - ->destroyable() - ->middlewareFor('show', RouteRegistrarMiddlewareStub::class) - ->middlewareFor(['create', 'store'], 'one') - ->middlewareFor(['edit'], ['one', 'two']) - ->middleware('default'); + ->creatable() + ->destroyable() + ->middlewareFor('show', RouteRegistrarMiddlewareStub::class) + ->middlewareFor(['create', 'store'], 'one') + ->middlewareFor(['edit'], ['one', 'two']) + ->middleware('default'); $this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['one', 'default']); $this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['one', 'default']); @@ -1431,12 +1431,12 @@ public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredSingletonReso public function testCanSetExcludedMiddlewareForSpecifiedMethodsOnRegisteredSingletonResource() { $this->router->singleton('users', RouteRegistrarControllerStub::class) - ->creatable() - ->destroyable() - ->withoutMiddleware('one') - ->withoutMiddlewareFor('show', 'two') - ->withoutMiddlewareFor(['create', 'store'], 'three') - ->withoutMiddlewareFor(['edit'], ['four', 'five']); + ->creatable() + ->destroyable() + ->withoutMiddleware('one') + ->withoutMiddlewareFor('show', 'two') + ->withoutMiddlewareFor(['create', 'store'], 'three') + ->withoutMiddlewareFor(['edit'], ['four', 'five']); $this->assertEquals($this->router->getRoutes()->getByName('users.create')->excludedMiddleware(), ['one', 'three']); $this->assertEquals($this->router->getRoutes()->getByName('users.store')->excludedMiddleware(), ['one', 'three']); diff --git a/tests/Support/SleepTest.php b/tests/Support/SleepTest.php index 809ba41b8629..228364891008 100644 --- a/tests/Support/SleepTest.php +++ b/tests/Support/SleepTest.php @@ -152,7 +152,7 @@ public function testItCanChainDurations() Sleep::fake(); $sleep = Sleep::for(1)->second() - ->and(500)->microseconds(); + ->and(500)->microseconds(); $this->assertSame((float) $sleep->duration->totalMicroseconds, 1000500.0); } diff --git a/tests/Support/SupportArrTest.php b/tests/Support/SupportArrTest.php index 82044805b7cb..73676e981acf 100644 --- a/tests/Support/SupportArrTest.php +++ b/tests/Support/SupportArrTest.php @@ -39,6 +39,10 @@ public function testAdd() $this->assertEquals(['developer' => ['name' => 'Ferid']], Arr::add([], 'developer.name', 'Ferid')); $this->assertEquals([1 => 'hAz'], Arr::add([], 1, 'hAz')); $this->assertEquals([1 => [1 => 'hAz']], Arr::add([], 1.1, 'hAz')); + + // Case where the key already exists + $this->assertEquals(['type' => 'Table'], Arr::add(['type' => 'Table'], 'type', 'Chair')); + $this->assertEquals(['category' => ['type' => 'Table']], Arr::add(['category' => ['type' => 'Table']], 'category.type', 'Chair')); } public function testCollapse() @@ -48,8 +52,8 @@ public function testCollapse() $this->assertEquals(['foo', 'bar', 'baz'], Arr::collapse($data)); // Case including numeric and string elements - $array = [[1], [2], [3], ['foo', 'bar'], collect(['baz', 'boom'])]; - $this->assertEquals([1, 2, 3, 'foo', 'bar', 'baz', 'boom'], Arr::collapse($array)); + $array = [[1], [2], [3], ['foo', 'bar']]; + $this->assertEquals([1, 2, 3, 'foo', 'bar'], Arr::collapse($array)); // Case with empty two-dimensional arrays $emptyArray = [[], [], []]; @@ -504,6 +508,10 @@ public function testGet() $this->assertSame('dayle', Arr::get($array, 'names.otherDeveloper', function () { return 'dayle'; })); + + // Test array has a null key + $this->assertSame('bar', Arr::get(['' => 'bar'], '')); + $this->assertSame('bar', Arr::get(['' => ['' => 'bar']], '.')); } public function testHas() @@ -619,6 +627,8 @@ public function testIsList() $this->assertTrue(Arr::isList([0 => 'foo', 'bar'])); $this->assertTrue(Arr::isList([0 => 'foo', 1 => 'bar'])); + $this->assertFalse(Arr::isList([-1 => 1])); + $this->assertFalse(Arr::isList([-1 => 1, 0 => 2])); $this->assertFalse(Arr::isList([1 => 'foo', 'bar'])); $this->assertFalse(Arr::isList([1 => 'foo', 0 => 'bar'])); $this->assertFalse(Arr::isList([0 => 'foo', 'bar' => 'baz'])); @@ -632,6 +642,17 @@ public function testOnly() $array = Arr::only($array, ['name', 'price']); $this->assertEquals(['name' => 'Desk', 'price' => 100], $array); $this->assertEmpty(Arr::only($array, ['nonExistingKey'])); + + $this->assertEmpty(Arr::only($array, null)); + + // Test with array having numeric keys + $this->assertEquals(['foo'], Arr::only(['foo', 'bar', 'baz'], 0)); + $this->assertEquals([1 => 'bar', 2 => 'baz'], Arr::only(['foo', 'bar', 'baz'], [1, 2])); + $this->assertEmpty(Arr::only(['foo', 'bar', 'baz'], [3])); + + // Test with array having numeric key and string key + $this->assertEquals(['foo'], Arr::only(['foo', 'bar' => 'baz'], 0)); + $this->assertEquals(['bar' => 'baz'], Arr::only(['foo', 'bar' => 'baz'], 'bar')); } public function testPluck() @@ -1501,5 +1522,15 @@ public function testSelect() 'name' => 'Abigail', ], ], Arr::select($array, 'name')); + + $this->assertEquals([ + [], + [], + ], Arr::select($array, 'nonExistingKey')); + + $this->assertEquals([ + [], + [], + ], Arr::select($array, null)); } } diff --git a/tests/Support/SupportLazyCollectionIsLazyTest.php b/tests/Support/SupportLazyCollectionIsLazyTest.php index 66cda339158e..328ca23b2af4 100644 --- a/tests/Support/SupportLazyCollectionIsLazyTest.php +++ b/tests/Support/SupportLazyCollectionIsLazyTest.php @@ -1583,7 +1583,7 @@ public function testWhereInstanceOfIsLazy() { $data = $this->make(['a' => 0])->concat( $this->make([['a' => 1], ['a' => 2], ['a' => 3], ['a' => 4]]) - ->mapInto(stdClass::class) + ->mapInto(stdClass::class) ); $this->assertDoesNotEnumerateCollection($data, function ($collection) { diff --git a/tests/Support/SupportTestingMailFakeTest.php b/tests/Support/SupportTestingMailFakeTest.php index 07189bb92efc..4a37c2a729ce 100644 --- a/tests/Support/SupportTestingMailFakeTest.php +++ b/tests/Support/SupportTestingMailFakeTest.php @@ -396,7 +396,7 @@ class MailableStub extends Mailable public function build() { $this->with('first_name', 'Taylor') - ->withLastName('Otwell'); + ->withLastName('Otwell'); } } @@ -414,7 +414,7 @@ class QueueableMailableStub extends Mailable implements ShouldQueue public function build() { $this->with('first_name', 'Taylor') - ->withLastName('Otwell'); + ->withLastName('Otwell'); } } diff --git a/tests/Validation/ValidationDateRuleTest.php b/tests/Validation/ValidationDateRuleTest.php new file mode 100644 index 000000000000..6c903a6f956a --- /dev/null +++ b/tests/Validation/ValidationDateRuleTest.php @@ -0,0 +1,138 @@ +assertEquals('date', (string) $rule); + + $rule = new Date; + $this->assertSame('date', (string) $rule); + } + + public function testDateFormatRule() + { + $rule = Rule::date()->format('d/m/Y'); + $this->assertEquals('date|date_format:d/m/Y', (string) $rule); + } + + public function testAfterTodayRule() + { + $rule = Rule::date()->afterToday(); + $this->assertEquals('date|after:today', (string) $rule); + + $rule = Rule::date()->todayOrAfter(); + $this->assertEquals('date|after_or_equal:today', (string) $rule); + } + + public function testBeforeTodayRule() + { + $rule = Rule::date()->beforeToday(); + $this->assertEquals('date|before:today', (string) $rule); + + $rule = Rule::date()->todayOrBefore(); + $this->assertEquals('date|before_or_equal:today', (string) $rule); + } + + public function testAfterSpecificDateRule() + { + $rule = Rule::date()->after(Carbon::parse('2024-01-01')); + $this->assertEquals('date|after:2024-01-01', (string) $rule); + } + + public function testBeforeSpecificDateRule() + { + $rule = Rule::date()->before(Carbon::parse('2024-01-01')); + $this->assertEquals('date|before:2024-01-01', (string) $rule); + } + + public function testAfterOrEqualSpecificDateRule() + { + $rule = Rule::date()->afterOrEqual(Carbon::parse('2024-01-01')); + $this->assertEquals('date|after_or_equal:2024-01-01', (string) $rule); + } + + public function testBeforeOrEqualSpecificDateRule() + { + $rule = Rule::date()->beforeOrEqual(Carbon::parse('2024-01-01')); + $this->assertEquals('date|before_or_equal:2024-01-01', (string) $rule); + } + + public function testBetweenDatesRule() + { + $rule = Rule::date()->between(Carbon::parse('2024-01-01'), Carbon::parse('2024-02-01')); + $this->assertEquals('date|after:2024-01-01|before:2024-02-01', (string) $rule); + } + + public function testBetweenOrEqualDatesRule() + { + $rule = Rule::date()->betweenOrEqual('2024-01-01', '2024-02-01'); + $this->assertEquals('date|after_or_equal:2024-01-01|before_or_equal:2024-02-01', (string) $rule); + } + + public function testChainedRules() + { + $rule = Rule::date('Y-m-d H:i:s') + ->format('Y-m-d') + ->after('2024-01-01 00:00:00') + ->before('2025-01-01 00:00:00'); + $this->assertEquals('date|date_format:Y-m-d|after:2024-01-01 00:00:00|before:2025-01-01 00:00:00', (string) $rule); + + $rule = Rule::date() + ->format('Y-m-d') + ->when(true, function ($rule) { + $rule->after('2024-01-01'); + }) + ->unless(true, function ($rule) { + $rule->before('2025-01-01'); + }); + $this->assertSame('date|date_format:Y-m-d|after:2024-01-01', (string) $rule); + } + + public function testDateValidation() + { + $trans = new Translator(new ArrayLoader, 'en'); + + $rule = Rule::date(); + + $validator = new Validator( + $trans, + ['date' => 'not a date'], + ['date' => $rule] + ); + + $this->assertSame( + $trans->get('validation.date'), + $validator->errors()->first('date') + ); + + $validator = new Validator( + $trans, + ['date' => '2024-01-01'], + ['date' => $rule] + ); + + $this->assertEmpty($validator->errors()->first('date')); + + $rule = Rule::date()->between('2024-01-01', '2025-01-01'); + + $validator = new Validator( + $trans, + ['date' => '2024-02-01'], + ['date' => (string) $rule] + ); + + $this->assertEmpty($validator->errors()->first('date')); + } +} diff --git a/tests/Validation/ValidationFactoryTest.php b/tests/Validation/ValidationFactoryTest.php index 4101e200d773..deb9134b150b 100755 --- a/tests/Validation/ValidationFactoryTest.php +++ b/tests/Validation/ValidationFactoryTest.php @@ -64,8 +64,8 @@ public function testValidateCallsValidateOnTheValidator() $factory = m::mock(Factory::class.'[make]', [$translator]); $factory->shouldReceive('make')->once() - ->with(['foo' => 'bar', 'baz' => 'boom'], ['foo' => 'required'], [], []) - ->andReturn($validator); + ->with(['foo' => 'bar', 'baz' => 'boom'], ['foo' => 'required'], [], []) + ->andReturn($validator); $validator->shouldReceive('validate')->once()->andReturn(['foo' => 'bar']);