From 56efcb46d694f2559ea4fefb741b4bd38df73ab2 Mon Sep 17 00:00:00 2001 From: Alexis Gousseau Date: Mon, 16 Jun 2025 16:38:23 +0200 Subject: [PATCH 1/2] fix: :bug: update with google feedback --- app/Models/Offer.php | 75 ++++++++++++++++++- app/Models/Product.php | 2 +- app/Models/Recipe.php | 70 ++++++++++------- app/Models/StaticPage.php | 74 +++--------------- composer.lock | 56 +++++++------- .../views/fo/pages/recruitment.blade.php | 4 +- 6 files changed, 159 insertions(+), 122 deletions(-) diff --git a/app/Models/Offer.php b/app/Models/Offer.php index 1fd3417a..42d8dd27 100644 --- a/app/Models/Offer.php +++ b/app/Models/Offer.php @@ -8,6 +8,9 @@ use App\Traits\Models\ActivityLog; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Cache; +use Spatie\SchemaOrg\Schema; +use Uwil\SchemaOrg\Laravel\Interfaces\SchemaOrgInterface; +use Uwil\SchemaOrg\Laravel\Traits\SchemaOrgTrait; /** * @property string|null $title Offer title. @@ -37,10 +40,11 @@ use Illuminate\Support\Facades\Cache; * * @property-read \App\Models\Product $products Product polymorphic relationship. */ -class Offer extends Model +class Offer extends Model implements SchemaOrgInterface { use ActivityLog; use HasFactory; + use SchemaOrgTrait; /** * The attributes that are mass assignable. @@ -149,4 +153,73 @@ class Offer extends Model return GeneralSetting::query()->where('type', SettingsType::offersCount)->firstOrFail()->value['count']; }); } + + /** + * Create a SchemaOrg. + * + * @return \Spatie\SchemaOrg\JobPosting + * @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh + */ + public function toSchemaOrg(): \Spatie\SchemaOrg\JobPosting + { + $schemaJob = Schema::jobPosting() + ->title($this->title) + ->hiringOrganization($this->getOrganizationSchema()); + + // Recruiter. + $recruiter = Schema::contactPoint(); + if (!empty($this->recruiter_firstname) || !empty($this->recruiter_lastname)) { + $recruiter->name(sprintf( + "%s %s", + $this->recruiter_firstname ?? '', + $this->recruiter_lastname ?? '', + )); + } + if (!empty($this->recruiter_job)) { + $recruiter->contactType($this->recruiter_job); + } + if (!empty($this->recruiter_picture)) { + $recruiter->image( + Schema::imageObject() + ->url(asset($this->recruiter_picture)) + ->headline(sprintf( + "Photo du recruteur %s %s", + $this->recruiter_firstname ?? '', + $this->recruiter_lastname ?? '', + )) + ); + $schemaJob->applicationContact($recruiter); + } + + // Location. + $location = Schema::place() + ->isAccessibleForFree(true) + ->publicAccess(true); + if (!empty($this->location_longitude)) { + $location->longitude($this->location_longitude); + } + if (!empty($this->location_latitude)) { + $location->latitude($this->location_latitude); + } + if (!empty($this->location)) { + $location->address($this->location); + } + if (!empty($this->location_details)) { + $location->description($this->location_details); + } + $schemaJob->jobLocation($location); + + // Other. + if (!empty($this->description)) { + $schemaJob->description($this->description); + } + if (!empty($this->contract)) { + $schemaJob->employmentType($this->contract); + } + if (!empty($this->created_at)) { + $schemaJob->datePosted($this->created_at); + } + + return $schemaJob; + } } diff --git a/app/Models/Product.php b/app/Models/Product.php index 5a110273..ab209df8 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -277,7 +277,7 @@ class Product extends Model implements SeoInterface, Sitemapable, SchemaOrgInter $schema = Schema::Product() ->name($model->seo_title) ->description($model->seo_description) - ->url(route('fo.products.show', $model)) + ->url(route('fo.products.show', $model->slug)) ->keywords('produit') ->releaseDate($model->created_at) ->image([ diff --git a/app/Models/Recipe.php b/app/Models/Recipe.php index 3b8a4291..49e020f8 100644 --- a/app/Models/Recipe.php +++ b/app/Models/Recipe.php @@ -285,7 +285,7 @@ class Recipe extends Model implements SeoInterface, Sitemapable, SchemaOrgInterf $schema = Schema::Recipe() ->name($model->title) ->description($model->seo_description) - ->url(route('fo.recipes.show', $model)) + ->url(route('fo.recipes.show', $model->slug)) ->inLanguage(config('schema-org.website.languages')) ->keywords('recette') ->recipeCategory([ @@ -297,9 +297,6 @@ class Recipe extends Model implements SeoInterface, Sitemapable, SchemaOrgInterf ->dateCreated($model->created_at) ->dateModified($model->updated_at) ->datePublished($model->created_at) - ->cookTime( - Schema::duration()->setProperty("@value", $model->formatDurationIso($model->cooking_time)) - ) ->prepTime( Schema::duration()->setProperty("@value", $model->formatDurationIso($model->preparation_time)) ) @@ -324,6 +321,13 @@ class Recipe extends Model implements SeoInterface, Sitemapable, SchemaOrgInterf ->reviewCount(1) ); + // Cooking time associated. + if ($model->cooking_time) { + $schema->cookTime( + Schema::duration()->setProperty("@value", $model->formatDurationIso($model->cooking_time)) + ); + } + // Steps associated. preg_match_all('/
  • (.*?)<\/li>/', $model->preparation, $step); $schema->steps($step[1]); @@ -331,46 +335,58 @@ class Recipe extends Model implements SeoInterface, Sitemapable, SchemaOrgInterf // Ingredients associated. $ingredientsList = collect($model->ingredients_list) ->map(function ($item) { - // @phpstan-ignore ternary.elseUnreachable - return trim(($item['label_unit'] !== null ? $item['label_unit'] : '') . ' ' . $item['label_value']); + return trim( + // @phpstan-ignore ternary.elseUnreachable + ($item['value'] !== null ? $item['value'] : '') . ' ' . + // @phpstan-ignore ternary.elseUnreachable + ($item['label_unit'] !== null ? $item['label_unit'] : '') . ' ' . + $item['label_value'] + ); }) ->all(); $schema->ingredients($ingredientsList); // Products associated. - if ($model->products->count() && !$asReference) { - $linkedProducts = $model->products->map(function (Product $product) { + $productsAssociated = $model->products->where('published', true)->values(); + if ($productsAssociated->count() && !$asReference) { + $linkedProducts = $productsAssociated->map(function (Product $product) { return $product->toSchemaOrg(true); })->toArray(); $schema->subjectOf($linkedProducts); // Nutritionnal values. - $allNutritionnalValues = $model->products->map(function (Product $productModel) { - return collect($productModel->getNutritionnalValue()->toArray()) - // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed - ->reject(function ($value, $key) { - return str_contains($key, '_percent') || in_array($key, [ - 'id', - 'product_id', - 'created_at', - 'updated_at' - ]); - }) - ->toArray(); + $allNutritionnalValues = $productsAssociated->map(function (Product $productModel) { + if ($productModel->getNutritionnalValue()) { + return collect($productModel->getNutritionnalValue()->toArray()) + // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed + ->reject(function ($value, $key) { + return str_contains($key, '_percent') || in_array($key, [ + 'id', + 'product_id', + 'created_at', + 'updated_at' + ]); + }) + ->toArray(); + } else { + return []; + } })->reduce(function (?array $carry, array $item) { foreach ($item as $key => $value) { $carry[$key] = ($carry[$key] ?? 0) + $value; } return $carry; }); - $schema->nutrition( - Schema::nutritionInformation() - ->carbohydrateContent($allNutritionnalValues['carbohydrates']) - ->fatContent($allNutritionnalValues['fat']) - ->proteinContent($allNutritionnalValues['protein']) - ->sugarContent($allNutritionnalValues['sugar']) - ); + if ($allNutritionnalValues) { + $schema->nutrition( + Schema::nutritionInformation() + ->carbohydrateContent($allNutritionnalValues['carbohydrates']) + ->fatContent($allNutritionnalValues['fat']) + ->proteinContent($allNutritionnalValues['protein']) + ->sugarContent($allNutritionnalValues['sugar']) + ); + } } //end if // Video associated. diff --git a/app/Models/StaticPage.php b/app/Models/StaticPage.php index bad731cb..6dc69731 100644 --- a/app/Models/StaticPage.php +++ b/app/Models/StaticPage.php @@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Str; use Spatie\SchemaOrg\Schema; +use Spatie\SchemaOrg\WebPage; use Uwil\SchemaOrg\Laravel\Interfaces\SchemaOrgInterface; use Uwil\SchemaOrg\Laravel\Traits\SchemaOrgTrait; @@ -207,7 +208,7 @@ class StaticPage extends Model implements SchemaOrgInterface */ public function toSchemaOrg(): \Spatie\SchemaOrg\WebPage { - return Schema::webPage() + $schema = Schema::webPage() ->name($this->seo_title) ->description($this->seo_description) ->headline($this->title) @@ -224,80 +225,29 @@ class StaticPage extends Model implements SchemaOrgInterface ->publisher($this->getOrganizationSchema()) ->author($this->getOrganizationSchema()) ->isPartOf($this->getWebsiteSchema()); + + if (request()->routeIs('fo.recruitment.show')) { + return $this->toSchemaOrgRecruitment($schema); + } + + return $schema; } /** * Create a SchemaOrg for the recruitment page. * + * @param \Spatie\SchemaOrg\WebPage $schemaBase * @return \Spatie\SchemaOrg\WebPage - * @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh */ - public function toSchemaOrgRecruitment(): \Spatie\SchemaOrg\WebPage + public function toSchemaOrgRecruitment(WebPage $schemaBase): \Spatie\SchemaOrg\WebPage { // @phpcs:enable - $schema = self::toSchemaOrg()->keywords(['recrutement', 'emplois']); + $schema = $schemaBase->keywords(['recrutement', 'emplois']); $offers = []; // @phpstan-ignore argument.type Offer::getOffersCached()->each(function (Offer $offerModel) use (&$offers) { - $schemaJob = Schema::jobPosting()->title($offerModel->title); - - // Recruiter. - $recruiter = Schema::person(); - if (!empty($offerModel->recruiter_firstname)) { - $recruiter->givenName($offerModel->recruiter_firstname); - } - if (!empty($offerModel->recruiter_lastname)) { - $recruiter->familyName($offerModel->recruiter_lastname); - } - if (!empty($offerModel->recruiter_job)) { - $recruiter->jobTitle($offerModel->recruiter_job); - } - $recruiter->affiliation($this->getOrganizationSchema()); - $recruiter->brand($this->getBrandSchema()); - if (!empty($offerModel->recruiter_picture)) { - $recruiter->image( - Schema::imageObject() - ->url(asset($offerModel->recruiter_picture)) - ->headline(sprintf( - "Photo du recruteur %s %s", - $offerModel->recruiter_firstname ?? '', - $offerModel->recruiter_lastname ?? '' - )) - ); - } - $schemaJob->hiringOrganization($recruiter); - - // Location. - $location = Schema::place() - ->isAccessibleForFree(true) - ->publicAccess(true); - if (!empty($offerModel->location_longitude)) { - $location->longitude($offerModel->location_longitude); - } - if (!empty($offerModel->location_latitude)) { - $location->latitude($offerModel->location_latitude); - } - if (!empty($offerModel->location)) { - $location->address($offerModel->location); - } - $schemaJob->jobLocation($location); - - // Other. - if (!empty($offerModel->description)) { - $schemaJob->description($offerModel->description); - } - if (!empty($offerModel->contract)) { - $schemaJob->employmentType($offerModel->contract); - } - if (!empty($offerModel->location_details)) { - $schemaJob->jobLocationType($offerModel->location_details); - } - if (!empty($offerModel->created_at)) { - $schemaJob->datePosted($offerModel->created_at); - } - - $offers[] = $schemaJob; + $offers[] = $offerModel->toSchemaOrg(); }); if (!empty($offers)) { diff --git a/composer.lock b/composer.lock index 7ccbc6ed..ccb8f1cf 100644 --- a/composer.lock +++ b/composer.lock @@ -6004,16 +6004,16 @@ }, { "name": "sentry/sentry", - "version": "4.13.0", + "version": "4.14.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-php.git", - "reference": "b54a0eaedfc27fc2da587e64455a66cd29cd3c4d" + "reference": "f8c50304bd2f1640704345274bdd5af150b6945d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/b54a0eaedfc27fc2da587e64455a66cd29cd3c4d", - "reference": "b54a0eaedfc27fc2da587e64455a66cd29cd3c4d", + "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/f8c50304bd2f1640704345274bdd5af150b6945d", + "reference": "f8c50304bd2f1640704345274bdd5af150b6945d", "shasum": "" }, "require": { @@ -6077,7 +6077,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-php/issues", - "source": "https://github.com/getsentry/sentry-php/tree/4.13.0" + "source": "https://github.com/getsentry/sentry-php/tree/4.14.0" }, "funding": [ { @@ -6089,27 +6089,27 @@ "type": "custom" } ], - "time": "2025-06-10T15:39:27+00:00" + "time": "2025-06-13T17:22:57+00:00" }, { "name": "sentry/sentry-laravel", - "version": "4.14.1", + "version": "4.15.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-laravel.git", - "reference": "8b71a9209e8a01cb5728e743e99299d87d688010" + "reference": "481398f05e4b0f5886f6092e1b95b626a438deff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/8b71a9209e8a01cb5728e743e99299d87d688010", - "reference": "8b71a9209e8a01cb5728e743e99299d87d688010", + "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/481398f05e4b0f5886f6092e1b95b626a438deff", + "reference": "481398f05e4b0f5886f6092e1b95b626a438deff", "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.10", + "sentry/sentry": "^4.13", "symfony/psr-http-message-bridge": "^1.0 | ^2.0 | ^6.0 | ^7.0" }, "require-dev": { @@ -6166,7 +6166,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-laravel/issues", - "source": "https://github.com/getsentry/sentry-laravel/tree/4.14.1" + "source": "https://github.com/getsentry/sentry-laravel/tree/4.15.0" }, "funding": [ { @@ -6178,7 +6178,7 @@ "type": "custom" } ], - "time": "2025-06-07T07:52:41+00:00" + "time": "2025-06-12T20:56:36+00:00" }, { "name": "setasign/fpdi", @@ -9523,16 +9523,16 @@ }, { "name": "uwil/schema-org-laravel", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "git@lab.s-web.fr:ldc-web/projets/schema-org-laravel.git", - "reference": "2a0e46155fbde286d52044959c4ef7188fa5136e" + "reference": "b3473db7c2bb423d030fac68dcaaee8d083e58f7" }, "dist": { "type": "zip", - "url": "https://lab.s-web.fr/api/v4/projects/120/packages/composer/archives/uwil/schema-org-laravel.zip?sha=2a0e46155fbde286d52044959c4ef7188fa5136e", - "reference": "2a0e46155fbde286d52044959c4ef7188fa5136e", + "url": "https://lab.s-web.fr/api/v4/projects/120/packages/composer/archives/uwil/schema-org-laravel.zip?sha=b3473db7c2bb423d030fac68dcaaee8d083e58f7", + "reference": "b3473db7c2bb423d030fac68dcaaee8d083e58f7", "shasum": "" }, "require": { @@ -10488,16 +10488,16 @@ }, { "name": "filp/whoops", - "version": "2.18.2", + "version": "2.18.3", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3" + "reference": "59a123a3d459c5a23055802237cb317f609867e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/89dabca1490bc77dbcab41c2b20968c7e44bf7c3", - "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3", + "url": "https://api.github.com/repos/filp/whoops/zipball/59a123a3d459c5a23055802237cb317f609867e5", + "reference": "59a123a3d459c5a23055802237cb317f609867e5", "shasum": "" }, "require": { @@ -10547,7 +10547,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.2" + "source": "https://github.com/filp/whoops/tree/2.18.3" }, "funding": [ { @@ -10555,7 +10555,7 @@ "type": "github" } ], - "time": "2025-06-11T20:42:19+00:00" + "time": "2025-06-16T00:02:10+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -13594,16 +13594,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.13.0", + "version": "3.13.1", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "65ff2489553b83b4597e89c3b8b721487011d186" + "reference": "1b71b4dd7e7ef651ac749cea67e513c0c832f4bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/65ff2489553b83b4597e89c3b8b721487011d186", - "reference": "65ff2489553b83b4597e89c3b8b721487011d186", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/1b71b4dd7e7ef651ac749cea67e513c0c832f4bd", + "reference": "1b71b4dd7e7ef651ac749cea67e513c0c832f4bd", "shasum": "" }, "require": { @@ -13674,7 +13674,7 @@ "type": "thanks_dev" } ], - "time": "2025-05-11T03:36:00+00:00" + "time": "2025-06-12T15:04:34+00:00" }, { "name": "symfony/polyfill-php81", diff --git a/resources/views/fo/pages/recruitment.blade.php b/resources/views/fo/pages/recruitment.blade.php index a8cac779..98c50a10 100644 --- a/resources/views/fo/pages/recruitment.blade.php +++ b/resources/views/fo/pages/recruitment.blade.php @@ -249,6 +249,4 @@ @endpush -@push('scripts') - {!! $staticPageModel->toSchemaOrgRecruitment()->toScript() !!} -@endpush + From 00ddb3a4f2426250d2f0191e67e7b67c3743f82f Mon Sep 17 00:00:00 2001 From: Alexis Gousseau Date: Tue, 17 Jun 2025 10:34:37 +0200 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=F0=9F=90=9B=20use=20the=20correct?= =?UTF-8?q?=20civility=20enum=20to=20validate=20the=20buying=20club?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Club/ShopController.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/Club/ShopController.php b/app/Http/Controllers/Club/ShopController.php index b733c284..fa957099 100644 --- a/app/Http/Controllers/Club/ShopController.php +++ b/app/Http/Controllers/Club/ShopController.php @@ -2,10 +2,10 @@ namespace App\Http\Controllers\Club; -use App\Enums\Customers\Civility; use App\Enums\Mercanet\PaymentResponse; use App\Enums\Mercanet\RedirectionStatusCode; use App\Enums\Mercanet\TransactionResponse; +use App\Enums\Shop\ContactFormCivility; use App\Enums\Shop\OrderStatus; use App\Enums\Shop\OrderType; use App\Exceptions\CBPayementError; @@ -66,10 +66,9 @@ class ShopController extends Controller // * Pseudo mutex let database fail (unique ref) to check if order has already been processed try { - $order = new Order(); - $order->ref = str_replace('-', '', $request->ref); - // @phpstan-ignore assign.propertyType - $order->civility = Civility::make(intval($request->coords->civility)); + $order = new Order(); + $order->ref = str_replace('-', '', $request->ref); + $order->civility = ContactFormCivility::make(intval($request->coords['civility'])); $order->payementType = OrderType::make(intval($request->payementMethod)); $order->status = OrderStatus::waiting; $order->customer_id = $customerModel->getKey();