diff --git a/app/Filament/Resources/OrganizationResource.php b/app/Filament/Resources/OrganizationResource.php index 166cbc41..61e2a481 100644 --- a/app/Filament/Resources/OrganizationResource.php +++ b/app/Filament/Resources/OrganizationResource.php @@ -23,7 +23,7 @@ use App\Service\Import\Importers\ImportException; use App\Service\Import\Importers\ReportDto; use App\Service\Import\ImportService; use App\Service\TimezoneService; -use Brick\Money\ISOCurrencyProvider; +use Brick\Money\IsoCurrencyProvider; use Exception; use Filament\Actions\Action; use Filament\Actions\DeleteAction; @@ -89,7 +89,7 @@ class OrganizationResource extends Resource Select::make('currency') ->label('Currency') ->options(function (): array { - $currencies = ISOCurrencyProvider::getInstance()->getAvailableCurrencies(); + $currencies = IsoCurrencyProvider::getInstance()->getAvailableCurrencies(); $select = []; foreach ($currencies as $currency) { $select[$currency->getCurrencyCode()] = $currency->getName().' ('.$currency->getCurrencyCode().')'; diff --git a/app/Filament/Resources/OrganizationResource/RelationManagers/UsersRelationManager.php b/app/Filament/Resources/OrganizationResource/RelationManagers/UsersRelationManager.php index 79ffd080..e1dd02b0 100644 --- a/app/Filament/Resources/OrganizationResource/RelationManagers/UsersRelationManager.php +++ b/app/Filament/Resources/OrganizationResource/RelationManagers/UsersRelationManager.php @@ -98,9 +98,10 @@ class UsersRelationManager extends RelationManager app(BillableRateService::class)->updateTimeEntriesBillableRateForMember($member); } - if ($data['role'] !== $member->role) { + $newRole = $data['role'] instanceof Role ? $data['role'] : Role::from($data['role']); + if ($newRole->value !== $member->role) { try { - app(MemberService::class)->changeRole($member, $organization, ($data['role'] instanceof Role ? $data['role'] : Role::from($data['role'])), true); + app(MemberService::class)->changeRole($member, $organization, $newRole, true); } catch (ApiException $exception) { Notification::make() ->danger() diff --git a/app/Filament/Resources/UserResource.php b/app/Filament/Resources/UserResource.php index dc4bfd4a..72965b99 100644 --- a/app/Filament/Resources/UserResource.php +++ b/app/Filament/Resources/UserResource.php @@ -16,7 +16,7 @@ use App\Models\User; use App\Service\DeletionService; use App\Service\TimezoneService; use App\Service\UserService; -use Brick\Money\ISOCurrencyProvider; +use Brick\Money\IsoCurrencyProvider; use Exception; use Filament\Actions\BulkAction; use Filament\Actions\DeleteAction; @@ -115,7 +115,7 @@ class UserResource extends Resource Select::make('currency') ->label('Currency (Personal Organization)') ->options(function (): array { - $currencies = ISOCurrencyProvider::getInstance()->getAvailableCurrencies(); + $currencies = IsoCurrencyProvider::getInstance()->getAvailableCurrencies(); $select = []; foreach ($currencies as $currency) { $select[$currency->getCurrencyCode()] = $currency->getName().' ('.$currency->getCurrencyCode().')'; diff --git a/app/Filament/Resources/UserResource/RelationManagers/OrganizationsRelationManager.php b/app/Filament/Resources/UserResource/RelationManagers/OrganizationsRelationManager.php index 1b80804d..549f7d51 100644 --- a/app/Filament/Resources/UserResource/RelationManagers/OrganizationsRelationManager.php +++ b/app/Filament/Resources/UserResource/RelationManagers/OrganizationsRelationManager.php @@ -59,9 +59,10 @@ class OrganizationsRelationManager extends RelationManager /** @var Member $member */ $member = $record->getRelation('membership'); - if ($data['role'] !== $member->role) { + $newRole = $data['role'] instanceof Role ? $data['role'] : Role::from($data['role']); + if ($newRole->value !== $member->role) { try { - app(MemberService::class)->changeRole($member, $record, ($data['role'] instanceof Role ? $data['role'] : Role::from($data['role'])), true); + app(MemberService::class)->changeRole($member, $record, $newRole, true); } catch (ApiException $exception) { Notification::make() ->danger() diff --git a/app/Http/Controllers/Api/V1/CurrencyController.php b/app/Http/Controllers/Api/V1/CurrencyController.php index 46f05157..2836034d 100644 --- a/app/Http/Controllers/Api/V1/CurrencyController.php +++ b/app/Http/Controllers/Api/V1/CurrencyController.php @@ -7,7 +7,7 @@ namespace App\Http\Controllers\Api\V1; use App\Http\Controllers\Controller; use App\Service\CurrencyService; use Brick\Money\Currency; -use Brick\Money\ISOCurrencyProvider; +use Brick\Money\IsoCurrencyProvider; use Illuminate\Http\JsonResponse; class CurrencyController extends Controller @@ -29,7 +29,7 @@ class CurrencyController extends Controller 'name' => $currency->getName(), 'symbol' => $currencyService->getCurrencySymbol($currency->getCurrencyCode()), ], - ISOCurrencyProvider::getInstance()->getAvailableCurrencies() + IsoCurrencyProvider::getInstance()->getAvailableCurrencies() )); return response()->json($currencies); diff --git a/app/Http/Controllers/Api/V1/TimeEntryController.php b/app/Http/Controllers/Api/V1/TimeEntryController.php index 5a1cd2f3..383e9ab9 100644 --- a/app/Http/Controllers/Api/V1/TimeEntryController.php +++ b/app/Http/Controllers/Api/V1/TimeEntryController.php @@ -191,8 +191,9 @@ class TimeEntryController extends Controller $roundingMinutes = $canAccessPremiumFeatures ? $request->getRoundingMinutes() : null; if ($roundingType !== null && $roundingMinutes !== null) { $select = array_diff($select, ['start', 'end']); - $select[] = DB::raw(app(TimeEntryService::class)->getStartSelectRawForRounding($roundingType, $roundingMinutes).' as start'); - $select[] = DB::raw(app(TimeEntryService::class)->getEndSelectRawForRounding($roundingType, $roundingMinutes).' as end'); + // These SQL fragments are generated from a rounding enum and validated minute count. + $select[] = DB::raw(app(TimeEntryService::class)->getStartSelectRawForRounding($roundingType, $roundingMinutes).' as start'); // @phpstan-ignore argument.type + $select[] = DB::raw(app(TimeEntryService::class)->getEndSelectRawForRounding($roundingType, $roundingMinutes).' as end'); // @phpstan-ignore argument.type } $timeEntriesQuery = TimeEntry::query() ->whereBelongsTo($organization, 'organization') diff --git a/app/Http/Controllers/Web/OrganizationController.php b/app/Http/Controllers/Web/OrganizationController.php index 7969eb82..0222f9ff 100644 --- a/app/Http/Controllers/Web/OrganizationController.php +++ b/app/Http/Controllers/Web/OrganizationController.php @@ -6,7 +6,7 @@ namespace App\Http\Controllers\Web; use App\Models\Organization; use Brick\Money\Currency; -use Brick\Money\ISOCurrencyProvider; +use Brick\Money\IsoCurrencyProvider; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Str; @@ -53,7 +53,7 @@ class OrganizationController extends Controller ], 'currencies' => array_map(function (Currency $currency): string { return $currency->getName(); - }, ISOCurrencyProvider::getInstance()->getAvailableCurrencies()), + }, IsoCurrencyProvider::getInstance()->getAvailableCurrencies()), 'permissions' => [ 'canDeleteTeam' => $this->hasPermission($organization, 'organizations:delete'), 'canUpdateTeam' => $this->hasPermission($organization, 'organizations:update'), diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 7ffd7f2f..981e53de 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -11,6 +11,7 @@ use App\Http\Middleware\EnsureEmailIsVerified; use App\Http\Middleware\ForceHttps; use App\Http\Middleware\ForceJsonResponse; use App\Http\Middleware\HandleInertiaRequests; +use App\Http\Middleware\PreventRequestForgery; use App\Http\Middleware\PreventRequestsDuringMaintenance; use App\Http\Middleware\RedirectIfAuthenticated; use App\Http\Middleware\ShareInertiaData; @@ -18,7 +19,6 @@ use App\Http\Middleware\TrimStrings; use App\Http\Middleware\TrustHosts; use App\Http\Middleware\TrustProxies; use App\Http\Middleware\ValidateSignature; -use App\Http\Middleware\VerifyCsrfToken; use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth; use Illuminate\Auth\Middleware\Authorize; use Illuminate\Auth\Middleware\RequirePassword; @@ -68,7 +68,7 @@ class Kernel extends HttpKernel AddQueuedCookiesToResponse::class, StartSession::class, ShareErrorsFromSession::class, - VerifyCsrfToken::class, + PreventRequestForgery::class, SubstituteBindings::class, HandleInertiaRequests::class, ShareInertiaData::class, diff --git a/app/Http/Middleware/AuthenticateFilamentPanel.php b/app/Http/Middleware/AuthenticateFilamentPanel.php new file mode 100644 index 00000000..e9a69e7f --- /dev/null +++ b/app/Http/Middleware/AuthenticateFilamentPanel.php @@ -0,0 +1,22 @@ +expectsJson() ? null : route('login')); + } +} diff --git a/app/Http/Middleware/PreventRequestForgery.php b/app/Http/Middleware/PreventRequestForgery.php new file mode 100644 index 00000000..49e81d9a --- /dev/null +++ b/app/Http/Middleware/PreventRequestForgery.php @@ -0,0 +1,19 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php deleted file mode 100644 index c4527eef..00000000 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ - protected $except = [ - // - ]; -} diff --git a/app/Models/Organization.php b/app/Models/Organization.php index 747ceb56..06d13c3e 100644 --- a/app/Models/Organization.php +++ b/app/Models/Organization.php @@ -19,7 +19,6 @@ use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Support\Carbon; use Illuminate\Support\Str; use OwenIt\Auditing\Contracts\Auditable as AuditableContract; @@ -100,7 +99,7 @@ class Organization extends Model implements AuditableContract /** * Get all the users that belong to the team. * - * @return BelongsToMany + * @return BelongsToMany */ public function users(): BelongsToMany { @@ -133,7 +132,7 @@ class Organization extends Model implements AuditableContract } /** - * @return BelongsToMany + * @return BelongsToMany */ public function realUsers(): BelongsToMany { diff --git a/app/Models/User.php b/app/Models/User.php index 9357dba9..3ba34234 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -20,7 +20,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Carbon; @@ -175,7 +174,7 @@ class User extends Authenticatable implements AuditableContract, FilamentUser, M } /** - * @return BelongsToMany + * @return BelongsToMany */ public function organizations(): BelongsToMany { @@ -190,7 +189,7 @@ class User extends Authenticatable implements AuditableContract, FilamentUser, M } /** - * @return BelongsToMany + * @return BelongsToMany */ public function ownedOrganizations(): BelongsToMany { diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index 26956a08..cd5d9eb6 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -9,7 +9,7 @@ use App\Filament\Widgets\ServerOverview; use App\Filament\Widgets\TimeEntriesCreated; use App\Filament\Widgets\TimeEntriesImported; use App\Filament\Widgets\UserRegistrations; -use Filament\Http\Middleware\Authenticate; +use App\Http\Middleware\AuthenticateFilamentPanel; use Filament\Http\Middleware\DisableBladeIconComponents; use Filament\Http\Middleware\DispatchServingFilamentEvent; use Filament\Navigation\NavigationGroup; @@ -19,7 +19,7 @@ use Filament\PanelProvider; use Filament\Support\Colors\Color; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; -use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; +use Illuminate\Foundation\Http\Middleware\PreventRequestForgery; use Illuminate\Routing\Middleware\SubstituteBindings; use Illuminate\Session\Middleware\AuthenticateSession; use Illuminate\Session\Middleware\StartSession; @@ -71,6 +71,9 @@ class AdminPanelProvider extends PanelProvider NavigationGroup::make() ->label('Auth') ->collapsed(), + NavigationGroup::make() + ->label('Self Hosting') + ->collapsed(), ]) ->middleware([ EncryptCookies::class, @@ -78,13 +81,13 @@ class AdminPanelProvider extends PanelProvider StartSession::class, AuthenticateSession::class, ShareErrorsFromSession::class, - VerifyCsrfToken::class, + PreventRequestForgery::class, SubstituteBindings::class, DisableBladeIconComponents::class, DispatchServingFilamentEvent::class, ]) ->authMiddleware([ - Authenticate::class, + AuthenticateFilamentPanel::class, ]); $modules = Module::allEnabled(); diff --git a/app/Rules/CurrencyRule.php b/app/Rules/CurrencyRule.php index a6cd850d..303f0411 100644 --- a/app/Rules/CurrencyRule.php +++ b/app/Rules/CurrencyRule.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace App\Rules; -use Brick\Money\ISOCurrencyProvider; +use Brick\Money\IsoCurrencyProvider; use Closure; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Translation\PotentiallyTranslatedString; @@ -24,7 +24,7 @@ class CurrencyRule implements ValidationRule return; } - $currencies = ISOCurrencyProvider::getInstance()->getAvailableCurrencies(); + $currencies = IsoCurrencyProvider::getInstance()->getAvailableCurrencies(); if (array_key_exists($value, $currencies)) { return; } diff --git a/app/Service/CurrencyService.php b/app/Service/CurrencyService.php index 2e353f42..77954719 100644 --- a/app/Service/CurrencyService.php +++ b/app/Service/CurrencyService.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace App\Service; -use Brick\Money\ISOCurrencyProvider; +use Brick\Money\IsoCurrencyProvider; use Brick\Money\Money; class CurrencyService @@ -378,7 +378,7 @@ class CurrencyService public function getRandomCurrencyCode(): string { - $currencies = ISOCurrencyProvider::getInstance()->getAvailableCurrencies(); + $currencies = IsoCurrencyProvider::getInstance()->getAvailableCurrencies(); $currencyCodes = array_keys($currencies); return $currencyCodes[array_rand($currencyCodes)]; diff --git a/app/Service/DashboardService.php b/app/Service/DashboardService.php index 58bcd1c8..9161cb9d 100644 --- a/app/Service/DashboardService.php +++ b/app/Service/DashboardService.php @@ -152,12 +152,13 @@ class DashboardService $possibleDays = $this->lastDays($days, $timezone); + // The SQL fragment contains only a timezone offset calculated as an integer. $query = TimeEntry::query() - ->select(DB::raw('DATE('.$dateWithTimeZone.') as date, round(sum(extract(epoch from (coalesce("end", now()) - start)))) as aggregate')) + ->select(DB::raw('DATE('.$dateWithTimeZone.') as date, round(sum(extract(epoch from (coalesce("end", now()) - start)))) as aggregate')) // @phpstan-ignore argument.type ->where('user_id', '=', $user->getKey()) ->where('organization_id', '=', $organization->getKey()) ->workTime() - ->groupBy(DB::raw('DATE('.$dateWithTimeZone.')')) + ->groupBy(DB::raw('DATE('.$dateWithTimeZone.')')) // @phpstan-ignore argument.type ->orderBy('date'); $query = $this->constrainDateByPossibleDates($query, $possibleDays, $timezone); @@ -194,12 +195,13 @@ class DashboardService } $possibleDays = $this->daysOfThisWeek($timezone, $user->week_start); + // The SQL fragment contains only a timezone offset calculated as an integer. $query = TimeEntry::query() - ->select(DB::raw('DATE('.$dateWithTimeZone.') as date, round(sum(extract(epoch from (coalesce("end", now()) - start)))) as aggregate')) + ->select(DB::raw('DATE('.$dateWithTimeZone.') as date, round(sum(extract(epoch from (coalesce("end", now()) - start)))) as aggregate')) // @phpstan-ignore argument.type ->where('user_id', '=', $user->getKey()) ->where('organization_id', '=', $organization->getKey()) ->workTime() - ->groupBy(DB::raw('DATE('.$dateWithTimeZone.')')) + ->groupBy(DB::raw('DATE('.$dateWithTimeZone.')')) // @phpstan-ignore argument.type ->orderBy('date'); $query = $this->constrainDateByPossibleDates($query, $possibleDays, $timezone); diff --git a/app/Service/TimeEntryAggregationService.php b/app/Service/TimeEntryAggregationService.php index 5a160ee6..d2597cf3 100644 --- a/app/Service/TimeEntryAggregationService.php +++ b/app/Service/TimeEntryAggregationService.php @@ -75,8 +75,9 @@ class TimeEntryAggregationService $startRawSelect = app(TimeEntryService::class)->getStartSelectRawForRounding($roundingType, $roundingMinutes); $endRawSelect = app(TimeEntryService::class)->getEndSelectRawForRounding($roundingType, $roundingMinutes); + // SQL fragments come from enum cases, integer offsets, and formatted dates. $timeEntriesQuery->selectRaw( - ($group1Select !== null ? $group1Select.' as group_1,' : ''). + ($group1Select !== null ? $group1Select.' as group_1,' : ''). // @phpstan-ignore argument.type ($group2Select !== null ? $group2Select.' as group_2,' : ''). ' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')))) as aggregate,'. ' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')) * (coalesce(billable_rate, 0)::float/60/60))) as cost' @@ -105,7 +106,7 @@ class TimeEntryAggregationService $baseTotalsPerGroup1Query = $baseTotalsQuery->clone(); $baseTotalsPerGroup1 = $baseTotalsPerGroup1Query ->selectRaw( - $group1Select.' as group_1,'. + $group1Select.' as group_1,'. // @phpstan-ignore argument.type ' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')))) as aggregate,'. ' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')) * (coalesce(billable_rate, 0)::float/60/60))) as cost' ) @@ -170,7 +171,7 @@ class TimeEntryAggregationService // Reset selects and ordering on the cloned base query $baseTotals = $baseTotalsQuery ->selectRaw( - ' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')))) as aggregate,'. + ' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')))) as aggregate,'. // @phpstan-ignore argument.type ' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')) * (coalesce(billable_rate, 0)::float/60/60))) as cost' ) ->first(); diff --git a/composer.json b/composer.json index 4ec8c6f0..3a5373a7 100644 --- a/composer.json +++ b/composer.json @@ -7,22 +7,22 @@ "require": { "php": "8.3.*", "ext-zip": "*", - "brick/money": "^0.11.2", + "brick/money": "^0.15.1", "datomatic/laravel-enum-helper": "^2.0.0", "dedoc/scramble": "^0.13.43", "filament/filament": "^5.0", "flowframe/laravel-trend": "^0.5.0", "gotenberg/gotenberg-php": "^2.8", - "guzzlehttp/guzzle": "^7.2", + "guzzlehttp/guzzle": "^8.2", "inertiajs/inertia-laravel": "^3.3.4", "korridor/laravel-computed-attributes": "^3.1", "korridor/laravel-has-many-sync": "^3.1", "korridor/laravel-model-validation-rules": "^3.0", "laravel/fortify": "^1.37", - "laravel/framework": "^12.19.3", + "laravel/framework": "^13.0", "laravel/octane": "^2.3", "laravel/passport": "^13.0.5", - "laravel/tinker": "^2.8", + "laravel/tinker": "^3.0", "league/csv": "^9.16.0", "league/flysystem-aws-s3-v3": "^3.0", "league/iso3166": "^4.3", diff --git a/composer.lock b/composer.lock index 270ffd87..f568b61f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c428b96257b9205de05fa5fd64f3f658", + "content-hash": "872c4458fb79e6e493b1c704973ca31a", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -127,16 +127,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.395.4", + "version": "3.395.7", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "0756c511a913b57ab5415db6136d8c0c250ace15" + "reference": "575425a1c7c1dabb808badc7995e67874cba102a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0756c511a913b57ab5415db6136d8c0c250ace15", - "reference": "0756c511a913b57ab5415db6136d8c0c250ace15", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/575425a1c7c1dabb808badc7995e67874cba102a", + "reference": "575425a1c7c1dabb808badc7995e67874cba102a", "shasum": "" }, "require": { @@ -218,9 +218,9 @@ "support": { "forum": "https://github.com/aws/aws-sdk-php/discussions", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.395.4" + "source": "https://github.com/aws/aws-sdk-php/tree/3.395.7" }, - "time": "2026-09-16T18:39:53+00:00" + "time": "2026-09-21T18:22:34+00:00" }, { "name": "bacon/bacon-qr-code", @@ -429,23 +429,22 @@ }, { "name": "brick/math", - "version": "0.14.8", + "version": "0.19.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" + "reference": "a89bc96a7cf3d7b59e725afe57ccb95eb03cf6ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", - "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", + "url": "https://api.github.com/repos/brick/math/zipball/a89bc96a7cf3d7b59e725afe57ccb95eb03cf6ce", + "reference": "a89bc96a7cf3d7b59e725afe57ccb95eb03cf6ce", "shasum": "" }, "require": { "php": "^8.2" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.2", "phpstan/phpstan": "2.1.22", "phpunit/phpunit": "^11.5" }, @@ -477,7 +476,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.8" + "source": "https://github.com/brick/math/tree/0.19.1" }, "funding": [ { @@ -485,36 +484,36 @@ "type": "github" } ], - "time": "2026-02-10T14:33:43+00:00" + "time": "2026-08-08T23:03:16+00:00" }, { "name": "brick/money", - "version": "0.11.2", + "version": "0.15.1", "source": { "type": "git", "url": "https://github.com/brick/money.git", - "reference": "57fb799337d9c532da7f7c7def52b926bfcf0917" + "reference": "1d07d80d8f1c04ee0efa8b8c2bccf129e7805646" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/money/zipball/57fb799337d9c532da7f7c7def52b926bfcf0917", - "reference": "57fb799337d9c532da7f7c7def52b926bfcf0917", + "url": "https://api.github.com/repos/brick/money/zipball/1d07d80d8f1c04ee0efa8b8c2bccf129e7805646", + "reference": "1d07d80d8f1c04ee0efa8b8c2bccf129e7805646", "shasum": "" }, "require": { - "brick/math": "~0.14.4", + "brick/math": "~0.15.0 || ~0.16.0 || ~0.17.0 || ~0.18.0 || ~0.19.0 || ~0.20.0 || ^1.0", "php": "^8.2", - "symfony/deprecation-contracts": "^2.2|^3.0" + "psr/simple-cache": "^2.0 || ^3.0" }, "require-dev": { "brick/varexporter": "0.7.0", "ext-dom": "*", "ext-pdo": "*", - "phpunit/phpunit": "^11.0", - "vimeo/psalm": "6.14.3" + "phpstan/phpstan": "2.1.40", + "phpunit/phpunit": "^11.0" }, "suggest": { - "ext-intl": "Required to format Money objects" + "ext-intl": "Required to format Money objects to a locale (needs ICU 62.1 or later)" }, "type": "library", "autoload": { @@ -534,7 +533,7 @@ ], "support": { "issues": "https://github.com/brick/money/issues", - "source": "https://github.com/brick/money/tree/0.11.2" + "source": "https://github.com/brick/money/tree/0.15.1" }, "funding": [ { @@ -542,7 +541,7 @@ "type": "github" } ], - "time": "2026-03-02T20:25:37+00:00" + "time": "2026-09-14T19:55:26+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -1449,16 +1448,16 @@ }, { "name": "danharrin/livewire-rate-limiting", - "version": "v2.2.1", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/danharrin/livewire-rate-limiting.git", - "reference": "69436717dc70e30f80d7f8fd02504c22992a9ad5" + "reference": "46c4ceaf7997c713c79afe7c507a39649f6c9d6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/danharrin/livewire-rate-limiting/zipball/69436717dc70e30f80d7f8fd02504c22992a9ad5", - "reference": "69436717dc70e30f80d7f8fd02504c22992a9ad5", + "url": "https://api.github.com/repos/danharrin/livewire-rate-limiting/zipball/46c4ceaf7997c713c79afe7c507a39649f6c9d6f", + "reference": "46c4ceaf7997c713c79afe7c507a39649f6c9d6f", "shasum": "" }, "require": { @@ -1499,7 +1498,7 @@ "type": "github" } ], - "time": "2026-08-06T08:41:51+00:00" + "time": "2026-09-18T07:38:01+00:00" }, { "name": "dasprid/enum", @@ -1663,16 +1662,16 @@ }, { "name": "dedoc/scramble", - "version": "0.13.43", + "version": "v0.13.45", "source": { "type": "git", "url": "https://github.com/dedoc/scramble.git", - "reference": "2226e6664b88b3af1e5c10693f8e63a07ffd8fc0" + "reference": "4f096c49d555c580d7ce7a978896bb3d8b28dc4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dedoc/scramble/zipball/2226e6664b88b3af1e5c10693f8e63a07ffd8fc0", - "reference": "2226e6664b88b3af1e5c10693f8e63a07ffd8fc0", + "url": "https://api.github.com/repos/dedoc/scramble/zipball/4f096c49d555c580d7ce7a978896bb3d8b28dc4d", + "reference": "4f096c49d555c580d7ce7a978896bb3d8b28dc4d", "shasum": "" }, "require": { @@ -1732,7 +1731,7 @@ ], "support": { "issues": "https://github.com/dedoc/scramble/issues", - "source": "https://github.com/dedoc/scramble/tree/0.13.43" + "source": "https://github.com/dedoc/scramble/tree/v0.13.45" }, "funding": [ { @@ -1740,7 +1739,7 @@ "type": "github" } ], - "time": "2026-09-08T18:27:41+00:00" + "time": "2026-09-18T15:47:56+00:00" }, { "name": "defuse/php-encryption", @@ -2130,27 +2129,25 @@ }, { "name": "doctrine/lexer", - "version": "3.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + "reference": "e96fe45e92a54233726014a7cc7340abf29bb14c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/e96fe45e92a54233726014a7cc7340abf29bb14c", + "reference": "e96fe45e92a54233726014a7cc7340abf29bb14c", "shasum": "" }, "require": { "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^5.21" + "doctrine/coding-standard": "^14", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^10.5.58 || ^12.5.4" }, "type": "library", "autoload": { @@ -2187,7 +2184,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.1" + "source": "https://github.com/doctrine/lexer/tree/3.0.2" }, "funding": [ { @@ -2203,7 +2200,7 @@ "type": "tidelift" } ], - "time": "2024-02-05T11:56:58+00:00" + "time": "2026-06-14T20:44:06+00:00" }, { "name": "dragonmantank/cron-expression", @@ -2338,7 +2335,7 @@ }, { "name": "filament/actions", - "version": "v5.8.2", + "version": "v5.8.4", "source": { "type": "git", "url": "https://github.com/filamentphp/actions.git", @@ -2387,16 +2384,16 @@ }, { "name": "filament/filament", - "version": "v5.8.2", + "version": "v5.8.4", "source": { "type": "git", "url": "https://github.com/filamentphp/panels.git", - "reference": "42dd202c91244526b01d94488ab16529f9e8511c" + "reference": "b5aad5862fe932c8013aa7c6cba2fd360711de0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/panels/zipball/42dd202c91244526b01d94488ab16529f9e8511c", - "reference": "42dd202c91244526b01d94488ab16529f9e8511c", + "url": "https://api.github.com/repos/filamentphp/panels/zipball/b5aad5862fe932c8013aa7c6cba2fd360711de0d", + "reference": "b5aad5862fe932c8013aa7c6cba2fd360711de0d", "shasum": "" }, "require": { @@ -2441,20 +2438,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2026-09-15T16:56:51+00:00" + "time": "2026-09-20T19:20:59+00:00" }, { "name": "filament/forms", - "version": "v5.8.2", + "version": "v5.8.4", "source": { "type": "git", "url": "https://github.com/filamentphp/forms.git", - "reference": "3e162d96ff679b05e9e61c919a0d19a3923b979b" + "reference": "d7567d2dd57222ac15700324bbaae38405196d46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/forms/zipball/3e162d96ff679b05e9e61c919a0d19a3923b979b", - "reference": "3e162d96ff679b05e9e61c919a0d19a3923b979b", + "url": "https://api.github.com/repos/filamentphp/forms/zipball/d7567d2dd57222ac15700324bbaae38405196d46", + "reference": "d7567d2dd57222ac15700324bbaae38405196d46", "shasum": "" }, "require": { @@ -2491,20 +2488,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2026-09-15T16:59:23+00:00" + "time": "2026-09-20T18:46:41+00:00" }, { "name": "filament/infolists", - "version": "v5.8.2", + "version": "v5.8.4", "source": { "type": "git", "url": "https://github.com/filamentphp/infolists.git", - "reference": "02616ec1a7ebf84ad72e82d5fe3464156d16628b" + "reference": "1046d42cf71cf96732e4b830b62f6787564d55f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/infolists/zipball/02616ec1a7ebf84ad72e82d5fe3464156d16628b", - "reference": "02616ec1a7ebf84ad72e82d5fe3464156d16628b", + "url": "https://api.github.com/repos/filamentphp/infolists/zipball/1046d42cf71cf96732e4b830b62f6787564d55f1", + "reference": "1046d42cf71cf96732e4b830b62f6787564d55f1", "shasum": "" }, "require": { @@ -2536,20 +2533,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2026-09-15T17:00:55+00:00" + "time": "2026-09-20T18:47:13+00:00" }, { "name": "filament/notifications", - "version": "v5.8.2", + "version": "v5.8.4", "source": { "type": "git", "url": "https://github.com/filamentphp/notifications.git", - "reference": "56f89733b1e220ebb1d24d5ea032947c4655dab4" + "reference": "1e4c9d6a781006550e18cd2f22d1d11b875b505b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/notifications/zipball/56f89733b1e220ebb1d24d5ea032947c4655dab4", - "reference": "56f89733b1e220ebb1d24d5ea032947c4655dab4", + "url": "https://api.github.com/repos/filamentphp/notifications/zipball/1e4c9d6a781006550e18cd2f22d1d11b875b505b", + "reference": "1e4c9d6a781006550e18cd2f22d1d11b875b505b", "shasum": "" }, "require": { @@ -2583,20 +2580,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2026-09-15T16:57:16+00:00" + "time": "2026-09-20T18:46:22+00:00" }, { "name": "filament/query-builder", - "version": "v5.8.2", + "version": "v5.8.4", "source": { "type": "git", "url": "https://github.com/filamentphp/query-builder.git", - "reference": "69402125ced6ee48d8ecd7ef895784fa46fb0423" + "reference": "f6aa1da14766f5eebbb3067061cda041c424054a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/query-builder/zipball/69402125ced6ee48d8ecd7ef895784fa46fb0423", - "reference": "69402125ced6ee48d8ecd7ef895784fa46fb0423", + "url": "https://api.github.com/repos/filamentphp/query-builder/zipball/f6aa1da14766f5eebbb3067061cda041c424054a", + "reference": "f6aa1da14766f5eebbb3067061cda041c424054a", "shasum": "" }, "require": { @@ -2629,20 +2626,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2026-09-15T16:57:17+00:00" + "time": "2026-09-20T18:46:45+00:00" }, { "name": "filament/schemas", - "version": "v5.8.2", + "version": "v5.8.4", "source": { "type": "git", "url": "https://github.com/filamentphp/schemas.git", - "reference": "ca4e860522d9149f3442468bf7eaebf04bccca67" + "reference": "59406389183fbdaccdae65a29f752fbfea4567bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/schemas/zipball/ca4e860522d9149f3442468bf7eaebf04bccca67", - "reference": "ca4e860522d9149f3442468bf7eaebf04bccca67", + "url": "https://api.github.com/repos/filamentphp/schemas/zipball/59406389183fbdaccdae65a29f752fbfea4567bd", + "reference": "59406389183fbdaccdae65a29f752fbfea4567bd", "shasum": "" }, "require": { @@ -2674,20 +2671,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2026-09-08T10:21:40+00:00" + "time": "2026-09-20T18:46:28+00:00" }, { "name": "filament/support", - "version": "v5.8.2", + "version": "v5.8.4", "source": { "type": "git", "url": "https://github.com/filamentphp/support.git", - "reference": "706ea6c6fee863b32ec3c249b2269e10cb307a78" + "reference": "c39b26bbcdad3086b1445590c5da955c7d1177d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/support/zipball/706ea6c6fee863b32ec3c249b2269e10cb307a78", - "reference": "706ea6c6fee863b32ec3c249b2269e10cb307a78", + "url": "https://api.github.com/repos/filamentphp/support/zipball/c39b26bbcdad3086b1445590c5da955c7d1177d6", + "reference": "c39b26bbcdad3086b1445590c5da955c7d1177d6", "shasum": "" }, "require": { @@ -2733,20 +2730,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2026-09-15T17:01:46+00:00" + "time": "2026-09-20T19:22:34+00:00" }, { "name": "filament/tables", - "version": "v5.8.2", + "version": "v5.8.4", "source": { "type": "git", "url": "https://github.com/filamentphp/tables.git", - "reference": "a6d1f24ce6cafcf4ef270ac90a383e3601935996" + "reference": "e07361e182118d85e02bce35653c19429f7a8349" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/tables/zipball/a6d1f24ce6cafcf4ef270ac90a383e3601935996", - "reference": "a6d1f24ce6cafcf4ef270ac90a383e3601935996", + "url": "https://api.github.com/repos/filamentphp/tables/zipball/e07361e182118d85e02bce35653c19429f7a8349", + "reference": "e07361e182118d85e02bce35653c19429f7a8349", "shasum": "" }, "require": { @@ -2779,20 +2776,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2026-09-15T17:01:34+00:00" + "time": "2026-09-20T18:52:31+00:00" }, { "name": "filament/widgets", - "version": "v5.8.2", + "version": "v5.8.4", "source": { "type": "git", "url": "https://github.com/filamentphp/widgets.git", - "reference": "20e52cc560b591d2e3cc01bf4a5e9aca8f176476" + "reference": "93f6f102b890376e68b609ba5bfce2781b5d5d33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/widgets/zipball/20e52cc560b591d2e3cc01bf4a5e9aca8f176476", - "reference": "20e52cc560b591d2e3cc01bf4a5e9aca8f176476", + "url": "https://api.github.com/repos/filamentphp/widgets/zipball/93f6f102b890376e68b609ba5bfce2781b5d5d33", + "reference": "93f6f102b890376e68b609ba5bfce2781b5d5d33", "shasum": "" }, "require": { @@ -2823,20 +2820,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2026-09-07T18:43:09+00:00" + "time": "2026-09-20T18:53:34+00:00" }, { "name": "firebase/php-jwt", - "version": "v7.1.1", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/googleapis/php-jwt.git", - "reference": "9bc93bd7e3ee5bead4cd23c365ec12f3c1fb0a6a" + "reference": "f502cdbf279cd7532060b041f7c22a05208c0b93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/9bc93bd7e3ee5bead4cd23c365ec12f3c1fb0a6a", - "reference": "9bc93bd7e3ee5bead4cd23c365ec12f3c1fb0a6a", + "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/f502cdbf279cd7532060b041f7c22a05208c0b93", + "reference": "f502cdbf279cd7532060b041f7c22a05208c0b93", "shasum": "" }, "require": { @@ -2846,7 +2843,7 @@ "guzzlehttp/guzzle": "^7.4||^8.0", "phpfastcache/phpfastcache": "^9.2", "phpseclib/phpseclib": "~3.0", - "phpspec/prophecy-phpunit": "^2.0", + "phpspec/prophecy-phpunit": "^2.2", "phpunit/phpunit": "^9.5", "psr/cache": "^2.0||^3.0", "psr/http-client": "^1.0", @@ -2858,6 +2855,14 @@ "phpseclib/phpseclib": "Support PS256 (RSASSA-PSS) signatures" }, "type": "library", + "extra": { + "component": { + "id": "jwt", + "path": "Jwt", + "entry": "README.md", + "target": "googleapis/php-jwt.git" + } + }, "autoload": { "psr-4": { "Firebase\\JWT\\": "src" @@ -2887,9 +2892,9 @@ ], "support": { "issues": "https://github.com/googleapis/php-jwt/issues", - "source": "https://github.com/googleapis/php-jwt/tree/v7.1.1" + "source": "https://github.com/googleapis/php-jwt/tree/v7.2.0" }, - "time": "2026-09-14T17:48:47+00:00" + "time": "2026-09-21T19:29:58+00:00" }, { "name": "flowframe/laravel-trend", @@ -3181,26 +3186,27 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.15.5", + "version": "8.2.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "ee80339fd9177ba44c49cdb653ff02a4d1106b9a" + "reference": "93939470950a9b11e2e84204166ef5e048c55fe4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee80339fd9177ba44c49cdb653ff02a4d1106b9a", - "reference": "ee80339fd9177ba44c49cdb653ff02a4d1106b9a", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/93939470950a9b11e2e84204166ef5e048c55fe4", + "reference": "93939470950a9b11e2e84204166ef5e048c55fe4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^2.5.3", - "guzzlehttp/psr7": "^2.13.1", - "php": "^7.2.5 || ^8.0", + "guzzlehttp/promises": "^3.0.2", + "guzzlehttp/psr7": "^3.1", + "php": "^7.4 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.25" + "psr/http-factory": "^1.0", + "symfony/polyfill-php80": "^1.25", + "symfony/polyfill-php82": "^1.27" }, "provide": { "psr/http-client-implementation": "1.0" @@ -3208,10 +3214,10 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "guzzle/client-integration-tests": "3.0.3", - "guzzlehttp/test-server": "^0.7", + "guzzle/client-integration-tests": "4.0.1", + "guzzlehttp/test-server": "^1.0.1", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.52 || ^9.6.34", + "phpunit/phpunit": "^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -3227,9 +3233,6 @@ } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\": "src/" } @@ -3289,7 +3292,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.15.5" + "source": "https://github.com/guzzle/guzzle/tree/8.2.0" }, "funding": [ { @@ -3305,29 +3308,28 @@ "type": "tidelift" } ], - "time": "2026-08-24T09:21:06+00:00" + "time": "2026-09-06T13:55:09+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.5.3", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "cde49999552d185d64715fe9c1f77a2aadd2f9f1" + "reference": "42118e66a53c492effaf92bc357e931985d5c6f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/cde49999552d185d64715fe9c1f77a2aadd2f9f1", - "reference": "cde49999552d185d64715fe9c1f77a2aadd2f9f1", + "url": "https://api.github.com/repos/guzzle/promises/zipball/42118e66a53c492effaf92bc357e931985d5c6f9", + "reference": "42118e66a53c492effaf92bc357e931985d5c6f9", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0", - "symfony/deprecation-contracts": "^2.5 || ^3.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.52 || ^9.6.34" + "phpunit/phpunit": "^9.6.34" }, "type": "library", "extra": { @@ -3373,7 +3375,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.5.3" + "source": "https://github.com/guzzle/promises/tree/3.0.2" }, "funding": [ { @@ -3389,39 +3391,39 @@ "type": "tidelift" } ], - "time": "2026-08-24T09:11:28+00:00" + "time": "2026-08-24T10:00:26+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.13.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "95e7828100de18b4e269fb1703be530082d5166d" + "reference": "a3059ba1a84c9139c4ae03cf0f45bea276c97c74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/95e7828100de18b4e269fb1703be530082d5166d", - "reference": "95e7828100de18b4e269fb1703be530082d5166d", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a3059ba1a84c9139c4ae03cf0f45bea276c97c74", + "reference": "a3059ba1a84c9139c4ae03cf0f45bea276c97c74", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0", - "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.25" + "php": "^7.4 || ^8.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^2.0", + "symfony/polyfill-php80": "^1.25", + "symfony/polyfill-php82": "^1.27" }, "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" + "psr/http-factory-implementation": "1.1", + "psr/http-message-implementation": "2.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "1.1.0", "jshttp/mime-db": "1.54.0.1", - "phpunit/phpunit": "^8.5.52 || ^9.6.34" + "php-http/psr7-integration-tests": "^1.5.1", + "phpunit/phpunit": "^9.6.34" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -3492,7 +3494,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.13.1" + "source": "https://github.com/guzzle/psr7/tree/3.1.0" }, "funding": [ { @@ -3508,30 +3510,30 @@ "type": "tidelift" } ], - "time": "2026-08-24T09:13:11+00:00" + "time": "2026-08-24T11:02:13+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.11", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "d0058dccf4299d70c3d9da3378b8908b32780368" + "reference": "7a466ad606491eb6528c717482f7cca77f1851f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/d0058dccf4299d70c3d9da3378b8908b32780368", - "reference": "d0058dccf4299d70c3d9da3378b8908b32780368", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/7a466ad606491eb6528c717482f7cca77f1851f3", + "reference": "7a466ad606491eb6528c717482f7cca77f1851f3", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0", + "php": "^7.4 || ^8.0", "symfony/polyfill-php80": "^1.25" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.52 || ^9.6.34", - "uri-template/tests": "1.0.0" + "phpunit/phpunit": "^9.6.34", + "uri-template/tests": "1.0.2" }, "type": "library", "extra": { @@ -3578,7 +3580,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.11" + "source": "https://github.com/guzzle/uri-template/tree/v2.0.1" }, "funding": [ { @@ -3594,7 +3596,7 @@ "type": "tidelift" } ], - "time": "2026-08-24T09:15:32+00:00" + "time": "2026-08-24T17:13:02+00:00" }, { "name": "inertiajs/inertia-laravel", @@ -4280,24 +4282,24 @@ }, { "name": "laravel/framework", - "version": "v12.69.2", + "version": "v13.32.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "17d034ef1e209b63a1d3a89c90ce5e89a4eb3fc5" + "reference": "cdd8b33c246719acdd118c705ce8c7ab5ef48a96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/17d034ef1e209b63a1d3a89c90ce5e89a4eb3fc5", - "reference": "17d034ef1e209b63a1d3a89c90ce5e89a4eb3fc5", + "url": "https://api.github.com/repos/laravel/framework/zipball/cdd8b33c246719acdd118c705ce8c7ab5ef48a96", + "reference": "cdd8b33c246719acdd118c705ce8c7ab5ef48a96", "shasum": "" }, "require": { - "brick/math": "^0.11|^0.12|^0.13|^0.14", + "brick/math": "^0.14.2 || ^0.15 || ^0.16 || ^0.17 || ^0.18 || ^0.19", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", "dragonmantank/cron-expression": "^3.4", - "egulias/email-validator": "^3.2.1|^4.0", + "egulias/email-validator": "^4.0", "ext-ctype": "*", "ext-filter": "*", "ext-hash": "*", @@ -4306,36 +4308,39 @@ "ext-session": "*", "ext-tokenizer": "*", "fruitcake/php-cors": "^1.3", - "guzzlehttp/guzzle": "^7.8.2", - "guzzlehttp/uri-template": "^1.0", - "laravel/prompts": "^0.3.0", - "laravel/serializable-closure": "^1.3|^2.0", + "guzzlehttp/guzzle": "^7.8.2 || ^8.0", + "guzzlehttp/promises": "^2.0.3 || ^3.0", + "guzzlehttp/psr7": "^2.9 || ^3.0", + "guzzlehttp/uri-template": "^1.0 || ^2.0", + "laravel/prompts": "^0.3.11", + "laravel/serializable-closure": "^2.0.10", "league/commonmark": "^2.8.1", "league/flysystem": "^3.25.1", "league/flysystem-local": "^3.25.1", "league/uri": "^7.5.1", - "monolog/monolog": "^3.0", + "monolog/monolog": "^3.10", "nesbot/carbon": "^3.8.4", "nunomaduro/termwind": "^2.0", - "php": "^8.2", - "psr/container": "^1.1.1|^2.0.1", - "psr/log": "^1.0|^2.0|^3.0", - "psr/simple-cache": "^1.0|^2.0|^3.0", + "php": "^8.3", + "psr/container": "^1.1.1 || ^2.0.1", + "psr/http-message": "^1.0 || ^2.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0", "ramsey/uuid": "^4.7", - "symfony/console": "^7.2.0", - "symfony/error-handler": "^7.2.0", - "symfony/finder": "^7.2.0", - "symfony/http-foundation": "^7.2.0", - "symfony/http-kernel": "^7.2.0", - "symfony/mailer": "^7.2.0", - "symfony/mime": "^7.2.0", - "symfony/polyfill-php83": "^1.33", - "symfony/polyfill-php84": "^1.34", - "symfony/polyfill-php85": "^1.34", - "symfony/process": "^7.2.0", - "symfony/routing": "^7.2.0", - "symfony/uid": "^7.2.0", - "symfony/var-dumper": "^7.2.0", + "symfony/console": "^7.4.0 || ^8.0.0", + "symfony/error-handler": "^7.4.0 || ^8.0.0", + "symfony/finder": "^7.4.0 || ^8.0.0", + "symfony/http-foundation": "^7.4.0 || ^8.0.0", + "symfony/http-kernel": "^7.4.0 || ^8.0.0", + "symfony/mailer": "^7.4.0 || ^8.0.0", + "symfony/mime": "^7.4.0 || ^8.0.0", + "symfony/polyfill-php84": "^1.36", + "symfony/polyfill-php85": "^1.36", + "symfony/polyfill-php86": "^1.36", + "symfony/process": "^7.4.5 || ^8.0.5", + "symfony/routing": "^7.4.0 || ^8.0.0", + "symfony/uid": "^7.4.0 || ^8.0.0", + "symfony/var-dumper": "^7.4.0 || ^8.0.0", "tijsverkoyen/css-to-inline-styles": "^2.2.5", "vlucas/phpdotenv": "^5.6.1", "voku/portable-ascii": "^2.0.2" @@ -4344,9 +4349,9 @@ "tightenco/collect": "<5.5.33" }, "provide": { - "psr/container-implementation": "1.1|2.0", - "psr/log-implementation": "1.0|2.0|3.0", - "psr/simple-cache-implementation": "1.0|2.0|3.0" + "psr/container-implementation": "1.1 || 2.0", + "psr/log-implementation": "1.0 || 2.0 || 3.0", + "psr/simple-cache-implementation": "1.0 || 2.0 || 3.0" }, "replace": { "illuminate/auth": "self.version", @@ -4367,6 +4372,7 @@ "illuminate/filesystem": "self.version", "illuminate/hashing": "self.version", "illuminate/http": "self.version", + "illuminate/image": "self.version", "illuminate/json-schema": "self.version", "illuminate/log": "self.version", "illuminate/macroable": "self.version", @@ -4392,8 +4398,7 @@ "aws/aws-sdk-php": "^3.322.9", "ext-gmp": "*", "fakerphp/faker": "^1.24", - "guzzlehttp/promises": "^2.0.3", - "guzzlehttp/psr7": "^2.4", + "intervention/image": "^4.0", "laravel/pint": "^1.18", "league/flysystem-aws-s3-v3": "^3.25.1", "league/flysystem-ftp": "^3.25.1", @@ -4402,22 +4407,25 @@ "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", "opis/json-schema": "^2.4.1", - "orchestra/testbench-core": "^10.9.0", - "pda/pheanstalk": "^5.0.6|^7.0.0", + "orchestra/testbench-core": "^11.0.0", + "pda/pheanstalk": "^7.0.0 || ^8.0.0", "php-http/discovery": "^1.15", - "phpstan/phpstan": "^2.1.41", - "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", - "predis/predis": "^2.3|^3.0", - "resend/resend-php": "^0.10.0|^1.0", - "symfony/cache": "^7.2.0", - "symfony/http-client": "^7.2.0", - "symfony/psr-http-message-bridge": "^7.2.0", - "symfony/translation": "^7.2.0" + "phpstan/phpstan": "^2.2.14", + "phpunit/phpunit": "^11.5.50 || ^12.5.8 || ^13.0.3", + "predis/predis": "^2.3 || ^3.0", + "rector/rector": "2.6.3", + "resend/resend-php": "^1.0", + "symfony/cache": "^7.4.0 || ^8.0.0", + "symfony/http-client": "^7.4.0 || ^8.0.0", + "symfony/mercure": "^0.8.0", + "symfony/psr-http-message-bridge": "^7.4.0 || ^8.0.0", + "symfony/translation": "^7.4.0 || ^8.0.0", + "web-token/jwt-library": "^4.1" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", - "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", + "brianium/paratest": "Required to run tests in parallel (^7.0 || ^8.0).", "ext-apcu": "Required to use the APC cache driver.", "ext-fileinfo": "Required to use the Filesystem class.", "ext-ftp": "Required to use the Flysystem FTP driver.", @@ -4426,9 +4434,10 @@ "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", "ext-pdo": "Required to use all database features.", "ext-posix": "Required to use all features of the queue worker.", - "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0 || ^5.0 || ^6.0).", "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "intervention/image": "Required to use the image processing features (^4.0).", "laravel/tinker": "Required to use the tinker console command (^2.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", @@ -4436,24 +4445,26 @@ "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", "mockery/mockery": "Required to use mocking (^1.6).", - "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^7.0 || ^8.0).", "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", - "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", - "predis/predis": "Required to use the predis connector (^2.3|^3.0).", - "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", - "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", - "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", - "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", - "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." + "phpunit/phpunit": "Required to use assertions and run tests (^11.5.50 || ^12.5.8 || ^13.0.3).", + "predis/predis": "Required to use the predis connector (^2.3 || ^3.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0 || ^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0 || ^1.0).", + "spatie/fork": "Required to use the 'fork' concurrency driver (^1.2).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.4 || ^8.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.4 || ^8.0).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.4 || ^8.0).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.4 || ^8.0).", + "symfony/mercure": "Required to use the Mercure broadcast driver (^0.8).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.4 || ^8.0).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.4 || ^8.0).", + "web-token/jwt-library": "Required to sign Mercure JWTs and use end-to-end encrypted channels (^4.1)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "12.x-dev" + "dev-master": "13.0.x-dev" } }, "autoload": { @@ -4498,7 +4509,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-09-08T14:29:09+00:00" + "time": "2026-09-15T14:55:30+00:00" }, { "name": "laravel/octane", @@ -4854,33 +4865,33 @@ }, { "name": "laravel/tinker", - "version": "v2.11.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741" + "reference": "4faba77764bd33411735936acdf30446d058c78b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/c9f80cc835649b5c1842898fb043f8cc098dd741", - "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741", + "url": "https://api.github.com/repos/laravel/tinker/zipball/4faba77764bd33411735936acdf30446d058c78b", + "reference": "4faba77764bd33411735936acdf30446d058c78b", "shasum": "" }, "require": { - "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "php": "^7.2.5|^8.0", - "psy/psysh": "^0.11.1|^0.12.0", - "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0|^8.0" + "illuminate/console": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "php": "^8.1", + "psy/psysh": "^0.12.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0|^8.0" }, "require-dev": { "mockery/mockery": "~1.3.3|^1.4.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" + "phpunit/phpunit": "^10.5|^11.5" }, "suggest": { - "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." + "illuminate/database": "The Illuminate Database package (^8.0|^9.0|^10.0|^11.0|^12.0|^13.0)." }, "type": "library", "extra": { @@ -4888,6 +4899,9 @@ "providers": [ "Laravel\\Tinker\\TinkerServiceProvider" ] + }, + "branch-alias": { + "dev-master": "3.x-dev" } }, "autoload": { @@ -4914,9 +4928,9 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.11.1" + "source": "https://github.com/laravel/tinker/tree/v3.0.2" }, - "time": "2026-02-06T14:12:35+00:00" + "time": "2026-03-17T14:54:13+00:00" }, { "name": "lcobucci/jwt", @@ -4993,16 +5007,16 @@ }, { "name": "league/commonmark", - "version": "2.10.1", + "version": "2.10.3", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "9d489ab67a02960fd8ffe624d93f751daf95439e" + "reference": "6efbd9c472b91db0a3350fcd601c8332c2382e1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/9d489ab67a02960fd8ffe624d93f751daf95439e", - "reference": "9d489ab67a02960fd8ffe624d93f751daf95439e", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/6efbd9c472b91db0a3350fcd601c8332c2382e1f", + "reference": "6efbd9c472b91db0a3350fcd601c8332c2382e1f", "shasum": "" }, "require": { @@ -5096,7 +5110,7 @@ "type": "tidelift" } ], - "time": "2026-09-07T13:44:26+00:00" + "time": "2026-09-21T13:07:34+00:00" }, { "name": "league/config", @@ -6005,16 +6019,16 @@ }, { "name": "livewire/livewire", - "version": "v4.4.5", + "version": "v4.4.6", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "10aa0b5ee44c99b5bce0f78ad265bbcf0e74abdc" + "reference": "c6b7db7a92103ac2738e8fdc926cecc93eb67853" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/10aa0b5ee44c99b5bce0f78ad265bbcf0e74abdc", - "reference": "10aa0b5ee44c99b5bce0f78ad265bbcf0e74abdc", + "url": "https://api.github.com/repos/livewire/livewire/zipball/c6b7db7a92103ac2738e8fdc926cecc93eb67853", + "reference": "c6b7db7a92103ac2738e8fdc926cecc93eb67853", "shasum": "" }, "require": { @@ -6069,7 +6083,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v4.4.5" + "source": "https://github.com/livewire/livewire/tree/v4.4.6" }, "funding": [ { @@ -6077,7 +6091,7 @@ "type": "github" } ], - "time": "2026-09-14T19:27:16+00:00" + "time": "2026-09-21T18:51:23+00:00" }, { "name": "maatwebsite/excel", @@ -7983,16 +7997,16 @@ }, { "name": "phpoffice/phpspreadsheet", - "version": "5.9.0", + "version": "5.10.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "05e99ebf61238a70227b4d9cc02d0030d34f6339" + "reference": "eb18727acf6b1f4cc67145a52ab04f9fd4c28d53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/05e99ebf61238a70227b4d9cc02d0030d34f6339", - "reference": "05e99ebf61238a70227b4d9cc02d0030d34f6339", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/eb18727acf6b1f4cc67145a52ab04f9fd4c28d53", + "reference": "eb18727acf6b1f4cc67145a52ab04f9fd4c28d53", "shasum": "" }, "require": { @@ -8021,6 +8035,7 @@ "dealerdirect/phpcodesniffer-composer-installer": "dev-main", "dompdf/dompdf": "^2.0 || ^3.0", "ext-intl": "*", + "ext-openssl": "*", "friendsofphp/php-cs-fixer": "^3.2", "mitoteam/jpgraph": "^10.5", "mpdf/mpdf": "^8.1.1", @@ -8030,11 +8045,12 @@ "phpstan/phpstan-phpunit": "^1.0 || ^2.0", "phpunit/phpunit": "^10.5 || ^11.0", "squizlabs/php_codesniffer": "^3.7", - "tecnickcom/tcpdf": "^6.5" + "tecnickcom/tcpdf": ">=6.8.0 <7.0.0" }, "suggest": { "dompdf/dompdf": "Option for rendering PDF with PDF Writer", "ext-intl": "PHP Internationalization Functions, required for NumberFormat Wizard and StringHelper::setLocale()", + "ext-openssl": "Handline Agile-encrypted Xlsx spreadsheets", "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", "mpdf/mpdf": "Option for rendering PDF with PDF Writer", "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" @@ -8086,9 +8102,9 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.9.0" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.10.0" }, - "time": "2026-07-12T19:17:39+00:00" + "time": "2026-09-17T03:54:50+00:00" }, { "name": "phpoption/phpoption", @@ -9179,50 +9195,6 @@ ], "time": "2026-08-12T16:39:12+00:00" }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, { "name": "ramsey/collection", "version": "2.1.1", @@ -10272,21 +10244,21 @@ }, { "name": "staudenmeir/eloquent-has-many-deep-contracts", - "version": "v1.3", + "version": "v1.4", "source": { "type": "git", "url": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts.git", - "reference": "37ce351e4db919b3af606bc8ca0e62e2e4939cde" + "reference": "6eadbd6904c057e9439f86f8de185f56693fa537" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep-contracts/zipball/37ce351e4db919b3af606bc8ca0e62e2e4939cde", - "reference": "37ce351e4db919b3af606bc8ca0e62e2e4939cde", + "url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep-contracts/zipball/6eadbd6904c057e9439f86f8de185f56693fa537", + "reference": "6eadbd6904c057e9439f86f8de185f56693fa537", "shasum": "" }, "require": { - "illuminate/database": "^12.0", - "php": "^8.2" + "illuminate/database": "^13.0", + "php": "^8.3" }, "type": "library", "autoload": { @@ -10307,37 +10279,37 @@ "description": "Contracts for staudenmeir/eloquent-has-many-deep", "support": { "issues": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/issues", - "source": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/tree/v1.3" + "source": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/tree/v1.4" }, - "time": "2025-02-15T17:11:01+00:00" + "time": "2026-02-28T21:33:50+00:00" }, { "name": "staudenmeir/eloquent-json-relations", - "version": "v1.14.2", + "version": "v1.15", "source": { "type": "git", "url": "https://github.com/staudenmeir/eloquent-json-relations.git", - "reference": "9e39de2270edaec445362a3d20562d863b0b50de" + "reference": "e7c924e03fb84d5042e4024265246ba86d9cbdc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/staudenmeir/eloquent-json-relations/zipball/9e39de2270edaec445362a3d20562d863b0b50de", - "reference": "9e39de2270edaec445362a3d20562d863b0b50de", + "url": "https://api.github.com/repos/staudenmeir/eloquent-json-relations/zipball/e7c924e03fb84d5042e4024265246ba86d9cbdc1", + "reference": "e7c924e03fb84d5042e4024265246ba86d9cbdc1", "shasum": "" }, "require": { - "illuminate/database": "^12.0", - "php": "^8.2", - "staudenmeir/eloquent-has-many-deep-contracts": "^1.3" + "illuminate/database": "^13.0", + "php": "^8.3", + "staudenmeir/eloquent-has-many-deep-contracts": "^1.4" }, "require-dev": { "barryvdh/laravel-ide-helper": "^3.0", "larastan/larastan": "^3.0", - "laravel/framework": "^12.0", + "laravel/framework": "^13.0", "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^10.0", - "phpunit/phpunit": "^11.0", - "staudenmeir/eloquent-has-many-deep": "^1.21" + "orchestra/testbench-core": "^11.0", + "phpunit/phpunit": "^12.5", + "staudenmeir/eloquent-has-many-deep": "^1.22" }, "type": "library", "extra": { @@ -10365,7 +10337,7 @@ "description": "Laravel Eloquent relationships with JSON keys", "support": { "issues": "https://github.com/staudenmeir/eloquent-json-relations/issues", - "source": "https://github.com/staudenmeir/eloquent-json-relations/tree/v1.14.2" + "source": "https://github.com/staudenmeir/eloquent-json-relations/tree/v1.15" }, "funding": [ { @@ -10373,7 +10345,7 @@ "type": "custom" } ], - "time": "2026-03-01T11:12:21+00:00" + "time": "2026-03-01T11:30:41+00:00" }, { "name": "stechstudio/filament-impersonate", @@ -12556,6 +12528,86 @@ ], "time": "2026-07-01T12:47:55+00:00" }, + { + "name": "symfony/polyfill-php86", + "version": "v1.41.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php86.git", + "reference": "6bc356ed3d8dbfeea8f0de235e34d670704e880e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/6bc356ed3d8dbfeea8f0de235e34d670704e880e", + "reference": "6bc356ed3d8dbfeea8f0de235e34d670704e880e", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php86\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php86/tree/v1.41.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-02T13:42:24+00:00" + }, { "name": "symfony/polyfill-uuid", "version": "v1.37.0", @@ -14835,16 +14887,16 @@ }, { "name": "filp/whoops", - "version": "2.18.4", + "version": "2.18.5", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + "reference": "baec851ad6ae00db8bd0fdd30d208afc1b71c56e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "url": "https://api.github.com/repos/filp/whoops/zipball/baec851ad6ae00db8bd0fdd30d208afc1b71c56e", + "reference": "baec851ad6ae00db8bd0fdd30d208afc1b71c56e", "shasum": "" }, "require": { @@ -14853,8 +14905,8 @@ }, "require-dev": { "mockery/mockery": "^1.0", - "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^4.0 || ^5.0" + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3 || ^10.5.58", + "symfony/var-dumper": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -14894,7 +14946,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.4" + "source": "https://github.com/filp/whoops/tree/2.18.5" }, "funding": [ { @@ -14902,7 +14954,7 @@ "type": "github" } ], - "time": "2025-08-08T12:00:00+00:00" + "time": "2026-09-18T12:00:00+00:00" }, { "name": "fumeapp/modeltyper", @@ -15126,16 +15178,16 @@ }, { "name": "larastan/larastan", - "version": "v3.12.1", + "version": "v3.12.2", "source": { "type": "git", "url": "https://github.com/larastan/larastan.git", - "reference": "168fd3b955e723c51dbf81709849b0c3ac4ac5f6" + "reference": "c328727e6103c1147d1c64cc96b3aedfda26bc20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/larastan/larastan/zipball/168fd3b955e723c51dbf81709849b0c3ac4ac5f6", - "reference": "168fd3b955e723c51dbf81709849b0c3ac4ac5f6", + "url": "https://api.github.com/repos/larastan/larastan/zipball/c328727e6103c1147d1c64cc96b3aedfda26bc20", + "reference": "c328727e6103c1147d1c64cc96b3aedfda26bc20", "shasum": "" }, "require": { @@ -15203,7 +15255,7 @@ ], "support": { "issues": "https://github.com/larastan/larastan/issues", - "source": "https://github.com/larastan/larastan/tree/v3.12.1" + "source": "https://github.com/larastan/larastan/tree/v3.12.2" }, "funding": [ { @@ -15211,7 +15263,7 @@ "type": "github" } ], - "time": "2026-09-13T15:17:35+00:00" + "time": "2026-09-19T10:46:58+00:00" }, { "name": "laravel/pint", diff --git a/config/app.php b/config/app.php index 8c07d081..cb3ef8cd 100644 --- a/config/app.php +++ b/config/app.php @@ -24,9 +24,9 @@ return [ | Application Name |-------------------------------------------------------------------------- | - | This value is the name of your application. This value is used when the + | This value is the name of your application, which will be used when the | framework needs to place the application's name in a notification or - | any other location as required by the application or its packages. + | other UI elements where an application name needs to be displayed. | */ @@ -110,8 +110,8 @@ return [ |-------------------------------------------------------------------------- | | Here you may specify the default timezone for your application, which - | will be used by the PHP date and date-time functions. We have gone - | ahead and set this to a sensible default for you out of the box. + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. | */ @@ -123,8 +123,8 @@ return [ |-------------------------------------------------------------------------- | | The application locale determines the default locale that will be used - | by the translation service provider. You are free to set this value - | to any of the locales which will be supported by the application. + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. | */ @@ -178,15 +178,21 @@ return [ | Encryption Key |-------------------------------------------------------------------------- | - | This key is used by the Illuminate encrypter service and should be set - | to a random, 32 character string, otherwise these encrypted strings - | will not be safe. Please do this before deploying an application! + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. | */ + 'cipher' => 'AES-256-CBC', + 'key' => env('APP_KEY'), - 'cipher' => 'AES-256-CBC', + 'previous_keys' => [ + ...array_filter( + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) + ), + ], 'localization' => [ 'default_currency' => env('LOCALIZATION_DEFAULT_CURRENCY', 'EUR'), @@ -206,13 +212,13 @@ return [ | manage Laravel's "maintenance mode" status. The "cache" driver will | allow maintenance mode to be controlled across multiple machines. | - | Supported drivers: "file", "cache" + | Supported drivers: "file", "cache", "array" | */ 'maintenance' => [ - 'driver' => 'file', - // 'store' => 'redis', + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), ], /* diff --git a/config/auth.php b/config/auth.php index 5127b741..c97823b3 100644 --- a/config/auth.php +++ b/config/auth.php @@ -10,8 +10,8 @@ return [ | Authentication Defaults |-------------------------------------------------------------------------- | - | This option controls the default authentication "guard" and password - | reset options for your application. You may change these defaults + | This option defines the default authentication "guard" and password + | reset "broker" for your application. You may change these values | as required, but they're a perfect start for most applications. | */ @@ -28,11 +28,11 @@ return [ | | Next, you may define every authentication guard for your application. | Of course, a great default configuration has been defined for you - | here which uses session storage and the Eloquent user provider. + | which utilizes session storage plus the Eloquent user provider. | - | All authentication drivers have a user provider. This defines how the + | All authentication guards have a user provider, which defines how the | users are actually retrieved out of your database or other storage - | mechanisms used by this application to persist your user's data. + | system used by the application. Typically, Eloquent is utilized. | | Supported: "session" | @@ -55,12 +55,12 @@ return [ | User Providers |-------------------------------------------------------------------------- | - | All authentication drivers have a user provider. This defines how the + | All authentication guards have a user provider, which defines how the | users are actually retrieved out of your database or other storage - | mechanisms used by this application to persist your user's data. + | system used by the application. Typically, Eloquent is utilized. | | If you have multiple user tables or models you may configure multiple - | sources which represent each model / table. These sources may then + | providers to represent the model / table. These providers may then | be assigned to any extra authentication guards you have defined. | | Supported: "database", "eloquent" @@ -80,9 +80,9 @@ return [ | Resetting Passwords |-------------------------------------------------------------------------- | - | You may specify multiple password reset configurations if you have more - | than one user table or model in the application and you want to have - | separate password reset settings based on the specific user types. + | These configuration options specify the behavior of Laravel's password + | reset functionality, including the table utilized for token storage + | and the user provider that is invoked to actually retrieve users. | | The expiry time is the number of minutes that each reset token will be | considered valid. This security feature keeps tokens short-lived so @@ -108,13 +108,13 @@ return [ | Password Confirmation Timeout |-------------------------------------------------------------------------- | - | Here you may define the amount of seconds before a password confirmation - | times out and the user is prompted to re-enter their password via the + | Here you may define the number of seconds before a password confirmation + | window expires and users are asked to re-enter their password via the | confirmation screen. By default, the timeout lasts for three hours. | */ - 'password_timeout' => 10800, + 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), 'super_admins' => ! is_string(env('SUPER_ADMINS', null)) ? [] : explode(',', env('SUPER_ADMINS')), diff --git a/config/cache.php b/config/cache.php index d6f36f73..74eb0302 100644 --- a/config/cache.php +++ b/config/cache.php @@ -11,9 +11,9 @@ return [ | Default Cache Store |-------------------------------------------------------------------------- | - | This option controls the default cache connection that gets used while - | using this caching library. This connection is used when another is - | not explicitly specified when executing a given caching function. + | This option controls the default cache store that will be used by the + | framework. This connection is utilized if another isn't explicitly + | specified when running a cache operation inside the application. | */ @@ -28,8 +28,9 @@ return [ | well as their drivers. You may even define multiple stores for the | same cache driver to group types of items stored in your caches. | - | Supported drivers: "apc", "array", "database", "file", - | "memcached", "redis", "dynamodb", "octane", "null" + | Supported drivers: "array", "database", "file", "memcached", + | "redis", "dynamodb", "storage", "octane", + | "session", "failover", "null" | */ @@ -46,9 +47,10 @@ return [ 'database' => [ 'driver' => 'database', - 'table' => 'cache', - 'connection' => null, - 'lock_connection' => null, + 'connection' => env('DB_CACHE_CONNECTION'), + 'table' => env('DB_CACHE_TABLE', 'cache'), + 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), + 'lock_table' => env('DB_CACHE_LOCK_TABLE'), ], 'file' => [ @@ -57,6 +59,12 @@ return [ 'lock_path' => storage_path('framework/cache/data'), ], + 'storage' => [ + 'driver' => 'storage', + 'disk' => env('CACHE_STORAGE_DISK'), + 'path' => env('CACHE_STORAGE_PATH', 'framework/cache/data'), + ], + 'memcached' => [ 'driver' => 'memcached', 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), @@ -78,8 +86,8 @@ return [ 'redis' => [ 'driver' => 'redis', - 'connection' => 'cache', - 'lock_connection' => 'default', + 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), + 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), ], 'dynamodb' => [ @@ -95,6 +103,14 @@ return [ 'driver' => 'octane', ], + 'failover' => [ + 'driver' => 'failover', + 'stores' => [ + 'database', + 'array', + ], + ], + ], /* @@ -102,12 +118,24 @@ return [ | Cache Key Prefix |-------------------------------------------------------------------------- | - | When utilizing the APC, database, memcached, Redis, or DynamoDB cache - | stores there might be other applications using the same cache. For + | When utilizing the APC, database, memcached, Redis, and DynamoDB cache + | stores, there might be other applications using the same cache. For | that reason, you may prefix every cache key to avoid collisions. | */ 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), + /* + |-------------------------------------------------------------------------- + | Serializable Classes + |-------------------------------------------------------------------------- + | + | This value determines the classes that can be unserialized from cache + | storage. By default, no PHP classes will be unserialized from your + | cache to prevent gadget chain attacks if your APP_KEY is leaked. + | + */ + + 'serializable_classes' => false, ]; diff --git a/config/database.php b/config/database.php index 5ac598cc..89aa0419 100644 --- a/config/database.php +++ b/config/database.php @@ -12,26 +12,22 @@ return [ |-------------------------------------------------------------------------- | | Here you may specify which of the database connections below you wish - | to use as your default connection for all database work. Of course - | you may use many connections at once using the Database library. + | to use as your default connection for database operations. This is + | the connection which will be utilized unless another connection + | is explicitly specified when you execute a query / statement. | */ - 'default' => env('DB_CONNECTION', 'mysql'), + 'default' => env('DB_CONNECTION', 'pgsql'), /* |-------------------------------------------------------------------------- | Database Connections |-------------------------------------------------------------------------- | - | Here are each of the database connections setup for your application. - | Of course, examples of configuring each database platform that is - | supported by Laravel is shown below to make development simple. - | - | - | All database work in Laravel is done through the PHP PDO facilities - | so make sure you have the driver for your particular database of - | choice installed on your machine before you begin development. + | Below are all of the database connections defined for your application. + | An example configuration is provided for each database system which + | is supported by Laravel. You're free to add / remove connections. | */ @@ -39,50 +35,34 @@ return [ 'sqlite' => [ 'driver' => 'sqlite', - 'url' => env('DATABASE_URL'), + 'url' => env('DB_URL'), 'database' => env('DB_DATABASE', database_path('database.sqlite')), 'prefix' => '', 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), - ], - - 'mysql' => [ - 'driver' => 'mysql', - 'url' => env('DATABASE_URL'), - 'host' => env('DB_HOST', '127.0.0.1'), - 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), - 'password' => env('DB_PASSWORD', ''), - 'unix_socket' => env('DB_SOCKET', ''), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => '', - 'prefix_indexes' => true, - 'strict' => true, - 'engine' => null, - 'options' => extension_loaded('pdo_mysql') ? array_filter([ - PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), - ]) : [], + 'busy_timeout' => null, + 'journal_mode' => null, + 'synchronous' => null, + 'transaction_mode' => 'DEFERRED', ], 'pgsql' => [ 'driver' => 'pgsql', - 'url' => env('DATABASE_URL'), + 'url' => env('DB_URL'), 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '5432'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), - 'charset' => 'utf8', + 'charset' => env('DB_CHARSET', 'utf8'), 'prefix' => '', 'prefix_indexes' => true, 'search_path' => 'public', - 'sslmode' => 'prefer', + 'sslmode' => env('DB_SSLMODE', 'prefer'), ], 'pgsql_test' => [ 'driver' => 'pgsql', - 'url' => env('DATABASE_URL'), + 'url' => env('DB_URL'), 'host' => env('DB_TEST_HOST', '127.0.0.1'), 'port' => env('DB_TEST_PORT', '5432'), 'database' => env('DB_TEST_DATABASE', 'forge'), @@ -92,18 +72,18 @@ return [ 'prefix' => '', 'prefix_indexes' => true, 'search_path' => 'public', - 'sslmode' => 'prefer', + 'sslmode' => env('DB_SSLMODE', 'prefer'), ], 'sqlsrv' => [ 'driver' => 'sqlsrv', - 'url' => env('DATABASE_URL'), + 'url' => env('DB_URL'), 'host' => env('DB_HOST', 'localhost'), 'port' => env('DB_PORT', '1433'), - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', ''), - 'charset' => 'utf8', + 'charset' => env('DB_CHARSET', 'utf8'), 'prefix' => '', 'prefix_indexes' => true, // 'encrypt' => env('DB_ENCRYPT', 'yes'), @@ -119,11 +99,14 @@ return [ | | This table keeps track of all the migrations that have already run for | your application. Using this information, we can determine which of - | the migrations on disk haven't actually been run in the database. + | the migrations on disk haven't actually been run on the database. | */ - 'migrations' => 'migrations', + 'migrations' => [ + 'table' => 'migrations', + 'update_date_on_publish' => false, + ], /* |-------------------------------------------------------------------------- @@ -132,7 +115,7 @@ return [ | | Redis is an open source, fast, and advanced key-value store that also | provides a richer body of commands than a typical key-value system - | such as APC or Memcached. Laravel makes it easy to dig right in. + | such as Memcached. You may define your connection settings here. | */ @@ -143,6 +126,7 @@ return [ 'options' => [ 'cluster' => env('REDIS_CLUSTER', 'redis'), 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + 'persistent' => env('REDIS_PERSISTENT', false), ], 'default' => [ @@ -152,6 +136,10 @@ return [ 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', '6379'), 'database' => env('REDIS_DB', '0'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), ], 'cache' => [ @@ -161,6 +149,10 @@ return [ 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', '6379'), 'database' => env('REDIS_CACHE_DB', '1'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), ], ], diff --git a/config/filesystems.php b/config/filesystems.php index 2a30fece..15d157e6 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -11,7 +11,7 @@ return [ | | Here you may specify the default filesystem disk that should be used | by the framework. The "local" disk, as well as a variety of cloud - | based disks are available to your application. Just store away! + | based disks are available to your application for file storage. | */ @@ -26,11 +26,11 @@ return [ | Filesystem Disks |-------------------------------------------------------------------------- | - | Here you may configure as many filesystem "disks" as you wish, and you - | may even configure multiple disks of the same driver. Defaults have - | been set up for each driver as an example of the required values. + | Below you may configure as many filesystem disks as necessary, and you + | may even configure multiple disks for the same driver. Examples for + | most supported storage drivers are configured here for reference. | - | Supported Drivers: "local", "ftp", "sftp", "s3" + | Supported drivers: "local", "ftp", "sftp", "s3" | */ @@ -41,14 +41,16 @@ return [ 'root' => storage_path('app'), 'serve' => true, 'throw' => true, + 'report' => false, ], 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', + 'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage', 'visibility' => 'public', 'throw' => true, + 'report' => false, ], 's3' => [ @@ -62,6 +64,7 @@ return [ 'endpoint' => env('S3_ENDPOINT'), 'use_path_style_endpoint' => env('S3_USE_PATH_STYLE_ENDPOINT', false), 'throw' => true, + 'report' => false, ], 'testfiles' => [ diff --git a/config/logging.php b/config/logging.php index 0a413716..51ba04b1 100644 --- a/config/logging.php +++ b/config/logging.php @@ -14,9 +14,9 @@ return [ | Default Log Channel |-------------------------------------------------------------------------- | - | This option defines the default log channel that gets used when writing - | messages to the logs. The name specified in this option should match - | one of the channels defined in the "channels" configuration array. + | This option defines the default log channel that is utilized to write + | messages to your logs. The value provided here should match one of + | the channels present in the list of "channels" configured below. | */ @@ -43,20 +43,20 @@ return [ | Log Channels |-------------------------------------------------------------------------- | - | Here you may configure the log channels for your application. Out of - | the box, Laravel uses the Monolog PHP logging library. This gives - | you a variety of powerful log handlers / formatters to utilize. + | Here you may configure the log channels for your application. Laravel + | utilizes the Monolog PHP logging library, which includes a variety + | of powerful log handlers and formatters that you're free to use. | - | Available Drivers: "single", "daily", "slack", "syslog", - | "errorlog", "monolog", - | "custom", "stack" + | Available drivers: "single", "daily", "monthly", "slack", "syslog", + | "errorlog", "monolog", "custom", "stack" | */ 'channels' => [ + 'stack' => [ 'driver' => 'stack', - 'channels' => ['single'], + 'channels' => explode(',', (string) env('LOG_STACK', 'single')), 'ignore_exceptions' => false, ], @@ -81,15 +81,23 @@ return [ 'driver' => 'daily', 'path' => storage_path('logs/laravel.log'), 'level' => env('LOG_LEVEL', 'debug'), - 'days' => 14, + 'max_files' => env('LOG_DAILY_DAYS', 14), + 'replace_placeholders' => true, + ], + + 'monthly' => [ + 'driver' => 'monthly', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'max_files' => 3, 'replace_placeholders' => true, ], 'slack' => [ 'driver' => 'slack', 'url' => env('LOG_SLACK_WEBHOOK_URL'), - 'username' => 'Laravel Log', - 'emoji' => ':boom:', + 'username' => env('LOG_SLACK_USERNAME', env('APP_NAME', 'Laravel')), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), 'level' => env('LOG_LEVEL', 'critical'), 'replace_placeholders' => true, ], @@ -116,17 +124,17 @@ return [ 'driver' => 'monolog', 'level' => env('LOG_LEVEL', 'debug'), 'handler' => StreamHandler::class, - 'formatter' => env('LOG_STDERR_FORMATTER'), - 'with' => [ + 'handler_with' => [ 'stream' => 'php://stderr', ], + 'formatter' => env('LOG_STDERR_FORMATTER'), 'processors' => [PsrLogMessageProcessor::class], ], 'syslog' => [ 'driver' => 'syslog', 'level' => env('LOG_LEVEL', 'debug'), - 'facility' => LOG_USER, + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), 'replace_placeholders' => true, ], diff --git a/config/mail.php b/config/mail.php index dceba072..1b3abb86 100644 --- a/config/mail.php +++ b/config/mail.php @@ -9,9 +9,10 @@ return [ | Default Mailer |-------------------------------------------------------------------------- | - | This option controls the default mailer that is used to send any email - | messages sent by your application. Alternative mailers may be setup - | and used as needed; however, this mailer will be used by default. + | This option controls the default mailer that is used to send all email + | messages unless another mailer is explicitly specified when sending + | the message. All additional mailers can be configured within the + | "mailers" array. Examples of each type of mailer are provided. | */ @@ -26,26 +27,28 @@ return [ | their respective settings. Several examples have been configured for | you and you are free to add your own as your application requires. | - | Laravel supports a variety of mail "transport" drivers to be used while - | sending an e-mail. You will specify which one you are using for your - | mailers below. You are free to add additional mailers as required. + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. | | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", - | "postmark", "log", "array", "failover", "roundrobin" + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" | */ 'mailers' => [ 'smtp' => [ 'transport' => 'smtp', + 'scheme' => env('MAIL_SCHEME'), 'url' => env('MAIL_URL'), - 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), - 'port' => env('MAIL_PORT', 587), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), 'timeout' => null, - 'local_domain' => env('MAIL_EHLO_DOMAIN'), + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), ], 'ses' => [ @@ -54,17 +57,14 @@ return [ 'postmark' => [ 'transport' => 'postmark', - // 'message_stream_id' => null, + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), // 'client' => [ // 'timeout' => 5, // ], ], - 'mailgun' => [ - 'transport' => 'mailgun', - // 'client' => [ - // 'timeout' => 5, - // ], + 'resend' => [ + 'transport' => 'resend', ], 'sendmail' => [ @@ -87,6 +87,7 @@ return [ 'smtp', 'log', ], + 'retry_after' => 60, ], 'roundrobin' => [ @@ -95,7 +96,9 @@ return [ 'ses', 'postmark', ], + 'retry_after' => 60, ], + ], /* @@ -103,15 +106,15 @@ return [ | Global "From" Address |-------------------------------------------------------------------------- | - | You may wish for all e-mails sent by your application to be sent from - | the same address. Here, you may specify a name and address that is - | used globally for all e-mails that are sent by your application. + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. | */ 'from' => [ 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), - 'name' => env('MAIL_FROM_NAME', 'Example'), + 'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Laravel')), ], 'reply_to' => [ diff --git a/config/queue.php b/config/queue.php index 4cca08e8..31cf6cec 100644 --- a/config/queue.php +++ b/config/queue.php @@ -9,9 +9,9 @@ return [ | Default Queue Connection Name |-------------------------------------------------------------------------- | - | Laravel's queue API supports an assortment of back-ends via a single - | API, giving you convenient access to each back-end using the same - | syntax for every one. Here you may define a default connection. + | Laravel's queue supports a variety of backends via a single, unified + | API, giving you convenient access to each backend using identical + | syntax for each. The default queue connection is defined below. | */ @@ -22,11 +22,12 @@ return [ | Queue Connections |-------------------------------------------------------------------------- | - | Here you may configure the connection information for each server that - | is used by your application. A default configuration has been added - | for each back-end shipped with Laravel. You are free to add more. + | Here you may configure the connection options for every queue backend + | used by your application. An example configuration is provided for + | each backend supported by Laravel. You're also free to add more. | - | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", + | "deferred", "background", "failover", "null" | */ @@ -38,17 +39,18 @@ return [ 'database' => [ 'driver' => 'database', - 'table' => 'jobs', - 'queue' => 'default', - 'retry_after' => 90, + 'connection' => env('DB_QUEUE_CONNECTION'), + 'table' => env('DB_QUEUE_TABLE', 'jobs'), + 'queue' => env('DB_QUEUE', 'default'), + 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), 'after_commit' => false, ], 'beanstalkd' => [ 'driver' => 'beanstalkd', - 'host' => 'localhost', - 'queue' => 'default', - 'retry_after' => 90, + 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), + 'queue' => env('BEANSTALKD_QUEUE', 'default'), + 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), 'block_for' => 0, 'after_commit' => false, ], @@ -66,13 +68,29 @@ return [ 'redis' => [ 'driver' => 'redis', - 'connection' => 'default', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), 'queue' => env('REDIS_QUEUE', 'default'), - 'retry_after' => 90, + 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), 'block_for' => null, 'after_commit' => false, ], + 'deferred' => [ + 'driver' => 'deferred', + ], + + 'background' => [ + 'driver' => 'background', + ], + + 'failover' => [ + 'driver' => 'failover', + 'connections' => [ + 'database', + 'deferred', + ], + ], + ], /* @@ -87,7 +105,7 @@ return [ */ 'batching' => [ - 'database' => env('DB_CONNECTION', 'mysql'), + 'database' => env('DB_CONNECTION', 'sqlite'), 'table' => 'job_batches', ], @@ -97,14 +115,16 @@ return [ |-------------------------------------------------------------------------- | | These options configure the behavior of failed queue job logging so you - | can control which database and table are used to store the jobs that - | have failed. You may change them to any database / table you wish. + | can control how and where failed jobs are stored. Laravel ships with + | support for storing failed jobs in a simple file or in a database. + | + | Supported drivers: "database-uuids", "dynamodb", "file", "null" | */ 'failed' => [ 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), - 'database' => env('DB_CONNECTION', 'mysql'), + 'database' => env('DB_CONNECTION', 'sqlite'), 'table' => 'failed_jobs', ], diff --git a/config/session.php b/config/session.php index 50cefaef..5dc9d193 100644 --- a/config/session.php +++ b/config/session.php @@ -9,12 +9,12 @@ return [ | Default Session Driver |-------------------------------------------------------------------------- | - | This option controls the default session "driver" that will be used on - | requests. By default, we will use the lightweight native driver but - | you may specify any of the other wonderful drivers provided here. + | This option determines the default session driver that is utilized for + | incoming requests. Laravel supports a variety of storage options to + | persist session data. Database storage is a great default choice. | - | Supported: "file", "cookie", "database", "apc", - | "memcached", "redis", "dynamodb", "array" + | Supported: "file", "cookie", "database", "memcached", + | "redis", "dynamodb", "array" | */ @@ -27,13 +27,14 @@ return [ | | Here you may specify the number of minutes that you wish the session | to be allowed to remain idle before it expires. If you want them - | to immediately expire on the browser closing, set that option. + | to expire immediately when the browser is closed then you may + | indicate that via the expire_on_close configuration option. | */ - 'lifetime' => env('SESSION_LIFETIME', 120), + 'lifetime' => (int) env('SESSION_LIFETIME', 120), - 'expire_on_close' => false, + 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), /* |-------------------------------------------------------------------------- @@ -79,24 +80,24 @@ return [ | Session Database Table |-------------------------------------------------------------------------- | - | When using the "database" session driver, you may specify the table we - | should use to manage the sessions. Of course, a sensible default is - | provided for you; however, you are free to change this as needed. + | When using the "database" session driver, you may specify the table to + | be used to store sessions. Of course, a sensible default is defined + | for you; however, you're welcome to change this to another table. | */ - 'table' => 'sessions', + 'table' => env('SESSION_TABLE', 'sessions'), /* |-------------------------------------------------------------------------- | Session Cache Store |-------------------------------------------------------------------------- | - | While using one of the framework's cache driven session backends you may - | list a cache store that should be used for these sessions. This value - | must match with one of the application's configured cache "stores". + | When using one of the framework's cache driven session backends, you may + | define the cache store which should be used to store the session data + | between requests. This must match one of your defined cache stores. | - | Affects: "apc", "dynamodb", "memcached", "redis" + | Affects: "dynamodb", "memcached", "redis" | */ @@ -120,9 +121,9 @@ return [ | Session Cookie Name |-------------------------------------------------------------------------- | - | Here you may change the name of the cookie used to identify a session - | instance by ID. The name specified here will get used every time a - | new session cookie is created by the framework for every driver. + | Here you may change the name of the session cookie that is created by + | the framework. Typically, you should not need to change this value + | since doing so does not grant a meaningful security improvement. | */ @@ -138,20 +139,20 @@ return [ | | The session cookie path determines the path for which the cookie will | be regarded as available. Typically, this will be the root path of - | your application but you are free to change this when necessary. + | your application, but you're free to change this when necessary. | */ - 'path' => '/', + 'path' => env('SESSION_PATH', '/'), /* |-------------------------------------------------------------------------- | Session Cookie Domain |-------------------------------------------------------------------------- | - | Here you may change the domain of the cookie used to identify a session - | in your application. This will determine which domains the cookie is - | available to in your application. A sensible default has been set. + | This value determines the domain and subdomains the session cookie is + | available to. By default, the cookie will be available to the root + | domain without subdomains. Typically, this shouldn't be changed. | */ @@ -177,11 +178,11 @@ return [ | | Setting this value to true will prevent JavaScript from accessing the | value of the cookie and the cookie will only be accessible through - | the HTTP protocol. You are free to modify this option if needed. + | the HTTP protocol. It's unlikely you should disable this option. | */ - 'http_only' => true, + 'http_only' => env('SESSION_HTTP_ONLY', true), /* |-------------------------------------------------------------------------- @@ -190,13 +191,15 @@ return [ | | This option determines how your cookies behave when cross-site requests | take place, and can be used to mitigate CSRF attacks. By default, we - | will set this value to "lax" since this is a secure default value. + | will set this value to "lax" to permit secure cross-site requests. + | + | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value | | Supported: "lax", "strict", "none", null | */ - 'same_site' => 'lax', + 'same_site' => env('SESSION_SAME_SITE', 'lax'), /* |-------------------------------------------------------------------------- @@ -209,6 +212,22 @@ return [ | */ - 'partitioned' => false, + 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), + + /* + |-------------------------------------------------------------------------- + | Session Serialization + |-------------------------------------------------------------------------- + | + | This value controls the serialization strategy for session data, which + | is JSON by default. Setting this to "php" allows the storage of PHP + | objects in the session but can make an application vulnerable to + | "gadget chain" serialization attacks if the APP_KEY is leaked. + | + | Supported: "json", "php" + | + */ + + 'serialization' => 'php', ]; diff --git a/e2e/utils/mailpit.ts b/e2e/utils/mailpit.ts index 4df6723a..b3092c9d 100644 --- a/e2e/utils/mailpit.ts +++ b/e2e/utils/mailpit.ts @@ -68,7 +68,7 @@ export async function getPasswordResetUrl( for (let attempt = 0; attempt < 5; attempt++) { searchResult = await searchEmails( request, - `to:${encodeURIComponent(recipientEmail)} subject:"Reset Password"` + `to:${encodeURIComponent(recipientEmail)} subject:"Reset your password"` ); if (searchResult.messages.length > 0) break; await new Promise((resolve) => setTimeout(resolve, 500)); diff --git a/extensions/manifest.json b/extensions/manifest.json index 2dadb287..8abd3f4d 100644 --- a/extensions/manifest.json +++ b/extensions/manifest.json @@ -1,14 +1,14 @@ { "Billing": { "repository": "solidtime-io/extension-billing", - "ref": "feature/composer-update" + "ref": "v0.0.7" }, "Services": { "repository": "solidtime-io/extension-services", - "ref": "feature/composer-update" + "ref": "v0.0.3" }, "Invoicing": { "repository": "solidtime-io/extension-invoicing", - "ref": "feature/composer-update" + "ref": "v0.0.7" } } diff --git a/phpunit.xml b/phpunit.xml index badabfb0..0d78ff1e 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -34,6 +34,7 @@ + diff --git a/tests/Unit/Endpoint/Api/V1/ApiEndpointTestAbstract.php b/tests/Unit/Endpoint/Api/V1/ApiEndpointTestAbstract.php index a59bd2ee..73e6d024 100644 --- a/tests/Unit/Endpoint/Api/V1/ApiEndpointTestAbstract.php +++ b/tests/Unit/Endpoint/Api/V1/ApiEndpointTestAbstract.php @@ -8,6 +8,7 @@ use Closure; use DateTimeInterface; use Illuminate\Support\Facades\Storage; use Illuminate\Testing\TestResponse; +use Mockery; use Tests\TestCaseWithDatabase; class ApiEndpointTestAbstract extends TestCaseWithDatabase @@ -21,21 +22,23 @@ class ApiEndpointTestAbstract extends TestCaseWithDatabase } /** - * Replaces the temporary URL builder of the private disk to capture the options - * passed to temporaryUrl. Returns a closure that yields the captured options. + * Captures the options passed to temporaryUrl on the private disk. * * @return Closure(): (array|null) */ protected function captureTemporaryUrlOptions(): Closure { $captured = null; - Storage::disk(config('filesystems.private'))->buildTemporaryUrlsUsing( + $diskName = config('filesystems.private'); + $disk = Mockery::mock(Storage::disk($diskName))->makePartial(); + $disk->shouldReceive('temporaryUrl')->andReturnUsing( function (string $path, DateTimeInterface $expiration, array $options) use (&$captured): string { $captured = $options; return 'https://storage.fake/'.$path; } ); + Storage::set($diskName, $disk); return function () use (&$captured): ?array { return $captured; diff --git a/tests/Unit/Filament/Resources/OrganizationResourceTest.php b/tests/Unit/Filament/Resources/OrganizationResourceTest.php index 94f5c0fc..54fe19e6 100644 --- a/tests/Unit/Filament/Resources/OrganizationResourceTest.php +++ b/tests/Unit/Filament/Resources/OrganizationResourceTest.php @@ -544,6 +544,68 @@ class OrganizationResourceTest extends FilamentTestCase $this->assertSame(Role::Owner->value, $ownerMember->refresh()->role); } + public function test_edit_related_user_does_not_change_role_if_role_is_unchanged_for_owner(): void + { + // Arrange + $owner = User::factory()->create(); + $organization = Organization::factory()->withOwner($owner)->create(); + $ownerMember = Member::factory()->forOrganization($organization)->forUser($owner)->role(Role::Owner)->billableRate(1000)->create(); + + // Act + $response = Livewire::test(OrganizationResource\RelationManagers\UsersRelationManager::class, [ + 'ownerRecord' => $organization, + 'pageClass' => OrganizationResource\Pages\EditOrganization::class, + ])->callTableAction('edit', $owner, data: [ + 'role' => Role::Owner->value, + 'billable_rate' => 2000, + ]); + + // Assert + $response->assertSuccessful(); + $response->assertHasNoTableActionErrors(); + $response->assertNotNotified( + Notification::make() + ->danger() + ->title('Update failed') + ->body(__('exceptions.api.organization_needs_at_least_one_owner')) + ->persistent() + ); + $ownerMember->refresh(); + $this->assertSame(Role::Owner->value, $ownerMember->role); + $this->assertSame(2000, $ownerMember->billable_rate); + } + + public function test_edit_related_user_does_not_change_role_if_role_is_unchanged_for_placeholder(): void + { + // Arrange + $organization = Organization::factory()->create(); + $user = User::factory()->placeholder()->create(); + $member = Member::factory()->forOrganization($organization)->forUser($user)->role(Role::Placeholder)->billableRate(1000)->create(); + + // Act + $response = Livewire::test(OrganizationResource\RelationManagers\UsersRelationManager::class, [ + 'ownerRecord' => $organization, + 'pageClass' => OrganizationResource\Pages\EditOrganization::class, + ])->callTableAction('edit', $user, data: [ + 'role' => Role::Placeholder->value, + 'billable_rate' => 2000, + ]); + + // Assert + $response->assertSuccessful(); + $response->assertHasNoTableActionErrors(); + $response->assertNotNotified( + Notification::make() + ->danger() + ->title('Update failed') + ->body(__('exceptions.api.changing_role_of_placeholder_is_not_allowed')) + ->persistent() + ); + $member->refresh(); + $this->assertSame(Role::Placeholder->value, $member->role); + $this->assertSame(2000, $member->billable_rate); + } + public function test_can_detach_related_user_from_organization(): void { // Arrange diff --git a/tests/Unit/Filament/Resources/UserResourceTest.php b/tests/Unit/Filament/Resources/UserResourceTest.php index d675aa23..1a20796a 100644 --- a/tests/Unit/Filament/Resources/UserResourceTest.php +++ b/tests/Unit/Filament/Resources/UserResourceTest.php @@ -286,6 +286,62 @@ class UserResourceTest extends FilamentTestCase $this->assertSame(Role::Owner->value, $ownerMember->refresh()->role); } + public function test_edit_related_organization_does_not_change_role_if_role_is_unchanged_for_owner(): void + { + // Arrange + $owner = User::factory()->create(); + $organization = Organization::factory()->withOwner($owner)->create(); + $ownerMember = Member::factory()->forOrganization($organization)->forUser($owner)->role(Role::Owner)->create(); + + // Act + $response = Livewire::test(UserResource\RelationManagers\OrganizationsRelationManager::class, [ + 'ownerRecord' => $owner, + 'pageClass' => UserResource\Pages\EditUser::class, + ])->callTableAction('edit', $organization, data: [ + 'role' => Role::Owner->value, + ]); + + // Assert + $response->assertSuccessful(); + $response->assertHasNoTableActionErrors(); + $response->assertNotNotified( + Notification::make() + ->danger() + ->title('Update failed') + ->body(__('exceptions.api.organization_needs_at_least_one_owner')) + ->persistent() + ); + $this->assertSame(Role::Owner->value, $ownerMember->refresh()->role); + } + + public function test_edit_related_organization_does_not_change_role_if_role_is_unchanged_for_placeholder(): void + { + // Arrange + $user = User::factory()->placeholder()->create(); + $organization = Organization::factory()->create(); + $member = Member::factory()->forOrganization($organization)->forUser($user)->role(Role::Placeholder)->create(); + + // Act + $response = Livewire::test(UserResource\RelationManagers\OrganizationsRelationManager::class, [ + 'ownerRecord' => $user, + 'pageClass' => UserResource\Pages\EditUser::class, + ])->callTableAction('edit', $organization, data: [ + 'role' => Role::Placeholder->value, + ]); + + // Assert + $response->assertSuccessful(); + $response->assertHasNoTableActionErrors(); + $response->assertNotNotified( + Notification::make() + ->danger() + ->title('Update failed') + ->body(__('exceptions.api.changing_role_of_placeholder_is_not_allowed')) + ->persistent() + ); + $this->assertSame(Role::Placeholder->value, $member->refresh()->role); + } + public function test_can_detach_related_organization_from_user(): void { // Arrange diff --git a/tests/Unit/Service/LocalizationServiceTest.php b/tests/Unit/Service/LocalizationServiceTest.php index 09c85416..b1dee414 100644 --- a/tests/Unit/Service/LocalizationServiceTest.php +++ b/tests/Unit/Service/LocalizationServiceTest.php @@ -187,7 +187,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase // Arrange $this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolAfterWithSpace); $this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma); - $money = Money::of(1234567.89, Currency::of('EUR')); + $money = Money::of('1234567.89', Currency::of('EUR')); // Act $formatted = $this->localizationService->formatCurrency($money); @@ -201,7 +201,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase // Arrange $this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolBeforeWithSpace); $this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma); - $money = Money::of(1234567.89, Currency::of('EUR')); + $money = Money::of('1234567.89', Currency::of('EUR')); // Act $formatted = $this->localizationService->formatCurrency($money); @@ -215,7 +215,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase // Arrange $this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolBefore); $this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma); - $money = Money::of(1234567.89, Currency::of('EUR')); + $money = Money::of('1234567.89', Currency::of('EUR')); // Act $formatted = $this->localizationService->formatCurrency($money); @@ -229,7 +229,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase // Arrange $this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolAfter); $this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma); - $money = Money::of(1234567.89, Currency::of('EUR')); + $money = Money::of('1234567.89', Currency::of('EUR')); // Act $formatted = $this->localizationService->formatCurrency($money); @@ -243,7 +243,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase // Arrange $this->localizationService->setCurrencyFormat(CurrencyFormat::ISOCodeAfterWithSpace); $this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma); - $money = Money::of(1234567.89, Currency::of('EUR')); + $money = Money::of('1234567.89', Currency::of('EUR')); // Act $formatted = $this->localizationService->formatCurrency($money); @@ -257,7 +257,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase // Arrange $this->localizationService->setCurrencyFormat(CurrencyFormat::ISOCodeBeforeWithSpace); $this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma); - $money = Money::of(1234567.89, Currency::of('EUR')); + $money = Money::of('1234567.89', Currency::of('EUR')); // Act $formatted = $this->localizationService->formatCurrency($money);