diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 434599026ab9..ffc28454040e 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -30,6 +30,7 @@ use Illuminate\Database\Eloquent\MissingAttributeException; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\LazyLoadingViolationException; +use Illuminate\Image\Image; use Illuminate\Support\Arr; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; @@ -38,6 +39,7 @@ use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Date; use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Image as ImageFacade; use Illuminate\Support\Str; use InvalidArgumentException; use LogicException; @@ -123,6 +125,7 @@ trait HasAttributes 'encrypted:object', 'float', 'hashed', + 'image', 'immutable_date', 'immutable_datetime', 'immutable_custom_datetime', @@ -897,6 +900,12 @@ protected function castAttribute($key, $value) return $this->asDateTime($value)->toImmutable(); case 'timestamp': return $this->asTimestamp($value); + case 'image': + case 'image:url': + case 'image:storage': + case 'image:base64': + case 'image:bytes': + return $this->toImage($key, $value, substr($castType, 6)); } if ($this->isEnumCastable($key)) { @@ -1640,6 +1649,17 @@ protected function asTimestamp($value) return $this->asDateTime($value)->getTimestamp(); } + protected function toImage(string $key, $value, string $sourceType): Image + { + return match ($sourceType) { + 'storage' => ImageFacade::fromStorage($value), + 'bytes' => ImageFacade::fromBytes($value), + 'base64' => ImageFacade::fromBase64($value), + 'url' => ImageFacade::fromUrl($value), + default => throw new InvalidCastException($this->getModel(), $key, $sourceType === '' ? 'image' : 'image:'.$sourceType), + }; + } + /** * Prepare a date for array / JSON serialization. * diff --git a/tests/Integration/Database/EloquentModelImageCastingTest.php b/tests/Integration/Database/EloquentModelImageCastingTest.php new file mode 100644 index 000000000000..387f9bacf3cf --- /dev/null +++ b/tests/Integration/Database/EloquentModelImageCastingTest.php @@ -0,0 +1,60 @@ +increments('id'); + $table->string('storage_path'); + $table->string('web_url'); + //$table->binary('image'); + $table->text('encoded'); + $table->timestamps(); + }); + } + + public function testImageCast() + { + $file = UploadedFile::fake()->image('test.jpg', 100, 100); + $image = new ImageFacade(file_get_contents($file->getRealPath())); + $base64 = $image->toBase64(); + $image->store('avatars/john_doe.png'); + $img = Image::create([ + 'web_url' => 'https://example.com/favicon.ico', + 'storage_path' => 'avatars/john_doe.png', + //'image' => $image->toBytes(), + 'encoded' => $image->toBase64(), + ]); + + $this->assertInstanceOf(ImageFacade::class, $img->web_url); + $this->assertInstanceOf(ImageFacade::class, $img->storage_path); + //$this->assertInstanceOf(ImageFacade::class, $img->image); + $this->assertInstanceOf(ImageFacade::class, $img->encoded); + $this->assertSame($base64, $img->storage_path->toBase64()); + //$this->assertSame($base64, $img->image->toBase64()); + $this->assertSame($base64, $img->encoded->toBase64()); + } +} + +class Image extends Model +{ + protected $guarded = []; + + protected $casts = [ + 'web_url' => 'image:url', + 'storage_path' => 'image:storage', + 'image' => 'image:bytes', + 'encoded' => 'image:base64', + ]; +}