Laravel 9.x + Backpack + Article with image (file upload)

Lyubomir Filipov
4 min readDec 11, 2022

In order to proceed you should have:
- Laravel installed + Backpack admin (teaching purposes)
- Tags CRUD created
- Article CRUD created

Note: There is an older version of this tutorial before Backpack was split on FREE vs PRO field types.

#STEP 1: Create migration to add an image to an article

php artisan make:migration add_image_to_articles_table --table=articles

The new file is created. Open the file and edit it

* Run the migrations.
* @return void
public function up()
Schema::table('articles', function (Blueprint $table) {

* Reverse the migrations.
* @return void
public function down()
Schema::table('articles', function (Blueprint $table) {

We are adding a new column to the articles table. In the down method, we are removing the column. The new column will hold information about the image name.

#STEP 2: Run migration

php artisan migrate

Expected result

#STEP 3: Alter the Articles model

We need to add extra items to use:

use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;

And then add two functions in the FUNCTIONS section.

public static function boot()
static::deleting(function($obj) {
Storage::delete(Str::replaceFirst('storage/','public/', $obj->image));

public function setImageAttribute($value)
$attribute_name = "image";
// destination path relative to the disk above
$destination_path = "articles";

// if the image was erased
if ($value==null) {
// delete the image from disk

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

$disk = "public";
// filename is generated - md5($file->getClientOriginalName().random_int(1, 9999).time()).'.'.$file->getClientOriginalExtension()
$this->uploadFileToDisk($value, $attribute_name, $disk, $destination_path, $fileName = null);
$this->attributes[$attribute_name] = 'storage/' . $this->attributes[$attribute_name];


The first function will make sure that once we delete an entry — the file will be deleted as well.

The second function is a mutator responsible for the upload the image to the correct folder.

We have logic replacing /storage folder with /public folder due to the fact that images are exposed to end users using /storage folder but if we need to delete them then they are living in public/articles/{image}.

#STEP 4: Adjust Articles controller

Edit getFieldsData method and add a new entry

'label' => "Article Image",
'name' => "image",
'type' => ($show ? 'view' : 'upload'),
'view' => 'partials/image',
'upload' => true,

We need to adjust getFieldsData function and add the image column.

Our next change is related to setupListOperation method. We need to update it to the following

* Define what happens when the List operation is loaded.
* @see
* @return void
protected function setupListOperation()
$this->crud->set('show.setFromDb', false);

* Columns can be defined using the fluent syntax or array syntax:
* - CRUD::column('price')->type('number');
* - CRUD::addColumn(['name' => 'price', 'type' => 'number']);

#STEP 5: Create new view for the image

Create a new view file in /resources/views/partials/image.blade.php

Note: You are probably missing the ‘partials’ folder as well, so please create it.

Content of the view file:

<img src="/{{ $entry->image }}" width="120" alt="Article image" />

We are checking whether the image is set and if so we are showing it.

#STEP 6: Make the disk public and allow files to be shown

php artisan storage:link

Test everything :)