1
0
Fork 0
mirror of https://github.com/archtechx/laravel-seo.git synced 2025-12-12 01:44:03 +00:00
This commit is contained in:
Mischa Sigtermans 2024-11-23 00:38:45 +01:00 committed by GitHub
commit 90d1e9f89d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 105 additions and 105 deletions

View file

@ -7,7 +7,7 @@ By default, it uses `<title>` and OpenGraph tags. It also ships with a Twitter e
**Features**: **Features**:
- Setting SEO tags from PHP - Setting SEO tags from PHP
- Setting SEO tags from Blade - Setting SEO tags from Blade
- Integration with [Flipp](https://useflipp.com) and [Previewify](https://previewify.app), to automatically generate cover images - Integration with [Flipp](https://useflipp.com) and [PreviewLinks](https://previewlinks.io), to automatically generate cover images
- Custom extension support - Custom extension support
- Expressive & simple API - Expressive & simple API
- Customizable views - Customizable views
@ -228,26 +228,26 @@ The `flipp()` method also returns a signed URL to the image, which lets you use
<img alt="@seo('title')" src="@seo('flipp', 'blog')"> <img alt="@seo('title')" src="@seo('flipp', 'blog')">
``` ```
### Previewify integration ### PreviewLinks integration
First, you need to add your Previewify API keys: First, you need to add your PreviewLinks API keys:
1. Add your API key to the `PREVIEWIFY_KEY` environment variable. You can get the key [here](https://previewify.app/app/account). 1. Add your API key to the `PREVIEWLINKS_API_TOKEN` environment variable. You can get the key [here](https://previewlinks.io/app/account).
2. Go to `config/services.php` and add: 2. Go to `config/services.php` and add:
```php ```php
'previewify' => [ 'previewlinks' => [
'key' => env('PREVIEWIFY_KEY'), 'key' => env('PREVIEWLINKS_API_TOKEN'),
], ],
``` ```
Then, register your templates, for example in `AppServiceProvider`: Then, register your templates, for example in `AppServiceProvider`:
```php ```php
seo()->previewify('blog', 24); seo()->previewlink('blog', 24);
seo()->previewify('page', 83); seo()->previewlink('page', 83);
``` ```
After that, you can use the templates by calling `seo()->previewify()` like this: After that, you can use the templates by calling `seo()->previewlink()` like this:
```php ```php
seo()->previewify('blog', ['title' => 'Foo', 'content' => 'bar'])` seo()->previewlink('blog', ['title' => 'Foo', 'content' => 'bar'])`
``` ```
The call will set the generated image as the OpenGraph and Twitter card images. The generated URLs are signed. The call will set the generated image as the OpenGraph and Twitter card images. The generated URLs are signed.
@ -257,16 +257,16 @@ If no data array is provided, the method will use the `title` and `description`
```php ```php
seo()->title($post->title); seo()->title($post->title);
seo()->description($post->excerpt); seo()->description($post->excerpt);
seo()->previewify('blog'); seo()->previewlink('blog');
``` ```
The `previewify()` method also returns a signed URL to the image, which lets you use it in other places, such as blog cover images. The `previewlink()` method also returns a signed URL to the image, which lets you use it in other places, such as blog cover images.
```php ```php
<img alt="@seo('title')" src="@seo('previewify', 'blog')"> <img alt="@seo('title')" src="@seo('previewlink', 'blog')">
``` ```
> **Note** > **Note**
> The `previewify:` prefix will be automatically prepended to all provided data keys. > The `previewlink:` prefix will be automatically prepended to all provided data keys.
## Examples ## Examples

View file

@ -14,6 +14,6 @@ parameters:
ignoreErrors: ignoreErrors:
- '#^Method ArchTech\\SEO\\SEOManager::flipp\(\) should return static\(ArchTech\\SEO\\SEOManager\)\|string but returns array\|string\|null\.$#' - '#^Method ArchTech\\SEO\\SEOManager::flipp\(\) should return static\(ArchTech\\SEO\\SEOManager\)\|string but returns array\|string\|null\.$#'
- '#^Method ArchTech\\SEO\\SEOManager::previewify\(\) should return static\(ArchTech\\SEO\\SEOManager\)\|string but returns array\|string\|null\.$#' - '#^Method ArchTech\\SEO\\SEOManager::previewlink\(\) should return static\(ArchTech\\SEO\\SEOManager\)\|string but returns array\|string\|null\.$#'
- '#^Method ArchTech\\SEO\\SEOManager::render\(\) has parameter \$args with no type specified\.$#' - '#^Method ArchTech\\SEO\\SEOManager::render\(\) has parameter \$args with no type specified\.$#'
- '#^Parameter \#1 \$value of function e expects#' - '#^Parameter \#1 \$value of function e expects#'

View file

@ -184,23 +184,23 @@ class SEOManager
return $this->set('image', "https://s.useflipp.com/{$template}.png?s={$signature}&v={$query}"); return $this->set('image', "https://s.useflipp.com/{$template}.png?s={$signature}&v={$query}");
} }
/** Configure or use Previewify. */ /** Configure or use PreviewLinks. */
public function previewify(string $alias, int|string|array $data = null): string|static public function previewlink(string $alias, int|string|array $data = null): string|static
{ {
if (is_string($data) || is_int($data)) { if (is_string($data) || is_int($data)) {
$this->meta("previewify.templates.$alias", (string) $data); $this->meta("previewlink.templates.$alias", (string) $data);
return $this; return $this;
} }
if ($data === null) { if ($data === null) {
$data = [ $data = [
'previewify:title' => $this->raw('title'), 'previewlinks:title' => $this->raw('title'),
'previewify:description' => $this->raw('description'), 'previewlinks:description' => $this->raw('description'),
]; ];
} else { } else {
$data = array_combine( $data = array_combine(
array_map(fn ($key) => str_starts_with($key, 'previewify:') ? $key : "previewify:{$key}", array_keys($data)), array_map(fn ($key) => str_starts_with($key, 'previewlinks:') ? $key : "previewlinks:{$key}", array_keys($data)),
$data, $data,
); );
} }
@ -208,11 +208,11 @@ class SEOManager
$query = base64_encode(json_encode($data, JSON_THROW_ON_ERROR)); $query = base64_encode(json_encode($data, JSON_THROW_ON_ERROR));
/** @var string $template */ /** @var string $template */
$template = $this->meta("previewify.templates.$alias"); $template = $this->meta("previewlink.templates.$alias");
$signature = hash_hmac('sha256', $query, config('services.previewify.key')); $signature = hash_hmac('sha256', $query, config('services.previewlinks.key'));
return $this->set('image', "https://previewify.app/generate/templates/{$template}/signed?signature={$signature}&fields={$query}"); return $this->set('image', "https://previewlinks.io/generate/templates/{$template}/signed?fields={$query}&signature={$signature}");
} }
/** Enable favicon extension. */ /** Enable favicon extension. */
@ -336,11 +336,11 @@ class SEOManager
*/ */
public function render(...$args): array|string|null public function render(...$args): array|string|null
{ {
// Flipp and Previewify support more arguments // Flipp and PreviewLinks support more arguments
if (in_array($args[0], ['flipp', 'previewify'], true)) { if (in_array($args[0], ['flipp', 'previewlink'], true)) {
$method = array_shift($args); $method = array_shift($args);
// The `flipp` and `previewify` methods return image URLs // The `flipp` and `previewlink` methods return image URLs
// so we don't sanitize the returned value with e() here // so we don't sanitize the returned value with e() here
return $this->{$method}(...$args); return $this->{$method}(...$args);
} }
@ -353,7 +353,7 @@ class SEOManager
// An array means we don't return anything, e.g. `@seo(['title' => 'foo']) // An array means we don't return anything, e.g. `@seo(['title' => 'foo'])
if (is_array($args[0])) { if (is_array($args[0])) {
foreach ($args[0] as $type => $value) { foreach ($args[0] as $type => $value) {
if (in_array($type, ['flipp', 'previewify'], true)) { if (in_array($type, ['flipp', 'previewlink'], true)) {
$this->{$type}(...Arr::wrap($value)); $this->{$type}(...Arr::wrap($value));
} else { } else {
$this->set($type, $value); $this->set($type, $value);

View file

@ -0,0 +1,77 @@
<?php
beforeEach(fn () => config(['services.previewlinks.key' => 'abc']));
test('previewlink templates can be set', function () {
seo()->previewlink('blog', 1);
expect(seo()->meta('previewlink.templates'))
->toHaveCount(1)
->toHaveKey('blog', '1');
});
test('previewlink makes a request to the template not the alias', function () {
seo()->previewlink('blog', 1);
expect(seo()->previewlink('blog'))
->toContain('previewlinks.io/generate/templates/1');
});
test('previewlink templates can be given data', function () {
seo()->previewlink('blog', 1);
expect(seo()->previewlink('blog', ['title' => 'abc', 'previewlinks:excerpt' => 'def']))
->toContain('previewlinks.io/generate/templates/1')
->toContain(base64_encode(json_encode(['previewlinks:title' => 'abc', 'previewlinks:excerpt' => 'def'])));
});
test('the previewlink method returns a link to a signed url', function () {
seo()->previewlink('blog', 1);
expect(seo()->previewlink('blog', ['title' => 'abc']))
->toContain('?signature=' . hash_hmac('sha256', base64_encode(json_encode(['previewlinks:title' => 'abc'])), config('services.previewlinks.key')));
});
test("previewlink templates use default data when they're not passed any data explicitly", function () {
seo()->previewlink('blog', 1);
seo()->title('foo')->description('bar');
expect(seo()->previewlink('blog'))
->toContain('previewlinks.io/generate/templates/1')
->toContain(base64_encode(json_encode(['previewlinks:title' => 'foo', 'previewlinks:description' => 'bar'])));
});
test('previewlink images are used as the cover images', function () {
seo()->previewlink('blog', 1);
seo()->title('foo')->description('bar');
expect(seo()->previewlink('blog'))
->toBe(seo('image'));
});
test('the blade directive can be used with previewlinks', function () {
seo()->previewlink('blog', 1);
seo()->title('foo')->description('bar');
expect(blade("@seo('previewlink', 'blog')"))->toBe(seo()->previewlink('blog'));
expect(blade("@seo('previewlink', 'blog', ['title' => 'abc'])"))->toBe(seo()->previewlink('blog', ['title' => 'abc']));
});
test('previewlink uses the raw title and description', function () {
seo()->previewlink('blog', 1);
seo()->title(modify: fn (string $title) => $title . ' - modified');
seo()->title('foo')->description('bar');
expect(seo()->previewlink('blog'))
->toContain('previewlinks.io/generate/templates/1')
->toContain(base64_encode(json_encode(['previewlinks:title' => 'foo', 'previewlinks:description' => 'bar'])));
});
test('the @seo helper can be used for setting a previewlinks image', function () {
seo()->previewlink('blog', 1);
blade("@seo(['previewlink' => ['blog', ['title' => 'abc', 'excerpt' => 'def']]])");
expect(seo('image'))->toContain('previewlinks.io/generate/templates/1');
});

View file

@ -1,77 +0,0 @@
<?php
beforeEach(fn () => config(['services.previewify.key' => 'abc']));
test('previewify templates can be set', function () {
seo()->previewify('blog', 1);
expect(seo()->meta('previewify.templates'))
->toHaveCount(1)
->toHaveKey('blog', '1');
});
test('previewify makes a request to the template not the alias', function () {
seo()->previewify('blog', 1);
expect(seo()->previewify('blog'))
->toContain('previewify.app/generate/templates/1');
});
test('previewify templates can be given data', function () {
seo()->previewify('blog', 1);
expect(seo()->previewify('blog', ['title' => 'abc', 'previewify:excerpt' => 'def']))
->toContain('previewify.app/generate/templates/1')
->toContain(base64_encode(json_encode(['previewify:title' => 'abc', 'previewify:excerpt' => 'def'])));
});
test('the previewify method returns a link to a signed url', function () {
seo()->previewify('blog', 1);
expect(seo()->previewify('blog', ['title' => 'abc']))
->toContain('?signature=' . hash_hmac('sha256', base64_encode(json_encode(['previewify:title' => 'abc'])), config('services.previewify.key')));
});
test("previewify templates use default data when they're not passed any data explicitly", function () {
seo()->previewify('blog', 1);
seo()->title('foo')->description('bar');
expect(seo()->previewify('blog'))
->toContain('previewify.app/generate/templates/1')
->toContain(base64_encode(json_encode(['previewify:title' => 'foo', 'previewify:description' => 'bar'])));
});
test('previewify images are used as the cover images', function () {
seo()->previewify('blog', 1);
seo()->title('foo')->description('bar');
expect(seo()->previewify('blog'))
->toBe(seo('image'));
});
test('the blade directive can be used with previewify', function () {
seo()->previewify('blog', 1);
seo()->title('foo')->description('bar');
expect(blade("@seo('previewify', 'blog')"))->toBe(seo()->previewify('blog'));
expect(blade("@seo('previewify', 'blog', ['title' => 'abc'])"))->toBe(seo()->previewify('blog', ['title' => 'abc']));
});
test('previewify uses the raw title and description', function () {
seo()->previewify('blog', 1);
seo()->title(modify: fn (string $title) => $title . ' - modified');
seo()->title('foo')->description('bar');
expect(seo()->previewify('blog'))
->toContain('previewify.app/generate/templates/1')
->toContain(base64_encode(json_encode(['previewify:title' => 'foo', 'previewify:description' => 'bar'])));
});
test('the @seo helper can be used for setting a previewify image', function () {
seo()->previewify('blog', 1);
blade("@seo(['previewify' => ['blog', ['title' => 'abc', 'excerpt' => 'def']]])");
expect(seo('image'))->toContain('previewify.app/generate/templates/1');
});