Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for FAQPage schema #77

Merged
merged 7 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 33 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This package generates **valid and useful meta tags straight out-of-the-box**, w
2. Meta tags (author, description, image, robots, etc.)
3. OpenGraph Tags (Facebook, LinkedIn, etc.)
4. Twitter Tags
5. Structured data (Article and Breadcrumbs)
5. Structured data (Article, Breadcrumbs and FAQPage)
6. Favicon
7. Robots tag

Expand Down Expand Up @@ -321,6 +321,7 @@ This package can also **generate structured data** for you (also called schema m

1. `Article`
2. `BreadcrumbList`
3. `FAQPage`

After generating the structured data it is always a good idea to [test your website with Google's rich result validator](https://search.google.com/test/rich-results).

Expand Down Expand Up @@ -356,7 +357,7 @@ You can completely customize the schema markup by using the `->markup()` method
```php
use Illuminate\Support\Collection;

SchemaCollection::initialize()->addArticle(function(ArticleSchema $article): ArticleSchema {
SchemaCollection::initialize()->addArticle(function (ArticleSchema $article): ArticleSchema {
return $article->markup(function(Collection $markup): Collection {
return $markup->put('alternativeHeadline', $this->tagline);
});
Expand All @@ -370,18 +371,20 @@ At this point, I'm just unable to fluently support every possible version of the
You can also add `BreadcrumbList` schema markup by using the `->addBreadcrumbs()` function on the `SchemaCollection`:

```php
SchemaCollection::initialize()->addBreadcrumbs(
function(BreadcrumbListSchema $breadcrumbs): BreadcrumbListSchema {
return $breadcrumbs->prependBreadcrumbs([
'Homepage' => 'https://example.com',
'Category' => 'https://example.com/test',
])->appendBreadcrumbs([
'Subarticle' => 'https://example.com/test/article/2',
])->markup(function(Collection $markup): Collection {
// ...
});
}
);
SchemaCollection::initialize()
->addBreadcrumbs(function (BreadcrumbListSchema $breadcrumbs): BreadcrumbListSchema {
return $breadcrumbs
->prependBreadcrumbs([
'Homepage' => 'https://example.com',
'Category' => 'https://example.com/test',
])
->appendBreadcrumbs([
'Subarticle' => 'https://example.com/test/article/2',
])
->markup(function(Collection $markup): Collection {
// ...
});
});
```

This code will generate `BreadcrumbList` JSON-LD structured data with the following four pages:
Expand All @@ -391,6 +394,22 @@ This code will generate `BreadcrumbList` JSON-LD structured data with the follow
3. [Current page]
4. Subarticle

### FAQPage schema markup

You can also add `FAQPage` schema markup by using the `->addFaqPage()` function on the `SchemaCollection`:

```php
use RalphJSmit\Laravel\SEO\Schema\FaqPageSchema;
use RalphJSmit\Laravel\SEO\SchemaCollection;

SchemaCollection::initialize()
->addFaqPage(function (FaqPageSchema $faqPage): FaqPageSchema {
return $faqPage
->addQuestion(name: "Can this package add FaqPage to the schema?", acceptedAnswer: "Yes!")
->addQuestion(name: "Does it support multiple questions?", acceptedAnswer: "Of course.");
});
```

## Advanced usage

Sometimes you may have advanced needs, that require you to apply your own logic to the `SEOData` class, just before it is used to generate the tags.
Expand Down
50 changes: 50 additions & 0 deletions src/Schema/FaqPageSchema.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace RalphJSmit\Laravel\SEO\Schema;

use Illuminate\Support\HtmlString;
use RalphJSmit\Laravel\SEO\Support\SEOData;

/**
* @see https://developers.google.com/search/docs/appearance/structured-data/faqpage
*/
class FaqPageSchema extends Schema
{
public string $type = 'FAQPage';

public array $questions = [];

public function addQuestion(
string $name,
string $acceptedAnswer
): static {
$this->questions[] = [
'@type' => 'Question',
'name' => $name,
'acceptedAnswer' => [
'@type' => 'Answer',
'text' => $acceptedAnswer,
],
];

return $this;
}

public function initializeMarkup(SEOData $SEOData, array $markupBuilders): void
{
//
}

public function generateInner(): HtmlString
{
$inner = collect([
'@context' => 'https://schema.org',
'@type' => $this->type,
'mainEntity' => $this->questions,
])
->pipeThrough($this->markupTransformers)
->toJson();

return new HtmlString($inner);
}
}
9 changes: 9 additions & 0 deletions src/SchemaCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
use Illuminate\Support\Collection;
use RalphJSmit\Laravel\SEO\Schema\ArticleSchema;
use RalphJSmit\Laravel\SEO\Schema\BreadcrumbListSchema;
use RalphJSmit\Laravel\SEO\Schema\FaqPageSchema;
use RalphJSmit\Laravel\SEO\Schema\Schema;

class SchemaCollection extends Collection
{
protected array $dictionary = [
'article' => ArticleSchema::class,
'breadcrumbs' => BreadcrumbListSchema::class,
'faqPage' => FaqPageSchema::class,
];

public array $markup = [];
Expand All @@ -31,6 +33,13 @@ public function addBreadcrumbs(?Closure $builder = null): static
return $this;
}

public function addFaqPage(?Closure $builder = null): static
{
$this->markup[$this->dictionary['faqPage']][] = $builder ?: fn (Schema $schema): Schema => $schema;

return $this;
}

public static function initialize(): static
{
return new static();
Expand Down
59 changes: 59 additions & 0 deletions tests/Feature/JSON-LD/FaqPageTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

use RalphJSmit\Laravel\SEO\Schema\FaqPageSchema;
use RalphJSmit\Laravel\SEO\SchemaCollection;
use RalphJSmit\Laravel\SEO\Tests\Fixtures\Page;

use function Pest\Laravel\get;

it('does not render by default the JSON-LD Schema markup: FaqPageTest', function () {
get(route('seo.test-plain'))
->assertDontSee('"application/ld+json"')
->assertDontSee('"@type": "FAQPage"');
});

it('can correctly render the JSON-LD Schema markup: FaqPageTest', function () {
config()->set('seo.title.suffix', ' | Laravel SEO');

$page = Page::create([]);

$page::$overrides = [
'title' => 'Test FAQ',
'enableTitleSuffix' => true,
'url' => 'https://example.com/test/faq',
'schema' => SchemaCollection::initialize()->addFaqPage(function (FaqPageSchema $faqPage): FaqPageSchema {
return $faqPage
->addQuestion(name: 'Can this package add FaqPage to the schema?', acceptedAnswer: 'Yes!')
->addQuestion(name: 'Does it support multiple questions?', acceptedAnswer: 'Of course.');
}),
];

get(route('seo.test-page', ['page' => $page]))
->assertSee('"application/ld+json"', false)
->assertSee(
'<script type="application/ld+json">' .
json_encode([
'@context' => 'https://schema.org',
'@type' => 'FAQPage',
'mainEntity' => [
[
'@type' => 'Question',
'name' => 'Can this package add FaqPage to the schema?',
'acceptedAnswer' => [
'@type' => 'Answer',
'text' => 'Yes!',
],
],
[
'@type' => 'Question',
'name' => 'Does it support multiple questions?',
'acceptedAnswer' => [
'@type' => 'Answer',
'text' => 'Of course.',
],
],
],
]) . '</script>',
false
);
});
Loading