Rich text editor did not had image upload feature - Here's how I fixed it

Rich text editor did not had image upload feature – Here’s how I fixed it

The rich text editor I was using did not had image upload feature. It only allow URL of image to be posted in post’s content. So I created my own file manager module. I was uploading files on my Laravel website and then use their links in my rich text editor.

1. Migration

Here is the migration I used to manage files.

<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('files', function (Blueprint $table) {
            $table->id();
            $table->string('name')->nullable();          // custom name
            $table->string('file_path')->nullable();                 // stored file path
            $table->string('alt')->nullable();           // alt attribute
            $table->string('caption')->nullable();       // short caption
            $table->text('description')->nullable();
            $table->enum("type", ["public", "private"])->default("public");
            $table->timestamps();
        });
    }

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

2. Routes

These are the routes I used to upload, display and delete files.

// routes/web.php

Route::post("/admin/files/delete", [FileController::class, "destroy"]);
Route::post("/admin/files/upload", [FileController::class, "upload"]);
Route::any("/admin/files", [FileController::class, "index"]);

3. Controller

This is my FileController that handles the file upload, view and delete functionality.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use DB;
use Storage;
use Validator;

class FileController extends Controller
{
    public function destroy()
    {
        $validator = Validator::make(request()->all(), [
            "id" => "required"
        ]);

        if ($validator->fails())
        {
            return response()->json([
                "status" => "error",
                "message" => $validator->errors()->first()
            ]);
        }

        $id = request()->id ?? 0;
        $media = DB::table('files')->where('id', $id)->first();

        if (!$media)
        {
            return response()->json([
                "status" => "error",
                "message" => "File not found."
            ]);
        }

        if ($media->file_path && Storage::exists($media->type . "/" . $media->file_path))
        {
            Storage::delete($media->type . "/" . $media->file_path);
        }

        DB::table('files')->where('id', $id)->delete();

        return response()->json([
            "status" => "success",
            "message" => "File has been deleted."
        ]);
    }

    public function upload()
    {
        $validator = Validator::make(request()->all(), [
            "file" => "required",
            "type" => "required"
        ]);

        if ($validator->fails())
        {
            return response()->json([
                "status" => "error",
                "message" => $validator->errors()->first()
            ]);
        }

        $type = request()->type ?? "";
        $file = request()->file("file");

        if (!in_array($type, ["public", "private"]))
        {
            return response()->json([
                "status" => "error",
                "message" => "In-valid type '" . $type . "'."
            ]);
        }

        $data = [
            'name'        => request()->name ?? "",
            'file_path'   => "",
            'alt'         => request()->alt ?? "",
            'caption'     => request()->caption ?? "",
            'description' => request()->description ?? "",
            'created_at'  => now()->utc(),
            'updated_at'  => now()->utc()
        ];

        $file_path = "";

        if ($file)
        {
            $file_path = "files/" . uniqid() . "." . $file->getClientOriginalExtension();
            $file->storeAs("/" . $type, $file_path);
            chmod(storage_path("app/" . $type . "/files"), 0755);

            $data["file_path"] = $file_path;
        }

        $id = DB::table('files')->insertGetId($data);

        return response()->json([
            "status" => "success",
            "message" => "File has been uploaded.",
            "id" => $id,
            "file_path" => url("/storage/" . $file_path)
        ]);
    }

    public function index()
    {
        if (request()->isMethod("post"))
        {
            set_timezone();

            $files = DB::table('files')
                ->where("type", "=", "public")
                ->orderByDesc('id')
                ->paginate();

            $files_arr = [];
            foreach ($files as $file)
            {
                $obj = [
                    "id" => $file->id ?? 0,
                    "name" => $file->name ?? "",
                    "file_path" => $file->file_path ?? "",
                    "alt" => $file->alt ?? "",
                    "caption" => $file->caption ?? "",
                    "description" => $file->description ?? "",
                    "created_at" => date("d F, Y h:i:s a", strtotime($file->created_at . " UTC"))
                ];

                if ($obj["file_path"] && Storage::exists("public/" . $obj["file_path"]))
                {
                    $obj["file_path"] = url("/storage/" . $obj["file_path"]);
                }

                array_push($files_arr, (object) $obj);
            }

            return response()->json([
                "status" => "success",
                "message" => "Data has been fetched.",
                "files" => $files_arr
            ]);
        }

        $files = DB::table('files')->orderByDesc('id')->paginate();

        return view("admin/files/index", [
            "files" => $files
        ]);
    }
}

Do let me know if you face any problem in this.