community-forum
community-forum copied to clipboard
[4.2][Proposal] Rename "image" field into "cropper"; Add new field "image" that only shows a preview
The issue was briefly raised in https://github.com/Laravel-Backpack/CRUD/pull/2166#issuecomment-545817627 . Basically: what we have now as the image field type isn't actually an image field type, it's become in time, after adding the cropper functionality, something that would probably be best named a "cropper" field type.
So we could:
- rename
imagefield intocropper; - add a new
imagefield type that only shows a preview of the selected/uploaded image (no cropping);
The way I see it:
PROs:
- that would probably be more intuitive;
- the new
imagefield type would have the added benefit of supporting SVGs, something that cropper cannot do;
CONs:
- it would, of course, be a breaking change, and require a step in the upgrade guide;
Thoughts anyone?
Hey @tabacitu
I got mixed feelings on this one. I think "image" it's more accurate than "cropper" anyway, in the end it's still it's job to upload an image.
Any chance making the cropper part optional ? Or having both files (cropper and image) and if you create an image field with cropper => true it loads the cropper file, otherwise the image ?
Best, Pedro
I understand your mixed feelings @pxpm . I'm not 100% either 😄
We could do an optional cropper => true indeed. But:
- it wouldn't be any more difficult for the developer to change the type from
imageto cropper, than it would be to docropper => true; - it wouldn't provide an
imagefield type that's simple and clear of unnecessary JS; it would just bloat up this one field (which is already... not the best code in Backpack 😆 );
Hi guys!
I was about to say that I'm against multiple fields doing "almost" the same thing, I actually think we should move the same way as the relationship field, one field = everything. So as less fields we have, the better, less noise to the developers.
Then I saw this;
it wouldn't provide an image field type that's simple and clear of unnecessary JS; it would just bloat up this one field (which is already... not the best code in Backpack 😆);
And then I got the same mixed fillings, so I think I agree with @pxpm;
having both files (cropper and image) and if you create an image field with cropper => true it loads the cropper file, otherwise the image ?
And this way we could have image field file with no JS, and cropper field file with the JS stuff.
Thanks for the feedback @promatik .
So from I understand we're all on the same page? To have both:
imagethat has no JS (or only shows image preview with JS)cropperthat has the cropping functionality (the logic that is now insideimage)
?
Thanks for the feedback @promatik .
So from I understand we're all on the same page? To have both:
imagethat has no JS (or only shows image preview with JS)cropperthat has the cropping functionality (the logic that is now insideimage)?
I think yes, but image would become a switchboard for simple_image or cropper depending if cropper => true. Kind of the same logic with relationship.
I just implemented support for svg in the image field. (and for png for transparency)
Here is the laravel mutator to support it if anyone needs it:
// ..
use Illuminate\Support\Str;
use Intervention\Image\ImageManagerStatic as Image;
// ..
Class Product extends Model
{
// ..
public function setImageAttribute($value)
{
$attribute_name = "image";
// or use your own disk, defined in config/filesystems.php
$disk = config('backpack.base.root_disk_name');
// destination path relative to the disk above
$destination_path = "public/uploads/folder_1/folder_2";
// if the image was erased
if (empty($value)) {
// delete the image from disk
if (isset($this->{$attribute_name}) && !empty($this->{$attribute_name})) {
\Storage::disk($disk)->delete($this->{$attribute_name});
}
// set null on database column
$this->attributes[$attribute_name] = null;
}
// if a base64 was sent, store it in the db
if (Str::startsWith($value, 'data:image'))
{
// 0. Detect image type
$format = 'jpg';
$mime = Str::of($value)->betweenFirst('data:', ';'); // betweenFirst is only available in laravel 9
if ($mime == 'image/svg+xml') {
$format = 'svg';
}
if ($mime == 'image/png') {
$format = 'png'; // if selected image is png, keep this encoding because of transparency
}
// 1. Generate a filename.
$filename = md5($value . time()) . '.' . $format;
// 2. Make and save the image
if ($format == 'svg') {
Storage::disk($disk)->put(
$destination_path . '/' . $filename,
base64_decode(Str::of($value)->after(','))
);
} else {
$image = Image::make($value)->encode($format, 90);
Storage::disk($disk)->put($destination_path . '/' . $filename, $image->stream());
}
// 3. Delete the previous image, if there was one.
if (isset($this->{$attribute_name}) && !empty($this->{$attribute_name})) {
\Storage::disk($disk)->delete($this->{$attribute_name});
}
// 4. Save the public path to the database
// but first, remove "public/" from the path, since we're pointing to it
// from the root folder; that way, what gets saved in the db
// is the public URL (everything that comes after the domain name)
$public_destination_path = Str::replaceFirst('public/', '', $destination_path);
$this->attributes[$attribute_name] = $public_destination_path.'/'.$filename;
} elseif (!empty($value)) {
// if value isn't empty, but it's not an image, assume it's the model value for that attribute.
$this->attributes[$attribute_name] = $this->{$attribute_name};
}
}
// ..
You also have to change the js mime type validation, like in this PR-2166