├── .github ├── dependabot.yml └── workflows │ ├── pint.yml │ └── run-tests.yml ├── .gitignore ├── .styleci.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── config.php ├── phpunit.xml.dist ├── resources └── views │ └── img.blade.php ├── src ├── Unavatar.php ├── UnavatarServiceProvider.php └── View │ └── Components │ └── Img.php └── tests ├── ComponentTest.php ├── TestCase.php └── UnavatarTest.php /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: composer 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/pint.yml: -------------------------------------------------------------------------------- 1 | name: pint 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | phpcs: 9 | runs-on: ubuntu-latest 10 | timeout-minutes: 5 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: shivammathur/setup-php@v2 14 | with: 15 | php-version: 8.2 16 | - run: composer install --no-interaction --no-scripts 17 | - run: vendor/bin/pint --test 18 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | php: ['8.3', '8.2', '8.1', '8.0'] 15 | laravel: ['11.*', '10.*', '9.*', '8.*'] 16 | dependency-version: ['prefer-lowest', 'prefer-stable'] 17 | exclude: 18 | - laravel: '11.*' 19 | php: '8.1' 20 | - laravel: '11.*' 21 | php: '8.0' 22 | - laravel: '10.*' 23 | php: '8.0' 24 | - laravel: '9.*' 25 | dependency-version: 'prefer-lowest' 26 | - laravel: '8.*' 27 | dependency-version: 'prefer-lowest' 28 | 29 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} 30 | 31 | steps: 32 | - uses: actions/checkout@v2 33 | 34 | - uses: shivammathur/setup-php@v2 35 | with: 36 | php-version: ${{ matrix.php }} 37 | extensions: dom, curl, libxml, mbstring, zip 38 | 39 | - name: Install dependencies 40 | run: | 41 | composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update 42 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest 43 | 44 | - run: vendor/bin/phpunit 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /coverage/ 3 | /composer.lock 4 | /phpunit.xml -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | 3 | risky: true 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-unavatar` will be documented in this file 4 | 5 | ## 0.5.0 - 2024-06-25 6 | 7 | - drop PHP7.4 support 8 | - add Laravel 10 & 11 support 9 | - drop `astrotomic/php-unavatar` v0.2 support 10 | 11 | ## 0.4.0 - 2023-08-09 12 | 13 | - add `astrotomic/php-unavatar` v0.3 support 14 | 15 | ## 0.3.0 - 2022-04-08 16 | 17 | - add Laravel 9 support 18 | 19 | ## 0.2.1 - 2020-12-20 20 | 21 | - fix namespace 22 | 23 | ## 0.2.0 - 2020-12-20 24 | 25 | - add Laravel 8 support 26 | - add PHP 8 support 27 | - add Blade component `` 28 | - drop Laravel 6 & 7 support 29 | 30 | ## 0.1.1 - 2020-03-12 31 | 32 | - add Laravel 7 support 33 | 34 | ## 0.1.0 - 2020-02-22 35 | 36 | - initial release 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Astrotomic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Unavatar 2 | 3 | [![Latest Version](http://img.shields.io/packagist/v/astrotomic/laravel-unavatar.svg?label=Release&style=for-the-badge)](https://packagist.org/packages/astrotomic/laravel-unavatar) 4 | [![MIT License](https://img.shields.io/github/license/Astrotomic/laravel-unavatar.svg?label=License&color=blue&style=for-the-badge)](https://github.com/Astrotomic/laravel-unavatar/blob/master/LICENSE) 5 | [![Offset Earth](https://img.shields.io/badge/Treeware-%F0%9F%8C%B3-green?style=for-the-badge)](https://plant.treeware.earth/Astrotomic/laravel-unavatar) 6 | [![Larabelles](https://img.shields.io/badge/Larabelles-%F0%9F%A6%84-lightpink?style=for-the-badge)](https://www.larabelles.com/) 7 | 8 | [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/Astrotomic/laravel-unavatar/run-tests?style=flat-square&logoColor=white&logo=github&label=Tests)](https://github.com/Astrotomic/laravel-unavatar/actions?query=workflow%3Arun-tests) 9 | [![StyleCI](https://styleci.io/repos/242217544/shield)](https://styleci.io/repos/242217544) 10 | [![Total Downloads](https://img.shields.io/packagist/dt/astrotomic/laravel-unavatar.svg?label=Downloads&style=flat-square)](https://packagist.org/packages/astrotomic/laravel-unavatar) 11 | 12 | This package provides a Laravel wrapper for [unavatar](https://unavatar.now.sh). 13 | 14 | ## Installation 15 | 16 | You can install the package via composer: 17 | 18 | ```bash 19 | composer require astrotomic/laravel-unavatar 20 | php artisan vendor:publish --provider="Astrotomic\LaravelUnavatar\UnavatarServiceProvider" --tag=config 21 | ``` 22 | 23 | ## Usage 24 | 25 | Most of the logic and possibilities is inherited from the [astrotomic/php-unavatar](https://github.com/Astrotomic/php-unavatar) base package. 26 | On top this package adds some Laravel specific possibilities. 27 | The `\Astrotomic\LaravelUnavatar\Unavatar` class implements several interfaces: 28 | 29 | - `\Illuminate\Contracts\Support\Renderable` 30 | - `\Illuminate\Contracts\Support\Responsable` 31 | - `\Illuminate\Contracts\Support\Htmlable` 32 | - `\Illuminate\Contracts\Support\Jsonable` 33 | - `\JsonSerializable` 34 | - `\Illuminate\Contracts\Support\Arrayable` 35 | 36 | So you can use the use your `Unavatar` instances in your controllers as response but for sure also in your views. 37 | The last three ones will use the [unavatar](https://unavatar.now.sh) JSON API - so they will start a HTTP request. 38 | 39 | ### Blade Component 40 | 41 | The package provides a blade component you can use in your views. 42 | 43 | ```html 44 | 45 | 46 | Gummibeer 51 | ``` 52 | 53 | You can also publish the used view and customize it. 54 | 55 | ### Testing 56 | 57 | ```bash 58 | composer test 59 | ``` 60 | 61 | ### Changelog 62 | 63 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. 64 | 65 | ## Contributing 66 | 67 | Please see [CONTRIBUTING](https://github.com/Astrotomic/.github/blob/master/CONTRIBUTING.md) for details. You could also be interested in [CODE OF CONDUCT](https://github.com/Astrotomic/.github/blob/master/CODE_OF_CONDUCT.md). 68 | 69 | ### Security 70 | 71 | If you discover any security related issues, please check [SECURITY](https://github.com/Astrotomic/.github/blob/master/SECURITY.md) for steps to report it. 72 | 73 | ## Credits 74 | 75 | - [Tom Witkowski](https://github.com/Gummibeer) 76 | - [All Contributors](../../contributors) 77 | 78 | ## License 79 | 80 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 81 | 82 | ## Treeware 83 | 84 | You're free to use this package, but if it makes it to your production environment I would highly appreciate you buying the world a tree. 85 | 86 | It’s now common knowledge that one of the best tools to tackle the climate crisis and keep our temperatures from rising above 1.5C is to [plant trees](https://www.bbc.co.uk/news/science-environment-48870920). If you contribute to my forest you’ll be creating employment for local families and restoring wildlife habitats. 87 | 88 | You can buy trees at [offset.earth/treeware](https://plant.treeware.earth/Astrotomic/laravel-unavatar) 89 | 90 | Read more about Treeware at [treeware.earth](https://treeware.earth) 91 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astrotomic/laravel-unavatar", 3 | "description": "Laravel integration of unavatar service.", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "astrotomic", 8 | "laravel-unavatar", 9 | "laravel", 10 | "unavatar", 11 | "avatar" 12 | ], 13 | "authors": [ 14 | { 15 | "name": "Tom Witkowski", 16 | "email": "gummibeer@astrotomic.info", 17 | "homepage": "https://gummibeer.de", 18 | "role": "Developer" 19 | } 20 | ], 21 | "homepage": "https://astrotomic.info", 22 | "support": { 23 | "email": "dev@astrotomic.info", 24 | "issues": "https://github.com/Astrotomic/laravel-unavatar/issues", 25 | "source": "https://github.com/Astrotomic/laravel-unavatar" 26 | }, 27 | "require": { 28 | "php": "^8.0", 29 | "ext-json": "*", 30 | "astrotomic/php-unavatar": "^0.3.0", 31 | "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0", 32 | "illuminate/view": "^8.0 || ^9.0 || ^10.0 || ^11.0" 33 | }, 34 | "require-dev": { 35 | "gajus/dindent": "^2.0", 36 | "laravel/pint": "^1.0", 37 | "orchestra/testbench": "^6.0 || ^7.0 || ^8.0 || ^9.0", 38 | "phpunit/phpunit": "^9.3 || ^10.0" 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "Astrotomic\\Unavatar\\Laravel\\": "src" 43 | } 44 | }, 45 | "autoload-dev": { 46 | "psr-4": { 47 | "Astrotomic\\Unavatar\\Laravel\\Tests\\": "tests" 48 | } 49 | }, 50 | "config": { 51 | "sort-packages": true 52 | }, 53 | "extra": { 54 | "composer-normalize": { 55 | "indent-size": 4, 56 | "indent-style": "space" 57 | }, 58 | "laravel": { 59 | "providers": [ 60 | "Astrotomic\\Unavatar\\Laravel\\UnavatarServiceProvider" 61 | ] 62 | } 63 | }, 64 | "scripts": { 65 | "fix": "@php vendor/bin/pint", 66 | "test": "vendor/bin/phpunit", 67 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | null, 8 | ]; 9 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | tests 17 | 18 | 19 | 20 | 21 | src/ 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /resources/views/img.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 14 | 15 | merge(['alt' => "{$identifier} avatar", 'loading' => 'lazy']) }} 18 | /> -------------------------------------------------------------------------------- /src/Unavatar.php: -------------------------------------------------------------------------------- 1 | fallback = config('unavatar.fallback'); 29 | } 30 | 31 | public function render(): string 32 | { 33 | return $this->toHtml(); 34 | } 35 | 36 | public function toHtml(): string 37 | { 38 | return $this->toImg(); 39 | } 40 | 41 | public function toResponse($request): Response 42 | { 43 | if ($request->expectsJson()) { 44 | return new JsonResponse($this->toArray()); 45 | } 46 | 47 | return new RedirectResponse($this->toUrl(), 302); 48 | } 49 | 50 | public function toJson($options = 0): string 51 | { 52 | return json_encode($this->jsonSerialize(), $options); 53 | } 54 | 55 | public function jsonSerialize(): array 56 | { 57 | return $this->toArray(); 58 | } 59 | 60 | public function toArray(): array 61 | { 62 | $url = $this->toUrl(); 63 | 64 | $url .= Str::contains($url, '?') 65 | ? '&json' 66 | : '?json'; 67 | 68 | return json_decode(file_get_contents($url), true); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/UnavatarServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 13 | $this->publishes([ 14 | __DIR__.'/../config/config.php' => config_path('unavatar.php'), 15 | ], 'config'); 16 | 17 | $this->publishes([ 18 | __DIR__.'/../resources/views' => resource_path('views/vendor/unavatar'), 19 | ], 'views'); 20 | } 21 | 22 | $this->loadViewsFrom(__DIR__.'/../resources/views', 'unavatar'); 23 | 24 | Blade::componentNamespace('Astrotomic\Unavatar\Laravel\View\Components', 'unavatar'); 25 | } 26 | 27 | public function register(): void 28 | { 29 | $this->mergeConfigFrom(__DIR__.'/../config/config.php', 'unavatar'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/View/Components/Img.php: -------------------------------------------------------------------------------- 1 | identifier = $clearbit; 39 | $this->provider = BaseUnavatar::PROVIDER_CLEARBIT; 40 | } 41 | 42 | if ($deviantart) { 43 | $this->identifier = $deviantart; 44 | $this->provider = BaseUnavatar::PROVIDER_DEVIANTART; 45 | } 46 | 47 | if ($dribbble) { 48 | $this->identifier = $dribbble; 49 | $this->provider = BaseUnavatar::PROVIDER_DRIBBBLE; 50 | } 51 | 52 | if ($duckduckgo) { 53 | $this->identifier = $duckduckgo; 54 | $this->provider = BaseUnavatar::PROVIDER_DUCKDUCKGO; 55 | } 56 | if ($facebook) { 57 | $this->identifier = $facebook; 58 | $this->provider = BaseUnavatar::PROVIDER_FACEBOOK; 59 | } 60 | 61 | if ($github) { 62 | $this->identifier = $github; 63 | $this->provider = BaseUnavatar::PROVIDER_GITHUB; 64 | } 65 | 66 | if ($gravatar) { 67 | $this->identifier = $gravatar; 68 | $this->provider = BaseUnavatar::PROVIDER_GRAVATAR; 69 | } 70 | 71 | if ($instagram) { 72 | $this->identifier = $instagram; 73 | $this->provider = BaseUnavatar::PROVIDER_INSTAGRAM; 74 | } 75 | 76 | if ($soundcloud) { 77 | $this->identifier = $soundcloud; 78 | $this->provider = BaseUnavatar::PROVIDER_SOUNDCLOUD; 79 | } 80 | 81 | if ($substack) { 82 | $this->identifier = $substack; 83 | $this->provider = BaseUnavatar::PROVIDER_SUBSTACK; 84 | } 85 | 86 | if ($telegram) { 87 | $this->identifier = $telegram; 88 | $this->provider = BaseUnavatar::PROVIDER_TELEGRAM; 89 | } 90 | 91 | if ($twitter) { 92 | $this->identifier = $twitter; 93 | $this->provider = BaseUnavatar::PROVIDER_TWITTER; 94 | } 95 | 96 | if ($youtube) { 97 | $this->identifier = $youtube; 98 | $this->provider = BaseUnavatar::PROVIDER_YOUTUBE; 99 | } 100 | 101 | $this->identifier ??= $username ?? $email ?? $domain; 102 | $this->fallback = $fallback; 103 | } 104 | 105 | public function render(): View 106 | { 107 | return view('unavatar::img'); 108 | } 109 | 110 | public function url(): string 111 | { 112 | $unavatar = LaravelUnavatar::make($this->identifier, $this->provider); 113 | 114 | if ($this->fallback) { 115 | $unavatar->fallback($this->fallback); 116 | } 117 | 118 | return $unavatar->toUrl(); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /tests/ComponentTest.php: -------------------------------------------------------------------------------- 1 | 16 | HTML; 17 | 18 | $this->assertComponentRenders($expected, ''); 19 | } 20 | 21 | /** @test */ 22 | public function it_can_render_with_fallback(): void 23 | { 24 | $expected = <<<'HTML' 25 | astrotomic.info avatar 30 | HTML; 31 | 32 | $this->assertComponentRenders($expected, ''); 33 | } 34 | 35 | /** @test */ 36 | public function it_can_render_clearbit(): void 37 | { 38 | $expected = <<<'HTML' 39 | astrotomic.info avatar 44 | HTML; 45 | 46 | $this->assertComponentRenders($expected, ''); 47 | } 48 | 49 | /** @test */ 50 | public function it_can_render_deviantart(): void 51 | { 52 | $expected = <<<'HTML' 53 | astrotomic avatar 58 | HTML; 59 | 60 | $this->assertComponentRenders($expected, ''); 61 | } 62 | 63 | /** @test */ 64 | public function it_can_render_dribbble(): void 65 | { 66 | $expected = <<<'HTML' 67 | astrotomic avatar 72 | HTML; 73 | 74 | $this->assertComponentRenders($expected, ''); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | blade($template, $data); 25 | 26 | $this->assertSame( 27 | $indenter->indent($expected), 28 | $indenter->indent($blade) 29 | ); 30 | } 31 | 32 | protected function blade(string $template, array $data = []): string 33 | { 34 | $tempDirectory = sys_get_temp_dir(); 35 | 36 | if (! in_array($tempDirectory, ViewFacade::getFinder()->getPaths())) { 37 | ViewFacade::addLocation(sys_get_temp_dir()); 38 | } 39 | 40 | $tempFile = tempnam($tempDirectory, 'laravel-blade').'.blade.php'; 41 | 42 | file_put_contents($tempFile, $template); 43 | 44 | return view(Str::before(basename($tempFile), '.blade.php'), $data); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/UnavatarTest.php: -------------------------------------------------------------------------------- 1 | ', $unavatar->render()); 24 | } 25 | 26 | /** @test */ 27 | public function it_is_htmlable(): void 28 | { 29 | $unavatar = Unavatar::github('Gummibeer'); 30 | 31 | self::assertArrayHasKey(Htmlable::class, class_implements($unavatar)); 32 | self::assertSame(''.htmlspecialchars('Gummibeer\'s github avatar').'', $unavatar->toHtml()); 33 | } 34 | 35 | /** @test */ 36 | public function it_is_responsable_redirect(): void 37 | { 38 | $unavatar = Unavatar::github('Gummibeer'); 39 | 40 | self::assertArrayHasKey(Responsable::class, class_implements($unavatar)); 41 | 42 | $redirect = $unavatar->toResponse(Request::createFromGlobals()); 43 | 44 | self::assertInstanceOf(RedirectResponse::class, $redirect); 45 | self::assertSame('https://unavatar.io/github/Gummibeer', $redirect->getTargetUrl()); 46 | } 47 | 48 | /** @test */ 49 | public function it_is_responsable_json(): void 50 | { 51 | $unavatar = Unavatar::github('Gummibeer'); 52 | 53 | self::assertArrayHasKey(Responsable::class, class_implements($unavatar)); 54 | 55 | $request = Request::createFromGlobals(); 56 | $request->headers->set('X-Requested-With', 'XMLHttpRequest'); 57 | $response = $unavatar->toResponse($request); 58 | 59 | self::assertInstanceOf(JsonResponse::class, $response); 60 | $data = $response->getData(true); 61 | self::assertIsArray($data); 62 | self::assertArrayHasKey('url', $data); 63 | } 64 | 65 | /** @test */ 66 | public function it_is_jsonable(): void 67 | { 68 | $unavatar = Unavatar::github('Gummibeer'); 69 | 70 | self::assertArrayHasKey(Jsonable::class, class_implements($unavatar)); 71 | 72 | $data = $unavatar->toJson(); 73 | self::assertJson($data); 74 | $data = json_decode($data, true); 75 | self::assertIsArray($data); 76 | self::assertArrayHasKey('url', $data); 77 | } 78 | 79 | /** @test */ 80 | public function it_is_arrayable(): void 81 | { 82 | $unavatar = Unavatar::github('Gummibeer'); 83 | 84 | self::assertArrayHasKey(Arrayable::class, class_implements($unavatar)); 85 | 86 | $data = $unavatar->toArray(); 87 | self::assertIsArray($data); 88 | self::assertArrayHasKey('url', $data); 89 | } 90 | 91 | /** @test */ 92 | public function it_is_arrayable_with_fallback(): void 93 | { 94 | $unavatar = Unavatar::github('Gummibeer')->fallback('https://example.com/avatar.jpg'); 95 | 96 | self::assertArrayHasKey(Arrayable::class, class_implements($unavatar)); 97 | 98 | $data = $unavatar->toArray(); 99 | self::assertIsArray($data); 100 | self::assertArrayHasKey('url', $data); 101 | } 102 | } 103 | --------------------------------------------------------------------------------