Skip to content

Commit

Permalink
Merge pull request #15 from chrisrhymes/feature/support-relative-links
Browse files Browse the repository at this point in the history
Add support for relative urls in html fields
  • Loading branch information
chrisrhymes authored Jan 28, 2024
2 parents 80539ee + 7832a55 commit 64c1cdc
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 12 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/laravel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ on:
branches:
- main
- develop
- "feature/**"
- 'feature/**'
pull_request:
branches: ["main", "develop"]
branches: ['main', 'develop']

jobs:
laravel-tests:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04

steps:
- uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e
- uses: shivammathur/setup-php@v2
with:
php-version: "8.1"
- uses: actions/checkout@v3
php-version: '8.2'
- uses: actions/checkout@v4
- name: Install Dependencies
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: Create Database
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
phplint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: 'laravel-pint'
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A package that will check for broken links in the specified model's fields. It w

- [Getting Started](#getting-started)
- [Usage](#usage)
- [Relative links](#relative-links)
- [Rate Limiting](#rate-limiting)
- [User Agent](#user-agent)
- [Verify SSL](#verify-ssl)
Expand Down Expand Up @@ -96,6 +97,25 @@ $post->brokenLinks[0]->broken_link; // The link that is broken
$post->brokenLinks[0]->exception_message; // The optional exception message
```

### Relative links

If you have relative links within a html field in your model (that don't begin with 'http'), then you can pass a 3rd parameter as the base. The CheckModelForBrokenLinks job will prepend the base to the relative url before it is checked.

If your relative links don't begin with `/`, then ensure your base parameter has a trailing slash, `'http://example.com/'`.

```php
use ChrisRhymes\LinkChecker\Jobs\CheckModelForBrokenLinks;
use ChrisRhymes\LinkChecker\Facades\LinkChecker;

$post = Post::first();

// Dispatch the job directly
CheckModelForBrokenLinks::dispatch($post, ['content', 'url'], 'http://example.com');

// Or using the facade
LinkChecker::checkForBrokenLinks($post, ['content', 'url'], 'http://example.com');
```

## Rate Limiting

In order to reduce the amount of requests sent to a domain at a time, this package has rate limiting enabled.
Expand Down
15 changes: 13 additions & 2 deletions src/Jobs/CheckModelForBrokenLinks.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@ class CheckModelForBrokenLinks implements ShouldQueue

private array $links = [];

private ?string $base = null;

/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Model $model, array $fields)
public function __construct(Model $model, array $fields, ?string $base = null)
{
$this->model = $model;
$this->fields = $fields;
$this->base = $base;
}

/**
Expand Down Expand Up @@ -65,8 +68,16 @@ public function handle()
$anchorTags = $doc->getElementsByTagName('a');

foreach ($anchorTags as $anchorTag) {
$href = $anchorTag->getAttribute('href');

if (Str::startsWith($href, ['mailto', 'tel'])) {
continue;
}

$link = new Link;
$link->url = $anchorTag->getAttribute('href');
$link->url = ! Str::startsWith($href, ['http://', 'https://']) && $this->base
? $this->base.$href
: $href;
$link->text = $anchorTag->nodeValue;

$this->links[] = $link;
Expand Down
4 changes: 2 additions & 2 deletions src/LinkChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

class LinkChecker
{
public function checkForBrokenLinks(Model $model, array $fields)
public function checkForBrokenLinks(Model $model, array $fields, ?string $base = null)
{
CheckModelForBrokenLinks::dispatch($model, $fields);
CheckModelForBrokenLinks::dispatch($model, $fields, $base);
}
}
10 changes: 10 additions & 0 deletions tests/Feature/AvoidTelMailtoLinksTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,14 @@
expect($this->post->fresh()->brokenLinks)->toHaveCount(0);

Http::assertNothingSent();

$this->assertDatabaseCount('broken_links', 0);
});

it('does not check tel and mailto links when base is added', function () {
CheckModelForBrokenLinks::dispatch($this->post, ['content'], 'https://this-is-a-relative-link.com/');

Http::assertNothingSent();

$this->assertDatabaseCount('broken_links', 0);
});
66 changes: 66 additions & 0 deletions tests/Feature/RelativeLinkTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

use ChrisRhymes\LinkChecker\Facades\LinkChecker;
use ChrisRhymes\LinkChecker\Jobs\CheckModelForBrokenLinks;
use ChrisRhymes\LinkChecker\Test\Models\Post;
use Illuminate\Support\Facades\Http;

beforeEach(function () {
Http::fake([
'https://this-is-a-relative-link.com/relative' => Http::response(null, 302),
]);

$this->post = Post::factory()
->create([
'content' => '
<a href="relative">Temporary redirect link</a>',
]);
});

it('records relative urls using base url', function () {
CheckModelForBrokenLinks::dispatch($this->post, ['content'], 'https://this-is-a-relative-link.com/');

$this->assertDatabaseHas('broken_links', [
'linkable_id' => $this->post->id,
'linkable_type' => 'ChrisRhymes\LinkChecker\Test\Models\Post',
'broken_link' => 'https://this-is-a-relative-link.com/relative',
'link_text' => 'Temporary redirect link',
'exception_message' => '302 Redirect',
]);
});

it('records relative urls using base url using the facade', function () {
LinkChecker::checkForBrokenLinks($this->post, ['content'], 'https://this-is-a-relative-link.com/');

$this->assertDatabaseHas('broken_links', [
'linkable_id' => $this->post->id,
'linkable_type' => 'ChrisRhymes\LinkChecker\Test\Models\Post',
'broken_link' => 'https://this-is-a-relative-link.com/relative',
'link_text' => 'Temporary redirect link',
'exception_message' => '302 Redirect',
]);
});

it('records curl error for relative urls without setting base', function () {
CheckModelForBrokenLinks::dispatch($this->post, ['content']);

$this->assertDatabaseHas('broken_links', [
'linkable_id' => $this->post->id,
'linkable_type' => 'ChrisRhymes\LinkChecker\Test\Models\Post',
'broken_link' => 'relative',
'link_text' => 'Temporary redirect link',
'exception_message' => 'cURL error 6: Could not resolve host: relative (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)',
]);
});

it('records curl error for relative urls without setting base using the facade', function () {
LinkChecker::checkForBrokenLinks($this->post, ['content']);

$this->assertDatabaseHas('broken_links', [
'linkable_id' => $this->post->id,
'linkable_type' => 'ChrisRhymes\LinkChecker\Test\Models\Post',
'broken_link' => 'relative',
'link_text' => 'Temporary redirect link',
'exception_message' => 'cURL error 6: Could not resolve host: relative (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)',
]);
});
2 changes: 1 addition & 1 deletion tests/Models/Post.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
class Post extends Model
{
use HasFactory, HasBrokenLinks;
use HasBrokenLinks, HasFactory;

protected $fillable = [
'title',
Expand Down

0 comments on commit 64c1cdc

Please sign in to comment.