diff --git a/CHANGELOG.md b/CHANGELOG.md index 21cf46c6..26ebb222 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +# v5.9.0 +# New +- Documented `public/{slugOrId}` endpoints + - You can now access public bar data via `public/{slugOrId}` endpoints + - Added GET `public/{slugOrId}/menu` endpoint + - Added GET `public/{slugOrId}/cocktails` endpoint + - Added GET `public/{slugOrId}/cocktails/{cocktailId}` endpoint + - Added `is_public` property to `Bar` schema + - If set to `true`, bar will expose public endpoints `/public/{barId}/*` +- Added `is_menu_enabled` to public `Bar` schema + +## Fixes +- Fixed search driver indexing calls when not using search driver +- Default ingredient unit bar setting is not correctly applied when importing data via datapack +- Fixed unique constraint violation when adding ingredients into bar shelf + +# v5.8.1 +## Fixes +- Fixed missing API ability on `import/cocktail` endpoint + # v5.8.0 ## New - Added `bars/{id}/sync-datapack` endpoint diff --git a/README.md b/README.md index 9fb3fc20..41f2a0b8 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,12 @@ This repository only contains the API server, if you are looking for easy to use - Data export support in various formats - Support for multiple ingredient prices - Automatic cocktail price calculation based on ingredients -- SSO Support +- Single sign-on (SSO) support +- Recipe recommendations based on your favorites and tags + +## Documentation + +[Documentation is available here.](https://docs.barassistant.app/) ## Container images @@ -63,13 +68,13 @@ We recommend that you always use the latest major release, as it will always be ## Managed instance -Bar Assistant will always be open-source and MIT-licensed, but if you want to support the project or don't want to self-host, you can try officialy managed instance. Visit [barassistant.app](https://barassistant.app/) for more information about our cloud offering. +Bar Assistant will always be open-source and MIT-licensed, but if you want to support the project or don't want to self-host, you can try our official managed instance. Visit [barassistant.app](https://barassistant.app/) for more information about our cloud offering. ![Cloud offering screenshot](/resources/art/art1.png) -## Documentation +## 3rd Party Integrations -[Documentation is available here.](https://bar-assistant.github.io/docs/) +There's an [unofficial Raycast extension](https://www.raycast.com/stupifier/barassistant) maintained by a [community member](https://github.com/zhdenny). ## Contributing diff --git a/app/External/Export/ToDataPack.php b/app/External/Export/ToDataPack.php index 10148dd1..81dedbce 100644 --- a/app/External/Export/ToDataPack.php +++ b/app/External/Export/ToDataPack.php @@ -65,7 +65,7 @@ public function process(int $barId, ?string $filename = null, ForceUnitConvertEn } $this->dumpCocktails($barId, $zip, $toUnits); - $this->dumpIngredients($barId, $zip); + $this->dumpIngredients($barId, $zip, $toUnits); $this->dumpBaseData($barId, $zip); $this->dumpCalculators($barId, $zip); @@ -111,13 +111,13 @@ private function dumpCocktails(int $barId, ZipArchive &$zip, ?Units $toUnits = n } } - private function dumpIngredients(int $barId, ZipArchive &$zip): void + private function dumpIngredients(int $barId, ZipArchive &$zip, ?Units $toUnits = null): void { $ingredients = Ingredient::with('images.imageable', 'calculator', 'prices.priceCategory', 'ingredientParts.ingredient.parentIngredient', 'ancestors', 'parentIngredient')->where('bar_id', $barId)->get(); /** @var Ingredient $ingredient */ foreach ($ingredients as $ingredient) { - $data = IngredientExternal::fromModel($ingredient, true); + $data = IngredientExternal::fromModel($ingredient, true, $toUnits); /** @var \Kami\Cocktail\Models\Image $img */ foreach ($ingredient->images as $img) { diff --git a/app/External/Import/FromDataPack.php b/app/External/Import/FromDataPack.php index f6b5bdf7..a0778ea7 100644 --- a/app/External/Import/FromDataPack.php +++ b/app/External/Import/FromDataPack.php @@ -76,10 +76,12 @@ public function process(Filesystem $dataDisk, Bar $bar, User $user, ?BarOptionsE $bar->setStatus(BarStatusEnum::Active)->save(); - /** @phpstan-ignore-next-line */ - Ingredient::where('bar_id', $bar->id)->searchable(); - /** @phpstan-ignore-next-line */ - Cocktail::where('bar_id', $bar->id)->searchable(); + if (!empty(config('scout.driver'))) { + /** @phpstan-ignore-next-line */ + Ingredient::where('bar_id', $bar->id)->searchable(); + /** @phpstan-ignore-next-line */ + Cocktail::where('bar_id', $bar->id)->searchable(); + } $timerEnd = microtime(true); @@ -192,6 +194,12 @@ private function importIngredients(Filesystem $dataDisk, Bar $bar, User $user): continue; } + $ingredientUnit = $externalIngredient->units?->value; + $barSettings = $bar->settings ?? []; + if ($externalIngredient->units?->isConvertable() && array_key_exists('default_units', $barSettings)) { + $ingredientUnit = $barSettings['default_units']; + } + $slug = $externalIngredient->id . '-' . $bar->id; $ingredientsToInsert[] = [ 'bar_id' => $bar->id, @@ -208,7 +216,7 @@ private function importIngredients(Filesystem $dataDisk, Bar $bar, User $user): 'sugar_g_per_ml' => $externalIngredient->sugarContent, 'acidity' => $externalIngredient->acidity, 'distillery' => $externalIngredient->distillery, - 'units' => $externalIngredient->units, + 'units' => $ingredientUnit, ]; if ($externalIngredient->parentId) { diff --git a/app/External/Model/Ingredient.php b/app/External/Model/Ingredient.php index 99d76e1e..a2709939 100644 --- a/app/External/Model/Ingredient.php +++ b/app/External/Model/Ingredient.php @@ -5,10 +5,12 @@ namespace Kami\Cocktail\External\Model; use Kami\Cocktail\External\SupportsCSV; +use Kami\RecipeUtils\UnitConverter\Units; use Kami\Cocktail\External\SupportsDraft2; use Kami\Cocktail\Models\ComplexIngredient; use Kami\Cocktail\External\SupportsDataPack; use Kami\Cocktail\Models\Image as ImageModel; +use Kami\Cocktail\Models\ValueObjects\UnitValueObject; use Kami\Cocktail\Models\Ingredient as IngredientModel; use Kami\Cocktail\Models\IngredientPrice as IngredientPriceModel; @@ -37,11 +39,11 @@ private function __construct( public ?float $sugarContent = null, public ?float $acidity = null, public ?string $distillery = null, - public ?string $units = null, + public ?UnitValueObject $units = null, ) { } - public static function fromModel(IngredientModel $model, bool $useFileURI = false): self + public static function fromModel(IngredientModel $model, bool $useFileURI = false, ?Units $toUnits = null): self { $images = $model->images->map(function (ImageModel $image) use ($useFileURI) { return Image::fromModel($image, $useFileURI); @@ -55,6 +57,11 @@ public static function fromModel(IngredientModel $model, bool $useFileURI = fals return IngredientPrice::fromModel($price); })->toArray(); + $defaultIngredientUnits = $model->getDefaultUnits(); + if ($model->getDefaultUnits()?->isConvertable() && $toUnits) { + $defaultIngredientUnits = new UnitValueObject($toUnits->value); + } + return new self( id: $model->getExternalId(), name: $model->name, @@ -73,7 +80,7 @@ public static function fromModel(IngredientModel $model, bool $useFileURI = fals sugarContent: $model->sugar_g_per_ml, acidity: $model->acidity, distillery: $model->distillery, - units: $model->getDefaultUnits()?->value, + units: $defaultIngredientUnits, ); } @@ -112,7 +119,7 @@ public static function fromDataPackArray(array $sourceArray): self sugarContent: $sourceArray['sugar_g_per_ml'] ?? null, acidity: $sourceArray['acidity'] ?? null, distillery: $sourceArray['distillery'] ?? null, - units: $sourceArray['units'] ?? null, + units: ($sourceArray['units'] ?? null) ? new UnitValueObject($sourceArray['units']) : null, ); } @@ -136,7 +143,7 @@ public function toDataPackArray(): array 'sugar_g_per_ml' => $this->sugarContent, 'acidity' => $this->acidity, 'distillery' => $this->distillery, - 'units' => $this->units, + 'units' => $this->units?->value, ]; } @@ -161,7 +168,7 @@ public static function fromCSV(array $sourceArray): self sugarContent: blank($sourceArray['sugar_g_per_ml']) ? null : $sourceArray['sugar_g_per_ml'], acidity: blank($sourceArray['acidity']) ? null : $sourceArray['acidity'], distillery: blank($sourceArray['distillery']) ? null : $sourceArray['distillery'], - units: blank($sourceArray['units']) ? null : $sourceArray['units'], + units: blank($sourceArray['units']) ? null : new UnitValueObject($sourceArray['units']), ); } diff --git a/app/Http/Controllers/BarController.php b/app/Http/Controllers/BarController.php index b0704c8d..bea54637 100644 --- a/app/Http/Controllers/BarController.php +++ b/app/Http/Controllers/BarController.php @@ -4,6 +4,7 @@ namespace Kami\Cocktail\Http\Controllers; +use Throwable; use Illuminate\Http\Request; use Illuminate\Http\Response; use Kami\Cocktail\Models\Bar; @@ -87,7 +88,7 @@ public function show(Request $request, int $id): JsonResource public function store(BarRequest $request): JsonResponse { if ($request->user()->cannot('create', Bar::class)) { - abort(403, 'You can not create anymore bars'); + abort(403, 'You can not create any more bars.'); } Cache::forget('metrics_bass_total_bars'); @@ -106,7 +107,7 @@ public function store(BarRequest $request): JsonResponse try { $imageModels = Image::findOrFail($barRequest->images); $bar->attachImages($imageModels); - } catch (\Throwable $e) { + } catch (Throwable $e) { abort(500, $e->getMessage()); } } @@ -167,7 +168,7 @@ public function update(int $id, BarRequest $request): JsonResource try { $imageModels = Image::findOrFail($barRequest->images); $bar->attachImages($imageModels); - } catch (\Throwable $e) { + } catch (Throwable $e) { abort(500, $e->getMessage()); } } diff --git a/app/Http/Controllers/GlassController.php b/app/Http/Controllers/GlassController.php index 44eccd91..7a7497d1 100644 --- a/app/Http/Controllers/GlassController.php +++ b/app/Http/Controllers/GlassController.php @@ -132,7 +132,9 @@ public function update(int $id, GlassRequest $request): JsonResource } } - $glass->cocktails->each(fn ($cocktail) => $cocktail->searchable()); + if (!empty(config('scout.driver'))) { + $glass->cocktails->each(fn ($cocktail) => $cocktail->searchable()); + } return new GlassResource($glass); } diff --git a/app/Http/Controllers/MenuController.php b/app/Http/Controllers/MenuController.php index 69f91310..ef877353 100644 --- a/app/Http/Controllers/MenuController.php +++ b/app/Http/Controllers/MenuController.php @@ -15,8 +15,9 @@ use Kami\Cocktail\Rules\ResourceBelongsToBar; use Kami\Cocktail\Http\Resources\MenuResource; use Illuminate\Http\Resources\Json\JsonResource; -use Kami\Cocktail\Models\Enums\MenuItemTypeEnum; +use Kami\Cocktail\OpenAPI\Schemas\MenuItemRequest; use Kami\Cocktail\Http\Resources\MenuPublicResource; +use Kami\Cocktail\OpenAPI\Schemas\MenuRequest as SchemasMenuRequest; class MenuController extends Controller { @@ -98,27 +99,26 @@ public function update(MenuRequest $request): MenuResource abort(403); } - /** @var array */ - $items = $request->input('items', []); + $schemaRequest = SchemasMenuRequest::fromIlluminateRequest($request); - $ingredients = collect($items)->where('type', MenuItemTypeEnum::Ingredient->value)->values()->toArray(); - $cocktails = collect($items)->where('type', MenuItemTypeEnum::Cocktail->value)->values()->toArray(); + $ingredients = $schemaRequest->getIngredients(); + $cocktails = $schemaRequest->getCocktails(); - Validator::make($ingredients, [ + Validator::make(collect($ingredients)->map(fn (MenuItemRequest $mi) => ['id' => $mi->id])->toArray(), [ '*.id' => [new ResourceBelongsToBar(bar()->id, 'ingredients')], ])->validate(); - Validator::make($cocktails, [ + Validator::make(collect($cocktails)->map(fn (MenuItemRequest $mi) => ['id' => $mi->id])->toArray(), [ '*.id' => [new ResourceBelongsToBar(bar()->id, 'cocktails')], ])->validate(); $menu = Menu::firstOrCreate(['bar_id' => bar()->id]); - $menu->is_enabled = $request->boolean('is_enabled'); + $menu->is_enabled = $schemaRequest->isEnabled; if (!$menu->created_at) { $menu->created_at = now(); } $menu->updated_at = now(); - $menu->syncItems($items); + $menu->syncItems($schemaRequest->items); $menu->save(); return new MenuResource($menu); diff --git a/app/Http/Controllers/Public/BarController.php b/app/Http/Controllers/Public/BarController.php index a4fe3169..8cab6f09 100644 --- a/app/Http/Controllers/Public/BarController.php +++ b/app/Http/Controllers/Public/BarController.php @@ -12,18 +12,18 @@ class BarController extends Controller { - #[OAT\Get(path: '/public/{barId}', tags: ['Public'], operationId: 'showPublicBar', description: 'Show public information about a single bar. To access this endpoint the bar must be marked as public.', summary: 'Show bar', parameters: [ - new OAT\Parameter(name: 'barId', in: 'path', required: true, description: 'Database id of bar', schema: new OAT\Schema(type: 'number')), + #[OAT\Get(path: '/public/{slugOrId}', tags: ['Public'], operationId: 'showPublicBar', description: 'Show public information about a single bar. To access this endpoint the bar must be marked as public.', summary: 'Show bar', parameters: [ + new OAT\Parameter(name: 'slugOrId', in: 'path', required: true, description: 'Database id of bar', schema: new OAT\Schema(type: 'string')), new BAO\Parameters\PageParameter(), ], security: [])] #[BAO\SuccessfulResponse(content: [ new BAO\WrapObjectWithData(BarResource::class), ])] #[BAO\NotFoundResponse] - public function show(int $barId): BarResource + public function show(string $slugOrId): BarResource { - $bar = Bar::findOrFail($barId); - if (!$bar->is_public) { + $bar = Bar::where('slug', $slugOrId)->orWhere('id', $slugOrId)->firstOrFail(); + if (!$bar->isPublic()) { abort(404); } diff --git a/app/Http/Controllers/Public/CocktailController.php b/app/Http/Controllers/Public/CocktailController.php index 99527cc2..903a05f7 100644 --- a/app/Http/Controllers/Public/CocktailController.php +++ b/app/Http/Controllers/Public/CocktailController.php @@ -18,76 +18,73 @@ class CocktailController extends Controller { - #[OAT\Get(path: '/public/{barId}/cocktails', tags: ['Public'], operationId: 'listPublicBarCocktails', description: 'List and filter bar cocktails. To access this endpoint the bar must be marked as public.', summary: 'List cocktails', parameters: [ - new OAT\Parameter(name: 'barId', in: 'path', required: true, description: 'Database id of bar', schema: new OAT\Schema(type: 'number')), + #[OAT\Get(path: '/public/{slugOrId}/cocktails', tags: ['Public'], operationId: 'listPublicBarCocktails', description: 'List and filter bar cocktails. To access this endpoint the bar must be marked as public.', summary: 'List cocktails', parameters: [ + new OAT\Parameter(name: 'slugOrId', in: 'path', required: true, description: 'Database id or slug of bar', schema: new OAT\Schema(type: 'string')), new BAO\Parameters\PageParameter(), new OAT\Parameter(name: 'filter', in: 'query', description: 'Filter by attributes. You can specify multiple matching filter values by passing a comma separated list of values.', explode: true, style: 'deepObject', schema: new OAT\Schema(type: 'object', properties: [ new OAT\Property(property: 'name', type: 'string', description: 'Filter by cocktail names(s) (fuzzy search)'), new OAT\Property(property: 'ingredient_name', type: 'string', description: 'Filter by cocktail ingredient names(s) (fuzzy search)'), + new OAT\Property(property: 'tag', type: 'string', description: 'Filter by cocktail tag name(s) (fuzzy search)'), + new OAT\Property(property: 'glass', type: 'string', description: 'Filter by cocktail glass type name(s) (fuzzy search)'), + new OAT\Property(property: 'method', type: 'string', description: 'Filter by cocktail method name(s) (fuzzy search)'), new OAT\Property(property: 'bar_shelf', type: 'boolean', description: 'Show only cocktails on the bar shelf'), - new OAT\Property(property: 'abv_min', type: 'number', description: 'Filter by greater than or equal ABV'), - new OAT\Property(property: 'abv_max', type: 'number', description: 'Filter by less than or equal ABV'), + new OAT\Property(property: 'abv', type: 'number', description: 'Filter by greater than or equal ABV. Use >=, >, <=, < operators (e.g., `filter[abv]=>=20` to get cocktails with ABV greater than or equal to 20).'), ])), - new OAT\Parameter(name: 'sort', in: 'query', description: 'Sort by attributes. Available attributes: `name`, `created_at`, `average_rating`, `user_rating`, `abv`, `total_ingredients`, `missing_ingredients`, `missing_bar_ingredients`, `favorited_at`, `random`.', schema: new OAT\Schema(type: 'string')), + new OAT\Parameter(name: 'sort', in: 'query', description: 'Sort by attributes. Available attributes: `name`, `created_at`, `abv`, `random`.', schema: new OAT\Schema(type: 'string')), ], security: [])] #[BAO\SuccessfulResponse(content: [ new BAO\PaginateData(CocktailResource::class), ])] #[BAO\NotFoundResponse] - public function index(Request $request, int $barId): JsonResource + public function index(Request $request, string $slugOrId): JsonResource { - $bar = Bar::findOrFail($barId); - if (!$bar->is_public) { + $bar = Bar::where('slug', $slugOrId)->orWhere('id', $slugOrId)->firstOrFail(); + if (!$bar->isPublic()) { abort(404); } + $queryParams = $request->only(['filter', 'sort', 'page']); + ksort($queryParams); + $cacheKey = 'public_cocktails_index:' . $bar->id . ':' . sha1(http_build_query($queryParams)); + + if (Cache::has($cacheKey)) { + $cocktails = Cache::get($cacheKey); + + return CocktailResource::collection($cocktails->withQueryString()); + } + try { $cocktailsQuery = new PublicCocktailQueryFilter($bar); } catch (InvalidFilterQuery $e) { abort(400, $e->getMessage()); } - $queryParams = $request->only([ - 'filter', - 'sort', - 'page', - ]); - ksort($queryParams); - $queryString = http_build_query($queryParams); - $cacheKey = 'public_cocktails_index_' . $barId . '_' . sha1($queryString); + $cocktails = $cocktailsQuery->paginate(50); - $cocktails = Cache::remember($cacheKey, 3600, function () use ($cocktailsQuery) { - return $cocktailsQuery->paginate(50); - }); + Cache::put($cacheKey, $cocktails, 3600); return CocktailResource::collection($cocktails->withQueryString()); } - #[OAT\Get(path: '/public/{barId}/cocktails/{slugOrPublicId}', tags: ['Public'], operationId: 'showPublicBarCocktail', description: 'Show public information about cocktail. If valid public ID is provided it will used, if not it will use cocktail slug.', summary: 'Show cocktail', parameters: [ - new OAT\Parameter(name: 'barId', in: 'path', required: true, description: 'Database id of bar', schema: new OAT\Schema(type: 'number')), - new OAT\Parameter(name: 'slugOrPublicId', in: 'path', required: true, description: 'Cocktail slug or public id (ULID)', schema: new OAT\Schema(type: 'string')), + #[OAT\Get(path: '/public/{slugOrId}/cocktails/{cocktailSlug}', tags: ['Public'], operationId: 'showPublicBarCocktail', description: 'Show public information about cocktail.', summary: 'Show cocktail', parameters: [ + new OAT\Parameter(name: 'slugOrId', in: 'path', required: true, description: 'Database id of bar', schema: new OAT\Schema(type: 'string')), + new OAT\Parameter(name: 'cocktailSlug', in: 'path', required: true, description: 'Cocktail slug', schema: new OAT\Schema(type: 'string')), ], security: [])] #[BAO\SuccessfulResponse(content: [ new BAO\WrapObjectWithData(CocktailResource::class), ])] #[BAO\NotFoundResponse] - public function show(int $barId, string $slugOrPublicId): CocktailResource + public function show(string $barId, string $cocktailSlug): CocktailResource { - $cocktail = Cocktail::where('bar_id', $barId) - ->where('public_id', $slugOrPublicId) - ->orWhere('slug', $slugOrPublicId) - ->with('ingredients.ingredient', 'ingredients.substitutes.ingredient', 'images', 'tags', 'utensils') - ->firstOrFail(); - - if ($cocktail->public_id === $slugOrPublicId) { - return new CocktailResource($cocktail); - } - - $bar = Bar::findOrFail($barId); - if (!$bar->is_public) { + $bar = Bar::where('slug', $barId)->orWhere('id', $barId)->firstOrFail(); + if (!$bar->isPublic()) { abort(404); } + $cocktail = Cocktail::where('slug', $cocktailSlug) + ->with('ingredients.ingredient', 'ingredients.substitutes.ingredient', 'images', 'tags', 'utensils') + ->firstOrFail(); + return new CocktailResource($cocktail); } } diff --git a/app/Http/Controllers/Public/MenuController.php b/app/Http/Controllers/Public/MenuController.php index b25e8cc9..bfb4a9be 100644 --- a/app/Http/Controllers/Public/MenuController.php +++ b/app/Http/Controllers/Public/MenuController.php @@ -5,16 +5,26 @@ namespace Kami\Cocktail\Http\Controllers\Public; use Kami\Cocktail\Models\Menu; +use OpenApi\Attributes as OAT; +use Kami\Cocktail\OpenAPI as BAO; use Kami\Cocktail\Http\Controllers\Controller; use Kami\Cocktail\Http\Resources\MenuPublicResource; class MenuController extends Controller { - public function show(string $barId): MenuPublicResource + #[OAT\Get(path: '/public/{slugOrId}/menu', tags: ['Public'], operationId: 'showPublicBarMenu', description: 'Show a public bar menu details. The bar must have menu enabled.', summary: 'Show public menu', parameters: [ + new OAT\Parameter(name: 'slugOrId', in: 'path', required: true, description: 'Database id or slug of bar', schema: new OAT\Schema(type: 'string')), + ], security: [])] + #[BAO\SuccessfulResponse(content: [ + new BAO\WrapObjectWithData(MenuPublicResource::class), + ])] + #[BAO\NotFoundResponse] + public function show(string $barSlugOrId): MenuPublicResource { $menu = Menu::select('menus.*') - ->where('bars.id', $barId) ->where('menus.is_enabled', true) + ->where('bars.id', $barSlugOrId) + ->orWhere('bars.slug', $barSlugOrId) ->join('bars', 'bars.id', '=', 'menus.bar_id') ->join('menu_cocktails', 'menu_cocktails.menu_id', '=', 'menus.id') ->orderBy('menu_cocktails.sort', 'asc') diff --git a/app/Http/Controllers/RatingController.php b/app/Http/Controllers/RatingController.php index 2eedd927..2f0889d3 100644 --- a/app/Http/Controllers/RatingController.php +++ b/app/Http/Controllers/RatingController.php @@ -39,7 +39,9 @@ public function rateCocktail(RatingRequest $request, int $id): Response $request->user()->id ); - $cocktail->searchable(); + if (!empty(config('scout.driver'))) { + $cocktail->searchable(); + } return new Response(null, 204); } @@ -60,7 +62,9 @@ public function deleteCocktailRating(Request $request, int $id): Response $cocktail->deleteUserRating($request->user()->id); - $cocktail->searchable(); + if (!empty(config('scout.driver'))) { + $cocktail->searchable(); + } return new Response(null, 204); } diff --git a/app/Http/Controllers/ShelfController.php b/app/Http/Controllers/ShelfController.php index 79e3c64c..8e945e29 100644 --- a/app/Http/Controllers/ShelfController.php +++ b/app/Http/Controllers/ShelfController.php @@ -262,7 +262,9 @@ public function batchStoreBarIngredients(ShelfIngredientsRequest $request, int $ if ($request->user()->cannot('manageShelf', $bar)) { abort(403); } + $bar->load('shelfIngredients'); + $existingBarShelfIngredients = $bar->shelfIngredients->pluck('ingredient_id'); $ingredients = DB::table('ingredients') ->select('id') ->where('bar_id', $bar->id) @@ -271,6 +273,10 @@ public function batchStoreBarIngredients(ShelfIngredientsRequest $request, int $ $models = []; foreach ($ingredients as $dbIngredientId) { + if ($existingBarShelfIngredients->contains($dbIngredientId)) { + continue; + } + $userIngredient = new BarIngredient(); $userIngredient->ingredient_id = $dbIngredientId; $models[] = $userIngredient; diff --git a/app/Http/Controllers/ShoppingListController.php b/app/Http/Controllers/ShoppingListController.php index a8fc6198..53c6d316 100644 --- a/app/Http/Controllers/ShoppingListController.php +++ b/app/Http/Controllers/ShoppingListController.php @@ -30,7 +30,7 @@ class ShoppingListController extends Controller public function index(Request $request, int $id): JsonResource { $user = User::findOrFail($id); - if ($request->user()->id !== $user->id && $request->user()->cannot('show', $user)) { + if ($request->user()->id !== $user->id || $request->user()->cannot('show', $user)) { abort(403); } @@ -54,7 +54,7 @@ public function index(Request $request, int $id): JsonResource public function batchStore(IngredientsBatchRequest $request, int $id): Response { $user = User::findOrFail($id); - if ($request->user()->id !== $user->id && $request->user()->cannot('show', $user)) { + if ($request->user()->id !== $user->id || $request->user()->cannot('show', $user)) { abort(403); } @@ -111,7 +111,7 @@ public function batchStore(IngredientsBatchRequest $request, int $id): Response public function batchDelete(IngredientsBatchRequest $request, int $id): Response { $user = User::findOrFail($id); - if ($request->user()->id !== $user->id && $request->user()->cannot('show', $user)) { + if ($request->user()->id !== $user->id || $request->user()->cannot('show', $user)) { abort(403); } @@ -150,7 +150,7 @@ public function batchDelete(IngredientsBatchRequest $request, int $id): Response public function share(Request $request, int $id): JsonResponse { $user = User::findOrFail($id); - if ($request->user()->id !== $user->id && $request->user()->cannot('show', $user)) { + if ($request->user()->id !== $user->id || $request->user()->cannot('show', $user)) { abort(403); } diff --git a/app/Http/Controllers/StatsController.php b/app/Http/Controllers/StatsController.php index 1e13f569..7b28a2a3 100644 --- a/app/Http/Controllers/StatsController.php +++ b/app/Http/Controllers/StatsController.php @@ -31,7 +31,14 @@ class StatsController extends Controller public function index(CocktailService $cocktailRepo, Request $request, int $id): JsonResponse { $bar = Bar::findOrFail($id); - $barMembership = $request->user()->getBarMembership($bar->id)->load('userIngredients'); + $barMembership = $request->user()->getBarMembership($bar->id); + + if ($barMembership === null) { + abort(403); + } + + $barMembership->load('userIngredients'); + $limit = 5; $stats = []; diff --git a/app/Http/Controllers/SubscriptionController.php b/app/Http/Controllers/SubscriptionController.php index 366a2c19..864b447d 100644 --- a/app/Http/Controllers/SubscriptionController.php +++ b/app/Http/Controllers/SubscriptionController.php @@ -9,6 +9,7 @@ use OpenApi\Attributes as OAT; use Illuminate\Http\JsonResponse; use Kami\Cocktail\OpenAPI as BAO; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; use Kami\Cocktail\Mail\SubscriptionChanged; use Kami\Cocktail\Http\Resources\UserSubscriptionResource; @@ -30,7 +31,11 @@ public function subscription(Request $request): UserSubscriptionResource $customer = $user->customer; if (!$customer) { - $customer = $user->createAsCustomer(); + try { + $customer = $user->createAsCustomer(); + } catch (Throwable $e) { + Log::warning($e->getMessage()); + } } return new UserSubscriptionResource($user); diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 387be505..0f49a98d 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -106,7 +106,9 @@ public function update(TagRequest $request, int $id): JsonResource $tag->save(); $cocktailIds = DB::table('cocktail_tag')->select('cocktail_id')->where('tag_id', $tag->id)->pluck('cocktail_id'); - Cocktail::find($cocktailIds)->each(fn ($cocktail) => $cocktail->searchable()); + if (!empty(config('scout.driver'))) { + Cocktail::find($cocktailIds)->each(fn ($cocktail) => $cocktail->searchable()); + } return new TagResource($tag); } @@ -127,7 +129,9 @@ public function delete(Request $request, int $id): Response $cocktailIds = DB::table('cocktail_tag')->select('cocktail_id')->where('tag_id', $id)->pluck('cocktail_id'); $tag->delete(); - Cocktail::find($cocktailIds)->each(fn ($cocktail) => $cocktail->searchable()); + if (!empty(config('scout.driver'))) { + Cocktail::find($cocktailIds)->each(fn ($cocktail) => $cocktail->searchable()); + } return new Response(null, 204); } diff --git a/app/Http/Filters/PublicCocktailQueryFilter.php b/app/Http/Filters/PublicCocktailQueryFilter.php index a694056f..3cc11510 100644 --- a/app/Http/Filters/PublicCocktailQueryFilter.php +++ b/app/Http/Filters/PublicCocktailQueryFilter.php @@ -9,6 +9,7 @@ use Spatie\QueryBuilder\AllowedSort; use Spatie\QueryBuilder\QueryBuilder; use Spatie\QueryBuilder\AllowedFilter; +use Spatie\QueryBuilder\Enums\FilterOperator; /** * @extends \Spatie\QueryBuilder\QueryBuilder @@ -24,43 +25,28 @@ public function __construct(Bar $bar) AllowedFilter::exact('id'), AllowedFilter::custom('name', new FilterNameSearch()), AllowedFilter::partial('ingredient_name', 'ingredients.ingredient.name'), + AllowedFilter::partial('tag', 'tags.name'), + AllowedFilter::partial('glass', 'glass.name'), + AllowedFilter::partial('method', 'method.name'), AllowedFilter::callback('bar_shelf', function ($query, $value) use ($bar) { if ($value === true) { $query->whereIn('cocktails.id', $bar->getShelfCocktailsOnce()); } }), - AllowedFilter::callback('abv_min', function ($query, $value) { - $query->where('abv', '>=', $value); - }), - AllowedFilter::callback('abv_max', function ($query, $value) { - $query->where('abv', '<=', $value); - }), + AllowedFilter::operator('abv', FilterOperator::DYNAMIC), ]) ->defaultSort('name') ->allowedSorts([ 'name', 'created_at', 'abv', - 'total_ingredients', AllowedSort::callback('random', function ($query) { $query->inRandomOrder(); }), ]) - ->allowedIncludes([ - 'glass', - 'method', - 'user', - 'utensils', - 'images', - 'tags', - 'ingredients.ingredient', - ]) ->select('cocktails.*') ->leftJoin('cocktail_ingredients AS ci', 'ci.cocktail_id', '=', 'cocktails.id') ->leftJoin('cocktail_ingredient_substitutes AS cis', 'cis.cocktail_ingredient_id', '=', 'ci.id') - ->leftJoin('bar_ingredients AS bi', function ($query) { - $query->on('bi.ingredient_id', '=', 'ci.ingredient_id'); - }) ->where('cocktails.bar_id', $bar->id) ->groupBy('cocktails.id') ->with( diff --git a/app/Http/Resources/AmountFormats.php b/app/Http/Resources/AmountFormats.php index de26b71f..81633232 100644 --- a/app/Http/Resources/AmountFormats.php +++ b/app/Http/Resources/AmountFormats.php @@ -43,7 +43,7 @@ public function __construct(private readonly CocktailIngredient $cocktailIngredi public function jsonSerialize(): mixed { - $unitsToConvertTo = ['ml', 'oz', 'cl']; + $unitsToConvertTo = UnitValueObject::CONVERTABLE_UNITS; $formats = []; foreach ($unitsToConvertTo as $unitTo) { diff --git a/app/Http/Resources/BarResource.php b/app/Http/Resources/BarResource.php index 97038ccc..c37b9246 100644 --- a/app/Http/Resources/BarResource.php +++ b/app/Http/Resources/BarResource.php @@ -99,7 +99,7 @@ public function toArray($request) $this->relationLoaded('images'), fn () => ImageResource::collection($this->images) ), - 'is_public' => (bool) $this->is_public, + 'is_public' => $this->is_public, ]; } } diff --git a/app/Http/Resources/Public/BarResource.php b/app/Http/Resources/Public/BarResource.php index 8d81da97..e245cdb6 100644 --- a/app/Http/Resources/Public/BarResource.php +++ b/app/Http/Resources/Public/BarResource.php @@ -20,8 +20,9 @@ new OAT\Property(property: 'subtitle', type: 'string', nullable: true, example: 'A short subtitle of a bar', description: 'Optional short quip about the bar'), new OAT\Property(property: 'description', type: 'string', nullable: true, example: 'Bar description', description: 'Description of the bar'), new OAT\Property(property: 'images', type: 'array', items: new OAT\Items(type: ImageResource::class), description: 'Images associated with the bar'), + new OAT\Property(property: 'is_menu_enabled', type: 'boolean', example: true, description: 'Whether the bar has enabled its menu for public viewing'), ], - required: ['id', 'slug', 'name', 'subtitle', 'description', 'images'], + required: ['id', 'slug', 'name', 'subtitle', 'description', 'images', 'is_menu_enabled'], )] class BarResource extends JsonResource { @@ -40,6 +41,7 @@ public function toArray($request) 'subtitle' => $this->subtitle, 'description' => $this->description, 'images' => ImageResource::collection($this->images), + 'is_menu_enabled' => $this->menu->is_enabled ?? false, ]; } } diff --git a/app/Http/Resources/Public/CocktailResource.php b/app/Http/Resources/Public/CocktailResource.php index e9dc243d..10225ca1 100644 --- a/app/Http/Resources/Public/CocktailResource.php +++ b/app/Http/Resources/Public/CocktailResource.php @@ -31,7 +31,10 @@ new OAT\Property(property: 'glass', type: 'string', nullable: true, example: 'Highball glass', description: 'Type of glass used for the cocktail'), new OAT\Property(property: 'utensils', type: 'array', items: new OAT\Items(type: 'string'), description: 'Utensils used for preparing the cocktail'), new OAT\Property(property: 'method', type: 'string', nullable: true, example: 'Shaken', description: 'Method of preparation for the cocktail'), + new OAT\Property(property: 'method_dilution_percentage', type: 'number', nullable: true, example: 12, description: 'Dilution percentage associated with the preparation method'), + new OAT\Property(property: 'volume_ml', type: 'number', nullable: true, example: 120, description: 'Total volume of the cocktail in milliliters'), new OAT\Property(property: 'created_at', type: 'string', format: 'date-time', example: '2023-10-01T12:00:00Z', description: 'Date and time when the cocktail was created'), + new OAT\Property(property: 'in_bar_shelf', type: 'boolean', example: true, description: 'Indicates if the cocktail can be made in the current bar'), new OAT\Property(property: 'abv', type: 'number', format: 'float', nullable: true, example: 0.15, description: 'Alcohol by volume percentage of the cocktail'), new OAT\Property(property: 'year', type: 'integer', nullable: true, example: 2023, description: 'Year the cocktail was created or published'), new OAT\Property( @@ -98,7 +101,10 @@ public function toArray($request) 'glass' => $this->glass->name ?? null, 'utensils' => $this->utensils->pluck('name'), 'method' => $this->method->name ?? null, + 'method_dilution_percentage' => $this->method->dilution_percentage ?? null, + 'volume_ml' => $this->getVolume(), 'created_at' => $this->created_at->toAtomString(), + 'in_bar_shelf' => $this->inBarShelf(), 'abv' => $this->abv, 'year' => $this->year, 'ingredients' => $this->ingredients->map(function (CocktailIngredient $cocktailIngredient) { diff --git a/app/Models/Bar.php b/app/Models/Bar.php index 8d7701d8..b2b0ec35 100644 --- a/app/Models/Bar.php +++ b/app/Models/Bar.php @@ -15,6 +15,7 @@ use Kami\Cocktail\Models\Enums\BarStatusEnum; use Kami\Cocktail\Services\Image\ImageService; use Kami\Cocktail\Services\MeilisearchService; +use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -40,10 +41,7 @@ public function getUploadPath(): string protected static function booted(): void { static::retrieved(function (Bar $bar) { - if ( - $bar->search_token || - config('scout.driver') === null - ) { + if (!empty($bar->search_token) || empty(config('scout.driver'))) { return; } @@ -136,6 +134,14 @@ public function exports(): HasMany return $this->hasMany(Export::class); } + /** + * @return HasOne + */ + public function menu(): HasOne + { + return $this->hasOne(Menu::class); + } + public function owner(): User { return $this->createdUser; @@ -180,6 +186,16 @@ public function isAccessible(): bool return $this->status !== BarStatusEnum::Deactivated->value; } + public function isProvisioning(): bool + { + return $this->status === BarStatusEnum::Provisioning->value; + } + + public function isPublic(): bool + { + return $this->is_public && $this->isAccessible() && !$this->isProvisioning(); + } + public function getIngredientsDirectory(): string { return 'ingredients/' . $this->id . '/'; diff --git a/app/Models/Glass.php b/app/Models/Glass.php index 2c644eaf..f703d0b4 100644 --- a/app/Models/Glass.php +++ b/app/Models/Glass.php @@ -73,7 +73,10 @@ public function bar(): BelongsTo public function delete(): bool { - $this->cocktails->each(fn ($cocktail) => $cocktail->searchable()); + if (!empty(config('scout.driver'))) { + $this->cocktails->each(fn ($cocktail) => $cocktail->searchable()); + } + $this->deleteImages(); return parent::delete(); diff --git a/app/Models/Ingredient.php b/app/Models/Ingredient.php index e273f7ef..a937c7ef 100644 --- a/app/Models/Ingredient.php +++ b/app/Models/Ingredient.php @@ -252,7 +252,7 @@ public function getIngredientsUsedAsSubstituteFor(): Collection /** * Return all ingredients that can be substituted with this ingredient * - * @return Collection + * @return Collection */ public function getCanBeSubstitutedWithIngredients(): Collection { diff --git a/app/Models/Menu.php b/app/Models/Menu.php index 1f38f0c6..9b9fb5b4 100644 --- a/app/Models/Menu.php +++ b/app/Models/Menu.php @@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Model; use Kami\Cocktail\Models\ValueObjects\MenuItem; use Kami\Cocktail\Models\Enums\MenuItemTypeEnum; +use Kami\Cocktail\OpenAPI\Schemas\MenuItemRequest; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -54,14 +55,14 @@ public function bar(): BelongsTo */ public function getMenuItems(): Collection { - $cocktails = $this->menuCocktails->map(fn (MenuCocktail $menuCocktail) => MenuItem::fromMenuCocktail($menuCocktail)); - $ingredients = $this->menuIngredients->map(fn (MenuIngredient $menuIngredient) => MenuItem::fromMenuIngredient($menuIngredient)); + $cocktails = $this->menuCocktails->map(fn (MenuCocktail $menuCocktail) => MenuItem::fromMenuCocktail($menuCocktail))->values(); + $ingredients = $this->menuIngredients->map(fn (MenuIngredient $menuIngredient) => MenuItem::fromMenuIngredient($menuIngredient))->values(); return $cocktails->merge($ingredients)->values(); } /** - * @param array> $menuItems + * @param array $menuItems */ public function syncItems(array $menuItems): void { @@ -70,33 +71,33 @@ public function syncItems(array $menuItems): void foreach ($menuItems as $menuItem) { $price = 0; - if ($menuItem['price'] ?? false) { + if ($menuItem->price ?? false) { $price = Money::of( - $menuItem['price'], - $menuItem['currency'], + $menuItem->price, + $menuItem->currency, roundingMode: RoundingMode::UP )->getMinorAmount()->toInt(); } - if (MenuItemTypeEnum::from($menuItem['type']) === MenuItemTypeEnum::Ingredient) { - $currentIngredientMenuItems[] = $menuItem['id']; + if ($menuItem->type === MenuItemTypeEnum::Ingredient) { + $currentIngredientMenuItems[] = $menuItem->id; $this->menuIngredients()->updateOrCreate([ - 'ingredient_id' => $menuItem['id'] + 'ingredient_id' => $menuItem->id ], [ - 'category_name' => $menuItem['category_name'], - 'sort' => $menuItem['sort'] ?? 0, + 'category_name' => $menuItem->categoryName, + 'sort' => $menuItem->sort ?? 0, 'price' => $price, - 'currency' => $menuItem['currency'] ?? null, + 'currency' => $menuItem->currency ?? null, ]); } else { - $currentCocktailMenuItems[] = $menuItem['id']; + $currentCocktailMenuItems[] = $menuItem->id; $this->menuCocktails()->updateOrCreate([ - 'cocktail_id' => $menuItem['id'] + 'cocktail_id' => $menuItem->id ], [ - 'category_name' => $menuItem['category_name'], - 'sort' => $menuItem['sort'] ?? 0, + 'category_name' => $menuItem->categoryName, + 'sort' => $menuItem->sort ?? 0, 'price' => $price, - 'currency' => $menuItem['currency'] ?? null, + 'currency' => $menuItem->currency ?? null, ]); } } diff --git a/app/Models/ValueObjects/AmountValueObject.php b/app/Models/ValueObjects/AmountValueObject.php index 13efd844..f03b727d 100644 --- a/app/Models/ValueObjects/AmountValueObject.php +++ b/app/Models/ValueObjects/AmountValueObject.php @@ -32,7 +32,7 @@ public function convertTo(UnitValueObject $toUnits): self $convertedMaxAmount = Converter::convertAmount(AmountValue::from($this->amountMax), $fromUnitsEnum, $toUnitsEnum); } - return new self($convertedMinAmount->getValue(), $toUnits, $convertedMaxAmount?->getValue()); + return new self(round($convertedMinAmount->getValue(), 4), $toUnits, $convertedMaxAmount?->getValue()); } public function __toString(): string diff --git a/app/Models/ValueObjects/UnitValueObject.php b/app/Models/ValueObjects/UnitValueObject.php index e99b2060..e8f34bab 100644 --- a/app/Models/ValueObjects/UnitValueObject.php +++ b/app/Models/ValueObjects/UnitValueObject.php @@ -6,6 +6,7 @@ use Stringable; use JsonSerializable; +use Kami\RecipeUtils\DefaultUnits; use Kami\RecipeUtils\UnitConverter\Units; final readonly class UnitValueObject implements Stringable, JsonSerializable @@ -15,34 +16,12 @@ /** @var array> */ public array $units; + public const CONVERTABLE_UNITS = ['ml', 'oz', 'cl']; + public function __construct( ?string $value, ) { - // TODO: Move to package - $this->units = [ - 'oz' => ['oz.', 'fl-oz', 'oz', 'ounce', 'ounces'], - 'ml' => ['ml', 'ml.', 'milliliter', 'milliliters'], - 'cl' => ['cl', 'cl.', 'centiliter', 'centiliters'], - 'dash' => ['dashes', 'dash', 'ds'], - 'sprigs' => ['sprig', 'sprigs'], - 'leaves' => ['leaves', 'leaf'], - 'whole' => ['whole'], - 'drops' => ['drop', 'drops'], - 'barspoon' => ['barspoon', 'teaspoon', 'bsp', 'tsp', 'tsp.', 'tspn', 't', 't.', 'teaspoon', 'teaspoons', 'tablespoons', 'tablespoon'], - 'slice' => ['slice', 'sliced', 'slices'], - 'cup' => ['c', 'c.', 'cup', 'cups'], - 'pint' => ['pt', 'pts', 'pt.', 'pint', 'pints'], - 'splash' => ['a splash', 'splash', 'splashes'], - 'pinch' => ['pinches', 'pinch'], - 'topup' => ['topup'], - 'part' => ['part', 'parts'], - 'wedge' => ['wedge', 'wedges'], - 'cube' => ['cubes', 'cube'], - 'bottle' => ['bottles', 'bottle'], - 'can' => ['cans', 'can'], - 'bag' => ['bags', 'bag'], - 'shot' => ['shots', 'shot'], - ]; + $this->units = DefaultUnits::get(); $this->value = trim(mb_strtolower($value ?? '')); } @@ -78,6 +57,11 @@ public function isBarspoon(): bool return str_contains($this->value, 'spoon') || in_array($this->value, $matches, true); } + public function isConvertable(): bool + { + return in_array($this->value, self::CONVERTABLE_UNITS, true); + } + public function __toString(): string { return $this->value; diff --git a/app/OpenAPI/Schemas/MenuItemRequest.php b/app/OpenAPI/Schemas/MenuItemRequest.php new file mode 100644 index 00000000..063c93e3 --- /dev/null +++ b/app/OpenAPI/Schemas/MenuItemRequest.php @@ -0,0 +1,43 @@ + $input + */ + public static function fromArray(array $input): self + { + return new self( + id: (int) $input['id'], + type: MenuItemTypeEnum::from($input['type']), + categoryName: $input['category_name'], + sort: (int) $input['sort'], + price: (float) $input['price'], + currency: $input['currency'] ?? 'EUR', + ); + } +} diff --git a/app/OpenAPI/Schemas/MenuRequest.php b/app/OpenAPI/Schemas/MenuRequest.php index 422f4fe4..9045bdb8 100644 --- a/app/OpenAPI/Schemas/MenuRequest.php +++ b/app/OpenAPI/Schemas/MenuRequest.php @@ -4,23 +4,52 @@ namespace Kami\Cocktail\OpenAPI\Schemas; +use Illuminate\Http\Request; use OpenApi\Attributes as OAT; -use Kami\Cocktail\Models\Enums\MenuItemTypeEnum; #[OAT\Schema(required: ['is_enabled', 'items'])] class MenuRequest { - #[OAT\Property(property: 'is_enabled')] - public bool $isEnabled = false; - /** @var array */ - #[OAT\Property(type: 'array', items: new OAT\Items(type: 'object', properties: [ - new OAT\Property(type: 'integer', property: 'id', example: 1), - new OAT\Property(type: 'schema', property: 'type', ref: MenuItemTypeEnum::class), - new OAT\Property(type: 'string', property: 'category_name', example: 'Category name'), - new OAT\Property(type: 'integer', property: 'sort', example: 1), - new OAT\Property(type: 'integer', property: 'price', example: 2252, format: 'minor'), - new OAT\Property(type: 'string', property: 'currency', example: 'EUR', format: 'ISO 4217'), - ], required: ['id', 'type', 'category_name', 'sort', 'price', 'currency'])), - ] - public array $items = []; + /** + * @param array $items + */ + public function __construct( + #[OAT\Property(property: 'is_enabled')] + public bool $isEnabled = false, + #[OAT\Property(items: new OAT\Items(type: MenuItemRequest::class))] + public array $items = [], + ) { + } + + public static function fromIlluminateRequest(Request $request): self + { + /** @var array */ + $formItems = $request->post('items', []); + + $items = []; + foreach ($formItems as $formItem) { + $items[] = MenuItemRequest::fromArray($formItem); + } + + return new self( + isEnabled: $request->boolean('is_enabled', false), + items: $items, + ); + } + + /** + * @return array + */ + public function getIngredients(): array + { + return array_filter($this->items, fn (MenuItemRequest $item) => $item->type === \Kami\Cocktail\Models\Enums\MenuItemTypeEnum::Ingredient); + } + + /** + * @return array + */ + public function getCocktails(): array + { + return array_filter($this->items, fn (MenuItemRequest $item) => $item->type === \Kami\Cocktail\Models\Enums\MenuItemTypeEnum::Cocktail); + } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 0c63daa4..7a1575c8 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,9 +3,12 @@ namespace Kami\Cocktail\Providers; use Throwable; +use Illuminate\Support\Str; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Event; +use Illuminate\Support\Facades\Redis; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -32,6 +35,23 @@ public function register() */ public function boot() { + Cache::macro('forgetWildcardRedis', function (string $key) { + try { + $prefix = config('database.redis.options.prefix'); + $redisCache = Redis::connection('cache'); + $foundKeys = $redisCache->keys('*' . $key); + foreach ($foundKeys as $foundKey) { + $keyToDelete = $prefix ? Str::after($foundKey, $prefix) : $foundKey; + Log::debug('Clearing cache key via wildcard', ['key' => $foundKey]); + $redisCache->unlink($keyToDelete); + } + } catch (Throwable $e) { + Log::error('Unable to clear cache with wildcard', ['message' => $e->getMessage()]); + + return; + } + }); + Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) { $event->extendSocialite('authentik', \SocialiteProviders\Authentik\Provider::class); $event->extendSocialite('authelia', \SocialiteProviders\Authelia\Provider::class); diff --git a/app/Services/BarOptimizerService.php b/app/Services/BarOptimizerService.php index f2568d57..e80bd02c 100644 --- a/app/Services/BarOptimizerService.php +++ b/app/Services/BarOptimizerService.php @@ -9,6 +9,7 @@ use Kami\Cocktail\Models\Cocktail; use Illuminate\Support\Facades\Log; use Kami\Cocktail\Models\Ingredient; +use Kami\Cocktail\Models\ValueObjects\UnitValueObject; final class BarOptimizerService { @@ -21,7 +22,7 @@ public function optimize(int $barId): void { $bar = Bar::findOrFail($barId); $barSettings = $bar->settings ?? []; - $forceToUnits = $barSettings['default_units'] ?? null; + $forceToUnits = new UnitValueObject($barSettings['default_units'] ?? null); Log::info('[' . $barId . '] Starting bar optimization for bar ID: ' . $barId); @@ -54,12 +55,11 @@ public function optimize(int $barId): void ->values(); foreach ($unitsPerIngredient as $commonUnit) { - $ingredientUnits = $commonUnit->units; + $ingredientUnits = new UnitValueObject($commonUnit->units); // Force unit conversion if specified and the unit is convertible - if ($forceToUnits && $forceToUnits !== $ingredientUnits) { - $convertableUnits = ['ml', 'oz', 'cl']; - if (in_array($ingredientUnits, $convertableUnits) && in_array($forceToUnits, $convertableUnits)) { + if ($forceToUnits->value && $forceToUnits->value !== $ingredientUnits->value) { + if ($ingredientUnits->isConvertable() && $forceToUnits->isConvertable()) { $ingredientUnits = $forceToUnits; Log::debug('[' . $barId . '] Forcing unit conversion from ' . $commonUnit->units . ' to ' . $forceToUnits . ' for ingredient ID: ' . $commonUnit->ingredient_id); } @@ -69,14 +69,16 @@ public function optimize(int $barId): void ->where('id', $commonUnit->ingredient_id) ->where('bar_id', $barId) ->whereNull('units') - ->update(['units' => $ingredientUnits]); + ->update(['units' => $ingredientUnits->value]); } Log::info('[' . $barId . '] Finished updating ingredient default units.'); - /** @phpstan-ignore-next-line */ - Cocktail::where('bar_id', $barId)->searchable(); - /** @phpstan-ignore-next-line */ - Ingredient::where('bar_id', $barId)->searchable(); + if (!empty(config('scout.driver'))) { + /** @phpstan-ignore-next-line */ + Cocktail::where('bar_id', $barId)->searchable(); + /** @phpstan-ignore-next-line */ + Ingredient::where('bar_id', $barId)->searchable(); + } Log::info('[' . $barId . '] Finished re-indexing cocktails and ingredients.'); } diff --git a/app/Services/CocktailService.php b/app/Services/CocktailService.php index 7eed74be..89999a22 100644 --- a/app/Services/CocktailService.php +++ b/app/Services/CocktailService.php @@ -11,6 +11,7 @@ use Kami\Cocktail\Models\Image; use Illuminate\Support\Collection; use Kami\Cocktail\Models\Cocktail; +use Illuminate\Support\Facades\Cache; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\DatabaseManager; use Kami\Cocktail\Models\CocktailFavorite; @@ -93,6 +94,8 @@ public function createCocktail(CocktailDTO $cocktailDTO): Cocktail $this->db->commit(); + Cache::forgetWildcardRedis('public_cocktails_index:' . $cocktail->bar_id . ':*'); + if (count($cocktailDTO->images) > 0) { try { $imageModels = Image::findOrFail($cocktailDTO->images); @@ -198,6 +201,8 @@ public function updateCocktail(int $id, CocktailDTO $cocktailDTO): Cocktail $this->db->commit(); + Cache::forgetWildcardRedis('public_cocktails_index:' . $cocktail->bar_id . ':*'); + if (count($cocktailDTO->images) > 0) { try { $imageModels = Image::findOrFail($cocktailDTO->images); diff --git a/app/Services/IngredientService.php b/app/Services/IngredientService.php index 0008cbbf..52a1668b 100644 --- a/app/Services/IngredientService.php +++ b/app/Services/IngredientService.php @@ -198,7 +198,9 @@ public function updateIngredient(int $id, IngredientRequest $dto): Ingredient }); } - $ingredient->cocktails->each(fn ($cocktail) => $cocktail->searchable()); + if (!empty(config('scout.driver'))) { + $ingredient->cocktails->each(fn ($cocktail) => $cocktail->searchable()); + } return $ingredient; } diff --git a/composer.json b/composer.json index 08b4f9d5..bee27e6f 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "guzzlehttp/guzzle": "^7.2", "http-interop/http-factory-guzzle": "^1.2", "jcupitt/vips": "^2.4", - "karlomikus/recipe-utils": "^0.16", + "karlomikus/recipe-utils": "^0.17", "kevinrob/guzzle-cache-middleware": "^6.0", "laminas/laminas-feed": "^2.23", "laravel/cashier-paddle": "^2.0", diff --git a/composer.lock b/composer.lock index 43b27df6..2a30b87a 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": "542ed20f3770dad61941fbf3a3c2adc7", + "content-hash": "832f605d9d287023e3ded62286dd19e0", "packages": [ { "name": "brick/math", @@ -765,22 +765,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.3", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -871,7 +871,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -887,20 +887,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:37:11+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -908,7 +908,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -954,7 +954,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.2.0" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -970,20 +970,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:27:01+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -999,7 +999,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1070,7 +1070,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.1" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -1086,20 +1086,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T12:30:47+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.4", + "version": "v1.0.5", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2" + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/30e286560c137526eccd4ce21b2de477ab0676d2", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", "shasum": "" }, "require": { @@ -1108,7 +1108,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", "uri-template/tests": "1.0.0" }, "type": "library", @@ -1156,7 +1156,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.4" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" }, "funding": [ { @@ -1172,7 +1172,7 @@ "type": "tidelift" } ], - "time": "2025-02-03T10:55:03+00:00" + "time": "2025-08-22T14:27:06+00:00" }, { "name": "http-interop/http-factory-guzzle", @@ -1355,16 +1355,16 @@ }, { "name": "karlomikus/recipe-utils", - "version": "0.16.0", + "version": "0.17.0", "source": { "type": "git", "url": "https://github.com/karlomikus/recipe-utils.git", - "reference": "e41949c25553ee458ae18483a934fc3cd3dc9403" + "reference": "4f8eac95619f99e29c4c6cc8e1ea30b942607372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/karlomikus/recipe-utils/zipball/e41949c25553ee458ae18483a934fc3cd3dc9403", - "reference": "e41949c25553ee458ae18483a934fc3cd3dc9403", + "url": "https://api.github.com/repos/karlomikus/recipe-utils/zipball/4f8eac95619f99e29c4c6cc8e1ea30b942607372", + "reference": "4f8eac95619f99e29c4c6cc8e1ea30b942607372", "shasum": "" }, "require": { @@ -1398,9 +1398,9 @@ "description": "Utilities for extracting normalized ingredient data from recipes", "support": { "issues": "https://github.com/karlomikus/recipe-utils/issues", - "source": "https://github.com/karlomikus/recipe-utils/tree/0.16.0" + "source": "https://github.com/karlomikus/recipe-utils/tree/0.17.0" }, - "time": "2025-03-24T15:29:40+00:00" + "time": "2025-08-27T15:09:01+00:00" }, { "name": "kevinrob/guzzle-cache-middleware", @@ -1631,30 +1631,30 @@ }, { "name": "laminas/laminas-stdlib", - "version": "3.20.0", + "version": "3.21.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-stdlib.git", - "reference": "8974a1213be42c3e2f70b2c27b17f910291ab2f4" + "reference": "b1c81514cfe158aadf724c42b34d3d0a8164c096" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/8974a1213be42c3e2f70b2c27b17f910291ab2f4", - "reference": "8974a1213be42c3e2f70b2c27b17f910291ab2f4", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/b1c81514cfe158aadf724c42b34d3d0a8164c096", + "reference": "b1c81514cfe158aadf724c42b34d3d0a8164c096", "shasum": "" }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "conflict": { "zendframework/zend-stdlib": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "^3.0", - "phpbench/phpbench": "^1.3.1", - "phpunit/phpunit": "^10.5.38", - "psalm/plugin-phpunit": "^0.19.0", - "vimeo/psalm": "^5.26.1" + "laminas/laminas-coding-standard": "^3.1.0", + "phpbench/phpbench": "^1.4.1", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" }, "type": "library", "autoload": { @@ -1686,7 +1686,7 @@ "type": "community_bridge" } ], - "time": "2024-10-29T13:46:07+00:00" + "time": "2025-10-11T18:13:12+00:00" }, { "name": "laravel/cashier-paddle", @@ -1772,20 +1772,20 @@ }, { "name": "laravel/framework", - "version": "v12.24.0", + "version": "v12.33.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "6dcf2c46da23d159f35d6246234953a74b740d83" + "reference": "124efc5f09d4668a4dc13f94a1018c524a58bcb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/6dcf2c46da23d159f35d6246234953a74b740d83", - "reference": "6dcf2c46da23d159f35d6246234953a74b740d83", + "url": "https://api.github.com/repos/laravel/framework/zipball/124efc5f09d4668a4dc13f94a1018c524a58bcb1", + "reference": "124efc5f09d4668a4dc13f94a1018c524a58bcb1", "shasum": "" }, "require": { - "brick/math": "^0.11|^0.12|^0.13", + "brick/math": "^0.11|^0.12|^0.13|^0.14", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", "dragonmantank/cron-expression": "^3.4", @@ -1821,9 +1821,9 @@ "symfony/http-kernel": "^7.2.0", "symfony/mailer": "^7.2.0", "symfony/mime": "^7.2.0", - "symfony/polyfill-php83": "^1.31", - "symfony/polyfill-php84": "^1.31", - "symfony/polyfill-php85": "^1.31", + "symfony/polyfill-php83": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33", "symfony/process": "^7.2.0", "symfony/routing": "^7.2.0", "symfony/uid": "^7.2.0", @@ -1859,6 +1859,7 @@ "illuminate/filesystem": "self.version", "illuminate/hashing": "self.version", "illuminate/http": "self.version", + "illuminate/json-schema": "self.version", "illuminate/log": "self.version", "illuminate/macroable": "self.version", "illuminate/mail": "self.version", @@ -1891,7 +1892,8 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^10.6.0", + "opis/json-schema": "^2.4.1", + "orchestra/testbench-core": "^10.6.5", "pda/pheanstalk": "^5.0.6|^7.0.0", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", @@ -1916,7 +1918,7 @@ "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).", - "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "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).", "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).", @@ -1985,20 +1987,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-08-13T20:30:36+00:00" + "time": "2025-10-07T14:30:39+00:00" }, { "name": "laravel/horizon", - "version": "v5.33.2", + "version": "v5.35.2", "source": { "type": "git", "url": "https://github.com/laravel/horizon.git", - "reference": "baa36725ed24dbbcd7ddb4ba3dcfd4c0f22028ed" + "reference": "11f9a980d84de56402dec19cf1e78050b211fcef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/baa36725ed24dbbcd7ddb4ba3dcfd4c0f22028ed", - "reference": "baa36725ed24dbbcd7ddb4ba3dcfd4c0f22028ed", + "url": "https://api.github.com/repos/laravel/horizon/zipball/11f9a980d84de56402dec19cf1e78050b211fcef", + "reference": "11f9a980d84de56402dec19cf1e78050b211fcef", "shasum": "" }, "require": { @@ -2063,22 +2065,22 @@ ], "support": { "issues": "https://github.com/laravel/horizon/issues", - "source": "https://github.com/laravel/horizon/tree/v5.33.2" + "source": "https://github.com/laravel/horizon/tree/v5.35.2" }, - "time": "2025-08-05T02:30:15+00:00" + "time": "2025-10-08T12:50:15+00:00" }, { "name": "laravel/prompts", - "version": "v0.3.6", + "version": "v0.3.7", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "86a8b692e8661d0fb308cec64f3d176821323077" + "reference": "a1891d362714bc40c8d23b0b1d7090f022ea27cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/86a8b692e8661d0fb308cec64f3d176821323077", - "reference": "86a8b692e8661d0fb308cec64f3d176821323077", + "url": "https://api.github.com/repos/laravel/prompts/zipball/a1891d362714bc40c8d23b0b1d7090f022ea27cc", + "reference": "a1891d362714bc40c8d23b0b1d7090f022ea27cc", "shasum": "" }, "require": { @@ -2095,8 +2097,8 @@ "illuminate/collections": "^10.0|^11.0|^12.0", "mockery/mockery": "^1.5", "pestphp/pest": "^2.3|^3.4", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-mockery": "^1.1" + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" }, "suggest": { "ext-pcntl": "Required for the spinner to be animated." @@ -2122,9 +2124,9 @@ "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.3.6" + "source": "https://github.com/laravel/prompts/tree/v0.3.7" }, - "time": "2025-07-07T14:17:42+00:00" + "time": "2025-09-19T13:47:56+00:00" }, { "name": "laravel/sanctum", @@ -2192,16 +2194,16 @@ }, { "name": "laravel/scout", - "version": "v10.17.0", + "version": "v10.19.1", "source": { "type": "git", "url": "https://github.com/laravel/scout.git", - "reference": "66b064ab1f987560d1edfbc10f46557fddfed600" + "reference": "bc8dbffc06472e28147ee739aec95fdf653f9d91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/scout/zipball/66b064ab1f987560d1edfbc10f46557fddfed600", - "reference": "66b064ab1f987560d1edfbc10f46557fddfed600", + "url": "https://api.github.com/repos/laravel/scout/zipball/bc8dbffc06472e28147ee739aec95fdf653f9d91", + "reference": "bc8dbffc06472e28147ee739aec95fdf653f9d91", "shasum": "" }, "require": { @@ -2269,20 +2271,20 @@ "issues": "https://github.com/laravel/scout/issues", "source": "https://github.com/laravel/scout" }, - "time": "2025-07-22T15:54:45+00:00" + "time": "2025-10-07T14:48:20+00:00" }, { "name": "laravel/serializable-closure", - "version": "v2.0.4", + "version": "v2.0.5", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841" + "reference": "3832547db6e0e2f8bb03d4093857b378c66eceed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b352cf0534aa1ae6b4d825d1e762e35d43f8a841", - "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3832547db6e0e2f8bb03d4093857b378c66eceed", + "reference": "3832547db6e0e2f8bb03d4093857b378c66eceed", "shasum": "" }, "require": { @@ -2330,7 +2332,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2025-03-19T13:51:03+00:00" + "time": "2025-09-22T17:29:40+00:00" }, { "name": "laravel/socialite", @@ -2595,16 +2597,16 @@ }, { "name": "league/csv", - "version": "9.24.1", + "version": "9.26.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "e0221a3f16aa2a823047d59fab5809d552e29bc8" + "reference": "7fce732754d043f3938899e5183e2d0f3d31b571" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/e0221a3f16aa2a823047d59fab5809d552e29bc8", - "reference": "e0221a3f16aa2a823047d59fab5809d552e29bc8", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/7fce732754d043f3938899e5183e2d0f3d31b571", + "reference": "7fce732754d043f3938899e5183e2d0f3d31b571", "shasum": "" }, "require": { @@ -2620,7 +2622,7 @@ "phpstan/phpstan-deprecation-rules": "^1.2.1", "phpstan/phpstan-phpunit": "^1.4.2", "phpstan/phpstan-strict-rules": "^1.6.2", - "phpunit/phpunit": "^10.5.16 || ^11.5.22", + "phpunit/phpunit": "^10.5.16 || ^11.5.22 || ^12.3.6", "symfony/var-dumper": "^6.4.8 || ^7.3.0" }, "suggest": { @@ -2682,7 +2684,7 @@ "type": "github" } ], - "time": "2025-06-25T14:53:51+00:00" + "time": "2025-10-01T11:24:54+00:00" }, { "name": "league/flysystem", @@ -3191,37 +3193,39 @@ }, { "name": "meilisearch/meilisearch-php", - "version": "v1.15.0", + "version": "v1.16.1", "source": { "type": "git", "url": "https://github.com/meilisearch/meilisearch-php.git", - "reference": "94930b9e4b8a6c1ad841eed1395a58a73d0db313" + "reference": "f9f63e0e7d12ffaae54f7317fa8f4f4dfa8ae7b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/meilisearch/meilisearch-php/zipball/94930b9e4b8a6c1ad841eed1395a58a73d0db313", - "reference": "94930b9e4b8a6c1ad841eed1395a58a73d0db313", + "url": "https://api.github.com/repos/meilisearch/meilisearch-php/zipball/f9f63e0e7d12ffaae54f7317fa8f4f4dfa8ae7b6", + "reference": "f9f63e0e7d12ffaae54f7317fa8f4f4dfa8ae7b6", "shasum": "" }, "require": { "ext-json": "*", "php": "^7.4 || ^8.0", "php-http/discovery": "^1.7", - "psr/http-client": "^1.0" + "psr/http-client": "^1.0", + "symfony/polyfill-php81": "^1.33" }, "require-dev": { - "guzzlehttp/guzzle": "^7.8.1", "http-interop/http-factory-guzzle": "^1.2.0", "php-cs-fixer/shim": "^3.59.3", "phpstan/phpstan": "^2.0", "phpstan/phpstan-deprecation-rules": "^2.0", "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.5 || ^10.5" + "phpunit/phpunit": "^9.5 || ^10.5", + "symfony/http-client": "^5.4|^6.0|^7.0" }, "suggest": { "guzzlehttp/guzzle": "Use Guzzle ^7 as HTTP client", - "http-interop/http-factory-guzzle": "Factory for guzzlehttp/guzzle" + "http-interop/http-factory-guzzle": "Factory for guzzlehttp/guzzle", + "symfony/http-client": "Use Symfony Http client" }, "type": "library", "autoload": { @@ -3236,8 +3240,20 @@ ], "authors": [ { - "name": "Clementine", + "name": "Clémentine Urquizar", "email": "clementine@meilisearch.com" + }, + { + "name": "Bruno Casali", + "email": "bruno@meilisearch.com" + }, + { + "name": "Laurent Cazanove", + "email": "lau.cazanove@gmail.com" + }, + { + "name": "Tomas Norkūnas", + "email": "norkunas.tom@gmail.com" } ], "description": "PHP wrapper for the Meilisearch API", @@ -3251,9 +3267,9 @@ ], "support": { "issues": "https://github.com/meilisearch/meilisearch-php/issues", - "source": "https://github.com/meilisearch/meilisearch-php/tree/v1.15.0" + "source": "https://github.com/meilisearch/meilisearch-php/tree/v1.16.1" }, - "time": "2025-06-10T04:33:15+00:00" + "time": "2025-09-18T10:15:45+00:00" }, { "name": "moneyphp/money", @@ -3450,16 +3466,16 @@ }, { "name": "nesbot/carbon", - "version": "3.10.2", + "version": "3.10.3", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24" + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", - "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", "shasum": "" }, "require": { @@ -3477,13 +3493,13 @@ "require-dev": { "doctrine/dbal": "^3.6.3 || ^4.0", "doctrine/orm": "^2.15.2 || ^3.0", - "friendsofphp/php-cs-fixer": "^3.75.0", + "friendsofphp/php-cs-fixer": "^v3.87.1", "kylekatarnls/multi-tester": "^2.5.3", "phpmd/phpmd": "^2.15.0", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.17", - "phpunit/phpunit": "^10.5.46", - "squizlabs/php_codesniffer": "^3.13.0" + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4" }, "bin": [ "bin/carbon" @@ -3551,7 +3567,7 @@ "type": "tidelift" } ], - "time": "2025-08-02T09:36:06+00:00" + "time": "2025-09-06T13:39:36+00:00" }, { "name": "nette/schema", @@ -3987,24 +4003,26 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v3.0.0", + "version": "v3.1.3", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", - "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", "shasum": "" }, "require": { "php": "^8" }, "require-dev": { - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4|^5" + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" }, "type": "library", "autoload": { @@ -4050,7 +4068,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2024-05-08T12:36:18+00:00" + "time": "2025-09-24T15:06:41+00:00" }, { "name": "paragonie/random_compat", @@ -4183,16 +4201,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.9.3", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", "shasum": "" }, "require": { @@ -4200,7 +4218,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { @@ -4242,7 +4260,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" }, "funding": [ { @@ -4254,20 +4272,20 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:41:07+00:00" + "time": "2025-08-21T11:53:16+00:00" }, { "name": "phpseclib/phpseclib", - "version": "3.0.46", + "version": "3.0.47", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d", + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d", "shasum": "" }, "require": { @@ -4348,7 +4366,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.47" }, "funding": [ { @@ -4364,7 +4382,7 @@ "type": "tidelift" } ], - "time": "2025-06-26T16:29:55+00:00" + "time": "2025-10-06T01:07:24+00:00" }, { "name": "promphp/prometheus_client_php", @@ -4968,20 +4986,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.0", + "version": "4.9.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -5040,22 +5058,22 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.0" + "source": "https://github.com/ramsey/uuid/tree/4.9.1" }, - "time": "2025-06-25T14:20:11+00:00" + "time": "2025-09-04T20:59:21+00:00" }, { "name": "sentry/sentry", - "version": "4.14.2", + "version": "4.16.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-php.git", - "reference": "bfeec74303d60d3f8bc33701ab3e86f8a8729f17" + "reference": "c5b086e4235762da175034bc463b0d31cbb38d2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/bfeec74303d60d3f8bc33701ab3e86f8a8729f17", - "reference": "bfeec74303d60d3f8bc33701ab3e86f8a8729f17", + "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/c5b086e4235762da175034bc463b0d31cbb38d2e", + "reference": "c5b086e4235762da175034bc463b0d31cbb38d2e", "shasum": "" }, "require": { @@ -5119,7 +5137,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-php/issues", - "source": "https://github.com/getsentry/sentry-php/tree/4.14.2" + "source": "https://github.com/getsentry/sentry-php/tree/4.16.0" }, "funding": [ { @@ -5131,27 +5149,27 @@ "type": "custom" } ], - "time": "2025-07-21T08:28:29+00:00" + "time": "2025-09-22T13:38:03+00:00" }, { "name": "sentry/sentry-laravel", - "version": "4.15.1", + "version": "4.16.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-laravel.git", - "reference": "7e0675e8e06d1ec5cb623792892920000a3aedb5" + "reference": "b33b2e487b02db02d92988228f142d7fa2be2bfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/7e0675e8e06d1ec5cb623792892920000a3aedb5", - "reference": "7e0675e8e06d1ec5cb623792892920000a3aedb5", + "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/b33b2e487b02db02d92988228f142d7fa2be2bfa", + "reference": "b33b2e487b02db02d92988228f142d7fa2be2bfa", "shasum": "" }, "require": { "illuminate/support": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0", "nyholm/psr7": "^1.0", "php": "^7.2 | ^8.0", - "sentry/sentry": "^4.14.1", + "sentry/sentry": "^4.15.2", "symfony/psr-http-message-bridge": "^1.0 | ^2.0 | ^6.0 | ^7.0" }, "require-dev": { @@ -5208,7 +5226,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-laravel/issues", - "source": "https://github.com/getsentry/sentry-laravel/tree/4.15.1" + "source": "https://github.com/getsentry/sentry-laravel/tree/4.16.0" }, "funding": [ { @@ -5220,7 +5238,7 @@ "type": "custom" } ], - "time": "2025-06-24T12:39:03+00:00" + "time": "2025-09-10T16:38:18+00:00" }, { "name": "socialiteproviders/authelia", @@ -5810,16 +5828,16 @@ }, { "name": "spatie/robots-txt", - "version": "2.5.1", + "version": "2.5.2", "source": { "type": "git", "url": "https://github.com/spatie/robots-txt.git", - "reference": "ef85dfaa48372c0a7fdfb144592f95de1a2e9b79" + "reference": "1b59dde3fd4e1b71967b40841369c6e9779282f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/robots-txt/zipball/ef85dfaa48372c0a7fdfb144592f95de1a2e9b79", - "reference": "ef85dfaa48372c0a7fdfb144592f95de1a2e9b79", + "url": "https://api.github.com/repos/spatie/robots-txt/zipball/1b59dde3fd4e1b71967b40841369c6e9779282f3", + "reference": "1b59dde3fd4e1b71967b40841369c6e9779282f3", "shasum": "" }, "require": { @@ -5854,7 +5872,7 @@ ], "support": { "issues": "https://github.com/spatie/robots-txt/issues", - "source": "https://github.com/spatie/robots-txt/tree/2.5.1" + "source": "https://github.com/spatie/robots-txt/tree/2.5.2" }, "funding": [ { @@ -5866,7 +5884,7 @@ "type": "github" } ], - "time": "2025-07-01T07:07:44+00:00" + "time": "2025-09-19T10:37:01+00:00" }, { "name": "spatie/url", @@ -6132,16 +6150,16 @@ }, { "name": "symfony/console", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", + "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", "shasum": "" }, "require": { @@ -6206,7 +6224,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.2" + "source": "https://github.com/symfony/console/tree/v7.3.4" }, "funding": [ { @@ -6226,7 +6244,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:13:41+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/css-selector", @@ -6362,16 +6380,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v7.3.1", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "8b2ee2e06ab99fa5f067b6699296d4e35c156bb9" + "reference": "efa076ea0eeff504383ff0dcf827ea5ce15690ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/8b2ee2e06ab99fa5f067b6699296d4e35c156bb9", - "reference": "8b2ee2e06ab99fa5f067b6699296d4e35c156bb9", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/efa076ea0eeff504383ff0dcf827ea5ce15690ba", + "reference": "efa076ea0eeff504383ff0dcf827ea5ce15690ba", "shasum": "" }, "require": { @@ -6409,7 +6427,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.3.1" + "source": "https://github.com/symfony/dom-crawler/tree/v7.3.3" }, "funding": [ { @@ -6420,25 +6438,29 @@ "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": "2025-06-15T10:07:06+00:00" + "time": "2025-08-06T20:13:54+00:00" }, { "name": "symfony/error-handler", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3" + "reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/0b31a944fcd8759ae294da4d2808cbc53aebd0c3", - "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/99f81bc944ab8e5dae4f21b4ca9972698bbad0e4", + "reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4", "shasum": "" }, "require": { @@ -6486,7 +6508,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.2" + "source": "https://github.com/symfony/error-handler/tree/v7.3.4" }, "funding": [ { @@ -6506,20 +6528,20 @@ "type": "tidelift" } ], - "time": "2025-07-07T08:17:57+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", "shasum": "" }, "require": { @@ -6570,7 +6592,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" }, "funding": [ { @@ -6581,12 +6603,16 @@ "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": "2025-04-22T09:11:45+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -6734,16 +6760,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "1c064a0c67749923483216b081066642751cc2c7" + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/1c064a0c67749923483216b081066642751cc2c7", - "reference": "1c064a0c67749923483216b081066642751cc2c7", + "url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62", + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62", "shasum": "" }, "require": { @@ -6751,6 +6777,7 @@ "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/polyfill-php83": "^1.29", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -6809,7 +6836,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.2" + "source": "https://github.com/symfony/http-client/tree/v7.3.4" }, "funding": [ { @@ -6829,7 +6856,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/http-client-contracts", @@ -6911,16 +6938,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6" + "reference": "c061c7c18918b1b64268771aad04b40be41dd2e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6877c122b3a6cc3695849622720054f6e6fa5fa6", - "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c061c7c18918b1b64268771aad04b40be41dd2e6", + "reference": "c061c7c18918b1b64268771aad04b40be41dd2e6", "shasum": "" }, "require": { @@ -6970,7 +6997,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.2" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.4" }, "funding": [ { @@ -6990,20 +7017,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-09-16T08:38:17+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c" + "reference": "b796dffea7821f035047235e076b60ca2446e3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6ecc895559ec0097e221ed2fd5eb44d5fede083c", - "reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b796dffea7821f035047235e076b60ca2446e3cf", + "reference": "b796dffea7821f035047235e076b60ca2446e3cf", "shasum": "" }, "require": { @@ -7088,7 +7115,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.3.2" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.4" }, "funding": [ { @@ -7108,20 +7135,20 @@ "type": "tidelift" } ], - "time": "2025-07-31T10:45:04+00:00" + "time": "2025-09-27T12:32:17+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b" + "reference": "ab97ef2f7acf0216955f5845484235113047a31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/d43e84d9522345f96ad6283d5dfccc8c1cfc299b", - "reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b", + "url": "https://api.github.com/repos/symfony/mailer/zipball/ab97ef2f7acf0216955f5845484235113047a31d", + "reference": "ab97ef2f7acf0216955f5845484235113047a31d", "shasum": "" }, "require": { @@ -7172,7 +7199,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.2" + "source": "https://github.com/symfony/mailer/tree/v7.3.4" }, "funding": [ { @@ -7192,20 +7219,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-09-17T05:51:54+00:00" }, { "name": "symfony/mime", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1" + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1", - "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "url": "https://api.github.com/repos/symfony/mime/zipball/b1b828f69cbaf887fa835a091869e55df91d0e35", + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35", "shasum": "" }, "require": { @@ -7260,7 +7287,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.3.2" + "source": "https://github.com/symfony/mime/tree/v7.3.4" }, "funding": [ { @@ -7280,20 +7307,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2025-09-16T08:38:17+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37" + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37", - "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d", + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d", "shasum": "" }, "require": { @@ -7331,7 +7358,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.2" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.3" }, "funding": [ { @@ -7351,11 +7378,11 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-08-05T10:16:07+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -7414,7 +7441,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -7425,6 +7452,10 @@ "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" @@ -7434,7 +7465,7 @@ }, { "name": "symfony/polyfill-iconv", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", @@ -7494,7 +7525,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.33.0" }, "funding": [ { @@ -7505,6 +7536,10 @@ "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" @@ -7514,16 +7549,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -7572,7 +7607,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -7583,25 +7618,29 @@ "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": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-icu", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-icu.git", - "reference": "763d2a91fea5681509ca01acbc1c5e450d127811" + "reference": "bfc8fa13dbaf21d69114b0efcd72ab700fb04d0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/763d2a91fea5681509ca01acbc1c5e450d127811", - "reference": "763d2a91fea5681509ca01acbc1c5e450d127811", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/bfc8fa13dbaf21d69114b0efcd72ab700fb04d0c", + "reference": "bfc8fa13dbaf21d69114b0efcd72ab700fb04d0c", "shasum": "" }, "require": { @@ -7656,7 +7695,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.33.0" }, "funding": [ { @@ -7667,16 +7706,20 @@ "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": "2024-12-21T18:38:29+00:00" + "time": "2025-06-20T22:24:30+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -7739,7 +7782,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -7750,6 +7793,10 @@ "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" @@ -7759,7 +7806,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -7820,7 +7867,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -7831,6 +7878,10 @@ "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" @@ -7840,7 +7891,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -7901,7 +7952,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -7912,6 +7963,10 @@ "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" @@ -7986,7 +8041,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -8046,7 +8101,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -8057,6 +8112,10 @@ "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" @@ -8064,18 +8123,98 @@ ], "time": "2025-01-02T08:10:11+00:00" }, + { + "name": "symfony/polyfill-php81", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "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\\Php81\\": "" + }, + "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.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.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": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/polyfill-php83", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -8122,7 +8261,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -8133,25 +8272,29 @@ "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": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-php84", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "000df7860439609837bbe28670b0be15783b7fbf" + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/000df7860439609837bbe28670b0be15783b7fbf", - "reference": "000df7860439609837bbe28670b0be15783b7fbf", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", "shasum": "" }, "require": { @@ -8198,7 +8341,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -8209,25 +8352,29 @@ "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": "2025-02-20T12:04:08+00:00" + "time": "2025-06-24T13:30:11+00:00" }, { "name": "symfony/polyfill-php85", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "6fedf31ce4e3648f4ff5ca58bfd53127d38f05fd" + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/6fedf31ce4e3648f4ff5ca58bfd53127d38f05fd", - "reference": "6fedf31ce4e3648f4ff5ca58bfd53127d38f05fd", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", "shasum": "" }, "require": { @@ -8274,7 +8421,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" }, "funding": [ { @@ -8285,16 +8432,20 @@ "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": "2025-05-02T08:40:52+00:00" + "time": "2025-06-23T16:12:55+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", @@ -8353,7 +8504,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" }, "funding": [ { @@ -8364,6 +8515,10 @@ "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" @@ -8373,16 +8528,16 @@ }, { "name": "symfony/process", - "version": "v7.3.0", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -8414,7 +8569,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -8425,12 +8580,16 @@ "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": "2025-04-17T09:11:12+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -8517,16 +8676,16 @@ }, { "name": "symfony/routing", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4" + "reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/7614b8ca5fa89b9cd233e21b627bfc5774f586e4", - "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "url": "https://api.github.com/repos/symfony/routing/zipball/8dc648e159e9bac02b703b9fbd937f19ba13d07c", + "reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c", "shasum": "" }, "require": { @@ -8578,7 +8737,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.3.2" + "source": "https://github.com/symfony/routing/tree/v7.3.4" }, "funding": [ { @@ -8598,7 +8757,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/service-contracts", @@ -8685,16 +8844,16 @@ }, { "name": "symfony/string", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -8709,7 +8868,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -8752,7 +8910,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.2" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -8772,20 +8930,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "symfony/translation", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90" + "reference": "ec25870502d0c7072d086e8ffba1420c85965174" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/81b48f4daa96272efcce9c7a6c4b58e629df3c90", - "reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90", + "url": "https://api.github.com/repos/symfony/translation/zipball/ec25870502d0c7072d086e8ffba1420c85965174", + "reference": "ec25870502d0c7072d086e8ffba1420c85965174", "shasum": "" }, "require": { @@ -8852,7 +9010,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.2" + "source": "https://github.com/symfony/translation/tree/v7.3.4" }, "funding": [ { @@ -8872,7 +9030,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:31:46+00:00" + "time": "2025-09-07T11:39:36+00:00" }, { "name": "symfony/translation-contracts", @@ -9028,16 +9186,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "53205bea27450dc5c65377518b3275e126d45e75" + "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/53205bea27450dc5c65377518b3275e126d45e75", - "reference": "53205bea27450dc5c65377518b3275e126d45e75", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", + "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", "shasum": "" }, "require": { @@ -9091,7 +9249,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.2" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.4" }, "funding": [ { @@ -9111,20 +9269,20 @@ "type": "tidelift" } ], - "time": "2025-07-29T20:02:46+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/yaml", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30" + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/b8d7d868da9eb0919e99c8830431ea087d6aae30", - "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30", + "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", "shasum": "" }, "require": { @@ -9167,7 +9325,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.2" + "source": "https://github.com/symfony/yaml/tree/v7.3.3" }, "funding": [ { @@ -9187,7 +9345,7 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-08-27T11:34:33+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -9614,16 +9772,16 @@ }, { "name": "zircote/swagger-php", - "version": "5.2.0", + "version": "5.4.2", "source": { "type": "git", "url": "https://github.com/zircote/swagger-php.git", - "reference": "b82e160f9dcc9c5c5e59bf9a4904ed4a8ffa86a4" + "reference": "4f6bac8bdb9e762c6a4de12ef62160d4e5a17caa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/b82e160f9dcc9c5c5e59bf9a4904ed4a8ffa86a4", - "reference": "b82e160f9dcc9c5c5e59bf9a4904ed4a8ffa86a4", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/4f6bac8bdb9e762c6a4de12ef62160d4e5a17caa", + "reference": "4f6bac8bdb9e762c6a4de12ef62160d4e5a17caa", "shasum": "" }, "require": { @@ -9694,9 +9852,9 @@ ], "support": { "issues": "https://github.com/zircote/swagger-php/issues", - "source": "https://github.com/zircote/swagger-php/tree/5.2.0" + "source": "https://github.com/zircote/swagger-php/tree/5.4.2" }, - "time": "2025-08-11T04:26:13+00:00" + "time": "2025-10-09T01:32:43+00:00" } ], "packages-dev": [ @@ -10013,16 +10171,16 @@ }, { "name": "larastan/larastan", - "version": "v3.6.0", + "version": "v3.7.2", "source": { "type": "git", "url": "https://github.com/larastan/larastan.git", - "reference": "6431d010dd383a9279eb8874a76ddb571738564a" + "reference": "a761859a7487bd7d0cb8b662a7538a234d5bb5ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/larastan/larastan/zipball/6431d010dd383a9279eb8874a76ddb571738564a", - "reference": "6431d010dd383a9279eb8874a76ddb571738564a", + "url": "https://api.github.com/repos/larastan/larastan/zipball/a761859a7487bd7d0cb8b662a7538a234d5bb5ae", + "reference": "a761859a7487bd7d0cb8b662a7538a234d5bb5ae", "shasum": "" }, "require": { @@ -10036,7 +10194,7 @@ "illuminate/pipeline": "^11.44.2 || ^12.4.1", "illuminate/support": "^11.44.2 || ^12.4.1", "php": "^8.2", - "phpstan/phpstan": "^2.1.11" + "phpstan/phpstan": "^2.1.28" }, "require-dev": { "doctrine/coding-standard": "^13", @@ -10090,7 +10248,7 @@ ], "support": { "issues": "https://github.com/larastan/larastan/issues", - "source": "https://github.com/larastan/larastan/tree/v3.6.0" + "source": "https://github.com/larastan/larastan/tree/v3.7.2" }, "funding": [ { @@ -10098,20 +10256,20 @@ "type": "github" } ], - "time": "2025-07-11T06:52:52+00:00" + "time": "2025-09-19T09:03:05+00:00" }, { "name": "laravel/pint", - "version": "v1.24.0", + "version": "v1.25.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a" + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a", - "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a", + "url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9", + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9", "shasum": "" }, "require": { @@ -10122,9 +10280,9 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.82.2", - "illuminate/view": "^11.45.1", - "larastan/larastan": "^3.5.0", + "friendsofphp/php-cs-fixer": "^3.87.2", + "illuminate/view": "^11.46.0", + "larastan/larastan": "^3.7.1", "laravel-zero/framework": "^11.45.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3.1", @@ -10135,9 +10293,6 @@ ], "type": "project", "autoload": { - "files": [ - "overrides/Runner/Parallel/ProcessFactory.php" - ], "psr-4": { "App\\": "app/", "Database\\Seeders\\": "database/seeders/", @@ -10167,7 +10322,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-07-10T18:09:32+00:00" + "time": "2025-09-19T02:57:12+00:00" }, { "name": "mockery/mockery", @@ -10604,16 +10759,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.22", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4" - }, + "version": "2.1.31", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/41600c8379eb5aee63e9413fe9e97273e25d57e4", - "reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ead89849d879fe203ce9292c6ef5e7e76f867b96", + "reference": "ead89849d879fe203ce9292c6ef5e7e76f867b96", "shasum": "" }, "require": { @@ -10658,20 +10808,20 @@ "type": "github" } ], - "time": "2025-08-04T19:17:37+00:00" + "time": "2025-10-10T14:14:11+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "11.0.10", + "version": "11.0.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "1a800a7446add2d79cc6b3c01c45381810367d76" + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1a800a7446add2d79cc6b3c01c45381810367d76", - "reference": "1a800a7446add2d79cc6b3c01c45381810367d76", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", "shasum": "" }, "require": { @@ -10728,7 +10878,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/show" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" }, "funding": [ { @@ -10748,7 +10898,7 @@ "type": "tidelift" } ], - "time": "2025-06-18T08:56:18+00:00" + "time": "2025-08-27T14:37:49+00:00" }, { "name": "phpunit/php-file-iterator", @@ -10997,16 +11147,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.32", + "version": "11.5.42", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "101e132dcf9e74a1eb3a309b4f686114ae8f7f36" + "reference": "1c6cb5dfe412af3d0dfd414cfd110e3b9cfdbc3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/101e132dcf9e74a1eb3a309b4f686114ae8f7f36", - "reference": "101e132dcf9e74a1eb3a309b4f686114ae8f7f36", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1c6cb5dfe412af3d0dfd414cfd110e3b9cfdbc3c", + "reference": "1c6cb5dfe412af3d0dfd414cfd110e3b9cfdbc3c", "shasum": "" }, "require": { @@ -11020,7 +11170,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.10", + "phpunit/php-code-coverage": "^11.0.11", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", @@ -11030,7 +11180,7 @@ "sebastian/comparator": "^6.3.2", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.1", - "sebastian/exporter": "^6.3.0", + "sebastian/exporter": "^6.3.2", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", "sebastian/type": "^5.1.3", @@ -11078,7 +11228,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.32" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.42" }, "funding": [ { @@ -11102,7 +11252,7 @@ "type": "tidelift" } ], - "time": "2025-08-12T07:32:49+00:00" + "time": "2025-09-28T12:09:13+00:00" }, { "name": "sebastian/cli-parser", @@ -11569,16 +11719,16 @@ }, { "name": "sebastian/exporter", - "version": "6.3.0", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", "shasum": "" }, "require": { @@ -11592,7 +11742,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -11635,15 +11785,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-12-05T09:17:50+00:00" + "time": "2025-09-24T06:12:51+00:00" }, { "name": "sebastian/global-state", diff --git a/config/auth.php b/config/auth.php index 2462b3ed..aeffd47d 100644 --- a/config/auth.php +++ b/config/auth.php @@ -39,9 +39,6 @@ 'web' => [ 'driver' => 'session', 'provider' => 'users', - ], - 'force-login' => [ - 'driver' => 'no-auth' ] ], diff --git a/config/debugbar.php b/config/debugbar.php index 009c81ce..597fc35d 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -185,7 +185,7 @@ 'logs' => false, // Add the latest log messages 'files' => false, // Show the included files 'config' => false, // Display config settings - 'cache' => false, // Display cache events + 'cache' => true, // Display cache events 'models' => true, // Display models 'livewire' => true, // Display Livewire (when available) 'jobs' => false, // Display dispatched jobs diff --git a/config/scout.php b/config/scout.php index 58b2203a..a452516c 100644 --- a/config/scout.php +++ b/config/scout.php @@ -15,7 +15,7 @@ | */ - 'driver' => env('SCOUT_DRIVER', 'algolia'), + 'driver' => env('SCOUT_DRIVER', null), /* |-------------------------------------------------------------------------- diff --git a/docs/openapi-generated.yaml b/docs/openapi-generated.yaml index 1110cff7..407d1900 100644 --- a/docs/openapi-generated.yaml +++ b/docs/openapi-generated.yaml @@ -5829,7 +5829,7 @@ paths: properties: data: { $ref: '#/components/schemas/APIError' } type: object - '/public/{barId}': + '/public/{slugOrId}': get: tags: - Public @@ -5838,12 +5838,12 @@ paths: operationId: showPublicBar parameters: - - name: barId + name: slugOrId in: path description: 'Database id of bar' required: true schema: - type: number + type: string - name: page in: query @@ -5888,7 +5888,7 @@ paths: data: { $ref: '#/components/schemas/APIError' } type: object security: [] - '/public/{barId}/cocktails': + '/public/{slugOrId}/cocktails': get: tags: - Public @@ -5897,12 +5897,12 @@ paths: operationId: listPublicBarCocktails parameters: - - name: barId + name: slugOrId in: path - description: 'Database id of bar' + description: 'Database id or slug of bar' required: true schema: - type: number + type: string - name: page in: query @@ -5923,20 +5923,26 @@ paths: ingredient_name: description: 'Filter by cocktail ingredient names(s) (fuzzy search)' type: string + tag: + description: 'Filter by cocktail tag name(s) (fuzzy search)' + type: string + glass: + description: 'Filter by cocktail glass type name(s) (fuzzy search)' + type: string + method: + description: 'Filter by cocktail method name(s) (fuzzy search)' + type: string bar_shelf: description: 'Show only cocktails on the bar shelf' type: boolean - abv_min: - description: 'Filter by greater than or equal ABV' - type: number - abv_max: - description: 'Filter by less than or equal ABV' + abv: + description: 'Filter by greater than or equal ABV. Use >=, >, <=, < operators (e.g., `filter[abv]=>=20` to get cocktails with ABV greater than or equal to 20).' type: number type: object - name: sort in: query - description: 'Sort by attributes. Available attributes: `name`, `created_at`, `average_rating`, `user_rating`, `abv`, `total_ingredients`, `missing_ingredients`, `missing_bar_ingredients`, `favorited_at`, `random`.' + description: 'Sort by attributes. Available attributes: `name`, `created_at`, `abv`, `random`.' schema: type: string responses: @@ -5977,25 +5983,25 @@ paths: data: { $ref: '#/components/schemas/APIError' } type: object security: [] - '/public/{barId}/cocktails/{slugOrPublicId}': + '/public/{slugOrId}/cocktails/{cocktailSlug}': get: tags: - Public summary: 'Show cocktail' - description: 'Show public information about cocktail. If valid public ID is provided it will used, if not it will use cocktail slug.' + description: 'Show public information about cocktail.' operationId: showPublicBarCocktail parameters: - - name: barId + name: slugOrId in: path description: 'Database id of bar' required: true schema: - type: number + type: string - - name: slugOrPublicId + name: cocktailSlug in: path - description: 'Cocktail slug or public id (ULID)' + description: 'Cocktail slug' required: true schema: type: string @@ -6037,6 +6043,59 @@ paths: data: { $ref: '#/components/schemas/APIError' } type: object security: [] + '/public/{slugOrId}/menu': + get: + tags: + - Public + summary: 'Show public menu' + description: 'Show a public bar menu details. The bar must have menu enabled.' + operationId: showPublicBarMenu + parameters: + - + name: slugOrId + in: path + description: 'Database id or slug of bar' + required: true + schema: + type: string + responses: + '200': + description: 'Successful response' + headers: + x-ratelimit-limit: + description: 'Max number of attempts.' + schema: + type: integer + x-ratelimit-remaining: + description: 'Remaining number of attempts.' + schema: + type: integer + content: + application/json: + schema: + required: + - data + properties: + data: { $ref: '#/components/schemas/MenuPublic' } + type: object + '404': + description: 'Resource record not found.' + headers: + x-ratelimit-limit: + description: 'Max number of attempts.' + schema: + type: integer + x-ratelimit-remaining: + description: 'Remaining number of attempts.' + schema: + type: integer + content: + application/json: + schema: + properties: + data: { $ref: '#/components/schemas/APIError' } + type: object + security: [] '/cocktails/{id}/ratings': post: tags: @@ -9980,6 +10039,7 @@ components: - subtitle - description - images + - is_menu_enabled properties: id: description: 'Unique number that can be used to reference a specific bar.' @@ -10010,6 +10070,10 @@ components: type: array items: $ref: '#/components/schemas/PublicImageResource' + is_menu_enabled: + description: 'Whether the bar has enabled its menu for public viewing' + type: boolean + example: true type: object PublicCocktailResource: description: 'Public details about a cocktail' @@ -10100,11 +10164,27 @@ components: - string - 'null' example: Shaken + method_dilution_percentage: + description: 'Dilution percentage associated with the preparation method' + type: + - number + - 'null' + example: 12 + volume_ml: + description: 'Total volume of the cocktail in milliliters' + type: + - number + - 'null' + example: 120 created_at: description: 'Date and time when the cocktail was created' type: string format: date-time example: '2023-10-01T12:00:00Z' + in_bar_shelf: + description: 'Indicates if the cocktail can be made in the current bar' + type: boolean + example: true abv: description: 'Alcohol by volume percentage of the cocktail' type: @@ -11179,6 +11259,35 @@ components: - 'null' example: 'My device' type: object + MenuItemRequest: + required: + - id + - type + - category_name + - sort + - price + - currency + properties: + id: + type: integer + example: 1 + type: + $ref: '#/components/schemas/MenuItemTypeEnum' + category_name: + type: string + example: 'Category name' + sort: + type: integer + example: 1 + price: + type: number + format: float + example: 22.52 + currency: + type: string + format: 'ISO 4217' + example: EUR + type: object MenuRequest: required: - is_enabled @@ -11189,34 +11298,7 @@ components: items: type: array items: - required: - - id - - type - - category_name - - sort - - price - - currency - properties: - id: - type: integer - example: 1 - type: - $ref: '#/components/schemas/MenuItemTypeEnum' - category_name: - type: string - example: 'Category name' - sort: - type: integer - example: 1 - price: - type: integer - format: minor - example: 2252 - currency: - type: string - format: 'ISO 4217' - example: EUR - type: object + $ref: '#/components/schemas/MenuItemRequest' type: object NoteRequest: required: diff --git a/resources/views/md_recipe_template.blade.php b/resources/views/md_recipe_template.blade.php index cc3a5d19..35b0eed4 100644 --- a/resources/views/md_recipe_template.blade.php +++ b/resources/views/md_recipe_template.blade.php @@ -1,8 +1,8 @@ -# {!! $cocktail->name !!} +# {{ $cocktail->name }} @if ($cocktail->source) [Recipe source]({{ $cocktail->source }}) @endif -{!! $cocktail->description !!} +{{ $cocktail->description }} @foreach ($cocktail->images as $image) ![{{ $image->copyright }}]({{ $image->uri }}) @@ -10,18 +10,18 @@ ## Ingredients @foreach ($cocktail->ingredients as $ci) -- {!! (new \Kami\Cocktail\Models\ValueObjects\CocktailIngredientFormatter($ci->amount, $ci->ingredient->name, $ci->optional))->format() !!}{!! $ci->note ? ' - ' . $ci->note : '' !!} +- {{ (new \Kami\Cocktail\Models\ValueObjects\CocktailIngredientFormatter($ci->amount, $ci->ingredient->name, $ci->optional))->format() }}{{ $ci->note ? ' - ' . $ci->note : '' }} @foreach ($ci->substitutes as $sub) - - or {!! (new \Kami\Cocktail\Models\ValueObjects\CocktailIngredientFormatter($sub->amount, $sub->ingredient->name))->format() !!} + - or {{ (new \Kami\Cocktail\Models\ValueObjects\CocktailIngredientFormatter($sub->amount, $sub->ingredient->name))->format() }} @endforeach @endforeach ## Instructions -{!! $cocktail->instructions !!} +{{ $cocktail->instructions }} @if ($cocktail->garnish) ### Garnish -{!! $cocktail->garnish !!} +{{ $cocktail->garnish }} @endif ---