Skip to content

Commit

Permalink
Add support for relative urls in html fields
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisrhymes committed Jan 17, 2024
1 parent 80539ee commit 89ca58b
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 5 deletions.
18 changes: 18 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,23 @@ $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 begin with '/', rather than absolute urls beginning 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.

```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
11 changes: 9 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,12 @@ public function handle()
$anchorTags = $doc->getElementsByTagName('a');

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

$link = new Link;
$link->url = $anchorTag->getAttribute('href');
$link->url = Str::startsWith($href, '/') && $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);
}
}
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 89ca58b

Please sign in to comment.