Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
"name": "van-ons/laravel-attachment-library",
"description": "A Laravel library for attaching files to Eloquent models.",
"keywords": [
"van ons",
"laravel",
"laravel-attachment-library",
"attachments",
"library",
"eloquent"
"eloquent",
"models"
],
"homepage": "https://github.com/VanOns/laravel-attachment-library",
"license": "MIT",
Expand Down
5 changes: 4 additions & 1 deletion config/glide.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

'driver' => env('GLIDE_DRIVER', 'gd'),
'source' => storage_path('app/public'),

/**
* The disk that will be used to store the resized images.
* Can be a name of an existing disk like public or a disk configuration.
Expand All @@ -15,11 +16,13 @@
'url' => env('APP_URL') . '/img',
'visibility' => 'public',
],

/**
* Here you can configure additional symbolic links that will
* be created when the `storage:link` command is run.
*/
'links' => [ public_path('img') => storage_path('app/img') ],
'links' => [public_path('img') => storage_path('app/img')],

'defaults' => [],
'presets' => [],
'max_image_size' => 2160 * 2160,
Expand Down
4 changes: 4 additions & 0 deletions docs/basic-usage/responsive-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ An example code snippet could be:
->resize();

/**
* Valid source:
* [
* 'width' => 500,
* 'height' => 200,
* 'url' => 'http://test.local/img/path-to-image.jpg?w=500&h=200&signature=....'
* ]
*
* Invalid source:
* []
*/
```
1 change: 1 addition & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
<php>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="CACHE_DRIVER" value="array"/>
</php>
</phpunit>
102 changes: 62 additions & 40 deletions resources/views/components/image.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use VanOns\LaravelAttachmentLibrary\Models\Attachment;

/**
* @var string $path
* @var string|int|Attachment|null $src
* @var string $fit
* @var string $size
* @var array $breakpoints
Expand All @@ -29,51 +29,73 @@

<picture @class(['block overflow-hidden', $class]) {{ $attributes->except('class') }}>
@if($src)
@foreach($formats as $format)
@foreach($keys->reverse() as $breakpoint)
@php
$index = $keys->search($breakpoint);
$nextBreakpoint = $keys->get($index + 1) ?? $keys->get($index);
$media = "(min-width: {$breakpoints[$breakpoint]}px)";
$width = $breakpoints[$nextBreakpoint];
$data = Resizer::src($src)->width($width)->size($size)->aspectRatio($aspectRatio)->format($format)->resize();
@endphp
@if($supportedByGlide)
@foreach($formats as $format)
@foreach($keys->reverse() as $breakpoint)
@php
$index = $keys->search($breakpoint);
$nextBreakpoint = $keys->get($index + 1) ?? $keys->get($index);
$media = "(min-width: {$breakpoints[$breakpoint]}px)";
$width = $breakpoints[$nextBreakpoint];
$data = Resizer::src($src)->width($width)->size($size)->aspectRatio($aspectRatio)->format($format)->resize();
@endphp

<source
srcset="{{ $data['url'] }}"
media="{{ $media }}"
@unless(empty($data))
<source
srcset="{{ $data['url'] }}"
media="{{ $media }}"
width="{{ $data['width'] }}"
height="{{ $data['height'] }}"
type="image/{{ $format }}"
>
@endunless
@endforeach

@unless(empty($data = Resizer::src($src)->width($breakpoints[$keys->first()])->size($size)->aspectRatio($aspectRatio)->format($format)->resize()))
<source
srcset="{{ $data['url'] }}"
width="{{ $data['width'] }}"
height="{{ $data['height'] }}"
type="image/{{ $format }}"
>
@endunless
@endforeach

@unless(empty($data = Resizer::src($src)->width(end($breakpoints))->size($size)->aspectRatio($aspectRatio)->resize()))
<img
src="{{ $data['url'] }}"
width="{{ $data['width'] }}"
height="{{ $data['height'] }}"
type="image/{{ $format }}"
>
@endforeach
alt="{{ $alt }}"

@php($data = Resizer::src($src)->width($breakpoints[$keys->first()])->size($size)->aspectRatio($aspectRatio)->format($format)->resize())
<source
srcset="{{ $data['url'] }}"
width="{{ $data['width'] }}"
height="{{ $data['height'] }}"
type="image/{{ $format }}"
>
@endforeach
@if($lightbox)
data-fancybox="{{ $lightboxGallery }}"
@endif

@php($data = Resizer::src($src)->width(end($breakpoints))->size($size)->aspectRatio($aspectRatio)->resize())
<img
src="{{ $data['url'] }}"
width="{{ $data['width'] }}"
height="{{ $data['height'] }}"
alt="{{ $alt }}"
@class([
'h-full w-full',
'object-cover' => $fit === 'cover',
'object-contain' => $fit === 'contain',
$imageClass,
])
>
@endunless
@else
<img
src="{{ $attachment->url }}"
alt="{{ $alt }}"

@if($lightbox)
data-fancybox="{{ $lightboxGallery }}"
@endif
@if($lightbox)
data-fancybox="{{ $lightboxGallery }}"
@endif

@class([
'h-full w-full',
'object-cover' => $fit === 'cover',
'object-contain' => $fit === 'contain',
$imageClass,
])
>
@class([
'h-full w-full',
'object-cover' => $fit === 'cover',
'object-contain' => $fit === 'contain',
$imageClass,
])
>
@endif
@endif
</picture>
91 changes: 87 additions & 4 deletions src/Glide/GlideManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace VanOns\LaravelAttachmentLibrary\Glide;

use Exception;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
use League\Glide\Responses\SymfonyResponseFactory;
Expand All @@ -13,7 +14,7 @@ class GlideManager
public function server(): Server
{
return ServerFactory::create([
'driver' => config('glide.driver'),
'driver' => $this->driver(),
'source' => config('glide.source'),
'cache' => $this->cacheDisk()->getDriver(),
'defaults' => config('glide.defaults'),
Expand All @@ -26,6 +27,11 @@ public function server(): Server
]);
}

public function driver(): string
{
return config('glide.driver', 'gd');
}

public function cacheDisk(): Filesystem
{
return is_string(config('glide.cache_disk'))
Expand Down Expand Up @@ -63,14 +69,91 @@ public function cacheSize(): int

public function cacheSizeHumanReadable(): string
{

return $this->humanReadableSize($this->cacheSize());
}

public function humanReadableSize(int $bytes, $decimals = 2): string
{
$size = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
$factor = floor((strlen(strval($bytes)) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . $size[$factor];
$factor = floor((strlen((string) $bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / (1024 ** $factor)) . ' ' . $size[$factor];
}

public function imageIsSupported(string $path, array $params = []): bool
{
try {
$this->server()->makeImage($path, $params);
return true;
} catch (Exception) {
return false;
}
}

/**
* Retrieve supported image formats for the current driver.
*
* @param bool $onlyCommon Limit to results to the most common formats.
*/
public function getSupportedImageFormats(bool $onlyCommon = true): array
{
$commonFormats = [
'AVIF',
'BMP',
'GIF',
'HEIC',
'HEIF',
'ICO',
'JPEG',
'JPG',
'PNG',
'SVG',
'TIFF',
'WEBP',
];

$driver = $this->driver();

if ($driver === 'gd' && function_exists('gd_info')) {
$formats = gd_info();

$supported = collect();
foreach ($formats as $key => $value) {
if ($value === false) {
continue;
}

if ($onlyCommon) {
foreach ($commonFormats as $format) {
if (str_contains($key, $format)) {
$supported->push($format);
break;
}
}
} else {
$format = strtoupper(str_replace([' ', 'Support'], '', $key));
$supported->push($format);
}
}

return $supported
->unique()
->values()
->sort()
->all();
}

if ($driver === 'imagick' && class_exists(\Imagick::class)) {
$formats = \Imagick::queryFormats();

return collect()
->when($onlyCommon, fn ($collection) => $collection->merge($commonFormats)->filter(fn ($format) => in_array($format, $formats)))
->when(!$onlyCommon, fn ($collection) => $collection->merge($formats))
->unique()
->values()
->sort()
->all();
}

return [];
}
}
13 changes: 9 additions & 4 deletions src/Glide/Resizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Support\Facades\URL;
use VanOns\LaravelAttachmentLibrary\Enums\Fit;
use VanOns\LaravelAttachmentLibrary\Facades\AttachmentManager;
use VanOns\LaravelAttachmentLibrary\Facades\Glide;
use VanOns\LaravelAttachmentLibrary\Models\Attachment;

/**
Expand Down Expand Up @@ -87,7 +88,7 @@ public function calculateWidth(): ?float
if ($this->height) {
[$width, $height] = $this->getImageSize();

return ! empty($height)
return !empty($height)
? round($this->calculateHeight() / $height * $width)
: 0;
}
Expand All @@ -114,7 +115,7 @@ public function calculateHeight(): ?float
if ($this->width) {
[$width, $height] = $this->getImageSize();

return ! empty($width)
return !empty($width)
? round($this->calculateWidth() / $width * $height)
: 0;
}
Expand Down Expand Up @@ -159,7 +160,7 @@ public function getImageSize(): ?array
/**
* Set the aspect ratio for the image.
*
* @param string|float|null $aspectRatio Aspect ratio as a float or a string in the format 'width/height'.
* @param string|float|null $aspectRatio Aspect ratio as a float or a string in the format 'width/height'.
*/
public function aspectRatio(string|float|null $aspectRatio): static
{
Expand Down Expand Up @@ -206,14 +207,18 @@ protected function getPath(string|int|Attachment $src): ?string

public function cacheKey(): string
{
return sha1($this->path.$this->width.$this->height.$this->format.$this->size.$this->aspectRatio);
return sha1($this->path . $this->width . $this->height . $this->format . $this->size . $this->aspectRatio);
}

/**
* Resize the image and return an array with the signed URL, width, and height of the image.
*/
public function resize(): array
{
if (!Glide::imageIsSupported($this->path)) {
return [];
}

$width = $this->calculateWidth();
$height = $this->calculateHeight();

Expand Down
12 changes: 12 additions & 0 deletions src/Http/Controllers/GlideController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
use Illuminate\Http\Request;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Middleware\ValidateSignature;
use Intervention\Image\Exception\NotReadableException;
use League\Glide\Filesystem\FileNotFoundException;
use League\Glide\Filesystem\FilesystemException;
use League\Glide\Server;
use Symfony\Component\HttpFoundation\Response;
use VanOns\LaravelAttachmentLibrary\Facades\AttachmentManager;
use VanOns\LaravelAttachmentLibrary\Glide\OptionsParser;
use VanOns\LaravelAttachmentLibrary\Glide\Resizer;

Expand All @@ -16,6 +19,7 @@ class GlideController implements HasMiddleware
/**
* Return image response with Glide parameters.
*
* @throws FilesystemException
* @see Resizer for all available Glide parameters.
*/
public function __invoke(Request $request, string $options, string $path, OptionsParser $parser): Response
Expand All @@ -27,6 +31,14 @@ public function __invoke(Request $request, string $options, string $path, Option
);
} catch (FileNotFoundException) {
abort(404);
} catch (NotReadableException) {
$attachment = AttachmentManager::file($path);
if (!$attachment) {
abort(404);
}

// Return the original file if Glide cannot parse the image.
return response()->file($attachment->absolute_path);
}
}

Expand Down
Loading