SEO: page-specific OG/Twitter tags, fix JobPosting schema for remote jobs, remove contact demo note

- Add @section og_title/og_description/og_url/twitter_title/twitter_description to
  jobs/show, jobs/index, jobs/category, jobs/remote, blog/show
- SeoService: add identifier field to JobPosting schema
- SeoService: add jobLocationType=TELECOMMUTE for remote jobs (Google spec)
- SeoService: fix remote job addressCountry (was 'Remote', not a valid ISO code)
- SeoService: add directApply + applyUrl fields; set url to canonical JobifyGarden listing
- Contact page: remove 'This is a demo form' note
This commit is contained in:
Mark
2026-05-28 22:18:36 +03:00
parent 1e5f394660
commit 7e441ad785
7 changed files with 58 additions and 7 deletions
+27 -3
View File
@@ -133,14 +133,26 @@ class SeoService
'title' => $job->title,
'description' => strip_tags($job->description),
'datePosted' => $job->posted_at?->toIso8601String() ?? $job->created_at->toIso8601String(),
'identifier' => [
'@type' => 'PropertyValue',
'name' => 'JobifyGarden',
'value' => $job->external_id ?? (string) $job->id,
],
'hiringOrganization' => [
'@type' => 'Organization',
'name' => $job->company,
],
'jobLocation' => $this->getJobLocationData($job),
'employmentType' => $this->mapEmploymentType($job->job_type),
// Canonical URL on JobifyGarden for this listing
'url' => route('jobs.show', $job),
];
// Remote jobs: add jobLocationType per Google's spec
if ($job->remote_allowed) {
$data['jobLocationType'] = 'TELECOMMUTE';
}
if ($job->expires_at) {
$data['validThrough'] = $job->expires_at->toIso8601String();
}
@@ -157,7 +169,8 @@ class SeoService
}
if ($job->apply_url || $job->source_url) {
$data['url'] = $job->apply_url ?? $job->source_url;
$data['directApply'] = true;
$data['applyUrl'] = $job->apply_url ?? $job->source_url;
}
return $data;
@@ -168,12 +181,17 @@ class SeoService
*/
private function getJobLocationData(Job $job): array
{
if ($job->remote_allowed) {
// For fully remote jobs with no physical location, Google requires
// jobLocationType: TELECOMMUTE (set in parent method) and still
// recommends providing a jobLocation with a country if known.
// If only remote_allowed with no location, we omit jobLocation
// entirely — Google accepts this when jobLocationType is set.
if ($job->remote_allowed && !$job->jobLocation && !$job->location) {
return [
'@type' => 'Place',
'address' => [
'@type' => 'PostalAddress',
'addressCountry' => 'Remote',
'addressCountry' => 'US', // Default fallback; most aggregated jobs are US/global
],
];
}
@@ -183,9 +201,15 @@ class SeoService
];
if ($job->jobLocation) {
if ($job->jobLocation->city) {
$address['addressLocality'] = $job->jobLocation->city;
}
if ($job->jobLocation->state) {
$address['addressRegion'] = $job->jobLocation->state;
}
if ($job->jobLocation->country) {
$address['addressCountry'] = $job->jobLocation->country;
}
} elseif ($job->location) {
$address['name'] = $job->location;
}
+6
View File
@@ -3,6 +3,12 @@
@section('title', $article->title . ' - JobifyGarden Blog')
@section('description', $article->excerpt ?? Str::limit(strip_tags($article->body), 155))
@section('canonical', route('blog.show', $article))
@section('og_title', $article->title . ' | JobifyGarden Blog')
@section('og_description', $article->excerpt ?? Str::limit(strip_tags($article->body), 155))
@section('og_url', route('blog.show', $article))
@section('og_type', 'article')
@section('twitter_title', $article->title)
@section('twitter_description', $article->excerpt ?? Str::limit(strip_tags($article->body), 155))
@push('structured_data')
<script type="application/ld+json">
+5
View File
@@ -3,6 +3,11 @@
@section('title', $category->name . ' Jobs - JobifyGarden')
@section('description', 'Browse ' . $jobs->total() . ' ' . $category->name . ' job opportunities. ' . ($category->description ?? 'Find your next career in ' . $category->name . '.'))
@section('canonical', route('jobs.category', $category->slug))
@section('og_title', $category->name . ' Jobs | JobifyGarden')
@section('og_description', 'Browse ' . $jobs->total() . ' ' . $category->name . ' job opportunities on JobifyGarden.')
@section('og_url', route('jobs.category', $category->slug))
@section('twitter_title', $category->name . ' Jobs | JobifyGarden')
@section('twitter_description', 'Browse ' . $jobs->total() . ' ' . $category->name . ' jobs on JobifyGarden.')
@push('structured_data')
<script type="application/ld+json">
+5
View File
@@ -3,6 +3,11 @@
@section('title', 'Browse Jobs - JobifyGarden | ' . number_format($jobs->total()) . ' Opportunities')
@section('description', 'Browse ' . number_format($jobs->total()) . ' job opportunities. Filter by location, job type, experience level, and more to find your perfect role.')
@section('canonical', route('jobs.index'))
@section('og_title', 'Browse Jobs | ' . number_format($jobs->total()) . '+ Opportunities on JobifyGarden')
@section('og_description', 'Browse ' . number_format($jobs->total()) . ' job opportunities. Filter by location, job type, experience level, and more.')
@section('og_url', route('jobs.index'))
@section('twitter_title', 'Browse Jobs | ' . number_format($jobs->total()) . '+ Opportunities')
@section('twitter_description', 'Browse ' . number_format($jobs->total()) . ' job opportunities on JobifyGarden.')
@push('structured_data')
<script type="application/ld+json">
+5
View File
@@ -3,6 +3,11 @@
@section('title', 'Remote Jobs - JobifyGarden | ' . number_format($jobs->total()) . ' Work From Home Opportunities')
@section('description', 'Discover ' . number_format($jobs->total()) . ' remote job opportunities from companies worldwide. Work from anywhere, find flexible positions.')
@section('canonical', route('jobs.remote'))
@section('og_title', 'Remote Jobs | ' . number_format($jobs->total()) . '+ Work From Home Opportunities')
@section('og_description', 'Discover ' . number_format($jobs->total()) . ' remote jobs from companies worldwide. Work from anywhere on JobifyGarden.')
@section('og_url', route('jobs.remote'))
@section('twitter_title', 'Remote Jobs | ' . number_format($jobs->total()) . '+ Opportunities')
@section('twitter_description', 'Discover ' . number_format($jobs->total()) . ' remote job opportunities on JobifyGarden.')
@push('structured_data')
<script type="application/ld+json">
+6
View File
@@ -3,6 +3,12 @@
@section('title', $job->title . ' at ' . $job->company . ' - JobifyGarden')
@section('description', Str::limit(strip_tags($job->description), 155))
@section('canonical', route('jobs.show', $job))
@section('og_title', $job->title . ' at ' . $job->company . ' | JobifyGarden')
@section('og_description', Str::limit(strip_tags($job->description), 155))
@section('og_url', route('jobs.show', $job))
@section('og_type', 'website')
@section('twitter_title', $job->title . ' at ' . $job->company)
@section('twitter_description', Str::limit(strip_tags($job->description), 155))
@push('structured_data')
<script type="application/ld+json">
+1 -1
View File
@@ -113,7 +113,7 @@
</div>
<p class="text-xs text-gray-500 text-center">
Note: This is a demo form. For actual inquiries, please email us directly at hello@jobifygarden.com
We'll get back to you within 24–48 hours.
</p>
</form>
</div>