From b7689e2fd7a7221d1db6cf817a68158e18f122e5 Mon Sep 17 00:00:00 2001 From: Ilya Stepenko <38462532+VampireAotD@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:23:18 +0300 Subject: [PATCH] Refactored code --- docker/mysql/Dockerfile | 4 +- docker/nginx/Dockerfile | 6 +- docker/php/Dockerfile | 17 +- src/.env.example | 1 - src/.env.testing | 3 +- .../Commands/Lists/Anime/GenerateCommand.php | 2 +- .../Database/Relations/OneOfMorphToMany.php | 67 ++ .../Controllers/Anime/AnimeController.php | 2 +- src/app/Jobs/Image/UploadJob.php | 2 +- src/app/Models/Anime.php | 13 +- src/app/Models/Concerns/HasImage.php | 42 ++ .../Concerns/HasOneOfMorphToManyRelation.php | 75 ++ src/app/Models/Image.php | 15 +- src/app/Models/User.php | 2 +- src/app/Providers/FakerServiceProvider.php | 3 + src/app/Services/AnimeService.php | 4 +- src/app/Services/ImageService.php | 42 +- src/app/UseCase/Scraper/ScraperUseCase.php | 2 +- src/composer.json | 20 +- src/composer.lock | 681 ++++++++---------- src/config/cloudinary.php | 13 +- src/config/ide-helper.php | 8 +- src/database/factories/ImageFactory.php | 5 +- .../2021_12_26_113712_create_images_table.php | 5 +- ..._09_10_232009_create_has_images_table.php} | 12 +- src/database/seeders/DatabaseSeeder.php | 11 +- .../Concerns/Fake/CanCreateFakeAnime.php | 2 +- .../Lists/Anime/GenerateCommandTest.php | 2 +- .../HasOneOfMorphToManyRelationTest.php | 125 ++++ .../Resources/TestModel.php | 48 ++ .../Resources/TestRelation.php | 38 + .../Feature/Services/ImageServiceTest.php | 64 +- .../Services/TelegramUserServiceTest.php | 2 +- .../Providers/AnimeInformationProvider.php | 6 +- .../Helpers/Faker/Providers/HashProvider.php | 15 + 35 files changed, 873 insertions(+), 486 deletions(-) create mode 100644 src/app/Database/Relations/OneOfMorphToMany.php create mode 100644 src/app/Models/Concerns/HasImage.php create mode 100644 src/app/Models/Concerns/HasOneOfMorphToManyRelation.php rename src/database/migrations/{2021_12_29_173201_add_alias_column_to_images_table.php => 2024_09_10_232009_create_has_images_table.php} (55%) create mode 100644 src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/HasOneOfMorphToManyRelationTest.php create mode 100644 src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/Resources/TestModel.php create mode 100644 src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/Resources/TestRelation.php create mode 100644 src/tests/Helpers/Faker/Providers/HashProvider.php diff --git a/docker/mysql/Dockerfile b/docker/mysql/Dockerfile index 457b07cd..1c2258c9 100644 --- a/docker/mysql/Dockerfile +++ b/docker/mysql/Dockerfile @@ -4,7 +4,7 @@ ARG MYSQL_VERSION=8.0 FROM mysql:${MYSQL_VERSION} -COPY ./conf.d/anilibrary.cnf /etc/mysql/conf.d/ -COPY ./docker-entrypoint-initdb.d /docker-entrypoint-initdb.d/ +COPY --link ./conf.d/anilibrary.cnf /etc/mysql/conf.d/ +COPY --link ./docker-entrypoint-initdb.d /docker-entrypoint-initdb.d/ EXPOSE 3306 \ No newline at end of file diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile index 7e95e9fa..55a86da1 100644 --- a/docker/nginx/Dockerfile +++ b/docker/nginx/Dockerfile @@ -4,9 +4,9 @@ ARG NGINX_VERSION=1.27 FROM nginx:${NGINX_VERSION}-alpine -COPY ./nginx.conf /etc/nginx -COPY ./conf.d/default.conf /etc/nginx/conf.d/ -COPY ./docker-entrypoint.d/logs-to-logstash.sh /docker-entrypoint.d/ +COPY --link ./nginx.conf /etc/nginx +COPY --link ./conf.d/default.conf /etc/nginx/conf.d/ +COPY --link ./docker-entrypoint.d/logs-to-logstash.sh /docker-entrypoint.d/ RUN chmod +x /docker-entrypoint.d/logs-to-logstash.sh diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 2abfa54b..0661ffb0 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -17,8 +17,7 @@ FROM php:${PHP_VERSION}-cli-alpine ARG GROUP_ID=1000 ARG USER_ID=1000 -RUN apk update \ - && apk add --no-cache libstdc++ libpq libzip-dev gmp-dev oniguruma-dev curl git zip unzip supervisor \ +RUN apk add --no-cache libstdc++ libpq libzip-dev gmp-dev oniguruma-dev curl git zip unzip supervisor \ && apk add --no-cache --virtual .build-deps $PHPIZE_DEPS linux-headers brotli-dev pcre-dev pcre2-dev zlib-dev \ && pecl install xdebug swoole redis uv \ && docker-php-ext-install -j$(nproc) \ @@ -41,15 +40,15 @@ RUN addgroup -g ${GROUP_ID} anilibrary \ USER anilibrary -COPY ./php.ini /usr/local/etc/php/php.ini -COPY ./supervisor/supervisord.conf /etc/supervisor/supervisord.conf +COPY --link ./php.ini /usr/local/etc/php/php.ini +COPY --link ./supervisor/supervisord.conf /etc/supervisor/supervisord.conf -COPY --from=composer /usr/bin/composer /usr/local/bin/composer +COPY --link --from=composer /usr/bin/composer /usr/local/bin/composer -COPY --from=node /usr/lib /usr/lib -COPY --from=node /usr/local/lib /usr/local/lib -COPY --from=node /usr/local/include /usr/local/include -COPY --from=node /usr/local/bin /usr/local/bin +COPY --link --from=node /usr/lib /usr/lib +COPY --link --from=node /usr/local/lib /usr/local/lib +COPY --link --from=node /usr/local/include /usr/local/include +COPY --link --from=node /usr/local/bin /usr/local/bin WORKDIR /anilibrary diff --git a/src/.env.example b/src/.env.example index b7ae80e8..4f80b020 100644 --- a/src/.env.example +++ b/src/.env.example @@ -89,7 +89,6 @@ CLOUDINARY_URL= CLOUDINARY_UPLOAD_PRESET= CLOUDINARY_NOTIFICATION_URL= CLOUDINARY_DEFAULT_IMAGE= -CLOUDINARY_DEFAULT_FOLDER= JWT_SECRET= diff --git a/src/.env.testing b/src/.env.testing index 50763f0a..e3a1e0bd 100644 --- a/src/.env.testing +++ b/src/.env.testing @@ -86,10 +86,9 @@ TELEGRAM_LOG_CHANNEL=null TELEGRAM_WHITELIST=1,2 CLOUDINARY_URL=https://testing.cloudinary.com -CLOUDINARY_UPLOAD_PRESET= +CLOUDINARY_UPLOAD_PRESET=test-preset CLOUDINARY_NOTIFICATION_URL= CLOUDINARY_DEFAULT_IMAGE=https://testing.cloudinary.com/test/image.jpg -CLOUDINARY_DEFAULT_FOLDER=test JWT_SECRET=test diff --git a/src/app/Console/Commands/Lists/Anime/GenerateCommand.php b/src/app/Console/Commands/Lists/Anime/GenerateCommand.php index 08b3e710..4d48da02 100644 --- a/src/app/Console/Commands/Lists/Anime/GenerateCommand.php +++ b/src/app/Console/Commands/Lists/Anime/GenerateCommand.php @@ -44,7 +44,7 @@ public function handle(AnimeService $animeService, UserRepositoryInterface $user new RelationFilter([ 'urls:anime_id,url', 'synonyms:anime_id,name', - 'image:id,model_id,path,alias', + 'image:id,path,name,hash', 'genres:id,name', 'voiceActing:id,name', ]), diff --git a/src/app/Database/Relations/OneOfMorphToMany.php b/src/app/Database/Relations/OneOfMorphToMany.php new file mode 100644 index 00000000..11a1a18b --- /dev/null +++ b/src/app/Database/Relations/OneOfMorphToMany.php @@ -0,0 +1,67 @@ + + */ +final class OneOfMorphToMany extends MorphToMany +{ + use SupportsDefaultModels; + + /** + * Match the eagerly loaded results to their parents. + * This method is used when `with` or `load` methods are called. + */ + #[Override] + public function match(array $models, Collection $results, $relation): array + { + $dictionary = $this->buildDictionary($results); + + foreach ($models as $model) { + $key = $this->getDictionaryKey($model->{$this->parentKey}); + + // If there are related entries to model, we need to take only first one and set it + if (isset($dictionary[$key])) { + $model->setRelation($relation, Arr::first($dictionary[$key])); + continue; + } + + // If there were no related entries - set default one + $model->setRelation($relation, $this->getDefaultFor($model)); + } + + return $models; + } + + /** + * Get results of the relationship. In the case of `OneOfMorphToMany` it will only get one result. + */ + #[Override] + public function getResults() + { + return $this->query->first() ?: $this->getDefaultFor($this->parent); + } + + /** + * Make a new related instance for the given model. + */ + #[Override] + protected function newRelatedInstanceFor(Model $parent): Model + { + return $this->related->newInstance() + ->setAttribute($this->getForeignPivotKeyName(), $parent->getKey()) + ->setAttribute($this->getMorphType(), $this->morphClass); + } +} diff --git a/src/app/Http/Controllers/Anime/AnimeController.php b/src/app/Http/Controllers/Anime/AnimeController.php index c10f3dbe..cbac63e8 100644 --- a/src/app/Http/Controllers/Anime/AnimeController.php +++ b/src/app/Http/Controllers/Anime/AnimeController.php @@ -82,7 +82,7 @@ public function store(CreateRequest $request): RedirectResponse public function show(Anime $anime): Response { $anime->load([ - 'image:model_id,path', + 'image:id,path', 'urls:anime_id,url', 'synonyms:anime_id,name', 'voiceActing:name', diff --git a/src/app/Jobs/Image/UploadJob.php b/src/app/Jobs/Image/UploadJob.php index acb7b979..741e2154 100644 --- a/src/app/Jobs/Image/UploadJob.php +++ b/src/app/Jobs/Image/UploadJob.php @@ -33,6 +33,6 @@ public function __construct(public readonly Anime $anime, public readonly string */ public function handle(ImageService $imageService): void { - $imageService->upsert($this->image, $this->anime); + $imageService->attachEncodedImageToAnime($this->image, $this->anime); } } diff --git a/src/app/Models/Anime.php b/src/app/Models/Anime.php index 5b83640d..4dd485d9 100644 --- a/src/app/Models/Anime.php +++ b/src/app/Models/Anime.php @@ -7,6 +7,7 @@ use App\Enums\Anime\StatusEnum; use App\Enums\Anime\TypeEnum; use App\Models\Concerns\Filterable; +use App\Models\Concerns\HasImage; use App\Models\Pivots\AnimeGenre; use App\Models\Pivots\AnimeVoiceActing; use App\Observers\AnimeObserver; @@ -17,7 +18,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Database\Eloquent\SoftDeletes; /** @@ -28,6 +28,7 @@ class Anime extends Model { use HasUuids; use HasFactory; + use HasImage; use Filterable; use SoftDeletes; @@ -53,13 +54,6 @@ protected function casts(): array ]; } - public function image(): MorphOne - { - return $this->morphOne(Image::class, 'model')->withDefault([ - 'path' => config('cloudinary.default_image'), - ]); - } - public function urls(): HasMany { return $this->hasMany(AnimeUrl::class); @@ -86,11 +80,12 @@ public function genres(): BelongsToMany */ protected function toTelegramCaption(): Attribute { + /** @see https://github.com/larastan/larastan/issues/2038 */ return Attribute::make( get: fn(): string => sprintf( "Название: %s\nСтатус: %s\nЭпизоды: %s\nОценка: %s\nОзвучки: %s\nЖанры: %s", $this->title, - $this->status->value, + $this->status->value, // @phpstan-ignore-line Ignored because of parser issues $this->episodes, $this->rating, $this->voiceActing->implode('name', ', '), diff --git a/src/app/Models/Concerns/HasImage.php b/src/app/Models/Concerns/HasImage.php new file mode 100644 index 00000000..05d8d7a7 --- /dev/null +++ b/src/app/Models/Concerns/HasImage.php @@ -0,0 +1,42 @@ +oneOfMorphToMany(Image::class, 'model', 'has_images')->withTimestamps()->withDefault([ + 'path' => config('cloudinary.default_image'), + ]); + } + + public function attachImage(Image $image): void + { + $this->detachImage(); + $this->image()->attach($image); + } + + public function detachImage(): void + { + if ($this->image->is_default) { + return; + } + + $this->image()->detach(); + } +} diff --git a/src/app/Models/Concerns/HasOneOfMorphToManyRelation.php b/src/app/Models/Concerns/HasOneOfMorphToManyRelation.php new file mode 100644 index 00000000..09b7845b --- /dev/null +++ b/src/app/Models/Concerns/HasOneOfMorphToManyRelation.php @@ -0,0 +1,75 @@ + $related + * @param string $name + * @param string|null $table + * @param string|null $foreignPivotKey + * @param string|null $relatedPivotKey + * @param string|null $parentKey + * @param string|null $relatedKey + * @param string|null $relation + * @param bool $inverse + * @return OneOfMorphToMany + */ + public function oneOfMorphToMany( + $related, + string $name, + ?string $table = null, + ?string $foreignPivotKey = null, + ?string $relatedPivotKey = null, + ?string $parentKey = null, + ?string $relatedKey = null, + ?string $relation = null, + bool $inverse = false + ): OneOfMorphToMany { + $relation = $relation ?: $this->guessBelongsToManyRelation(); + + $instance = $this->newRelatedInstance($related); + + $foreignPivotKey = $foreignPivotKey ?: $name . '_id'; + + $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey(); + + if (!$table) { + $words = preg_split('/(_)/u', $name, -1, PREG_SPLIT_DELIM_CAPTURE); + + $lastWord = array_pop($words); + + $table = implode('', $words) . Str::plural($lastWord); + } + + return new OneOfMorphToMany( + query : $instance->newQuery(), + parent : $this, + name : $name, + table : $table, + foreignPivotKey: $foreignPivotKey, + relatedPivotKey: $relatedPivotKey, + parentKey : $parentKey ?: $this->getKeyName(), + relatedKey : $relatedKey ?: $instance->getKeyName(), + relationName : $relation, + inverse : $inverse + ); + } +} diff --git a/src/app/Models/Image.php b/src/app/Models/Image.php index 41bf6b6a..bc978196 100644 --- a/src/app/Models/Image.php +++ b/src/app/Models/Image.php @@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\MorphTo; +use Illuminate\Database\Eloquent\Relations\MorphToMany; /** * @mixin IdeHelperImage @@ -18,23 +18,18 @@ class Image extends Model use HasUuids; use HasFactory; - protected $fillable = [ - 'model_type', - 'model_id', - 'path', - 'alias', - ]; + protected $fillable = ['path', 'name', 'hash']; - public function anime(): MorphTo + public function animes(): MorphToMany { - return $this->morphTo(Anime::class); + return $this->morphedByMany(Anime::class, 'model', 'has_images'); } /** * @psalm-suppress TooManyTemplateParams Suppressed because PHPStan needs description, but Psalm conflicts with it * @return Attribute */ - protected function default(): Attribute + protected function isDefault(): Attribute { return Attribute::make( get: fn(): bool => $this->path === config('cloudinary.default_image'), diff --git a/src/app/Models/User.php b/src/app/Models/User.php index 476032cb..bdbbce7c 100644 --- a/src/app/Models/User.php +++ b/src/app/Models/User.php @@ -20,10 +20,10 @@ */ class User extends Authenticatable implements MustVerifyEmail { + use HasUuids; use HasApiTokens; use HasFactory; use HasRoles; - use HasUuids; use Notifiable; protected $fillable = [ diff --git a/src/app/Providers/FakerServiceProvider.php b/src/app/Providers/FakerServiceProvider.php index 49ed7db9..5dc6d3ed 100644 --- a/src/app/Providers/FakerServiceProvider.php +++ b/src/app/Providers/FakerServiceProvider.php @@ -8,6 +8,7 @@ use Faker\Generator; use Illuminate\Support\ServiceProvider; use Tests\Helpers\Faker\Providers\AnimeInformationProvider; +use Tests\Helpers\Faker\Providers\HashProvider; class FakerServiceProvider extends ServiceProvider { @@ -19,7 +20,9 @@ public function register(): void if ($this->app->environment('testing')) { $this->app->singleton(Generator::class, function () { $faker = Factory::create(); + $faker->addProvider(new AnimeInformationProvider($faker)); + $faker->addProvider(new HashProvider($faker)); return $faker; }); diff --git a/src/app/Services/AnimeService.php b/src/app/Services/AnimeService.php index d1643a42..f354d57f 100644 --- a/src/app/Services/AnimeService.php +++ b/src/app/Services/AnimeService.php @@ -104,7 +104,7 @@ public function getParsedAnimePerMonth(): array */ public function getTenLatestAnime(): Collection { - return $this->animeRepository->withFilters([new RelationFilter(['image:model_id,path'])])->getLatestAnime(); + return $this->animeRepository->withFilters([new RelationFilter(['image:id,path'])])->getLatestAnime(); } public function countAnime(): int @@ -121,7 +121,7 @@ private function upsertRelations(Anime $anime, UpsertAnimeDTO $dto): void // of Laravel withDefault method on image relation, so here // need to check if image has default path then it can be // replaced with new one - if ($dto->image && $anime->image->default) { + if ($dto->image && $anime->image->is_default) { UploadJob::dispatch($anime, $dto->image); } diff --git a/src/app/Services/ImageService.php b/src/app/Services/ImageService.php index 8a8b955c..87e230c8 100644 --- a/src/app/Services/ImageService.php +++ b/src/app/Services/ImageService.php @@ -5,36 +5,48 @@ namespace App\Services; use App\Models\Anime; +use App\Models\Image; use Cloudinary\Api\Exception\ApiError; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; final readonly class ImageService { - public function upsert(string $image, Anime $anime): void + public function attachEncodedImageToAnime(string $image, Anime $anime): void { - // Same situation as in `AnimeService`, anime will always - // have a default image path, so need to check if it is an - // actual image in DB and delete it if it is - if (!$anime->image->default) { - // TODO Use Cloudinary facade when it will have a mixin - cloudinary()->destroy($anime->image->alias); + $content = preg_replace('#data:image/\w+;base64,#', '', $image); + + $hash = hash('sha512', base64_decode($content)); + + if ($duplicate = Image::query()->where('hash', $hash)->first()) { + $anime->attachImage($duplicate); + return; } try { - $alias = sprintf('%s/%s', $anime->id, Str::random()); - $path = cloudinary()->uploadFile( + $publicId = sprintf('%s/%s', Str::random(), Str::random()); + $response = cloudinary()->uploadFile( file : $image, options: [ - 'folder' => config('cloudinary.default_folder'), - 'public_id' => $alias, + 'public_id' => $publicId, + 'upload_preset' => config('cloudinary.upload_preset'), ] - )->getSecurePath(); + ); - $anime->image()->updateOrCreate([ - 'path' => $path, - 'alias' => $alias, + $image = Image::query()->create([ + 'path' => $response->getSecurePath(), + 'name' => $response->getPublicId(), + 'hash' => $hash, ]); + + // Same situation as in `AnimeService`, anime will always + // have a default image path, so need to check if it is an + // actual image in DB and delete it if it is + if (!$anime->image->is_default) { + cloudinary()->destroy($anime->image->name); + } + + $anime->attachImage($image); } catch (ApiError $exception) { Log::error('Failed to upload image', [ 'anime' => $anime->id, diff --git a/src/app/UseCase/Scraper/ScraperUseCase.php b/src/app/UseCase/Scraper/ScraperUseCase.php index 6cf3c378..94c109a4 100644 --- a/src/app/UseCase/Scraper/ScraperUseCase.php +++ b/src/app/UseCase/Scraper/ScraperUseCase.php @@ -80,7 +80,7 @@ private function upsertAnime(ScrapedDataDTO $dto): Anime $anime, new UpsertAnimeDTO( $anime->title, - $anime->type, + $anime->type, // @phpstan-ignore-line Ignored because of parser issues (int) $anime->year, [['url' => $dto->url]], $dto->status, diff --git a/src/composer.json b/src/composer.json index 0a7bf2d4..66be94a2 100644 --- a/src/composer.json +++ b/src/composer.json @@ -11,20 +11,20 @@ "license": "BSL-1.0", "require": { "php": "^8.3", - "aws/aws-sdk-php": "^3.320.1", + "aws/aws-sdk-php": "^3.321.11", "cloudinary-labs/cloudinary-laravel": "^2.2.1", "elasticsearch/elasticsearch": "^8.15.0", "firebase/php-jwt": "^6.10.1", "guzzlehttp/guzzle": "^7.9.2", "inertiajs/inertia-laravel": "^1.3.0", - "laravel/framework": "v11.20.0", - "laravel/horizon": "^5.27.1", - "laravel/octane": "^2.5.3", - "laravel/reverb": "^1.1.0", + "laravel/framework": "v11.23.5", + "laravel/horizon": "^5.28.1", + "laravel/octane": "^2.5.5", + "laravel/reverb": "^1.3.0", "laravel/sanctum": "^4.0.2", "laravel/tinker": "^2.9.0", "nutgram/laravel": "^1.4.2", - "opcodesio/log-viewer": "^3.10.2", + "opcodesio/log-viewer": "^3.11.1", "pusher/pusher-php-server": "^7.2.4", "spatie/laravel-permission": "^6.9.0", "tightenco/ziggy": "^2.3.0", @@ -36,14 +36,14 @@ "fakerphp/faker": "^1.23.1", "infection/infection": "^0.27.11", "larastan/larastan": "^2.9.8", - "laravel/breeze": "^2.1.3", - "laravel/pint": "^1.17.2", + "laravel/breeze": "^2.2.0", + "laravel/pint": "^1.17.3", "mockery/mockery": "^1.6.12", "nunomaduro/collision": "^8.4.0", - "phpunit/phpunit": "^10.5.30", + "phpunit/phpunit": "^10.5.34", "psalm/plugin-laravel": "^2.11.0", "spatie/laravel-ignition": "^2.8.0", - "vimeo/psalm": "^5.25.0" + "vimeo/psalm": "^5.26.1" }, "autoload": { "psr-4": { diff --git a/src/composer.lock b/src/composer.lock index 0c8068d1..3e859ec4 100644 --- a/src/composer.lock +++ b/src/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": "62d57e15f185c388017461ec46d9465c", + "content-hash": "3200a9c145e272a8300725c557adbfd3", "packages": [ { "name": "aws/aws-crt-php", @@ -62,16 +62,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.320.1", + "version": "3.321.11", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "653549ab0e274747a46a96fd375df642704f21e2" + "reference": "bbd357d246350ffcd0dd8df30951d2d46c5ddadb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/653549ab0e274747a46a96fd375df642704f21e2", - "reference": "653549ab0e274747a46a96fd375df642704f21e2", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/bbd357d246350ffcd0dd8df30951d2d46c5ddadb", + "reference": "bbd357d246350ffcd0dd8df30951d2d46c5ddadb", "shasum": "" }, "require": { @@ -154,9 +154,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.320.1" + "source": "https://github.com/aws/aws-sdk-php/tree/3.321.11" }, - "time": "2024-08-15T18:07:13+00:00" + "time": "2024-09-13T18:05:10+00:00" }, { "name": "brick/math", @@ -1834,16 +1834,16 @@ }, { "name": "laminas/laminas-diactoros", - "version": "3.3.1", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45" + "reference": "2cce7e77ca4c6c4183e9e8d4edeba483afc14487" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45", - "reference": "74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/2cce7e77ca4c6c4183e9e8d4edeba483afc14487", + "reference": "2cce7e77ca4c6c4183e9e8d4edeba483afc14487", "shasum": "" }, "require": { @@ -1852,7 +1852,7 @@ "psr/http-message": "^1.1 || ^2.0" }, "provide": { - "psr/http-factory-implementation": "^1.1 || ^2.0", + "psr/http-factory-implementation": "^1.0", "psr/http-message-implementation": "^1.1 || ^2.0" }, "require-dev": { @@ -1860,12 +1860,12 @@ "ext-dom": "*", "ext-gd": "*", "ext-libxml": "*", - "http-interop/http-factory-tests": "^0.9.0", + "http-interop/http-factory-tests": "^2.2.0", "laminas/laminas-coding-standard": "~2.5.0", - "php-http/psr7-integration-tests": "^1.3", - "phpunit/phpunit": "^9.6.16", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.22.1" + "php-http/psr7-integration-tests": "^1.4.0", + "phpunit/phpunit": "^10.5.31", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.25.0" }, "type": "library", "extra": { @@ -1915,20 +1915,20 @@ "type": "community_bridge" } ], - "time": "2024-02-16T16:06:16+00:00" + "time": "2024-09-11T00:55:07+00:00" }, { "name": "laravel/framework", - "version": "v11.20.0", + "version": "v11.23.5", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "3cd7593dd9b67002fc416b46616f4d4d1da3e571" + "reference": "16b31ab0e1dad5cb2ed6dcc1818c02f02fc48453" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/3cd7593dd9b67002fc416b46616f4d4d1da3e571", - "reference": "3cd7593dd9b67002fc416b46616f4d4d1da3e571", + "url": "https://api.github.com/repos/laravel/framework/zipball/16b31ab0e1dad5cb2ed6dcc1818c02f02fc48453", + "reference": "16b31ab0e1dad5cb2ed6dcc1818c02f02fc48453", "shasum": "" }, "require": { @@ -1990,6 +1990,7 @@ "illuminate/bus": "self.version", "illuminate/cache": "self.version", "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", "illuminate/conditionable": "self.version", "illuminate/config": "self.version", "illuminate/console": "self.version", @@ -2032,7 +2033,7 @@ "league/flysystem-sftp-v3": "^3.0", "mockery/mockery": "^1.6", "nyholm/psr7": "^1.2", - "orchestra/testbench-core": "^9.1.5", + "orchestra/testbench-core": "^9.4.0", "pda/pheanstalk": "^5.0", "phpstan/phpstan": "^1.11.5", "phpunit/phpunit": "^10.5|^11.0", @@ -2090,6 +2091,7 @@ "src/Illuminate/Events/functions.php", "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", "src/Illuminate/Support/helpers.php" ], "psr-4": { @@ -2121,20 +2123,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-08-06T14:39:21+00:00" + "time": "2024-09-13T13:36:30+00:00" }, { "name": "laravel/horizon", - "version": "v5.27.1", + "version": "v5.28.1", "source": { "type": "git", "url": "https://github.com/laravel/horizon.git", - "reference": "184449be3eb296ab16c13a69ce22049f32d0fc2c" + "reference": "9d2c4eaeb11408384401f8a7d1b0ea4c76554f3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/184449be3eb296ab16c13a69ce22049f32d0fc2c", - "reference": "184449be3eb296ab16c13a69ce22049f32d0fc2c", + "url": "https://api.github.com/repos/laravel/horizon/zipball/9d2c4eaeb11408384401f8a7d1b0ea4c76554f3f", + "reference": "9d2c4eaeb11408384401f8a7d1b0ea4c76554f3f", "shasum": "" }, "require": { @@ -2198,22 +2200,22 @@ ], "support": { "issues": "https://github.com/laravel/horizon/issues", - "source": "https://github.com/laravel/horizon/tree/v5.27.1" + "source": "https://github.com/laravel/horizon/tree/v5.28.1" }, - "time": "2024-08-05T14:23:30+00:00" + "time": "2024-09-04T14:06:50+00:00" }, { "name": "laravel/octane", - "version": "v2.5.3", + "version": "v2.5.5", "source": { "type": "git", "url": "https://github.com/laravel/octane.git", - "reference": "2b5b4d08982cb33a692d467ca96c5bb2f0fcd9ae" + "reference": "a6cb30a609a997386533201344196f01bcd90e2b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/octane/zipball/2b5b4d08982cb33a692d467ca96c5bb2f0fcd9ae", - "reference": "2b5b4d08982cb33a692d467ca96c5bb2f0fcd9ae", + "url": "https://api.github.com/repos/laravel/octane/zipball/a6cb30a609a997386533201344196f01bcd90e2b", + "reference": "a6cb30a609a997386533201344196f01bcd90e2b", "shasum": "" }, "require": { @@ -2290,20 +2292,20 @@ "issues": "https://github.com/laravel/octane/issues", "source": "https://github.com/laravel/octane" }, - "time": "2024-08-05T13:53:57+00:00" + "time": "2024-09-11T20:54:55+00:00" }, { "name": "laravel/prompts", - "version": "v0.1.24", + "version": "v0.1.25", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "409b0b4305273472f3754826e68f4edbd0150149" + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/409b0b4305273472f3754826e68f4edbd0150149", - "reference": "409b0b4305273472f3754826e68f4edbd0150149", + "url": "https://api.github.com/repos/laravel/prompts/zipball/7b4029a84c37cb2725fc7f011586e2997040bc95", + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95", "shasum": "" }, "require": { @@ -2346,22 +2348,22 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.1.24" + "source": "https://github.com/laravel/prompts/tree/v0.1.25" }, - "time": "2024-06-17T13:58:22+00:00" + "time": "2024-08-12T22:06:33+00:00" }, { "name": "laravel/reverb", - "version": "v1.1.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/laravel/reverb.git", - "reference": "f24dda9de689395e83f3eab9aafa134d08222c2b" + "reference": "46bb5dd0b14ba0ae9d3dd740e60bca279b689a63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/reverb/zipball/f24dda9de689395e83f3eab9aafa134d08222c2b", - "reference": "f24dda9de689395e83f3eab9aafa134d08222c2b", + "url": "https://api.github.com/repos/laravel/reverb/zipball/46bb5dd0b14ba0ae9d3dd740e60bca279b689a63", + "reference": "46bb5dd0b14ba0ae9d3dd740e60bca279b689a63", "shasum": "" }, "require": { @@ -2428,9 +2430,9 @@ ], "support": { "issues": "https://github.com/laravel/reverb/issues", - "source": "https://github.com/laravel/reverb/tree/v1.1.0" + "source": "https://github.com/laravel/reverb/tree/v1.3.0" }, - "time": "2024-08-06T13:45:39+00:00" + "time": "2024-09-03T10:04:47+00:00" }, { "name": "laravel/sanctum", @@ -3102,16 +3104,16 @@ }, { "name": "mtdowling/jmespath.php", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b" + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/bbb69a935c2cbb0c03d7f481a238027430f6440b", - "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc", "shasum": "" }, "require": { @@ -3128,7 +3130,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { @@ -3162,22 +3164,22 @@ ], "support": { "issues": "https://github.com/jmespath/jmespath.php/issues", - "source": "https://github.com/jmespath/jmespath.php/tree/2.7.0" + "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0" }, - "time": "2023-08-25T10:54:48+00:00" + "time": "2024-09-04T18:46:31+00:00" }, { "name": "nesbot/carbon", - "version": "3.7.0", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "cb4374784c87d0a0294e8513a52eb63c0aff3139" + "reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/cb4374784c87d0a0294e8513a52eb63c0aff3139", - "reference": "cb4374784c87d0a0294e8513a52eb63c0aff3139", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bbd3eef89af8ba66a3aa7952b5439168fbcc529f", + "reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f", "shasum": "" }, "require": { @@ -3270,7 +3272,7 @@ "type": "tidelift" } ], - "time": "2024-07-16T22:29:20+00:00" + "time": "2024-08-19T06:22:39+00:00" }, { "name": "nette/schema", @@ -3478,16 +3480,16 @@ }, { "name": "nunomaduro/termwind", - "version": "v2.0.1", + "version": "v2.1.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a" + "reference": "e5f21eade88689536c0cdad4c3cd75f3ed26e01a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/58c4c58cf23df7f498daeb97092e34f5259feb6a", - "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/e5f21eade88689536c0cdad4c3cd75f3ed26e01a", + "reference": "e5f21eade88689536c0cdad4c3cd75f3ed26e01a", "shasum": "" }, "require": { @@ -3497,11 +3499,11 @@ }, "require-dev": { "ergebnis/phpstan-rules": "^2.2.0", - "illuminate/console": "^11.0.0", - "laravel/pint": "^1.14.0", - "mockery/mockery": "^1.6.7", - "pestphp/pest": "^2.34.1", - "phpstan/phpstan": "^1.10.59", + "illuminate/console": "^11.1.1", + "laravel/pint": "^1.15.0", + "mockery/mockery": "^1.6.11", + "pestphp/pest": "^2.34.6", + "phpstan/phpstan": "^1.10.66", "phpstan/phpstan-strict-rules": "^1.5.2", "symfony/var-dumper": "^7.0.4", "thecodingmachine/phpstan-strict-rules": "^1.0.0" @@ -3546,7 +3548,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.0.1" + "source": "https://github.com/nunomaduro/termwind/tree/v2.1.0" }, "funding": [ { @@ -3562,7 +3564,7 @@ "type": "github" } ], - "time": "2024-03-06T16:17:14+00:00" + "time": "2024-09-05T15:25:50+00:00" }, { "name": "nutgram/hydrator", @@ -3711,16 +3713,16 @@ }, { "name": "nutgram/nutgram", - "version": "4.26.0", + "version": "4.27.0", "source": { "type": "git", "url": "https://github.com/nutgram/nutgram.git", - "reference": "cc853b9712d33720c7c33358bd3f36cddf6a2783" + "reference": "b0232bcf6e65ed7abfbc17f1d669ebbd0fe7c968" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nutgram/nutgram/zipball/cc853b9712d33720c7c33358bd3f36cddf6a2783", - "reference": "cc853b9712d33720c7c33358bd3f36cddf6a2783", + "url": "https://api.github.com/repos/nutgram/nutgram/zipball/b0232bcf6e65ed7abfbc17f1d669ebbd0fe7c968", + "reference": "b0232bcf6e65ed7abfbc17f1d669ebbd0fe7c968", "shasum": "" }, "require": { @@ -3779,7 +3781,7 @@ ], "support": { "issues": "https://github.com/nutgram/nutgram/issues", - "source": "https://github.com/nutgram/nutgram/tree/4.26.0" + "source": "https://github.com/nutgram/nutgram/tree/4.27.0" }, "funding": [ { @@ -3791,20 +3793,20 @@ "type": "github" } ], - "time": "2024-08-15T12:28:40+00:00" + "time": "2024-09-06T21:47:22+00:00" }, { "name": "opcodesio/log-viewer", - "version": "v3.10.2", + "version": "v3.11.1", "source": { "type": "git", "url": "https://github.com/opcodesio/log-viewer.git", - "reference": "8d44a049fce71753905ac4cd35e01c00544b17cb" + "reference": "608cde8a5fbac1e9959c060b780ef3ed26598aae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opcodesio/log-viewer/zipball/8d44a049fce71753905ac4cd35e01c00544b17cb", - "reference": "8d44a049fce71753905ac4cd35e01c00544b17cb", + "url": "https://api.github.com/repos/opcodesio/log-viewer/zipball/608cde8a5fbac1e9959c060b780ef3ed26598aae", + "reference": "608cde8a5fbac1e9959c060b780ef3ed26598aae", "shasum": "" }, "require": { @@ -3867,7 +3869,7 @@ ], "support": { "issues": "https://github.com/opcodesio/log-viewer/issues", - "source": "https://github.com/opcodesio/log-viewer/tree/v3.10.2" + "source": "https://github.com/opcodesio/log-viewer/tree/v3.11.1" }, "funding": [ { @@ -3879,7 +3881,7 @@ "type": "github" } ], - "time": "2024-08-03T17:20:45+00:00" + "time": "2024-08-23T07:25:44+00:00" }, { "name": "opcodesio/mail-parser", @@ -4774,16 +4776,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -4818,9 +4820,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "psr/simple-cache", @@ -6039,16 +6041,16 @@ }, { "name": "symfony/console", - "version": "v7.1.3", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9" + "reference": "1eed7af6961d763e7832e874d7f9b21c3ea9c111" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", - "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", + "url": "https://api.github.com/repos/symfony/console/zipball/1eed7af6961d763e7832e874d7f9b21c3ea9c111", + "reference": "1eed7af6961d763e7832e874d7f9b21c3ea9c111", "shasum": "" }, "require": { @@ -6112,7 +6114,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.3" + "source": "https://github.com/symfony/console/tree/v7.1.4" }, "funding": [ { @@ -6128,7 +6130,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:41:01+00:00" + "time": "2024-08-15T22:48:53+00:00" }, { "name": "symfony/css-selector", @@ -6495,16 +6497,16 @@ }, { "name": "symfony/finder", - "version": "v7.1.3", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "717c6329886f32dc65e27461f80f2a465412fdca" + "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/717c6329886f32dc65e27461f80f2a465412fdca", - "reference": "717c6329886f32dc65e27461f80f2a465412fdca", + "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", + "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", "shasum": "" }, "require": { @@ -6539,7 +6541,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.3" + "source": "https://github.com/symfony/finder/tree/v7.1.4" }, "funding": [ { @@ -6555,7 +6557,7 @@ "type": "tidelift" } ], - "time": "2024-07-24T07:08:44+00:00" + "time": "2024-08-13T14:28:19+00:00" }, { "name": "symfony/http-foundation", @@ -6636,16 +6638,16 @@ }, { "name": "symfony/http-kernel", - "version": "v7.1.3", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "db9702f3a04cc471ec8c70e881825db26ac5f186" + "reference": "6efcbd1b3f444f631c386504fc83eeca25963747" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/db9702f3a04cc471ec8c70e881825db26ac5f186", - "reference": "db9702f3a04cc471ec8c70e881825db26ac5f186", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6efcbd1b3f444f631c386504fc83eeca25963747", + "reference": "6efcbd1b3f444f631c386504fc83eeca25963747", "shasum": "" }, "require": { @@ -6730,7 +6732,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.1.3" + "source": "https://github.com/symfony/http-kernel/tree/v7.1.4" }, "funding": [ { @@ -6746,7 +6748,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T14:58:15+00:00" + "time": "2024-08-30T17:02:28+00:00" }, { "name": "symfony/mailer", @@ -6830,16 +6832,16 @@ }, { "name": "symfony/mime", - "version": "v7.1.2", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "26a00b85477e69a4bab63b66c5dce64f18b0cbfc" + "reference": "ccaa6c2503db867f472a587291e764d6a1e58758" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/26a00b85477e69a4bab63b66c5dce64f18b0cbfc", - "reference": "26a00b85477e69a4bab63b66c5dce64f18b0cbfc", + "url": "https://api.github.com/repos/symfony/mime/zipball/ccaa6c2503db867f472a587291e764d6a1e58758", + "reference": "ccaa6c2503db867f472a587291e764d6a1e58758", "shasum": "" }, "require": { @@ -6894,7 +6896,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.1.2" + "source": "https://github.com/symfony/mime/tree/v7.1.4" }, "funding": [ { @@ -6910,24 +6912,24 @@ "type": "tidelift" } ], - "time": "2024-06-28T10:03:55+00:00" + "time": "2024-08-13T14:28:19+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -6973,7 +6975,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -6989,24 +6991,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -7051,7 +7053,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -7067,26 +7069,25 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c" + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", - "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" }, "suggest": { "ext-intl": "For best performance" @@ -7135,7 +7136,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" }, "funding": [ { @@ -7151,24 +7152,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -7216,7 +7217,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -7232,24 +7233,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -7296,80 +7297,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-06-19T12:30:46+00:00" - }, - { - "name": "symfony/polyfill-php72", - "version": "v1.30.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "10112722600777e02d2745716b70c5db4ca70442" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/10112722600777e02d2745716b70c5db4ca70442", - "reference": "10112722600777e02d2745716b70c5db4ca70442", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - } - }, - "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 7.2+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -7385,24 +7313,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -7449,7 +7377,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" }, "funding": [ { @@ -7465,24 +7393,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af" + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af", - "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -7525,7 +7453,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" }, "funding": [ { @@ -7541,24 +7469,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php82", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php82.git", - "reference": "77ff49780f56906788a88974867ed68bc49fae5b" + "reference": "5d2ed36f7734637dacc025f179698031951b1692" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/77ff49780f56906788a88974867ed68bc49fae5b", - "reference": "77ff49780f56906788a88974867ed68bc49fae5b", + "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/5d2ed36f7734637dacc025f179698031951b1692", + "reference": "5d2ed36f7734637dacc025f179698031951b1692", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -7601,7 +7529,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php82/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php82/tree/v1.31.0" }, "funding": [ { @@ -7617,24 +7545,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9" + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", - "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -7677,7 +7605,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0" }, "funding": [ { @@ -7693,24 +7621,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:35:24+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9" + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/2ba1f33797470debcda07fe9dce20a0003df18e9", - "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-uuid": "*" @@ -7756,7 +7684,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" }, "funding": [ { @@ -7772,7 +7700,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", @@ -7837,16 +7765,16 @@ }, { "name": "symfony/psr-http-message-bridge", - "version": "v7.1.3", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "1365d10f5476f74a27cf9c2d1eee70c069019db0" + "reference": "405a7bcd872f1563966f64be19f1362d94ce71ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/1365d10f5476f74a27cf9c2d1eee70c069019db0", - "reference": "1365d10f5476f74a27cf9c2d1eee70c069019db0", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/405a7bcd872f1563966f64be19f1362d94ce71ab", + "reference": "405a7bcd872f1563966f64be19f1362d94ce71ab", "shasum": "" }, "require": { @@ -7900,7 +7828,7 @@ "psr-7" ], "support": { - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.1.3" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.1.4" }, "funding": [ { @@ -7916,20 +7844,20 @@ "type": "tidelift" } ], - "time": "2024-07-17T06:10:24+00:00" + "time": "2024-08-15T22:48:53+00:00" }, { "name": "symfony/routing", - "version": "v7.1.3", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "8a908a3f22d5a1b5d297578c2ceb41b02fa916d0" + "reference": "1500aee0094a3ce1c92626ed8cf3c2037e86f5a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/8a908a3f22d5a1b5d297578c2ceb41b02fa916d0", - "reference": "8a908a3f22d5a1b5d297578c2ceb41b02fa916d0", + "url": "https://api.github.com/repos/symfony/routing/zipball/1500aee0094a3ce1c92626ed8cf3c2037e86f5a7", + "reference": "1500aee0094a3ce1c92626ed8cf3c2037e86f5a7", "shasum": "" }, "require": { @@ -7981,7 +7909,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.1.3" + "source": "https://github.com/symfony/routing/tree/v7.1.4" }, "funding": [ { @@ -7997,7 +7925,7 @@ "type": "tidelift" } ], - "time": "2024-07-17T06:10:24+00:00" + "time": "2024-08-29T08:16:25+00:00" }, { "name": "symfony/service-contracts", @@ -8084,16 +8012,16 @@ }, { "name": "symfony/string", - "version": "v7.1.3", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ea272a882be7f20cad58d5d78c215001617b7f07" + "reference": "6cd670a6d968eaeb1c77c2e76091c45c56bc367b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07", - "reference": "ea272a882be7f20cad58d5d78c215001617b7f07", + "url": "https://api.github.com/repos/symfony/string/zipball/6cd670a6d968eaeb1c77c2e76091c45c56bc367b", + "reference": "6cd670a6d968eaeb1c77c2e76091c45c56bc367b", "shasum": "" }, "require": { @@ -8151,7 +8079,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.3" + "source": "https://github.com/symfony/string/tree/v7.1.4" }, "funding": [ { @@ -8167,7 +8095,7 @@ "type": "tidelift" } ], - "time": "2024-07-22T10:25:37+00:00" + "time": "2024-08-12T09:59:40+00:00" }, { "name": "symfony/translation", @@ -8343,16 +8271,16 @@ }, { "name": "symfony/uid", - "version": "v7.1.1", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "bb59febeecc81528ff672fad5dab7f06db8c8277" + "reference": "82177535395109075cdb45a70533aa3d7a521cdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/bb59febeecc81528ff672fad5dab7f06db8c8277", - "reference": "bb59febeecc81528ff672fad5dab7f06db8c8277", + "url": "https://api.github.com/repos/symfony/uid/zipball/82177535395109075cdb45a70533aa3d7a521cdf", + "reference": "82177535395109075cdb45a70533aa3d7a521cdf", "shasum": "" }, "require": { @@ -8397,7 +8325,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.1.1" + "source": "https://github.com/symfony/uid/tree/v7.1.4" }, "funding": [ { @@ -8413,20 +8341,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-08-12T09:59:40+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.1.3", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "86af4617cca75a6e28598f49ae0690f3b9d4591f" + "reference": "a5fa7481b199090964d6fd5dab6294d5a870c7aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/86af4617cca75a6e28598f49ae0690f3b9d4591f", - "reference": "86af4617cca75a6e28598f49ae0690f3b9d4591f", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a5fa7481b199090964d6fd5dab6294d5a870c7aa", + "reference": "a5fa7481b199090964d6fd5dab6294d5a870c7aa", "shasum": "" }, "require": { @@ -8480,7 +8408,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.1.3" + "source": "https://github.com/symfony/var-dumper/tree/v7.1.4" }, "funding": [ { @@ -8496,7 +8424,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:41:01+00:00" + "time": "2024-08-30T16:12:47+00:00" }, { "name": "tightenco/ziggy", @@ -9457,26 +9385,26 @@ }, { "name": "composer/pcre", - "version": "3.2.0", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90" + "reference": "63aaeac21d7e775ff9bc9d45021e1745c97521c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/ea4ab6f9580a4fd221e0418f2c357cdd39102a90", - "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90", + "url": "https://api.github.com/repos/composer/pcre/zipball/63aaeac21d7e775ff9bc9d45021e1745c97521c4", + "reference": "63aaeac21d7e775ff9bc9d45021e1745c97521c4", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, "conflict": { - "phpstan/phpstan": "<1.11.8" + "phpstan/phpstan": "<1.11.10" }, "require-dev": { - "phpstan/phpstan": "^1.11.8", + "phpstan/phpstan": "^1.11.10", "phpstan/phpstan-strict-rules": "^1.1", "phpunit/phpunit": "^8 || ^9" }, @@ -9516,7 +9444,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.2.0" + "source": "https://github.com/composer/pcre/tree/3.3.1" }, "funding": [ { @@ -9532,7 +9460,7 @@ "type": "tidelift" } ], - "time": "2024-07-25T09:36:02+00:00" + "time": "2024-08-27T18:44:43+00:00" }, { "name": "composer/semver", @@ -9931,16 +9859,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42" + "reference": "8520451a140d3f46ac33042715115e290cf5785f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/f92996c4d5c1a696a6a970e20f7c4216200fcc42", - "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", "shasum": "" }, "require": { @@ -9980,7 +9908,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.1.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" }, "funding": [ { @@ -9988,7 +9916,7 @@ "type": "github" } ], - "time": "2024-02-07T09:43:46+00:00" + "time": "2024-08-06T10:04:20+00:00" }, { "name": "filp/whoops", @@ -10589,16 +10517,16 @@ }, { "name": "laravel/breeze", - "version": "v2.1.3", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/laravel/breeze.git", - "reference": "1446994ea5042e0b340e39f1e4629656de843058" + "reference": "8f4176b48eb9b7c4ddc5012d19e94003461f03da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/breeze/zipball/1446994ea5042e0b340e39f1e4629656de843058", - "reference": "1446994ea5042e0b340e39f1e4629656de843058", + "url": "https://api.github.com/repos/laravel/breeze/zipball/8f4176b48eb9b7c4ddc5012d19e94003461f03da", + "reference": "8f4176b48eb9b7c4ddc5012d19e94003461f03da", "shasum": "" }, "require": { @@ -10645,20 +10573,20 @@ "issues": "https://github.com/laravel/breeze/issues", "source": "https://github.com/laravel/breeze" }, - "time": "2024-07-17T13:05:17+00:00" + "time": "2024-09-11T20:19:47+00:00" }, { "name": "laravel/pint", - "version": "v1.17.2", + "version": "v1.17.3", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110" + "reference": "9d77be916e145864f10788bb94531d03e1f7b482" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e8a88130a25e3f9d4d5785e6a1afca98268ab110", - "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110", + "url": "https://api.github.com/repos/laravel/pint/zipball/9d77be916e145864f10788bb94531d03e1f7b482", + "reference": "9d77be916e145864f10788bb94531d03e1f7b482", "shasum": "" }, "require": { @@ -10669,13 +10597,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.61.1", - "illuminate/view": "^10.48.18", + "friendsofphp/php-cs-fixer": "^3.64.0", + "illuminate/view": "^10.48.20", "larastan/larastan": "^2.9.8", "laravel-zero/framework": "^10.4.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.35.0" + "pestphp/pest": "^2.35.1" }, "bin": [ "builds/pint" @@ -10711,20 +10639,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-08-06T15:11:54+00:00" + "time": "2024-09-03T15:00:28+00:00" }, { "name": "maximebf/debugbar", - "version": "v1.22.3", + "version": "v1.22.5", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "7aa9a27a0b1158ed5ad4e7175e8d3aee9a818b96" + "reference": "1b5cabe0ce013134cf595bfa427bbf2f6abcd989" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/7aa9a27a0b1158ed5ad4e7175e8d3aee9a818b96", - "reference": "7aa9a27a0b1158ed5ad4e7175e8d3aee9a818b96", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/1b5cabe0ce013134cf595bfa427bbf2f6abcd989", + "reference": "1b5cabe0ce013134cf595bfa427bbf2f6abcd989", "shasum": "" }, "require": { @@ -10777,9 +10705,9 @@ ], "support": { "issues": "https://github.com/maximebf/php-debugbar/issues", - "source": "https://github.com/maximebf/php-debugbar/tree/v1.22.3" + "source": "https://github.com/maximebf/php-debugbar/tree/v1.22.5" }, - "time": "2024-04-03T19:39:26+00:00" + "time": "2024-09-09T08:05:55+00:00" }, { "name": "mockery/mockery", @@ -10926,16 +10854,16 @@ }, { "name": "netresearch/jsonmapper", - "version": "v4.4.1", + "version": "v4.5.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0" + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/132c75c7dd83e45353ebb9c6c9f591952995bbf0", - "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5", "shasum": "" }, "require": { @@ -10971,9 +10899,9 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.5.0" }, - "time": "2024-01-31T06:18:54+00:00" + "time": "2024-09-08T10:13:13+00:00" }, { "name": "nunomaduro/collision", @@ -11152,16 +11080,16 @@ }, { "name": "orchestra/testbench-core", - "version": "v9.3.0", + "version": "v9.4.1", "source": { "type": "git", "url": "https://github.com/orchestral/testbench-core.git", - "reference": "d66217119f326f82190a3638739a92a985ad73b3" + "reference": "b1f1a046cafd07d2a7b0cb96e9a2911aee160515" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/d66217119f326f82190a3638739a92a985ad73b3", - "reference": "d66217119f326f82190a3638739a92a985ad73b3", + "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/b1f1a046cafd07d2a7b0cb96e9a2911aee160515", + "reference": "b1f1a046cafd07d2a7b0cb96e9a2911aee160515", "shasum": "" }, "require": { @@ -11172,6 +11100,7 @@ "conflict": { "brianium/paratest": "<7.3.0 || >=8.0.0", "laravel/framework": "<11.11.0 || >=12.0.0", + "laravel/serializable-closure": "<1.3.0 || >=2.0.0", "nunomaduro/collision": "<8.0.0 || >=9.0.0", "phpunit/phpunit": "<10.5.0 || 11.0.0 || >=11.4.0" }, @@ -11237,7 +11166,7 @@ "issues": "https://github.com/orchestral/testbench/issues", "source": "https://github.com/orchestral/testbench-core" }, - "time": "2024-08-14T05:55:35+00:00" + "time": "2024-09-12T10:54:24+00:00" }, { "name": "phar-io/manifest", @@ -11534,16 +11463,16 @@ }, { "name": "phpmyadmin/sql-parser", - "version": "5.9.1", + "version": "5.10.0", "source": { "type": "git", "url": "https://github.com/phpmyadmin/sql-parser.git", - "reference": "169a9f11f1957ea36607c9b29eac1b48679f1ecc" + "reference": "91d980ab76c3f152481e367f62b921adc38af451" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/169a9f11f1957ea36607c9b29eac1b48679f1ecc", - "reference": "169a9f11f1957ea36607c9b29eac1b48679f1ecc", + "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/91d980ab76c3f152481e367f62b921adc38af451", + "reference": "91d980ab76c3f152481e367f62b921adc38af451", "shasum": "" }, "require": { @@ -11617,20 +11546,20 @@ "type": "other" } ], - "time": "2024-08-13T19:01:01+00:00" + "time": "2024-08-29T20:56:34+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.29.1", + "version": "1.30.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" + "reference": "51b95ec8670af41009e2b2b56873bad96682413e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/51b95ec8670af41009e2b2b56873bad96682413e", + "reference": "51b95ec8670af41009e2b2b56873bad96682413e", "shasum": "" }, "require": { @@ -11662,22 +11591,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.30.1" }, - "time": "2024-05-31T08:52:43+00:00" + "time": "2024-09-07T20:13:05+00:00" }, { "name": "phpstan/phpstan", - "version": "1.11.10", + "version": "1.12.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f" + "reference": "0fcbf194ab63d8159bb70d9aa3e1350051632009" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/640410b32995914bde3eed26fa89552f9c2c082f", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0fcbf194ab63d8159bb70d9aa3e1350051632009", + "reference": "0fcbf194ab63d8159bb70d9aa3e1350051632009", "shasum": "" }, "require": { @@ -11722,36 +11651,36 @@ "type": "github" } ], - "time": "2024-08-08T09:02:50+00:00" + "time": "2024-09-09T08:10:35+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.15", + "version": "10.1.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae" + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", - "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=8.1", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-text-template": "^3.0", - "sebastian/code-unit-reverse-lookup": "^3.0", - "sebastian/complexity": "^3.0", - "sebastian/environment": "^6.0", - "sebastian/lines-of-code": "^2.0", - "sebastian/version": "^4.0", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { "phpunit/phpunit": "^10.1" @@ -11763,7 +11692,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1-dev" + "dev-main": "10.1.x-dev" } }, "autoload": { @@ -11792,7 +11721,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" }, "funding": [ { @@ -11800,7 +11729,7 @@ "type": "github" } ], - "time": "2024-06-29T08:25:15+00:00" + "time": "2024-08-22T04:31:57+00:00" }, { "name": "phpunit/php-file-iterator", @@ -12047,16 +11976,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.30", + "version": "10.5.34", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b15524febac0153876b4ba9aab3326c2ee94c897" + "reference": "3c69d315bdf79080c8e115b69d1961c6905b0e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b15524febac0153876b4ba9aab3326c2ee94c897", - "reference": "b15524febac0153876b4ba9aab3326c2ee94c897", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3c69d315bdf79080c8e115b69d1961c6905b0e18", + "reference": "3c69d315bdf79080c8e115b69d1961c6905b0e18", "shasum": "" }, "require": { @@ -12070,7 +11999,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.15", + "phpunit/php-code-coverage": "^10.1.16", "phpunit/php-file-iterator": "^4.1.0", "phpunit/php-invoker": "^4.0.0", "phpunit/php-text-template": "^3.0.1", @@ -12128,7 +12057,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.30" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.34" }, "funding": [ { @@ -12144,7 +12073,7 @@ "type": "tidelift" } ], - "time": "2024-08-13T06:09:37+00:00" + "time": "2024-09-13T05:19:38+00:00" }, { "name": "psalm/plugin-laravel", @@ -13968,16 +13897,16 @@ }, { "name": "vimeo/psalm", - "version": "5.25.0", + "version": "5.26.1", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "01a8eb06b9e9cc6cfb6a320bf9fb14331919d505" + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/01a8eb06b9e9cc6cfb6a320bf9fb14331919d505", - "reference": "01a8eb06b9e9cc6cfb6a320bf9fb14331919d505", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", "shasum": "" }, "require": { @@ -13998,7 +13927,7 @@ "felixfbecker/language-server-protocol": "^1.5.2", "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.16", + "nikic/php-parser": "^4.17", "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", "sebastian/diff": "^4.0 || ^5.0 || ^6.0", "spatie/array-to-xml": "^2.17.0 || ^3.0", @@ -14074,7 +14003,7 @@ "issues": "https://github.com/vimeo/psalm/issues", "source": "https://github.com/vimeo/psalm" }, - "time": "2024-06-16T15:08:35+00:00" + "time": "2024-09-08T18:53:08+00:00" } ], "aliases": [], diff --git a/src/config/cloudinary.php b/src/config/cloudinary.php index bb9a2512..33020079 100644 --- a/src/config/cloudinary.php +++ b/src/config/cloudinary.php @@ -62,17 +62,6 @@ */ 'upload_action' => env('CLOUDINARY_UPLOAD_ACTION'), - /* - |-------------------------------------------------------------------------- - | Default folder - |-------------------------------------------------------------------------- - | - | Default folder to store media on Cloudinary. - | - */ - - 'default_folder' => env('CLOUDINARY_DEFAULT_FOLDER', ''), - /* |-------------------------------------------------------------------------- | Default image @@ -81,5 +70,5 @@ | Default image url that will be used when no image is provided. | */ - 'default_image' => env('CLOUDINARY_DEFAULT_IMAGE', ''), + 'default_image' => env('CLOUDINARY_DEFAULT_IMAGE'), ]; diff --git a/src/config/ide-helper.php b/src/config/ide-helper.php index c506f802..df909432 100644 --- a/src/config/ide-helper.php +++ b/src/config/ide-helper.php @@ -284,7 +284,9 @@ | name of the Relationship, e.g. `'relationName' => RelationShipClass::class`. | */ - 'additional_relation_types' => [], + 'additional_relation_types' => [ + 'oneOfMorphToMany' => \App\Database\Relations\OneOfMorphToMany::class, + ], /* |-------------------------------------------------------------------------- @@ -298,7 +300,9 @@ | e.g. `'relationName' => 'many'`. | */ - 'additional_relation_return_types' => [], + 'additional_relation_return_types' => [ + 'oneOfMorphToMany' => 'one', + ], /* |-------------------------------------------------------------------------- diff --git a/src/database/factories/ImageFactory.php b/src/database/factories/ImageFactory.php index 47f1fc31..e49fb9df 100644 --- a/src/database/factories/ImageFactory.php +++ b/src/database/factories/ImageFactory.php @@ -20,8 +20,9 @@ class ImageFactory extends Factory public function definition(): array { return [ - 'alias' => $this->faker->sentence, - 'path' => $this->faker->imageUrl, + 'path' => $this->faker->imageUrl, + 'name' => $this->faker->word, + 'hash' => $this->faker->sha512(), ]; } } diff --git a/src/database/migrations/2021_12_26_113712_create_images_table.php b/src/database/migrations/2021_12_26_113712_create_images_table.php index dab53936..dbf008f2 100644 --- a/src/database/migrations/2021_12_26_113712_create_images_table.php +++ b/src/database/migrations/2021_12_26_113712_create_images_table.php @@ -14,8 +14,9 @@ public function up(): void { Schema::create('images', function (Blueprint $table) { $table->uuid('id')->primary(); - $table->uuidMorphs('model'); - $table->string('path'); + $table->string('path')->unique(); + $table->string('name'); + $table->string('hash', 128)->unique(); $table->timestamps(); }); } diff --git a/src/database/migrations/2021_12_29_173201_add_alias_column_to_images_table.php b/src/database/migrations/2024_09_10_232009_create_has_images_table.php similarity index 55% rename from src/database/migrations/2021_12_29_173201_add_alias_column_to_images_table.php rename to src/database/migrations/2024_09_10_232009_create_has_images_table.php index a84fc6c7..ee30b768 100644 --- a/src/database/migrations/2021_12_29_173201_add_alias_column_to_images_table.php +++ b/src/database/migrations/2024_09_10_232009_create_has_images_table.php @@ -12,8 +12,12 @@ */ public function up(): void { - Schema::table('images', function (Blueprint $table) { - $table->string('alias')->after('path'); + Schema::create('has_images', function (Blueprint $table) { + $table->foreignUuid('image_id')->constrained(); + $table->uuidMorphs('model'); + $table->timestamps(); + + $table->primary(['image_id', 'model_id', 'model_type']); }); } @@ -22,8 +26,6 @@ public function up(): void */ public function down(): void { - Schema::table('images', function (Blueprint $table) { - $table->dropColumn(['alias']); - }); + Schema::dropIfExists('has_images'); } }; diff --git a/src/database/seeders/DatabaseSeeder.php b/src/database/seeders/DatabaseSeeder.php index cbcfc25f..49543b7b 100644 --- a/src/database/seeders/DatabaseSeeder.php +++ b/src/database/seeders/DatabaseSeeder.php @@ -5,15 +5,22 @@ namespace Database\Seeders; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\DB; +use Throwable; class DatabaseSeeder extends Seeder { /** * Seed the application's database. + * @throws Throwable */ public function run(): void { - $this->call(VoiceActingSeeder::class); - $this->call(RoleSeeder::class); + DB::transaction(function () { + $this->call([ + VoiceActingSeeder::class, + RoleSeeder::class, + ]); + }); } } diff --git a/src/tests/Concerns/Fake/CanCreateFakeAnime.php b/src/tests/Concerns/Fake/CanCreateFakeAnime.php index b7cbacb4..e95eb3c0 100644 --- a/src/tests/Concerns/Fake/CanCreateFakeAnime.php +++ b/src/tests/Concerns/Fake/CanCreateFakeAnime.php @@ -44,7 +44,7 @@ protected function createAnimeWithRelations(array $data = []): Anime { $anime = $this->createAnime($data); - $anime->image()->save(Image::factory()->make()); + $anime->image()->attach(Image::factory()->create()); $anime->genres()->save(Genre::factory()->make()); $anime->voiceActing()->save(VoiceActing::factory()->make()); $anime->urls()->save(AnimeUrl::factory()->make()); diff --git a/src/tests/Feature/Console/Commands/Lists/Anime/GenerateCommandTest.php b/src/tests/Feature/Console/Commands/Lists/Anime/GenerateCommandTest.php index 5c7a7b78..5ec226c4 100644 --- a/src/tests/Feature/Console/Commands/Lists/Anime/GenerateCommandTest.php +++ b/src/tests/Feature/Console/Commands/Lists/Anime/GenerateCommandTest.php @@ -70,7 +70,7 @@ public function testCommandCanGenerateAnimeList(): void new RelationFilter([ 'urls:anime_id,url', 'synonyms:anime_id,name', - 'image:id,model_id,path,alias', + 'image:id,path,name,hash', 'genres:id,name', 'voiceActing:id,name', ]), diff --git a/src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/HasOneOfMorphToManyRelationTest.php b/src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/HasOneOfMorphToManyRelationTest.php new file mode 100644 index 00000000..a39fcca9 --- /dev/null +++ b/src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/HasOneOfMorphToManyRelationTest.php @@ -0,0 +1,125 @@ +foreignUuid('test_relation_id'); + $table->uuidMorphs('test_model'); + }); + } + + public function testCanAttachOneRelation(): void + { + $model = TestModel::query()->create(); + $relation = TestRelation::query()->create(); + + $this->assertNull($model->testRelation); + $this->assertDatabaseCount('has_test_relations', 0); + + $model->testRelation()->attach($relation); + + $model->refresh(); + + $this->assertNotNull($model->testRelation); + $this->assertEquals($relation->id, $model->testRelation->id); + $this->assertDatabaseCount('has_test_relations', 1); + } + + public function testCanDetachOneRelation(): void + { + $model = TestModel::query()->create(); + $relation = TestRelation::query()->create(); + + $this->assertDatabaseCount($model, 1); + $this->assertDatabaseCount($relation, 1); + + $model->testRelation()->attach($relation); + + $this->assertDatabaseCount('has_test_relations', 1); + + $model->testRelation()->detach(); + + $this->assertDatabaseCount($model, 1); + $this->assertDatabaseCount($relation, 1); + $this->assertDatabaseCount('has_test_relations', 0); + } + + public function testCanGetRelation(): void + { + $model = TestModel::query()->create(); + $relation = TestRelation::query()->create(); + + $this->assertNull($model->testRelation); + + $model->testRelation()->attach($relation); + $model->refresh(); + + $this->assertNotNull($model->testRelation); + $this->assertEquals($relation->id, $model->testRelation->id); + } + + public function testWillGetOnlyOneRelation(): void + { + $model = TestModel::query()->create(); + + $firstRelation = TestRelation::query()->create(); + $secondRelation = TestRelation::query()->create(); + + $model->testRelation()->sync([$firstRelation->id, $secondRelation->id]); + $model->refresh(); + + $this->assertNotInstanceOf(Collection::class, $model->testRelation); + $this->assertInstanceOf(TestRelation::class, $model->testRelation); + $this->assertEquals($firstRelation->id, $model->testRelation->id); + } + + public function testWillGetOnlyOneRelationUsingEagerLoading(): void + { + $model = TestModel::query()->create(); + + $firstRelation = TestRelation::query()->create(); + $secondRelation = TestRelation::query()->create(); + + $model->testRelation()->sync([$firstRelation->id, $secondRelation->id]); + + $model->load('testRelation'); + + $this->assertNotInstanceOf(Collection::class, $model->testRelation); + $this->assertInstanceOf(TestRelation::class, $model->testRelation); + $this->assertEquals($firstRelation->id, $model->testRelation->id); + + $modelWith = TestModel::with('testRelation')->first(); + + $this->assertNotInstanceOf(Collection::class, $modelWith->testRelation); + $this->assertInstanceOf(TestRelation::class, $modelWith->testRelation); + $this->assertEquals($firstRelation->id, $modelWith->testRelation->id); + } + + public function testCanGetDefaultRelation(): void + { + $model = TestModel::query()->create(); + + $this->assertDatabaseCount(TestRelation::class, 0); + $this->assertDatabaseCount('has_test_relations', 0); + + $this->assertNull($model->testRelation); + $this->assertNotNull($model->testRelationWithDefaultModel); + $this->assertEquals('default', $model->testRelationWithDefaultModel->content); + } +} diff --git a/src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/Resources/TestModel.php b/src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/Resources/TestModel.php new file mode 100644 index 00000000..7dd24e68 --- /dev/null +++ b/src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/Resources/TestModel.php @@ -0,0 +1,48 @@ +oneOfMorphToMany(TestRelation::class, 'test_model', 'has_test_relations'); + } + + public function testRelationWithDefaultModel(): OneOfMorphToMany + { + return $this->testRelation()->withDefault([ + 'content' => 'default', + ]); + } + + protected static function booted(): void + { + parent::booted(); + + Relation::enforceMorphMap([ + 'test_model' => TestModel::class, + ]); + + Schema::dropIfExists('test_models'); + Schema::create('test_models', function (Blueprint $table) { + $table->uuid('id')->primary(); + $table->string('content')->nullable(); + }); + } +} diff --git a/src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/Resources/TestRelation.php b/src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/Resources/TestRelation.php new file mode 100644 index 00000000..f71403be --- /dev/null +++ b/src/tests/Feature/Models/Concerns/HasOneOfMorphToMany/Resources/TestRelation.php @@ -0,0 +1,38 @@ +morphToMany(TestModel::class, 'test_model', 'has_test_relations'); + } + + protected static function booted(): void + { + parent::booted(); + + Relation::enforceMorphMap([ + 'test_relation' => TestRelation::class, + ]); + + Schema::dropIfExists('test_relations'); + Schema::create('test_relations', function (Blueprint $table) { + $table->uuid('id')->primary(); + }); + } +} diff --git a/src/tests/Feature/Services/ImageServiceTest.php b/src/tests/Feature/Services/ImageServiceTest.php index 2dc96ca7..1141c98e 100644 --- a/src/tests/Feature/Services/ImageServiceTest.php +++ b/src/tests/Feature/Services/ImageServiceTest.php @@ -4,6 +4,7 @@ namespace Tests\Feature\Services; +use App\Models\Image; use App\Services\ImageService; use Cloudinary\Api\Exception\ApiError; use CloudinaryLabs\CloudinaryLaravel\Facades\Cloudinary; @@ -32,6 +33,35 @@ protected function setUp(): void $this->imageService = $this->app->make(ImageService::class); } + public function testWillNotUploadImageIfItIsAlreadyInDatabase(): void + { + // Create anime with no image + $anime = $this->createAnime(); + + // By default, anime will have a default image, but that will be due to `withDefault` method on relation, + // in database there must be no records + $this->assertDatabaseCount(Image::class, 0); + $this->assertEquals(0, $anime->image()->count()); + $this->assertNotNull($anime->image); + $this->assertTrue($anime->image->is_default); + + $image = Image::factory()->create([ + 'path' => $imageContent = $this->faker->imageUrl, + 'hash' => $this->faker->sha512($imageContent), + 'name' => $this->faker->word, + ]); + $this->assertDatabaseCount($image, 1); + + $this->imageService->attachEncodedImageToAnime($this->faker->randomAnimeImage($imageContent), $anime); + $anime->refresh(); + + // If image is already in database, it should not be uploaded again, but should be attached to anime + $this->assertDatabaseCount($image, 1); + $this->assertEquals(1, $anime->image()->count()); + $this->assertFalse($anime->image->is_default); + $this->assertEquals($image->hash, $anime->image->hash); + } + public function testWillNotDeleteImageIfAnimeDontHaveOneOrImageHasDefaultPathFromConfig(): void { // Create anime with no image @@ -39,9 +69,12 @@ public function testWillNotDeleteImageIfAnimeDontHaveOneOrImageHasDefaultPathFro Cloudinary::shouldReceive('destroy')->never(); Cloudinary::shouldReceive('uploadFile')->once(); - Cloudinary::shouldReceive('getSecurePath')->once()->andReturn($this->faker->imageUrl); + Cloudinary::shouldReceive([ + 'getSecurePath' => $this->faker->imageUrl, + 'getPublicId' => $this->faker->word, + ])->once(); - $this->imageService->upsert($this->faker->imageUrl, $anime); + $this->imageService->attachEncodedImageToAnime($this->faker->randomAnimeImage(), $anime); } public function testWillDeleteImageIfAnimeAlreadyHaveOneAndItHasNotADefaultPath(): void @@ -50,12 +83,15 @@ public function testWillDeleteImageIfAnimeAlreadyHaveOneAndItHasNotADefaultPath( Cloudinary::shouldReceive('destroy')->once(); Cloudinary::shouldReceive('uploadFile')->once(); - Cloudinary::shouldReceive('getSecurePath')->once()->andReturn($this->faker->imageUrl); + Cloudinary::shouldReceive([ + 'getSecurePath' => $this->faker->imageUrl, + 'getPublicId' => $this->faker->word, + ])->once(); - $this->imageService->upsert($this->faker->imageUrl, $anime); + $this->imageService->attachEncodedImageToAnime($this->faker->randomAnimeImage(), $anime); } - public function testWillLogErrorIfFailedToUploadImage(): void + public function testWillLogErrorIfFailedToUploadEncodedAnimeImage(): void { $anime = $this->createAnime(); $exception = new ApiError('test'); @@ -69,29 +105,33 @@ public function testWillLogErrorIfFailedToUploadImage(): void 'exception_message' => $exception->getMessage(), ]); - $this->imageService->upsert($this->faker->randomAnimeImage(), $anime); + $this->imageService->attachEncodedImageToAnime($this->faker->randomAnimeImage(), $anime); } - public function testCanUploadImage(): void + public function testCanAttachAndUploadEncodedAnimeImage(): void { $anime = $this->createAnime(); // Ensure that anime has default image $this->assertNotNull($anime->image); - $this->assertTrue($anime->image->default); + $this->assertTrue($anime->image->is_default); Cloudinary::shouldReceive('destroy')->never(); Cloudinary::shouldReceive('uploadFile')->once(); - Cloudinary::shouldReceive('getSecurePath')->once()->andReturn($path = $this->faker->imageUrl); + Cloudinary::shouldReceive([ + 'getSecurePath' => $path = $this->faker->imageUrl, + 'getPublicId' => $this->faker->word, + ])->once(); - $this->imageService->upsert($this->faker->randomAnimeImage(), $anime); + $this->imageService->attachEncodedImageToAnime($this->faker->randomAnimeImage(), $anime); $anime->refresh(); // Ensure that anime has image store in DB and path from CDN - $this->assertFalse($anime->image->default); + $this->assertFalse($anime->image->is_default); $this->assertNotNull($anime->image->id); - $this->assertNotNull($anime->image->alias); + $this->assertNotNull($anime->image->hash); + $this->assertNotNull($anime->image->name); $this->assertEquals($path, $anime->image->path); } } diff --git a/src/tests/Feature/Services/TelegramUserServiceTest.php b/src/tests/Feature/Services/TelegramUserServiceTest.php index 556b677c..c1fdfb65 100644 --- a/src/tests/Feature/Services/TelegramUserServiceTest.php +++ b/src/tests/Feature/Services/TelegramUserServiceTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Feature\Services; +namespace Tests\Feature\Services; use App\DTO\Service\Telegram\User\TelegramUserDTO; use App\Exceptions\Service\Telegram\TelegramUserException; diff --git a/src/tests/Helpers/Faker/Providers/AnimeInformationProvider.php b/src/tests/Helpers/Faker/Providers/AnimeInformationProvider.php index e267cd24..26acd390 100644 --- a/src/tests/Helpers/Faker/Providers/AnimeInformationProvider.php +++ b/src/tests/Helpers/Faker/Providers/AnimeInformationProvider.php @@ -10,9 +10,11 @@ final class AnimeInformationProvider extends Base { - public function randomAnimeImage(): string + public function randomAnimeImage(?string $content = null): string { - return sprintf("data:image/png;base64,%s", base64_encode($this->generator->sentence)); + $extension = $this->generator->randomElement(['png', 'jpg', 'jpeg', 'webp', 'gif']); + + return sprintf("data:image/%s;base64,%s", $extension, base64_encode($content ?? $this->generator->sentence)); } public function randomAnimeRating(int $min = 0, int $max = 10): float diff --git a/src/tests/Helpers/Faker/Providers/HashProvider.php b/src/tests/Helpers/Faker/Providers/HashProvider.php new file mode 100644 index 00000000..adc2f0be --- /dev/null +++ b/src/tests/Helpers/Faker/Providers/HashProvider.php @@ -0,0 +1,15 @@ +generator->sentence); + } +}