This commit is contained in:
TiclemFR
2024-01-20 23:14:52 +01:00
parent a068f54957
commit 031f7071e6
881 changed files with 241469 additions and 247870 deletions

View File

@@ -1,6 +1,51 @@
# Release Notes for 10.x
## [Unreleased](https://github.com/laravel/framework/compare/v10.38.2...10.x)
## [Unreleased](https://github.com/laravel/framework/compare/v10.40.0...10.x)
## [v10.40.0](https://github.com/laravel/framework/compare/v10.39.0...v10.40.0) - 2024-01-09
* [10.x] `Model::preventAccessingMissingAttributes()` raises exception for enums & primitive castable attributes that were not retrieved by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/49480
* [10.x] Include system versioned tables for MariaDB by [@hafezdivandari](https://github.com/hafezdivandari) in https://github.com/laravel/framework/pull/49509
* [10.x] Fixes the `Arr::dot()` method to properly handle indexes array by [@kayw-geek](https://github.com/kayw-geek) in https://github.com/laravel/framework/pull/49507
* [10.x] Expand Gate::allows & Gate::denies signature by [@antonkomarev](https://github.com/antonkomarev) in https://github.com/laravel/framework/pull/49503
* [10.x] Improve numeric comparison for custom casts by [@imahmood](https://github.com/imahmood) in https://github.com/laravel/framework/pull/49504
* [10.x] Add session except method by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/49520
* [10.x] Add `Number::clamp` by [@jbrooksuk](https://github.com/jbrooksuk) in https://github.com/laravel/framework/pull/49512
* [10.x] Fix Schedule test by [@michaelnabil230](https://github.com/michaelnabil230) in https://github.com/laravel/framework/pull/49538
* [10.x] Use correct format of date by [@buismaarten](https://github.com/buismaarten) in https://github.com/laravel/framework/pull/49541
* [10.x] Clean Arr by [@michaelnabil230](https://github.com/michaelnabil230) in https://github.com/laravel/framework/pull/49530
* [10.x] Make ComponentAttributeBag Arrayable by [@iamgergo](https://github.com/iamgergo) in https://github.com/laravel/framework/pull/49524
* [10.x] Fix whenAggregated when default is not specified by [@lovePizza](https://github.com/lovePizza) in https://github.com/laravel/framework/pull/49521
* [10.x] Update AsArrayObject.php to use ARRAY_AS_PROPS flag by [@pintend](https://github.com/pintend) in https://github.com/laravel/framework/pull/49534
* [10.x] Remove invalid `RedisCluster::client()` call by [@tillkruss](https://github.com/tillkruss) in https://github.com/laravel/framework/pull/49560
* [10.x] Remove unused code from `PhpRedisConnector` by [@tillkruss](https://github.com/tillkruss) in https://github.com/laravel/framework/pull/49559
* [10.x] Flush about command during test runs by [@timacdonald](https://github.com/timacdonald) in https://github.com/laravel/framework/pull/49557
* [10.x] Fix parentOfParameter method by [@iamgergo](https://github.com/iamgergo) in https://github.com/laravel/framework/pull/49548
* [10.x] Make the Schema Builder macroable by [@kevinb1989](https://github.com/kevinb1989) in https://github.com/laravel/framework/pull/49547
* [10.x] Remove unused code from tests by [@imahmood](https://github.com/imahmood) in https://github.com/laravel/framework/pull/49566
* [10.x] Update Query/Builder.php $columns typehint by [@Grldk](https://github.com/Grldk) in https://github.com/laravel/framework/pull/49563
* [10.x] Add assertViewEmpty to TestView by [@dwightwatson](https://github.com/dwightwatson) in https://github.com/laravel/framework/pull/49558
* [10.x] Update tailwind.blade.php for dark mode by [@sabinchacko03](https://github.com/sabinchacko03) in https://github.com/laravel/framework/pull/49515
* [10.x] Fix deprecation with null value in cache FileStore by [@driesvints](https://github.com/driesvints) in https://github.com/laravel/framework/pull/49578
* [10.x] Allow Vite asset path customization by [@timacdonald](https://github.com/timacdonald) in https://github.com/laravel/framework/pull/49437
* [10.x] Type hinting of the second parameter of date- and time-related `where*()` methods of `Illuminate\Database\Query\Builder` by [@lorenzolosa](https://github.com/lorenzolosa) in https://github.com/laravel/framework/pull/49599
* [10.x] Fix Stringable::convertCase() return type by [@vaites](https://github.com/vaites) in https://github.com/laravel/framework/pull/49590
* Allow \Blade::stringable() to be called on native Iterables by [@tsjason](https://github.com/tsjason) in https://github.com/laravel/framework/pull/49591
* [10.x] Refactor time handling using `InteractsWithTime` trait method by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/49601
* [10.x] Add `assertCount` test helper by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/49609
* [10.x] Ability to establish connection without using Config Repository by [@deleugpn](https://github.com/deleugpn) in https://github.com/laravel/framework/pull/49527
* [10.x] Add APA style title helper by [@hotmeteor](https://github.com/hotmeteor) in https://github.com/laravel/framework/pull/49572
* [10.x] Fix usage of alternatives in error output by [@Mrjavaci](https://github.com/Mrjavaci) in https://github.com/laravel/framework/pull/49614
* [10.x] Use locks for queue job popping for PlanetScale's MySQL-compatible Vitess 19 engine by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/49561
## [v10.39.0](https://github.com/laravel/framework/compare/v10.38.2...v10.39.0) - 2023-12-27
* [9.x] Support for phpredis 6.0.0 by [@MichalHubatka](https://github.com/MichalHubatka) in https://github.com/laravel/framework/pull/48380
* [10.x] Dynamic `maxTries` for queued jobs by [@mechelon](https://github.com/mechelon) in https://github.com/laravel/framework/pull/49473
* [10.x] Avoid TypeError when using json validation rule when PHP < 8.3 by [@Xint0](https://github.com/Xint0) in https://github.com/laravel/framework/pull/49474
* [10.x] Fix use statement compilation in Blade templates by [@MrPunyapal](https://github.com/MrPunyapal) in https://github.com/laravel/framework/pull/49479
* [10.x] Allow testing prompts validation by [@cerbero90](https://github.com/cerbero90) in https://github.com/laravel/framework/pull/49447
* [10.x] Add 'Roundrobin' Symfony mailer transport driver by [@me-shaon](https://github.com/me-shaon) in https://github.com/laravel/framework/pull/49435
## [v10.38.2](https://github.com/laravel/framework/compare/v10.38.1...v10.38.2) - 2023-12-22

View File

@@ -318,9 +318,9 @@ class Gate implements GateContract
}
/**
* Determine if the given ability should be granted for the current user.
* Determine if all of the given abilities should be granted for the current user.
*
* @param string $ability
* @param iterable|string $ability
* @param array|mixed $arguments
* @return bool
*/
@@ -330,9 +330,9 @@ class Gate implements GateContract
}
/**
* Determine if the given ability should be denied for the current user.
* Determine if any of the given abilities should be denied for the current user.
*
* @param string $ability
* @param iterable|string $ability
* @param array|mixed $arguments
* @return bool
*/

View File

@@ -344,4 +344,26 @@ class PendingBatch
new BatchDispatched($batch)
);
}
/**
* Dispatch the batch if the given truth test passes.
*
* @param bool|\Closure $boolean
* @return \Illuminate\Bus\Batch|null
*/
public function dispatchIf($boolean)
{
return value($boolean) ? $this->dispatch() : null;
}
/**
* Dispatch the batch unless the given truth test passes.
*
* @param bool|\Closure $boolean
* @return \Illuminate\Bus\Batch|null
*/
public function dispatchUnless($boolean)
{
return ! value($boolean) ? $this->dispatch() : null;
}
}

View File

@@ -285,7 +285,7 @@ class DynamoDbStore implements LockProvider, Store
],
'ExpressionAttributeValues' => [
':now' => [
'N' => (string) Carbon::now()->getTimestamp(),
'N' => (string) $this->currentTime(),
],
],
]);
@@ -326,7 +326,7 @@ class DynamoDbStore implements LockProvider, Store
],
'ExpressionAttributeValues' => [
':now' => [
'N' => (string) Carbon::now()->getTimestamp(),
'N' => (string) $this->currentTime(),
],
':amount' => [
'N' => (string) $value,
@@ -371,7 +371,7 @@ class DynamoDbStore implements LockProvider, Store
],
'ExpressionAttributeValues' => [
':now' => [
'N' => (string) Carbon::now()->getTimestamp(),
'N' => (string) $this->currentTime(),
],
':amount' => [
'N' => (string) $value,
@@ -469,7 +469,7 @@ class DynamoDbStore implements LockProvider, Store
{
return $seconds > 0
? $this->availableAt($seconds)
: Carbon::now()->getTimestamp();
: $this->currentTime();
}
/**

View File

@@ -290,9 +290,11 @@ class FileStore implements Store, LockProvider
// just return null. Otherwise, we'll get the contents of the file and get
// the expiration UNIX timestamps from the start of the file's contents.
try {
$expire = substr(
$contents = $this->files->get($path, true), 0, 10
);
if (is_null($contents = $this->files->get($path, true))) {
return $this->emptyPayload();
}
$expire = substr($contents, 0, 10);
} catch (Exception) {
return $this->emptyPayload();
}

View File

@@ -113,13 +113,13 @@ class Arr
foreach ($array as $key => $value) {
if (is_array($value) && ! empty($value)) {
$results[] = static::dot($value, $prepend.$key.'.');
$results = array_merge($results, static::dot($value, $prepend.$key.'.'));
} else {
$results[] = [$prepend.$key => $value];
$results[$prepend.$key] = $value;
}
}
return array_merge(...$results);
return $results;
}
/**
@@ -476,9 +476,7 @@ class Arr
*/
public static function prependKeysWith($array, $prependWith)
{
return Collection::make($array)->mapWithKeys(function ($item, $key) use ($prependWith) {
return [$prependWith.$key => $item];
})->all();
return static::mapWithKeys($array, fn ($item, $key) => [$prependWith.$key => $item]);
}
/**

View File

@@ -249,7 +249,7 @@ abstract class GeneratorCommand extends Command implements PromptsForMissingInpu
{
$modelPath = is_dir(app_path('Models')) ? app_path('Models') : app_path();
return collect((new Finder)->files()->depth(0)->in($modelPath))
return collect(Finder::create()->files()->depth(0)->in($modelPath))
->map(fn ($file) => $file->getBasename('.php'))
->sort()
->values()
@@ -269,7 +269,7 @@ abstract class GeneratorCommand extends Command implements PromptsForMissingInpu
return [];
}
return collect((new Finder)->files()->depth(0)->in($eventPath))
return collect(Finder::create()->files()->depth(0)->in($eventPath))
->map(fn ($file) => $file->getBasename('.php'))
->sort()
->values()

View File

@@ -57,18 +57,18 @@ interface Gate
public function after(callable $callback);
/**
* Determine if the given ability should be granted for the current user.
* Determine if all of the given abilities should be granted for the current user.
*
* @param string $ability
* @param iterable|string $ability
* @param array|mixed $arguments
* @return bool
*/
public function allows($ability, $arguments = []);
/**
* Determine if the given ability should be denied for the current user.
* Determine if any of the given abilities should be denied for the current user.
*
* @param string $ability
* @param iterable|string $ability
* @param array|mixed $arguments
* @return bool
*/

View File

@@ -18,7 +18,7 @@ interface Translator
* Get a translation according to an integer value.
*
* @param string $key
* @param \Countable|int|array $number
* @param \Countable|int|float|array $number
* @param array $replace
* @param string|null $locale
* @return string

View File

@@ -24,6 +24,7 @@ class PruneCommand extends Command
protected $signature = 'model:prune
{--model=* : Class names of the models to be pruned}
{--except=* : Class names of the models to be excluded from pruning}
{--path=* : Absolute path(s) to directories where models are located}
{--chunk=1000 : The number of models to retrieve per chunk of models to be deleted}
{--pretend : Display the number of prunable records found instead of deleting them}';
@@ -125,7 +126,7 @@ class PruneCommand extends Command
throw new InvalidArgumentException('The --models and --except options cannot be combined.');
}
return collect((new Finder)->in($this->getDefaultPath())->files()->name('*.php'))
return collect(Finder::create()->in($this->getPath())->files()->name('*.php'))
->map(function ($model) {
$namespace = $this->laravel->getNamespace();
@@ -146,12 +147,18 @@ class PruneCommand extends Command
}
/**
* Get the default path where models are located.
* Get the path where models are located.
*
* @return string|string[]
* @return string[]|string
*/
protected function getDefaultPath()
protected function getPath()
{
if (! empty($path = $this->option('path'))) {
return collect($path)->map(function ($path) {
return base_path($path);
})->all();
}
return app_path('Models');
}

View File

@@ -101,16 +101,39 @@ class DatabaseManager implements ConnectionResolverInterface
$this->makeConnection($database), $type
);
if ($this->app->bound('events')) {
$this->app['events']->dispatch(
new ConnectionEstablished($this->connections[$name])
);
}
$this->dispatchConnectionEstablishedEvent($this->connections[$name]);
}
return $this->connections[$name];
}
/**
* Get a database connection instance from the given configuration.
*
* @param string $name
* @param array $config
* @param bool $force
* @return \Illuminate\Database\ConnectionInterface
*/
public function connectUsing(string $name, array $config, bool $force = false)
{
if ($force) {
$this->purge($name);
}
if (isset($this->connections[$name])) {
throw new RuntimeException("Cannot establish connection [$name] because another connection with that name already exists.");
}
$connection = $this->configure(
$this->factory->make($config, $name), null
);
$this->dispatchConnectionEstablishedEvent($connection);
return tap($connection, fn ($connection) => $this->connections[$name] = $connection);
}
/**
* Parse the connection into an array of the name and read / write type.
*
@@ -209,6 +232,23 @@ class DatabaseManager implements ConnectionResolverInterface
return $connection;
}
/**
* Dispatch the ConnectionEstablished event if the event dispatcher is available.
*
* @param \Illuminate\Database\Connection $connection
* @return void
*/
protected function dispatchConnectionEstablishedEvent(Connection $connection)
{
if (! $this->app->bound('events')) {
return;
}
$this->app['events']->dispatch(
new ConnectionEstablished($connection)
);
}
/**
* Prepare the read / write mode for database connection instance.
*

View File

@@ -25,7 +25,7 @@ class AsArrayObject implements Castable
$data = Json::decode($attributes[$key]);
return is_array($data) ? new ArrayObject($data) : null;
return is_array($data) ? new ArrayObject($data, ArrayObject::ARRAY_AS_PROPS) : null;
}
public function set($model, $key, $value, $attributes)

View File

@@ -2126,6 +2126,13 @@ trait HasAttributes
// an appropriate native PHP type dependent upon the associated value
// given with the key in the pair. Dayle made this comment line up.
if ($this->hasCast($key)) {
if (static::preventsAccessingMissingAttributes() &&
! array_key_exists($key, $this->attributes) &&
($this->isEnumCastable($key) ||
in_array($this->getCastType($key), static::$primitiveCastTypes))) {
$this->throwMissingAttributeExceptionIfApplicable($key);
}
return $this->castAttribute($key, $value);
}

View File

@@ -82,7 +82,7 @@ class Builder implements BuilderContract
/**
* The columns that should be returned.
*
* @var array
* @var array|null
*/
public $columns;
@@ -1379,7 +1379,7 @@ class Builder implements BuilderContract
* Add a "where date" statement to the query.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string $operator
* @param \DateTimeInterface|string|null $operator
* @param \DateTimeInterface|string|null $value
* @param string $boolean
* @return $this
@@ -1403,7 +1403,7 @@ class Builder implements BuilderContract
* Add an "or where date" statement to the query.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string $operator
* @param \DateTimeInterface|string|null $operator
* @param \DateTimeInterface|string|null $value
* @return $this
*/
@@ -1420,7 +1420,7 @@ class Builder implements BuilderContract
* Add a "where time" statement to the query.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string $operator
* @param \DateTimeInterface|string|null $operator
* @param \DateTimeInterface|string|null $value
* @param string $boolean
* @return $this
@@ -1444,7 +1444,7 @@ class Builder implements BuilderContract
* Add an "or where time" statement to the query.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string $operator
* @param \DateTimeInterface|string|null $operator
* @param \DateTimeInterface|string|null $value
* @return $this
*/
@@ -1461,7 +1461,7 @@ class Builder implements BuilderContract
* Add a "where day" statement to the query.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string $operator
* @param \DateTimeInterface|string|int|null $operator
* @param \DateTimeInterface|string|int|null $value
* @param string $boolean
* @return $this
@@ -1489,7 +1489,7 @@ class Builder implements BuilderContract
* Add an "or where day" statement to the query.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string $operator
* @param \DateTimeInterface|string|int|null $operator
* @param \DateTimeInterface|string|int|null $value
* @return $this
*/
@@ -1506,7 +1506,7 @@ class Builder implements BuilderContract
* Add a "where month" statement to the query.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string $operator
* @param \DateTimeInterface|string|int|null $operator
* @param \DateTimeInterface|string|int|null $value
* @param string $boolean
* @return $this
@@ -1534,7 +1534,7 @@ class Builder implements BuilderContract
* Add an "or where month" statement to the query.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string $operator
* @param \DateTimeInterface|string|int|null $operator
* @param \DateTimeInterface|string|int|null $value
* @return $this
*/
@@ -1551,7 +1551,7 @@ class Builder implements BuilderContract
* Add a "where year" statement to the query.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string $operator
* @param \DateTimeInterface|string|int|null $operator
* @param \DateTimeInterface|string|int|null $value
* @param string $boolean
* @return $this
@@ -1575,7 +1575,7 @@ class Builder implements BuilderContract
* Add an "or where year" statement to the query.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string $operator
* @param \DateTimeInterface|string|int|null $operator
* @param \DateTimeInterface|string|int|null $value
* @return $this
*/

View File

@@ -5,11 +5,14 @@ namespace Illuminate\Database\Schema;
use Closure;
use Illuminate\Container\Container;
use Illuminate\Database\Connection;
use Illuminate\Support\Traits\Macroable;
use InvalidArgumentException;
use LogicException;
class Builder
{
use Macroable;
/**
* The database connection instance.
*

View File

@@ -88,7 +88,7 @@ class MySqlGrammar extends Grammar
return sprintf(
'select table_name as `name`, (data_length + index_length) as `size`, '
.'table_comment as `comment`, engine as `engine`, table_collation as `collation` '
."from information_schema.tables where table_schema = %s and table_type = 'BASE TABLE' "
."from information_schema.tables where table_schema = %s and table_type in ('BASE TABLE', 'SYSTEM VERSIONED') "
.'order by table_name',
$this->quoteString($database)
);

View File

@@ -40,7 +40,7 @@ class Application extends Container implements ApplicationContract, CachesConfig
*
* @var string
*/
const VERSION = '10.39.0';
const VERSION = '10.41.0';
/**
* The base path for the Laravel installation.

View File

@@ -132,7 +132,7 @@ class PendingChain
}
/**
* Dispatch the job with the given arguments.
* Dispatch the job chain.
*
* @return \Illuminate\Foundation\Bus\PendingDispatch
*/
@@ -165,4 +165,26 @@ class PendingChain
return app(Dispatcher::class)->dispatch($firstJob);
}
/**
* Dispatch the job chain if the given truth test passes.
*
* @param bool|\Closure $boolean
* @return \Illuminate\Foundation\Bus\PendingDispatch|null
*/
public function dispatchIf($boolean)
{
return value($boolean) ? $this->dispatch() : null;
}
/**
* Dispatch the job chain unless the given truth test passes.
*
* @param bool|\Closure $boolean
* @return \Illuminate\Foundation\Bus\PendingDispatch|null
*/
public function dispatchUnless($boolean)
{
return ! value($boolean) ? $this->dispatch() : null;
}
}

View File

@@ -302,4 +302,16 @@ class AboutCommand extends Command
{
return (string) Str::of($value)->lower()->snake();
}
/**
* Flush the registered about data.
*
* @return void
*/
public static function flushState()
{
static::$data = [];
static::$customDataResolvers = [];
}
}

View File

@@ -340,7 +340,7 @@ class Kernel implements KernelContract
$namespace = $this->app->getNamespace();
foreach ((new Finder)->in($paths)->files() as $file) {
foreach (Finder::create()->in($paths)->files() as $file) {
$command = $this->commandClassFromFile($file, $namespace);
if (is_subclass_of($command, Command::class) &&

View File

@@ -3,7 +3,6 @@
namespace {{ namespace }};
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;

View File

@@ -29,7 +29,7 @@ class DiscoverEvents
public static function within($listenerPath, $basePath)
{
$listeners = collect(static::getListenerEvents(
(new Finder)->files()->in($listenerPath), $basePath
Finder::create()->files()->in($listenerPath), $basePath
));
$discoveredEvents = [];

View File

@@ -843,7 +843,7 @@ class Handler implements ExceptionHandlerContract
$message .= '. Did you mean one of these?';
with(new Error($output))->render($message);
with(new BulletList($output))->render($e->getAlternatives());
with(new BulletList($output))->render($alternatives);
$output->writeln('');
} else {

View File

@@ -6,6 +6,7 @@ use Carbon\CarbonImmutable;
use Illuminate\Console\Application as Artisan;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bootstrap\HandleExceptions;
use Illuminate\Foundation\Console\AboutCommand;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\TrimStrings;
use Illuminate\Queue\Queue;
@@ -242,6 +243,7 @@ abstract class TestCase extends BaseTestCase
$this->originalExceptionHandler = null;
$this->originalDeprecationHandler = null;
AboutCommand::flushState();
Artisan::forgetBootstrappers();
Component::flushCache();
Component::forgetComponentsResolver();

View File

@@ -55,6 +55,13 @@ class Vite implements Htmlable
*/
protected $manifestFilename = 'manifest.json';
/**
* The custom asset path resolver.
*
* @var callable|null
*/
protected $assetPathResolver = null;
/**
* The script tag attributes resolvers.
*
@@ -160,6 +167,19 @@ class Vite implements Htmlable
return $this;
}
/**
* Resolve asset paths using the provided resolver.
*
* @param callable|null $urlResolver
* @return $this
*/
public function createAssetPathsUsing($resolver)
{
$this->assetPathResolver = $resolver;
return $this;
}
/**
* Get the Vite "hot" file path.
*
@@ -688,7 +708,7 @@ class Vite implements Htmlable
*/
protected function assetPath($path, $secure = null)
{
return asset($path, $secure);
return ($this->assetPathResolver ?? asset(...))($path, $secure);
}
/**

View File

@@ -929,7 +929,7 @@ if (! function_exists('trans_choice')) {
* Translates the given message based on a count.
*
* @param string $key
* @param \Countable|int|array $number
* @param \Countable|int|float|array $number
* @param array $replace
* @param string|null $locale
* @return string

View File

@@ -78,7 +78,7 @@ class PendingRequest
/**
* The raw body for the request.
*
* @var string
* @var \Psr\Http\Message\StreamInterface|string
*/
protected $pendingBody;
@@ -259,7 +259,7 @@ class PendingRequest
/**
* Attach a raw body to the request.
*
* @param string $content
* @param \Psr\Http\Message\StreamInterface|string $content
* @param string $contentType
* @return $this
*/

View File

@@ -320,6 +320,10 @@ trait ConditionallyLoadsAttributes
*/
public function whenAggregated($relationship, $column, $aggregate, $value = null, $default = null)
{
if (func_num_args() < 5) {
$default = new MissingValue;
}
$attribute = (string) Str::of($relationship)->snake()->append('_')->append($aggregate)->append('_')->finish($column);
if (! isset($this->resource->getAttributes()[$attribute])) {

View File

@@ -501,6 +501,22 @@ class LogManager implements LoggerInterface
return $this->sharedContext;
}
/**
* Flush the log context on all currently resolved channels.
*
* @return $this
*/
public function withoutContext()
{
foreach ($this->channels as $channel) {
if (method_exists($channel, 'withoutContext')) {
$channel->withoutContext();
}
}
return $this;
}
/**
* Flush the shared context.
*

View File

@@ -1231,7 +1231,7 @@ class Mailable implements MailableContract, Renderable
PHPUnit::assertTrue(
$this->hasTo($address, $name),
"Did not see expected recipient [{$recipient}] in email recipients."
"Did not see expected recipient [{$recipient}] in email 'to' recipients."
);
return $this;
@@ -1264,7 +1264,7 @@ class Mailable implements MailableContract, Renderable
PHPUnit::assertTrue(
$this->hasCc($address, $name),
"Did not see expected recipient [{$recipient}] in email recipients."
"Did not see expected recipient [{$recipient}] in email 'cc' recipients."
);
return $this;
@@ -1285,7 +1285,7 @@ class Mailable implements MailableContract, Renderable
PHPUnit::assertTrue(
$this->hasBcc($address, $name),
"Did not see expected recipient [{$recipient}] in email recipients."
"Did not see expected recipient [{$recipient}] in email 'bcc' recipients."
);
return $this;

View File

@@ -2,22 +2,22 @@
<nav role="navigation" aria-label="Pagination Navigation" class="flex justify-between">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md">
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md dark:text-gray-600 dark:bg-gray-800 dark:border-gray-600">
{!! __('pagination.previous') !!}
</span>
@else
<a href="{{ $paginator->previousPageUrl() }}" rel="prev" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
<a href="{{ $paginator->previousPageUrl() }}" rel="prev" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
{!! __('pagination.previous') !!}
</a>
@endif
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<a href="{{ $paginator->nextPageUrl() }}" rel="next" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
<a href="{{ $paginator->nextPageUrl() }}" rel="next" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
{!! __('pagination.next') !!}
</a>
@else
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md">
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md dark:text-gray-600 dark:bg-gray-800 dark:border-gray-600">
{!! __('pagination.next') !!}
</span>
@endif

View File

@@ -2,21 +2,21 @@
<nav role="navigation" aria-label="{{ __('Pagination Navigation') }}" class="flex items-center justify-between">
<div class="flex justify-between flex-1 sm:hidden">
@if ($paginator->onFirstPage())
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md">
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md dark:text-gray-600 dark:bg-gray-800 dark:border-gray-600">
{!! __('pagination.previous') !!}
</span>
@else
<a href="{{ $paginator->previousPageUrl() }}" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
<a href="{{ $paginator->previousPageUrl() }}" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
{!! __('pagination.previous') !!}
</a>
@endif
@if ($paginator->hasMorePages())
<a href="{{ $paginator->nextPageUrl() }}" class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
<a href="{{ $paginator->nextPageUrl() }}" class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
{!! __('pagination.next') !!}
</a>
@else
<span class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md">
<span class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md dark:text-gray-600 dark:bg-gray-800 dark:border-gray-600">
{!! __('pagination.next') !!}
</span>
@endif
@@ -24,7 +24,7 @@
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
<div>
<p class="text-sm text-gray-700 leading-5">
<p class="text-sm text-gray-700 leading-5 dark:text-gray-400">
{!! __('Showing') !!}
@if ($paginator->firstItem())
<span class="font-medium">{{ $paginator->firstItem() }}</span>
@@ -40,18 +40,18 @@
</div>
<div>
<span class="relative z-0 inline-flex shadow-sm rounded-md">
<span class="relative z-0 inline-flex rtl:flex-row-reverse shadow-sm rounded-md">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<span aria-disabled="true" aria-label="{{ __('pagination.previous') }}">
<span class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-l-md leading-5" aria-hidden="true">
<span class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-l-md leading-5 dark:bg-gray-800 dark:border-gray-600" aria-hidden="true">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
</span>
</span>
@else
<a href="{{ $paginator->previousPageUrl() }}" rel="prev" class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-l-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150" aria-label="{{ __('pagination.previous') }}">
<a href="{{ $paginator->previousPageUrl() }}" rel="prev" class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-l-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:active:bg-gray-700 dark:focus:border-blue-800" aria-label="{{ __('pagination.previous') }}">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
@@ -63,7 +63,7 @@
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<span aria-disabled="true">
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 cursor-default leading-5">{{ $element }}</span>
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 cursor-default leading-5 dark:bg-gray-800 dark:border-gray-600">{{ $element }}</span>
</span>
@endif
@@ -72,10 +72,10 @@
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<span aria-current="page">
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5">{{ $page }}</span>
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 dark:bg-gray-800 dark:border-gray-600">{{ $page }}</span>
</span>
@else
<a href="{{ $url }}" class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 hover:text-gray-500 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150" aria-label="{{ __('Go to page :page', ['page' => $page]) }}">
<a href="{{ $url }}" class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 hover:text-gray-500 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400 dark:hover:text-gray-300 dark:active:bg-gray-700 dark:focus:border-blue-800" aria-label="{{ __('Go to page :page', ['page' => $page]) }}">
{{ $page }}
</a>
@endif
@@ -85,14 +85,14 @@
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<a href="{{ $paginator->nextPageUrl() }}" rel="next" class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-r-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150" aria-label="{{ __('pagination.next') }}">
<a href="{{ $paginator->nextPageUrl() }}" rel="next" class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-r-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:active:bg-gray-700 dark:focus:border-blue-800" aria-label="{{ __('pagination.next') }}">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
</svg>
</a>
@else
<span aria-disabled="true" aria-label="{{ __('pagination.next') }}">
<span class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-r-md leading-5" aria-hidden="true">
<span class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-r-md leading-5 dark:bg-gray-800 dark:border-gray-600" aria-hidden="true">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
</svg>

View File

@@ -57,6 +57,8 @@ class ClearCommand extends Command
$this->components->info('Cleared '.$count.' '.Str::plural('job', $count).' from the ['.$queueName.'] queue');
} else {
$this->components->error('Clearing queues is not supported on ['.(new ReflectionClass($queue))->getShortName().']');
return 1;
}
return 0;

View File

@@ -25,7 +25,7 @@ class ForgetFailedCommand extends Command
/**
* Execute the console command.
*
* @return void
* @return int|null
*/
public function handle()
{
@@ -33,6 +33,8 @@ class ForgetFailedCommand extends Command
$this->components->info('Failed job deleted successfully.');
} else {
$this->components->error('No failed job matches the given ID.');
return 1;
}
}
}

View File

@@ -268,7 +268,8 @@ class DatabaseQueue extends Queue implements QueueContract, ClearableQueue
if (($databaseEngine === 'mysql' && version_compare($databaseVersion, '8.0.1', '>=')) ||
($databaseEngine === 'mariadb' && version_compare($databaseVersion, '10.6.0', '>=')) ||
($databaseEngine === 'pgsql' && version_compare($databaseVersion, '9.5', '>='))) {
($databaseEngine === 'pgsql' && version_compare($databaseVersion, '9.5', '>=')) ||
($databaseEngine === 'vitess' && version_compare($databaseVersion, '19.0', '>='))) {
return 'FOR UPDATE SKIP LOCKED';
}

View File

@@ -198,7 +198,9 @@ class QueueServiceProvider extends ServiceProvider implements DeferrableProvider
};
$resetScope = function () use ($app) {
if (method_exists($app['log']->driver(), 'withoutContext')) {
$app['log']->flushSharedContext();
if (method_exists($app['log'], 'withoutContext')) {
$app['log']->withoutContext();
}

View File

@@ -63,9 +63,7 @@ class PhpRedisConnector implements Connector
*/
protected function buildClusterConnectionString(array $server)
{
return $this->formatHost($server).':'.$server['port'].'?'.Arr::query(Arr::only($server, [
'database', 'password', 'prefix', 'read_timeout',
]));
return $this->formatHost($server).':'.$server['port'];
}
/**
@@ -199,10 +197,6 @@ class PhpRedisConnector implements Connector
$client->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $options['failover']);
}
if (! empty($options['name'])) {
$client->client('SETNAME', $options['name']);
}
if (array_key_exists('serializer', $options)) {
$client->setOption(Redis::OPT_SERIALIZER, $options['serializer']);
}

View File

@@ -583,7 +583,7 @@ class Route
{
$key = array_search($parameter, array_keys($this->parameters));
if ($key === 0) {
if ($key === 0 || $key === false) {
return;
}

View File

@@ -245,6 +245,17 @@ class Store implements Session
return Arr::only($this->attributes, $keys);
}
/**
* Get all the session data except for a specified array of items.
*
* @param array $keys
* @return array
*/
public function except(array $keys)
{
return Arr::except($this->attributes, $keys);
}
/**
* Checks if a key exists.
*

View File

@@ -4,6 +4,7 @@ namespace Illuminate\Support\Facades;
/**
* @method static \Illuminate\Database\Connection connection(string|null $name = null)
* @method static \Illuminate\Database\ConnectionInterface connectUsing(string $name, array $config, bool $force = false)
* @method static void registerDoctrineType(string $class, string $name, string $type)
* @method static void purge(string|null $name = null)
* @method static void disconnect(string|null $name = null)

View File

@@ -13,8 +13,8 @@ use Illuminate\Contracts\Auth\Access\Gate as GateContract;
* @method static \Illuminate\Auth\Access\Gate policy(string $class, string $policy)
* @method static \Illuminate\Auth\Access\Gate before(callable $callback)
* @method static \Illuminate\Auth\Access\Gate after(callable $callback)
* @method static bool allows(string $ability, array|mixed $arguments = [])
* @method static bool denies(string $ability, array|mixed $arguments = [])
* @method static bool allows(iterable|string $ability, array|mixed $arguments = [])
* @method static bool denies(iterable|string $ability, array|mixed $arguments = [])
* @method static bool check(iterable|string $abilities, array|mixed $arguments = [])
* @method static bool any(iterable|string $abilities, array|mixed $arguments = [])
* @method static bool none(iterable|string $abilities, array|mixed $arguments = [])

View File

@@ -27,7 +27,7 @@ use Illuminate\Http\Client\Factory;
* @method static void flushMacros()
* @method static mixed macroCall(string $method, array $parameters)
* @method static \Illuminate\Http\Client\PendingRequest baseUrl(string $url)
* @method static \Illuminate\Http\Client\PendingRequest withBody(string $content, string $contentType = 'application/json')
* @method static \Illuminate\Http\Client\PendingRequest withBody(\Psr\Http\Message\StreamInterface|string $content, string $contentType = 'application/json')
* @method static \Illuminate\Http\Client\PendingRequest asJson()
* @method static \Illuminate\Http\Client\PendingRequest asForm()
* @method static \Illuminate\Http\Client\PendingRequest attach(string|array $name, string|resource $contents = '', string|null $filename = null, array $headers = [])

View File

@@ -6,7 +6,7 @@ namespace Illuminate\Support\Facades;
* @method static bool hasForLocale(string $key, string|null $locale = null)
* @method static bool has(string $key, string|null $locale = null, bool $fallback = true)
* @method static string|array get(string $key, array $replace = [], string|null $locale = null, bool $fallback = true)
* @method static string choice(string $key, \Countable|int|array $number, array $replace = [], string|null $locale = null)
* @method static string choice(string $key, \Countable|int|float|array $number, array $replace = [], string|null $locale = null)
* @method static void addLines(array $lines, string $locale, string $namespace = '*')
* @method static void load(string $namespace, string $group, string $locale)
* @method static \Illuminate\Translation\Translator handleMissingKeysUsing(callable|null $callback)

View File

@@ -9,6 +9,7 @@ namespace Illuminate\Support\Facades;
* @method static \Psr\Log\LoggerInterface driver(string|null $driver = null)
* @method static \Illuminate\Log\LogManager shareContext(array $context)
* @method static array sharedContext()
* @method static \Illuminate\Log\LogManager withoutContext()
* @method static \Illuminate\Log\LogManager flushSharedContext()
* @method static string|null getDefaultDriver()
* @method static void setDefaultDriver(string $name)
@@ -26,7 +27,6 @@ namespace Illuminate\Support\Facades;
* @method static void log(mixed $level, string $message, array $context = [])
* @method static void write(string $level, \Illuminate\Contracts\Support\Arrayable|\Illuminate\Contracts\Support\Jsonable|\Illuminate\Support\Stringable|array|string $message, array $context = [])
* @method static \Illuminate\Log\Logger withContext(array $context = [])
* @method static \Illuminate\Log\Logger withoutContext()
* @method static void listen(\Closure $callback)
* @method static \Psr\Log\LoggerInterface getLogger()
* @method static \Illuminate\Contracts\Events\Dispatcher getEventDispatcher()

View File

@@ -45,6 +45,7 @@ use Illuminate\Support\Testing\Fakes\QueueFake;
* @method static void assertClosurePushed(callable|int|null $callback = null)
* @method static void assertClosureNotPushed(callable|null $callback = null)
* @method static void assertNotPushed(string|\Closure $job, callable|null $callback = null)
* @method static void assertCount(int $expectedCount)
* @method static void assertNothingPushed()
* @method static \Illuminate\Support\Collection pushed(string $job, callable|null $callback = null)
* @method static bool hasPushed(string $job)

View File

@@ -39,6 +39,10 @@ namespace Illuminate\Support\Facades;
* @method static \Illuminate\Database\Connection getConnection()
* @method static \Illuminate\Database\Schema\Builder setConnection(\Illuminate\Database\Connection $connection)
* @method static void blueprintResolver(\Closure $resolver)
* @method static void macro(string $name, object|callable $macro)
* @method static void mixin(object $mixin, bool $replace = true)
* @method static bool hasMacro(string $name)
* @method static void flushMacros()
*
* @see \Illuminate\Database\Schema\Builder
*/

View File

@@ -21,6 +21,7 @@ namespace Illuminate\Support\Facades;
* @method static void ageFlashData()
* @method static array all()
* @method static array only(array $keys)
* @method static array except(array $keys)
* @method static bool exists(string|array $key)
* @method static bool missing(string|array $key)
* @method static bool has(string|array $key)

View File

@@ -9,6 +9,7 @@ namespace Illuminate\Support\Facades;
* @method static \Illuminate\Foundation\Vite useIntegrityKey(string|false $key)
* @method static \Illuminate\Foundation\Vite withEntryPoints(array $entryPoints)
* @method static \Illuminate\Foundation\Vite useManifestFilename(string $filename)
* @method static \Illuminate\Foundation\Vite createAssetPathsUsing(void $resolver)
* @method static string hotFile()
* @method static \Illuminate\Foundation\Vite useHotFile(string $path)
* @method static \Illuminate\Foundation\Vite useBuildDirectory(string $path)

View File

@@ -46,12 +46,22 @@ class Number
*
* @param int|float $number
* @param string|null $locale
* @param int|null $after
* @param int|null $until
* @return string
*/
public static function spell(int|float $number, ?string $locale = null)
public static function spell(int|float $number, ?string $locale = null, ?int $after = null, ?int $until = null)
{
static::ensureIntlExtensionIsInstalled();
if (! is_null($after) && $number <= $after) {
return static::format($number, locale: $locale);
}
if (! is_null($until) && $number >= $until) {
return static::format($number, locale: $locale);
}
$formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::SPELLOUT);
return $formatter->format($number);
@@ -134,12 +144,12 @@ class Number
}
/**
* Convert the number to its human readable equivalent.
* Convert the number to its human-readable equivalent.
*
* @param int $number
* @param int|float $number
* @param int $precision
* @param int|null $maxPrecision
* @return string
* @return bool|string
*/
public static function abbreviate(int|float $number, int $precision = 0, ?int $maxPrecision = null)
{
@@ -147,12 +157,13 @@ class Number
}
/**
* Convert the number to its human readable equivalent.
* Convert the number to its human-readable equivalent.
*
* @param int $number
* @param int|float $number
* @param int $precision
* @param int|null $maxPrecision
* @return string
* @param bool $abbreviate
* @return bool|string
*/
public static function forHumans(int|float $number, int $precision = 0, ?int $maxPrecision = null, bool $abbreviate = false)
{
@@ -172,13 +183,13 @@ class Number
}
/**
* Convert the number to its human readable equivalent.
* Convert the number to its human-readable equivalent.
*
* @param int $number
* @param int|float $number
* @param int $precision
* @param int|null $maxPrecision
* @param array $units
* @return string
* @return string|false
*/
protected static function summarize(int|float $number, int $precision = 0, ?int $maxPrecision = null, array $units = [])
{
@@ -208,6 +219,19 @@ class Number
return trim(sprintf('%s%s', static::format($number, $precision, $maxPrecision), $units[$displayExponent] ?? ''));
}
/**
* Clamp the given number between the given minimum and maximum.
*
* @param int|float $number
* @param int|float $min
* @param int|float $max
* @return int|float
*/
public static function clamp(int|float $number, int|float $min, int|float $max)
{
return min(max($number, $min), $max);
}
/**
* Execute the given callback using the given locale.
*
@@ -243,7 +267,9 @@ class Number
protected static function ensureIntlExtensionIsInstalled()
{
if (! extension_loaded('intl')) {
throw new RuntimeException('The "intl" PHP extension is required to use this method.');
$method = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'];
throw new RuntimeException('The "intl" PHP extension is required to use the ['.$method.'] method.');
}
}
}

View File

@@ -1238,7 +1238,7 @@ class Str
}
/**
* Convert the given string to title case.
* Convert the given string to proper case.
*
* @param string $value
* @return string
@@ -1249,7 +1249,7 @@ class Str
}
/**
* Convert the given string to title case for each word.
* Convert the given string to proper case for each word.
*
* @param string $value
* @return string
@@ -1267,6 +1267,52 @@ class Str
return implode(' ', array_filter(explode('_', $collapsed)));
}
/**
* Convert the given string to APA-style title case.
*
* See: https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case
*
* @param string $value
* @return string
*/
public static function apa($value)
{
$minorWords = [
'and', 'as', 'but', 'for', 'if', 'nor', 'or', 'so', 'yet', 'a', 'an',
'the', 'at', 'by', 'for', 'in', 'of', 'off', 'on', 'per', 'to', 'up', 'via',
];
$endPunctuation = ['.', '!', '?', ':', '—', ','];
$words = preg_split('/\s+/', $value, -1, PREG_SPLIT_NO_EMPTY);
$words[0] = ucfirst(mb_strtolower($words[0]));
for ($i = 0; $i < count($words); $i++) {
$lowercaseWord = mb_strtolower($words[$i]);
if (str_contains($lowercaseWord, '-')) {
$hyphenatedWords = explode('-', $lowercaseWord);
$hyphenatedWords = array_map(function ($part) use ($minorWords) {
return (in_array($part, $minorWords) && mb_strlen($part) <= 3) ? $part : ucfirst($part);
}, $hyphenatedWords);
$words[$i] = implode('-', $hyphenatedWords);
} else {
if (in_array($lowercaseWord, $minorWords) &&
mb_strlen($lowercaseWord) <= 3 &&
! ($i === 0 || in_array(mb_substr($words[$i - 1], -1), $endPunctuation))) {
$words[$i] = $lowercaseWord;
} else {
$words[$i] = ucfirst($lowercaseWord);
}
}
}
return implode(' ', $words);
}
/**
* Get the singular form of an English word.
*

View File

@@ -205,7 +205,7 @@ class Stringable implements JsonSerializable, ArrayAccess
*
* @param int $mode
* @param string $encoding
* @return string
* @return static
*/
public function convertCase(int $mode = MB_CASE_FOLD, ?string $encoding = 'UTF-8')
{
@@ -788,7 +788,7 @@ class Stringable implements JsonSerializable, ArrayAccess
}
/**
* Convert the given string to title case.
* Convert the given string to proper case.
*
* @return static
*/
@@ -797,6 +797,26 @@ class Stringable implements JsonSerializable, ArrayAccess
return new static(Str::title($this->value));
}
/**
* Convert the given string to proper case for each word.
*
* @return static
*/
public function headline()
{
return new static(Str::headline($this->value));
}
/**
* Convert the given string to APA-style title case.
*
* @return static
*/
public function apa()
{
return new static(Str::apa($this->value));
}
/**
* Transliterate a string to its closest ASCII representation.
*
@@ -809,16 +829,6 @@ class Stringable implements JsonSerializable, ArrayAccess
return new static(Str::transliterate($this->value, $unknown, $strict));
}
/**
* Convert the given string to title case for each word.
*
* @return static
*/
public function headline()
{
return new static(Str::headline($this->value));
}
/**
* Get the singular form of an English word.
*
@@ -1271,11 +1281,12 @@ class Stringable implements JsonSerializable, ArrayAccess
/**
* Get the underlying string value as an integer.
*
* @param int $base
* @return int
*/
public function toInteger()
public function toInteger($base = 10)
{
return intval($this->value);
return intval($this->value, $base);
}
/**

View File

@@ -278,6 +278,22 @@ class QueueFake extends QueueManager implements Fake, Queue
);
}
/**
* Assert the total count of jobs that were pushed.
*
* @param int $expectedCount
* @return void
*/
public function assertCount($expectedCount)
{
$actualCount = collect($this->jobs)->flatten(1)->count();
PHPUnit::assertSame(
$expectedCount, $actualCount,
"Expected {$expectedCount} jobs to be pushed, but found {$actualCount} instead."
);
}
/**
* Assert that no jobs were pushed.
*

View File

@@ -106,6 +106,18 @@ class TestView
return $this;
}
/**
* Assert that the view's rendered content is empty.
*
* @return $this
*/
public function assertViewEmpty()
{
PHPUnit::assertEmpty($this->rendered);
return $this;
}
/**
* Assert that the given string is contained within the view.
*

View File

@@ -57,7 +57,7 @@ class PotentiallyTranslatedString implements Stringable
/**
* Translates the string based on a count.
*
* @param \Countable|int|array $number
* @param \Countable|int|float|array $number
* @param array $replace
* @param string|null $locale
* @return $this

View File

@@ -183,7 +183,7 @@ class Translator extends NamespacedItemResolver implements TranslatorContract
* Get a translation according to an integer value.
*
* @param string $key
* @param \Countable|int|array $number
* @param \Countable|int|float|array $number
* @param array $replace
* @param string|null $locale
* @return string

View File

@@ -162,6 +162,10 @@ trait CompilesEchos
return call_user_func($this->echoHandlers[get_class($value)], $value);
}
if (is_iterable($value) && isset($this->echoHandlers['iterable'])) {
return call_user_func($this->echoHandlers['iterable'], $value);
}
return $value;
}
}