Laravel 8 Upload image using Summernote editor

Summernote editor is a simple Javascript library to build online WYSIWYG web editors. You can use Summernote editor in any web application using download option or through third party CDN links. Using Summernote, you can also upload images to server.

In this article, we will integrate Summernote editor in Laravel 8 application for blog content writing. We will also cover image uploading for in-article images. We will start tutorial step by step from the scratch. Follow the below steps to create Summernote editor example into Laravel 8 application.

  • Step 1: Generate new Laravel application
  • Step 2: Configure mysql database in .env file
  • Step 3: Create migration
  • Step 4: Create Article model class
  • Step 5: Create controller class
  • Step 6: Create new routes
  • Step 7: Create blade views

Step 1: Generate new Laravel application

In the first step, we will create a new fresh Laravel application using composer command. So open the Terminal or Command Prompt and run the below command to create a new Laravel application

composer create-project --prefer-dist laravel/laravel article

And change the Terminal directory to Laravel application

cd article

Step 2: Configure mysql database in .env file

After creating Laravel application, open the application into your favourite text editor. Now in the root directory, open envrironment .env file and change database credentials according to your MySQL.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=article
DB_USERNAME=root
DB_PASSWORD=secret

Step 3: Create migration

In the third step, we will create a migration file for articles table. From the Terminal, run the below artisan command to create a migration.

php artisan make:migration create_articles_table

This will create a migration file at database/migrations directory. Open the migration file and add two new fields into up() method. These are the table fields.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('description');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
}

Now run the migrate command to create table into database.

php artisan migrate

Step 4: Create Article model class

We will also need model class which will used to run eloquent query. A controller class will execute all database query through Model class. To create a new model, run the below command into Terminal.

php artisan make:model Article

Open the model class at app/Models/Article.php file and add $fillable attribute into class. This attrubute tells application which fields are mass assignable,

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var string[]
     */
    protected $fillable = [
        'title',
        'description',
    ];
}

Step 5: Create controller class

Controller class implements application logic, uses model for database query and returns response to browser. Create a controller using below artisan command:

php artisan make:controller ArticleController

In this appication we will implement, article list, create article and show article.

Open the controller class at app/Http/Controllers/ArticleController.php and add the below code in it.

<?php

namespace App\Http\Controllers;

use File;
use App\Models\Article;
use Illuminate\Http\Request;

class ArticleController extends Controller
{
    /**
     * The article list view.
     *
     * @return Illuminate\Http\View
     */
    public function index()
    {
        $articles = Article::get();

        return view('articles.index', compact('articles'));
    }

    /**
     * The article view.
     *
     * @return Illuminate\Http\View
     */
    public function show($id)
    {
        $article = Article::where('id', $id)
            ->first();

        return view('articles.show', compact('article'));
    }

    /**
     * The article create view.
     *
     * @return Illuminate\Http\View
     */
    public function create()
    {
        return view('articles.create');
    }

    /**
     * The article create view.
     *
     * @return Illuminate\Http\View
     */
    public function store(Request $request)
    {
        $validated = $request->validate([
             'title' => 'required',
             'description' => 'required'
        ]);

        $dom = new \DomDocument();
        $dom->loadHtml($validated['description'], LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
        $image_file = $dom->getElementsByTagName('img');

        if (!File::exists(public_path('uploads'))) {
            File::makeDirectory(public_path('uploads'));
        }
 
        foreach($image_file as $key => $image) {
            $data = $image->getAttribute('src');

            list($type, $data) = explode(';', $data);
            list(, $data) = explode(',', $data);

            $img_data = base64_decode($data);
            $image_name = "/uploads/" . time().$key.'.png';
            $path = public_path() . $image_name;
            file_put_contents($path, $img_data);

            $image->removeAttribute('src');
            $image->setAttribute('src', $image_name);
        }
 
        $validated['description'] = $dom->saveHTML();

        Article::create($validated);

        return redirect()
            ->route('article.index')
            ->with('success', 'Article created successfully.');
    }
}

Methods explained:

index() method show the list of articles.
show() method will show the single article.
create() method will show create article form view.
store() method will save image and article.

Step 6: Create new routes

So far we have created new controller and added methods. In this step, we will create routes for controller methods.

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ArticleController;

Route::get('article/index', [ArticleController::class, 'index'])->name('article.index');
Route::get('article/show/{id}', [ArticleController::class, 'show'])->name('article.show');
Route::get('article/create', [ArticleController::class, 'create'])->name('article.create');
Route::post('article/store', [ArticleController::class, 'store'])->name('article.store');

Step 7: Create blade views

In our last coding step, we will create blade views for articles list, create form and show. Laravel views are stored into resources/views directory. Create a articles folder into it and add below views files into this directory.

resources/views/articles/
                -index.blade.php
                -show.blade.php
                -create.blade.php

index.blade.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Articles</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <div class="row">
            <h1 class="text-center">ALL Articles</h1>
            <div class="mb-3">
                <a class="btn btn-primary" href="{{ route('article.create') }}">New Article</a>
            </div>
            <div class="col-12">
                <table class="table table-hover">
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>Title</th>
                            <th>Description</th>
                            <th>Action</th>
                        </tr>
                    </thead>
                    <tbody>
                        @foreach($articles as $article)
                            <tr>
                                <th scope="row">{{ $article->id }}</th>
                                <td>{{ $article->title }}</td>
                                <td>{{ substr($article->description, 0, 10) }}</td>
                                <td>
                                    <a href="{{ route('article.show', $article->id) }}" class="btn btn-secondary">View</a>
                                </td>
                            </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</body>
</html>

show.blade.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>{{ $article->title }}</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <div class="row">
            <h1 class="text-center">{{ $article->title }}</h1>
            <div class="col-12">{!! $article->description !!}</div>
        </div>
        <div class="mt-3">
            <a class="btn btn-primary" href="{{ route('article.index') }}">Back</a>
        </div>
    </div>
</body>
</html>

create.blade.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Create Article</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.18/summernote.min.css" />
</head>
<body>
    <div class="container">
        <div class="row">
            <h1 class="text-center">Create Article</h1>
            <div class="col-12">
                <form method="post" action="{{ route('article.store') }}">
                    @csrf
                    <div class="mb-3">
                        <label for="title" class="form-label">Title</label>
                        <input type="text" class="form-control" id="title" name="title">
                        @error('title')
                            <div class="mt-1 alert alert-danger">{{ $message }}</div>
                        @enderror
                    </div>
                    <div class="mb-3">
                        <label for="description" class="form-label">Description</label>
                        <textarea class="form-control" id="description" name="description"></textarea>
                        @error('description')
                            <div class="mt-1 alert alert-danger">{{ $message }}</div>
                        @enderror
                    </div>
                    <div>
                        <input type="submit" class="btn btn-primary">
                        <a class="btn btn-danger" href="{{ route('article.index') }}">Back</a>
                    </div>                    
                </form>
            </div>
        </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.18/summernote.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function() {
            $('#description').summernote({
                height: 500,
            });
        });
    </script>
</body>
</html>

Now everything of coding part is done. Now we are going to test application. First start Laravel application using below artisan command.

php artisan serve

And in your browser, open http://localhost:8000/article/index. Create New articles, add image into article and save the article.

Here are screenshots how our application looks:

Create article

Upload image

Article list

Show article

Conclusion

SO far we have learned how to integrate Summernote editor and save in-article images into Laravel application.

Thanks for giving time in reading article. I hope you liked this article.