Merge branch 'schema-feedback' into 'develop'

fix: 🐛 update with google feedback

See merge request ldc-web/projets/le-gaulois!229
This commit is contained in:
noah.lancien 2025-06-17 09:06:30 +00:00
commit cf20e0f503
7 changed files with 163 additions and 127 deletions

View file

@ -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();

View file

@ -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;
}
}

View file

@ -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([

View file

@ -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>(.*?)<\/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.

View file

@ -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)) {

56
composer.lock generated
View file

@ -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",

View file

@ -249,6 +249,4 @@
</script>
@endpush
@push('scripts')
{!! $staticPageModel->toSchemaOrgRecruitment()->toScript() !!}
@endpush
<x-uwil-schema-org::import :model="$staticPageModel" />