question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Bug image field, store in fake field (store as base64, not path)

See original GitHub issue

Hello,

I try to store the path of an image in a fake field. And it converts it to “base64”, it’s a problem. It works when I specify a field other than “extras” and I create my mutator.

As I have several images on my pages (I use PageManager for simple pages). I need to store a different number on each page, which is why I would like a “fake field” solution, it would keep it flexible.

I try to follow the solution given here by @tabacitu Laravel-Backpack/CRUD#400.

Whatever I do, if I try to store an image in “extras”, it converts it to base64 and that bug and even with the following code

public function setImageAttribute($value, $attribute_name = 'image')
    {
        $disk = "public_folder";
        $destination_path = "uploads/folder_1/subfolder_3";

        // if the image was erased
        if ($value==null) {
            // delete the image from disk
            \Storage::disk($disk)->delete($this->image);

            // set null in the database column
            $this->attributes[$attribute_name] = null;
        }

        // if a base64 was sent, store it in the db
        if (starts_with($value, 'data:image'))
        {
            // 0. Make the image
            $image = \Image::make($value);
            // 1. Generate a filename.
            $filename = md5($value.time()).'.jpg';
            // 2. Store the image on disk.
            \Storage::disk($disk)->put($destination_path.'/'.$filename, $image->stream());
            // 3. Save the path to the database
            $this->attributes[$attribute_name] = $destination_path.'/'.$filename;
        }
    }

I try to have something dynamic, otherwise I could post only one entry per column in the DB.

@tabacitu has given information to try to solve this problem. But I can not create it correctly.

So, is there a way to store multiple images from different fields on the same page in a fake-field?

Thank you

PS : Sorry for my english.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:25 (17 by maintainers)

github_iconTop GitHub Comments

4reactions
rampolcommented, Apr 23, 2019

Hi everyone. Allow me to post this to this closed topic. It could be useful to someone.

Looking for a solution to @Jimmylet post I landed here. I finally managed to store image paths into a fake field. It’s not the best solution, as I didn’t rewrite the backpack code to add this functionality, but it works. I’m not sure it’s a backpack bug, but it’s just something the fake fields are not thought for. Nothing mentioned in the documentation though (whether it should work or shouldn’t).

As far as I could guess, the moment in the store call stack where actual values are written into the fake fields is inside some of the backpack structure (Model.php). To modify that behavior I should override quite a lot of files, and I didn’t want to mess them up. Not now. So, why previous comments didn’t work for me: The getters and setters like getSomeFieldAttribute don’t work as we would expect for fake fields. They do work for actual fields in the table. If you try to do setExtrasAttribute and do there the image processing, it also doesn’t work, as the extras field is at the same time a “protected $fake” field and a “protected $appends” field.

So, what I did is to surface to the controller itself and insert the following code into the update and create methods. The code basically checks for fields ending with _image, then stores images to disk, gets their filename, and replaces the fake field data (data:image) with the recently created filename. Volià! Don’t pay attention to the specific code that you won’t need. This code is mostly the part of the setImageAttribute function where images are processed for actual DB fields. The model keeps the getImageAttribute (second part of the code) with the getAttributeValueExtended trick to parse the value to store and read it json_decoded from the fake field.

In the controller:

public function update(UpdateRequest $request)
    {
        $this->addDefaultPageFields(\Request::input('template'));
        $this->useTemplate(\Request::input('template'));

        $fields = array_keys(request()->all());
        foreach ($fields as $field) {
            if (Str::endsWith($field, 'image'))
                $request[$field] = $this->processImage($request, $field, 'extras');
        }
        return parent::updateCrud($request);
    }

    /**
     * Stores the actual image to disk and returns the stored filename. Deletes previous file from disk, if any.
     *
     * @param $request
     * @param $attribute_name
     * @param null $store_in If not null, image is stored in a fake field
     * @return null|string
     */
    private function processImage($request, $attribute_name, $store_in = null)
    {
        $value = $request[$attribute_name];
        $prev_value = $this->crud->getCurrentEntry()->{$attribute_name};
        $type = 'pages';

        $destination_path = env('IMAGES_STORAGE_WRITE_PATH') . $type;

        // if the image was erased
        if ($value == null) {
            $this->deleteImage($prev_value);
            $value = null;
        }

        // if a new image was loaded
        if (Str::startsWith($value, 'data:image')) {
            $this->deleteImage($prev_value);
            $image = \Image::make($value);
            switch ($image->mime()) {
                case 'image/png':
                    $extension = '.png';
                    break;
                default:
                    $extension = '.jpg';
            }
            $filename = $type . '_' . $attribute_name . '_' . md5($value.time()) . $extension;
            \Storage::disk(env('IMAGES_STORAGE_DISK'))->put($destination_path . '/' . $filename, (string)$image->stream());
            $value = $filename;
        }

        return basename($value);
    }

    private function deleteImage($value)
    {
        $type = 'pages';
        if (!empty($value)) {
            $file = basename($value);
            $file_path = env('IMAGES_STORAGE_WRITE_PATH') . Str::plural($type) . '/' . $file;
            \Storage::disk(env('IMAGES_STORAGE_DISK'))->delete($file_path);
        }
    }

In the model:

    public function getHeader1ImageAttribute()
    {
        return $this->retrieveImage('header_1_image', 'extras');
    }

    public function getHeader2ImageAttribute()
    {
        return $this->retrieveImage('header_2_image', 'extras');
    }

    public function getHomeCoverImageAttribute()
    {
        return $this->retrieveImage('home_cover_image', 'extras');
    }

    private function retrieveImage($attribute_name, $stored_in = null)
    {
        $type = Str::snake(class_basename($this));
        $value = $this->getAttributeValueExtended($attribute_name, $stored_in);

        if ($value && Str::contains($value, '_default.')) {
            return env('IMAGES_STORAGE_READ_PATH'). '/' . $value;
        }
        if ($value) {
            return env('IMAGES_STORAGE_READ_PATH') . Str::plural($type) . '/' . $value;
        }
    }

    private function getAttributeValueExtended($attribute_name, $stored_in = null)
    {
        if ($stored_in) {
            if (isset(json_decode($this->{$stored_in})->{$attribute_name}))
                return json_decode($this->{$stored_in})->{$attribute_name};
            return null;
        }

        return $this->{$attribute_name};
    }

Hope this helps someone!

1reaction
tabacitucommented, Nov 23, 2019

Thanks a lot for sharing this @brynnb . I’m sure it will help others indeed.

To re-use the Update operation in Backpack 4, take a look at this section in the Update operation docs (or point 6.2 in the upgrade guide). You should be able to do something like this:

<?php

namespace App\Http\Controllers\Admin;

use Backpack\CRUD\app\Http\Controllers\CrudController;

class ProductCrudController extends CrudController
{
    use \Backpack\CRUD\app\Http\Controllers\Operations\UpdateOperation { update as traitUpdate; }

    // ...

    public function update()
    {
        // do things before save, maybe something like
        $fields = array_keys(request()->all());
        foreach ($fields as $field) {
            if (Str::endsWith($field, 'image')) {
                $data[$field] = $this->processImage($request, $field, 'extras');
            }
        }
        
        // call the original update function, from the trait
        $response = $this->traitUpdate();
        return $response;
    }
}

(not exact code, just a suggestion)

Read more comments on GitHub >

github_iconTop Results From Across the Web

Bug image field, store in fake field (store as base64, not path)
Hello,. I try to store the path of an image in a fake field. And it converts it to "base64", it's a problem....
Read more >
Encode ImageField in base64 and store it in DB instead of ...
I don't want to store the file path in the database, I want to read the file, convert it to base64 and store...
Read more >
create image from base64 string laravel - Laracasts
Hi I'm writing an API To Upload Image form Android Phone trying to convert the base64 string to image and store in public...
Read more >
Base64 Image Encoder - Convert any image file or URL online
With Base64 Image Encoder, you can convert any image file or URL to either Base64, HTML, CSS, JSON, or XML online. Everything happens...
Read more >
Storing Large Images In Power BI Datasets
Hi Chris, I'm using a SQL Server to store the image file and path, how do I convert them into Base64 on the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found