lukeboy_2002's avatar

Error Upload image with CKEditor

I think this is the wright channel to aks this Questtion.

Hi I want to use CKEeditor for my blog. The text is working, but when I add an image to the textarea I get immediately an error Cannot upload file: name.extention. When I check the network tab I have an error 500 xhr.

I tried this locally and on my domain. same error.

This is my code: Route

    Route::post('upload', [\App\Http\Controllers\admin\PostController::class, 'upload'])->name('upload');

Blade/Livewire post-add.blade.php

@push('styles')
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css" rel="stylesheet" />
@endpush
<div class="p-6 bg-white border border-gray-200 shadow-md dark:bg-gray-800 dark:border-gray-700">
    <form wire:submit.prevent="save" enctype="multipart/form-data" method="POST" class="space-y-6">
        @csrf

        <div class="flex justify-between space-x-4">
            <div class="w-1/2">
                <x-form.label for="title" value="Title" />
                <x-form.input wire:model="title" type="text" name="title" required />
                <x-form.input-error for="title" class="mt-2" />
            </div>
            <div class="w-1/2">
                <x-form.label for="categories" value="Categories" />
                <div wire:ignore>
                    <select id="categories" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 select2" multiple>
                        @foreach($categories as $category)
                            <option value="{{ $category->id }}">{{ $category->title }}</option>
                        @endforeach
                    </select>
                </div>
                <x-form.input-error for="post.categories" class="mt-2" />
            </div>
        </div>

        <div class="flex space-x-4">
            <div class="w-1/2">
                <x-form.label for="image" value="Image" />
                <x-form.input wire:model="image" name="image" type="file" required />
                <div wire:loading wire:target="image">
                    <x-popups.loading />
                </div>
                <x-form.input-error for="image" class="mt-2" />
            </div>
        </div>

        <div wire:ignore>
            <x-form.label for="body" value="Post" />
            <x-form.textarea wire:model.defer="body" name="body" id="body" />
            <x-form.input-error for="body" class="mt-2" />
        </div>

        <div class="flex items-center space-x-4">
            <div class="w-1/2">
                <x-form.label for="published_at" value="Date to publish" />
                <x-form.input wire:model="published_at" type="date" name="published_at" />
                <x-form.input-error for="published_at" class="mt-2" />
            </div>
            <div>
                <label class="relative inline-flex items-center mr-5 cursor-pointer">
                    <input wire:model="active"
                           name="active"
                           id="active"
                           value="1"
                           aria-describedby="active"
                           type="checkbox"
                           class="sr-only peer"
                           checked
                    >
                    <div class="w-11 h-6 bg-gray-200 rounded-full peer dark:bg-gray-700 peer-focus:ring-4 peer-focus:ring-orange-300 dark:peer-focus:ring-orange-800 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-orange-500"></div>
                    <span class="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300">Active</span>
                </label>
            </div>
        </div>

        <div class="flex justify-end space-x-2">
            <x-buttons.secondary type="button" onclick="history.back()" class="px-3 py-2 text-xs font-medium">Cancel</x-buttons.secondary>
            <x-buttons.primair class="px-3 py-2 text-xs font-medium">Save</x-buttons.primair>
        </div>
    </form>

</div>
@push('scripts')
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js"></script>
    <script src="https://cdn.ckeditor.com/ckeditor5/31.1.0/classic/ckeditor.js"></script>

    <script>
        document.addEventListener("livewire:load", () => {
            let el = $('#categories')
            initSelect()

            Livewire.hook('message.processed', (message, component) => {
                initSelect()
            })

            Livewire.on('setCategoriesSelect', values => {
                el.val(values).trigger('change.select2')
            })

            el.on('change', function (e) {
            @this.set('post.categories', el.select2("val"))
            })

            function initSelect () {
                el.select2({
                    placeholder: '{{__('Select your option')}}',
                    allowClear: !el.attr('required'),
                })
            }
        })
    </script>
    <script>
        ClassicEditor
            .create(document.querySelector('#body'), {
                ckfinder: {
                    uploadUrl: '{{ route('admin.upload', ['_token' => csrf_token()]) }}'
                }
            })
            .catch(error => {
                console.error(error);
            });
    </script>
@endpush

Controller PostController

<?php

namespace App\Http\Controllers\admin;

use App\Http\Controllers\Controller;
use App\Models\Category;
use App\Models\Post;
use Cviebrock\EloquentSluggable\Services\SlugService;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        return view('admin.posts.index');
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        $categories = Category::all();

        return view('admin.posts.create');
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     */
    public function show(Post $post)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(Post $post)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Post $post)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Post $post)
    {
        //
    }

    public function upload(Request $request)
    {
        try {
            $post = new Post();
            $post->id = 0;
            $post->exists = true;
            $image = $post->addMediaFromRequest('upload')->toMediaCollection('thumb');

            return response()->json([
                'uploaded' => true,
                'url' => $image->getUrl('thumb')
            ]);
        } catch (\Exception $e) {
            return response()->json(
                [
                    'uploaded' => false,
                    'error'    => [
                        'message' => $e->getMessage()
                    ]
                ]
            );
        }
    }
}

Livewire PostAdd

<?php

namespace App\Http\Livewire\Admin\Posts;

use App\Models\Category;
use App\Models\Post;
use Cviebrock\EloquentSluggable\Services\SlugService;
use Livewire\Component;
use Livewire\WithFileUploads;

class PostAdd extends Component
{
    use WithFileUploads;

    public $categories = [];

    public $title;

    public $slug;

    public $image;

    public $body;

    public $active;

    public $published_at;

    public $post = [
        'categories' => []
    ];

    protected $rules = [
        'title' => 'required|min:3',
        'slug' => 'required|string',
        'image' => 'nullable|mimes:jpg,jpeg,png,svg,gif|max:2048',
        'body' => 'nullable|min:10',
        'active' => 'nullable',
        'published_at' => 'nullable',
        'post.categories' => 'array',
        'post.categories.*' => 'required|integer',
    ];

    public function updatedTitle()
    {
        $this->slug = SlugService::createSlug(Post::class, 'slug', $this->title);
    }

    public function mount()
    {
        $this->categories = Category::all();
        $this->user_id = current_user()->id;
    }

    public function save()
    {
        $this->validate();

        $post = Post::create([
            'user_id' => $this->user_id,
            'title' => $this->title,
            'slug' => $this->slug,
            'body' => $this->body,
            'active' => $this->active,
            'published_at' => $this->published_at,

        ]);
        $this->image->storeAs('posts', $post->id.'.'.$this->image->extension());

        $post->update(['image' => 'posts/'.$post->id.'.'.$this->image->extension()]);

        $post->categories()->sync($this->post['categories']);

        $this->reset('post');
        $this->emit('setCategoriesSelect', []);

        session()->flash('success', 'Blogpost has been created');

        return redirect()->route('admin.posts.index');
    }
}

I can't figured out what I'm doing wrong. Please Help me.

0 likes
2 replies
LaryAI's avatar
Level 58

The error message "Cannot upload file: name.extension" and the 500 xhr error suggest that there is an issue with the file upload process. Looking at the code, it seems that the file upload is handled by the upload method in the PostController.

One possible issue is that the addMediaFromRequest method is being called on a new Post instance, which has not been saved to the database yet. This could cause issues with the media library package being used.

To fix this, try modifying the upload method to use the request object directly to upload the file, instead of using the addMediaFromRequest method on a new Post instance. Here's an example:

public function upload(Request $request)
{
    try {
        $path = $request->file('upload')->store('public/images');
        $url = Storage::url($path);

        return response()->json([
            'uploaded' => true,
            'url' => $url
        ]);
    } catch (\Exception $e) {
        return response()->json(
            [
                'uploaded' => false,
                'error'    => [
                    'message' => $e->getMessage()
                ]
            ]
        );
    }
}

This code uses the store method on the file object in the request object to save the file to the public/images directory. It then uses the Storage::url method to get the URL of the saved file. Finally, it returns a JSON response with the uploaded flag set to true and the URL of the saved file.

Note that you may need to modify the store method call to save the file to a different directory, depending on your application's needs.

lukeboy_2002's avatar
lukeboy_2002
OP
Best Answer
Level 18

Solved. I forgot the implement Hasmedia.

Please or to participate in this conversation.