Skip to content

Commit 81e8f2d

Browse files
committed
Add canonical URL support
1 parent 1d6d829 commit 81e8f2d

File tree

11 files changed

+71
-8
lines changed

11 files changed

+71
-8
lines changed

app/Http/Requests/ArticleRequest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public function rules()
1414
'body' => ['required', new HttpImageRule],
1515
'tags' => 'array',
1616
'tags.*' => 'exists:tags,id',
17+
'series' => 'exists:series,id',
18+
'canonical_url' => 'url',
1719
];
1820
}
1921

@@ -41,4 +43,9 @@ public function series(): ?string
4143
{
4244
return $this->get('series');
4345
}
46+
47+
public function canonicalUrl(): ?string
48+
{
49+
return $this->get('canonical_url');
50+
}
4451
}

app/Jobs/CreateArticle.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@ final class CreateArticle
1515

1616
private $author;
1717

18+
private $canonicalUrl;
19+
1820
private $tags;
1921

2022
private $series;
2123

22-
public function __construct(string $title, string $body, User $author, array $tags = [], string $series = null)
24+
public function __construct(string $title, string $body, User $author, string $canonicalUrl = null, array $tags = [], string $series = null)
2325
{
2426
$this->title = $title;
2527
$this->body = $body;
2628
$this->author = $author;
29+
$this->canonicalUrl = $canonicalUrl;
2730
$this->tags = $tags;
2831
$this->series = $series;
2932
}
@@ -34,6 +37,7 @@ public static function fromRequest(ArticleRequest $request): self
3437
$request->title(),
3538
$request->body(),
3639
$request->author(),
40+
$request->canonicalUrl(),
3741
$request->tags(),
3842
$request->series()
3943
);
@@ -44,6 +48,7 @@ public function handle(): Article
4448
$article = new Article([
4549
'title' => $this->title,
4650
'body' => $this->body,
51+
'canonical_url' => $this->canonicalUrl,
4752
'slug' => $this->title,
4853
]);
4954
$article->authoredBy($this->author);

app/Jobs/UpdateArticle.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@ final class UpdateArticle
1414

1515
private $body;
1616

17+
private $canonicalUrl;
18+
1719
private $tags;
1820

1921
private $series;
2022

21-
public function __construct(Article $article, string $title, string $body, array $tags = [], string $series = null)
23+
public function __construct(Article $article, string $title, string $body, string $canonicalUrl = null, array $tags = [], string $series = null)
2224
{
2325
$this->article = $article;
2426
$this->title = $title;
2527
$this->body = $body;
28+
$this->canonicalUrl = $canonicalUrl;
2629
$this->tags = $tags;
2730
$this->series = $series;
2831
}
@@ -33,6 +36,7 @@ public static function fromRequest(Article $article, ArticleRequest $request): s
3336
$article,
3437
$request->title(),
3538
$request->body(),
39+
$request->canonicalUrl(),
3640
$request->tags(),
3741
$request->series()
3842
);
@@ -43,6 +47,7 @@ public function handle(): Article
4347
$this->article->update([
4448
'title' => $this->title,
4549
'body' => $this->body,
50+
'canonical_url' => $this->canonicalUrl,
4651
'slug' => $this->title,
4752
]);
4853
$this->article->syncTags($this->tags);

app/Models/Article.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ final class Article extends Model
1919
protected $fillable = [
2020
'title',
2121
'body',
22+
'canonical_url',
2223
'slug',
2324
];
2425

@@ -37,6 +38,11 @@ public function body(): string
3738
return $this->body;
3839
}
3940

41+
public function canonicalUrl(): string
42+
{
43+
return $this->canonical_url ?: route('articles.show', $this->slug);
44+
}
45+
4046
public function series()
4147
{
4248
return $this->belongsTo(Series::class);

database/migrations/2020_04_07_185543_create_community_articles_table.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public function up()
1919
$table->unsignedInteger('author_id');
2020
$table->string('title');
2121
$table->text('body');
22+
$table->string('canonical_url')->nullable();
2223
$table->string('slug')->unique();
2324
$table->timestamps();
2425

resources/views/articles/_form.blade.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ class="form-control"
2626
@error('body')
2727
@endFormGroup
2828

29+
@formGroup('canonical_url')
30+
<label for="canonical_url">Canonical URL</label>
31+
<input
32+
type="text"
33+
name="canonical_url"
34+
id="canonical_url"
35+
value="{{ isset($article) ? $article->canonicalUrl() : null }}"
36+
class="form-control"
37+
required maxlength="60"
38+
/>
39+
<span class="text-gray-600 text-sm">If you have already posted this article on your own site, enter the URL here and the content will be attributed to you.</span>
40+
@error('canonical_url')
41+
@endFormGroup
42+
2943
@formGroup('tags')
3044
<label for="tags">Tags</label>
3145

resources/views/articles/show.blade.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
@extends('layouts.default')
22

3+
@push('meta')
4+
<link rel="canonical" href="{{ $article->canonicalUrl() }}" />
5+
@endpush
6+
37
@section('content')
48
<div class="max-w-screen-md mx-auto p-4 pt-8">
59
<h1 class="text-4xl tracking-tight leading-10 font-extrabold text-gray-900 sm:leading-none mb-4">{{ $article->title() }}</h1>

resources/views/layouts/base.blade.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
1818
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
1919
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
20+
@stack('meta')
2021

2122
<script>
2223
window.Laravel = {!! json_encode(['csrfToken' => csrf_token()]) !!};

tests/Feature/ArticleTest.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,29 @@ public function users_can_delete_their_own_articles()
145145
/** @test */
146146
public function users_cannot_delete_an_article_they_do_not_own()
147147
{
148-
factory(Article::class)->create(['slug' => 'my-first-thread']);
148+
factory(Article::class)->create(['slug' => 'my-first-article']);
149149

150150
$this->login();
151151

152-
$this->delete('/articles/my-first-thread')
152+
$this->delete('/articles/my-first-article')
153153
->assertForbidden();
154154
}
155+
156+
/** @test */
157+
public function canonical_urls_are_rendered()
158+
{
159+
factory(Article::class)->create(['slug' => 'my-first-article']);
160+
161+
$this->get('/articles/my-first-article')
162+
->see('<link rel="canonical" href="http://localhost/articles/my-first-article" />');
163+
}
164+
165+
/** @test */
166+
public function custom_canonical_urls_are_rendered()
167+
{
168+
factory(Article::class)->create(['slug' => 'my-first-article', 'canonical_url' => 'https://joedixon.co.uk/my-first-article']);
169+
170+
$this->get('/articles/my-first-article')
171+
->see('<link rel="canonical" href="https://joedixon.co.uk/my-first-article" />');
172+
}
155173
}

tests/Integration/Jobs/CreateArticleTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ public function we_can_create_an_article()
1616
{
1717
$user = $this->createUser();
1818

19-
$article = $this->dispatch(new CreateArticle('Title', 'Body', $user));
19+
$article = $this->dispatch(new CreateArticle('Title', 'Body', $user, 'https://laravel.io'));
2020

2121
$this->assertEquals('Title', $article->title());
2222
$this->assertEquals('Body', $article->body());
23+
$this->assertEquals('https://laravel.io', $article->canonicalUrl());
2324
}
2425

2526
/** @test */
@@ -28,10 +29,11 @@ public function we_can_create_an_article_with_a_series()
2829
$user = $this->createUser();
2930
$series = factory(Series::class)->create(['author_id' => $user->id()]);
3031

31-
$article = $this->dispatch(new CreateArticle('Title', 'Body', $user, [], $series->id));
32+
$article = $this->dispatch(new CreateArticle('Title', 'Body', $user, 'https://laravel.io', [], $series->id));
3233

3334
$this->assertEquals('Title', $article->title());
3435
$this->assertEquals('Body', $article->body());
36+
$this->assertEquals('https://laravel.io', $article->canonicalUrl());
3537
$this->assertEquals($series->id, $article->id());
3638
}
3739
}

0 commit comments

Comments
 (0)