Laravel 9.x + Backpack + Article with image (file upload)
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) {
$table->string('image',255)->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('articles', function (Blueprint $table) {
$table->dropColumn('image');
});
}
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()
{
parent::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
Storage::delete(Str::replaceFirst('storage/','public/',$this->{$attribute_name}));
// 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 https://backpackforlaravel.com/docs/crud-operation-list-entries
* @return void
*/
protected function setupListOperation()
{
$this->crud->set('show.setFromDb', false);
$this->crud->addColumns($this->getFieldsData(TRUE));
/**
* 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:
@if(isset($entry->image))
<img src="/{{ $entry->image }}" width="120" alt="Article image" />
@endif
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 :)