Making the $header_image public was the issue, so I moved the $header image in the render() method, and then I only work with the $this->image from the ImageUpload component and send it via events whenever its needed in the parent controller.
Only issue I have to solve now is that I have to pass a variable to the child component differently
Livewire 3 child component throws a 404 after Model deletion
I have a problem with the way a Livewire parent and child component interact. I have an image component (Usually used as a child component) that I use for handling images and my child component throws a 404 error when I delete the image model. The error is thrown in almost like a new window, so thats how I know that the child component throws it. If the parent throws it, the 404 would be in full screen. The only trace, error log or exception I have is:
- No query results for model [App\Models\Image].
- vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php#620
(Illuminate\Database\Eloquent\ModelNotFoundException):
return $model;
}
throw (new ModelNotFoundException)->setModel(get_class($this->model));
}
Im really not a fan of Livewire's child component functionality, especially after Livewire 3 was released and no significant changes were made regarding the following issue. The child components are made very difficult to debug and most of the time, the errors thrown have no log or trace, which blows my mind. My child component isnt deeply nested, I only have a parent and a child component, which shouldnt cause issues. How it generally works:
- I have a
Listing\Create&Listing\Editthat use theImage\Uploadas a child component. At first I made the Listing components to MUST have an image, but I have changed it to be optional, and the issue arises only in the Edit page. If I delete the image and refresh the Edit page, the image is deleted and everything is as it should be. However, it throws a 404 immediately after I delete the image and the code I have below is focused on the delete method, because the component has lot of code.
I have since found out that the dispatch('remove-image') triggers the error, or maybe the dispatch() triggers the Component to reset, but with the old hydrated data and therefore it throws a 404.
My Listing\Edit component-view integrates the child component like this:
<livewire:image.image-upload wire:key="{{ $uniqueKey }}" :labelText="__('messages.photo_recommend', ['imagetype' => 'Header', 'format' => '960px:320px', 'min' => '200'])" :imageSize="'w-full h-52'" :image="$header_image" wire:model="image"></livewire:image.image-upload>
My image component goes like (only relevant code, regarding the removal of the image):
class ImageUpload extends Component
{
use WithFileUploads;
#[Validate]
public $image;
#[Validate]
public $alt;
public $type;
public $errorMsg = null, $imageSize = null, $labelText;
public function mount(): void
{
if(($this->image instanceof Image)) {
$this->fill(
$this->image->only('alt')
);
}
}
public function updatedImage($value): void
{
\Debugbar::addMessage('updatedImage()', 'Image');
if(!is_null($this->image)) {
if (!$this->_validateImage()) $this->removeImage();
$imagePath = $value->temporaryUrl();
$this->dispatch('on-image-change', imagePath: $imagePath);
}
}
public function removeImage(bool $completeRemove = false): void
{
\Debugbar::addMessage('removeImage()', 'Image');
if($completeRemove && ($this->image instanceof Image)) {
$path = 'public' .$this->image->path .$this->image->filename;
\Debugbar::addMessage($path, 'Image Path');
if ($this->image->delete()) {
\Debugbar::addMessage('Image deleted from database', 'Image');
//$this->image = new Image();
\Debugbar::addMessage($this->image, 'Removed image');
if (Storage::exists($path)) {
Storage::delete($path);
\Debugbar::addMessage('Image deleted from storage', 'Image');
}
\Debugbar::addMessage('Dispatching remove-image event', 'Image');
$this->dispatch('remove-image'); // Goes in the Listing\Edit
}
}
$this->reset();
// $this->reset('image');
// $this->reset('alt');
//$this->dispatch('on-image-change', imagePath: '');
}
}
And finally my Listings\Edit:
class Edit extends Component
{
public $header_image = null, $isImageValidated = true, $uniqueKey;
public $meta_title, $meta_description, $og_title, $og_description, $og_image = '';
public function mount()
{
$this->authorize('update', $this->listing);
$this->uniqueKey = Random::generateRandomUniqueKey();
$listingMeta = $this->listing->meta;
// Pre-Fills fields
$headerImageModel = $this->listing->images()->where('type', 'header')->first();
if ($headerImageModel) {
$this->header_image = $headerImageModel;
$this->og_image = asset('/storage/' . $headerImageModel->path . $headerImageModel->filename);
} else {
$this->header_image = null;
}
// Pre-Fills inputs
$this->fill(
$this->listing->only('title', 'description', 'contact_name', 'contact_email', 'is_published'),
);
}
public function render()
{
$this->sub_categories = Category::where('parent_id', $this->main_category)->get();
// $parentId = Category::where('id', $this->listing->category_id)->pluck('parent_id');
return view('livewire.listing.edit', [
'work_time_options' => $this->work_time_options,
'employment_options' => $this->employment_options,
'income_options' => $this->income_options,
'main_categories',
'sub_categories' => $this->sub_categories,
'header_image' => $this->header_image,
]);
}
#[On('remove-image')]
public function headerImageRemoved()
{
\Debugbar::addMessage('headerImageRemoved()', 'Listing');
if ($this->listing && $this->header_image) {
$this->header_image = null;
$this->og_image = null;
}
}
#[On('on-image-change')]
public function updateOgImage($imagePath)
{
\Debugbar::addMessage('updateOgImage()', 'Listing');
$this->og_image = $imagePath;
}
#[On('test-call')]
public function test()
{
\Debugbar::addMessage('Very nice test', 'Listing');
}
}
Here's the interesting part: In the Image component, in the removeImage() function, If I make the code like this:
public function removeImage(bool $completeRemove = false): void
{
if($completeRemove && ($this->image instanceof Image)) {
$path = 'public' .$this->image->path .$this->image->filename;
if ($this->image->delete()) $this->image = new Image();
if (Storage::exists($path)) Storage::delete($path);
$this->dispatch('test-call'); // Goes in the Listing\Edit
}
}
}
If I just make it to delete from DB and storage and then call the test-call, it still throws that 404 error. If I comment out the dispatch and no more events are called, then the image is actually removed, but the preview is still here, because i have(For the Listing\Edit):
@if($og_image)
<img class="w-full h-96" src="{{ $og_image }}" alt="Open Graph Image">
The debug log shows that the image was deleted from all places, so now the dispatch() causes the error? The dispatch() doesn't even reach the parent controller, because there is no new messages for the Debugbar, so the error occurs somewhere before it goes to the parent component. What can it really be? I have been debugging this for so long, I'm not really sure what works anymore, I get that this whole Framework works by magic, but so do the error logs for it, as in they disappear.
Resource I have tried:
Please or to participate in this conversation.