From 5ca343a1165cf28394c6f2cf816574781709c99c Mon Sep 17 00:00:00 2001 From: Lars Klopstra Date: Sun, 14 Nov 2021 19:28:19 +0100 Subject: [PATCH] Add support for Favicons (#11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * wip * wip * Update tests/Pest/FaviconTest.php Co-authored-by: Samuel Štancl * Update tests/Pest/FaviconTest.php Co-authored-by: Samuel Štancl * Update README.md Co-authored-by: Samuel Štancl --- README.md | 12 ++++++ .../components/extensions/favicon.blade.php | 2 + composer.json | 3 +- src/SEOManager.php | 36 ++++++++++++++++++ tests/Pest/FaviconTest.php | 14 +++++++ tests/stubs/logo.png | Bin 0 -> 10664 bytes 6 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 assets/views/components/extensions/favicon.blade.php create mode 100644 tests/Pest/FaviconTest.php create mode 100644 tests/stubs/logo.png diff --git a/README.md b/README.md index de69c8e..37b3f62 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,18 @@ When a value is set specifically for Twitter, it will be prioritized over the ge seo()->twitterTitle('About us') ``` +### Favicons + +By default, no favicon links will be included. You can manually enable the extension by calling: + +```php +seo()->favicon('path/to/logo.png'); +``` + +We'll generate a 32x32px `public/favicon.ico` & `public/favicon.png` icon. This should be sufficient for most cases. + +**Please keep in mind that you need to install the [imagick](https://pecl.php.net/package/imagick) php extension and [intervention/image](http://image.intervention.io/) composer package.** + ### Defaults To configure default values, call the methods with the `default` argument: diff --git a/assets/views/components/extensions/favicon.blade.php b/assets/views/components/extensions/favicon.blade.php new file mode 100644 index 0000000..bafadca --- /dev/null +++ b/assets/views/components/extensions/favicon.blade.php @@ -0,0 +1,2 @@ + + diff --git a/composer.json b/composer.json index 6c0e997..6f0e1a6 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,8 @@ "orchestra/testbench": "^6.9", "nunomaduro/larastan": "^0.6.10", "pestphp/pest": "^1.2", - "pestphp/pest-plugin-laravel": "^1.0" + "pestphp/pest-plugin-laravel": "^1.0", + "intervention/image": "^2.7" }, "extra": { "laravel": { diff --git a/src/SEOManager.php b/src/SEOManager.php index d036034..6af7578 100644 --- a/src/SEOManager.php +++ b/src/SEOManager.php @@ -5,7 +5,9 @@ declare(strict_types=1); namespace ArchTech\SEO; use Closure; +use Exception; use Illuminate\Support\Str; +use Intervention\Image\ImageManager; /** * @method $this title(string $title = null, ...$args) Set the title. @@ -176,6 +178,40 @@ class SEOManager return $this->set('image', "https://s.useflipp.com/{$template}.png?s={$signature}&v={$query}"); } + /** Enable favicon extension. */ + public function favicon(string $path): static + { + if (! class_exists(ImageManager::class)) { + throw new Exception('Intervention not available, please run `composer require intervention/image`'); + } + + $this->extensions['favicon'] = true; + + $doesntHaveFavicon = ! file_exists(public_path('favicon.ico')); + $sourceIconDoesntExist = ! file_exists($path); + + if ($sourceIconDoesntExist) { + throw new Exception("Given icon path `{$path}` does not exist."); + } + + if ($doesntHaveFavicon) { + // GD driver doesn't support .ico, that's why we use ImageMagick. + $manager = new ImageManager(['driver' => 'imagick']); + + $manager + ->make($path) + ->resize(32, 32) + ->save(public_path('favicon.ico')); + + $manager + ->make($path) + ->resize(32, 32) + ->save(public_path('favicon.png')); + } + + return $this; + } + /** Append canonical URL tags to the document head. */ public function withUrl(): static { diff --git a/tests/Pest/FaviconTest.php b/tests/Pest/FaviconTest.php new file mode 100644 index 0000000..50d47cc --- /dev/null +++ b/tests/Pest/FaviconTest.php @@ -0,0 +1,14 @@ +favicon('i-dont-exist.png'); +})->throws(Exception::class, 'Given icon path `i-dont-exist.png` does not exist.'); + +test('it should generate two favicons', function () { + seo()->favicon(__DIR__ . '/../stubs/logo.png'); + + assertFileExists(public_path('favicon.ico')); + assertFileExists(public_path('favicon.png')); +}); diff --git a/tests/stubs/logo.png b/tests/stubs/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..afac3bbb3c6506a033569a68b59024803f3af177 GIT binary patch literal 10664 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