Skip to content
Advertisement

Laravel 7 Invalid argument supplied for foreach() when trying to delete post with multiple images

I am running Laravel 7 and have a list of tasks (would be posts if it were a blog) and I need to make sure that when the task is deleted, that all subsequent images are deleted in both the database and in the disc. When I click the delete button, the page throws an error: Invalid argument supplied for foreach(). Unfortunately, this is a vague error and can be caused by a variety of causes. My hopes are that someone can take a look at my code and see if I am missing something. I am relatively new to Laravel so tracking down this issue has been more than a challenge. Than you in advance for helping my work out this issue. In my Task.php model, I have:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;
use AppImage;
use IlluminateSupportFacadesStorage;


class Task extends Model
{
    protected $fillable = [
        'task_name', 'task_priority', 'task_assigned_to', 'task_assigned_by', 'task_description', 'task_to_be_completed_date', 'task_status',
        'task_notes'
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function image()
    {
        // return $this->hasMany('AppImage');
        return $this->hasMany(Image::class);
    }

    public static function boot()
    {
        parent::boot();
        self::deleting(function ($task) {
            foreach ($task->images as $image) {
                $image->delete();
            }
        });
    }
}

In my Image.php model, I have

<?php

namespace App;

use IlluminateDatabaseEloquentModel;
use IlluminateSupportFacadesStorage;
use AppTask;

class Image extends Model
{
    protected $fillable = [
        'task_id',
        'name',
    ];

    protected $uploads = '/task-images/';

    public function getFileAttribute($image)
    {
        return $this->uploads . $image;
    }


    public function task()
    {
        // return $this->belongsTo('AppTask', 'task_id');
        return $this->belongsTo(Task::class);
    }

    public static function boot()
    {
        parent::boot();
        self::deleting(function ($image) {
            Storage::delete(Storage::path($image->name));
        });
    }
}

In my TasksController.php, (all code in case something is causing a conflict here) here is what I have:

    <?php
    
    namespace AppHttpControllers;
    
    use AppTask;
    use AppImage;
    
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesAuth;
    use IlluminateSupportFacadesFile;
    use IlluminateSupportFacadesStorage;
    
    
    class TasksController extends Controller
    {
        public function index()
        {
            $tasks = Task::orderBy('created_at', 'desc')->paginate(10);
            return view('/tasks')->with('tasks', $tasks);
        }
        public function create()
        {
            return view('tasks.create');
        }
    
    
        public function store(Request $request)
        {
            $this->validate($request, [
                'task_name' => 'required',
                'task_description' => 'required',
            ]);
    
            // Create Task
            $user = Auth::user();
            $task = new Task();
            $data = $request->all();
            $task->user_id = $user->id;
            $task = $user->task()->create($data);
            if ($request->hasFile('images')) {
                $files = $request->file('images');
                foreach ($files as $file) {
                    $name = time() . '-' . $file->getClientOriginalName();
                    $name = str_replace(' ', '-', $name);
                    $file->move('task-images', $name);
                    $task->image()->create(['name' => $name]);
                    $images = new Image;
                    $images->name = $name;
                }
            }
            $task->task_name = $request->input('task_name');
            $task->task_description = $request->input('task_description');
            $task->task_priority = $request->input('task_priority');
            $task->task_assigned_by = $request->input('task_assigned_by');
            $task->task_assigned_to = $request->input('task_assigned_to');
            $task->task_to_be_completed_date = $request->input('task_to_be_completed_date');
            $task->task_notes = $request->input('task_notes');
            $task->task_status = $request->task_status;
            $task->save();
    
    
    
            return redirect('/home')->with('success', 'Task Created');
        }
        public function edit($id)
        {
            $task = Task::find($id);
            return view('tasks.edit', ['task' => $task]);
        }
        public function update(Request $request, $id)
        {
    
            $this->validate($request, [
                'task_name' => 'required',
                'task_description' => 'required',
            ]);
    
    
            $task = Task::find($id);
            $task->task_name = $request->input('task_name');
            $task->task_description = $request->input('task_description');
            $task->task_priority = $request->input('task_priority');
            $task->task_assigned_by = $request->input('task_assigned_by');
            $task->task_assigned_to = $request->input('task_assigned_to');
            $task->task_to_be_completed_date = $request->input('task_to_be_completed_date');
            $task->task_notes = $request->input('task_notes');
            $task->task_status = $request->input('task_status');
            if ($request->hasFile('images')) {
                $files = $request->file('images');
                foreach ($files as $file) {
                    $name = time() . '-' . $file->getClientOriginalName();
                    $name = str_replace(' ', '-', $name);
                    $file->move('task-images', $name);
                    $task->image()->create(['name' => $name]);
                }
            }
            $task->update();
            return redirect('/home')->with('success', 'Task Updated');
        }
        public function show($id)
        {
            $task =  Task::find($id);
            return view('tasks.show')->with('task', $task);
        }
        public function destroy($id)
        {
            $task = Task::findOrFail($id);
            // dd($task);
            $task->delete();
            return redirect('home')->with('success', 'Task Deleted');
        }
    }
And in the home page where I am calling the delete function, `home.blade.php`, I have:
@extends('layouts.master')

@section('content')
<div class="custom-container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            @include('layouts.includes.messages')
            <div class="card w-100">
                <div class="card-header text-white" style="background-color: #605ca8;">
                  <h3 class="card-title">Tasks</h3>
                  <div class="card-tools">
                    <a href="tasks/create" class="btn btn-success">
                      <i class="fas fa-tasks"></i> Add New Task
                    </a>
                  </div>
                </div>
                <!-- /.card-header -->
            </div>
            <div class="row">
                <div class="col-12">
                  <div class="card">
                    <div class="card-header">
                      <h3 class="card-title">Ongoing Tasks</h3>

                      <div class="card-tools">
                        <div class="input-group input-group-sm" style="width: 150px;">
                          <input type="text" name="table_search" class="form-control float-right" placeholder="Search">

                          <div class="input-group-append">
                            <button type="submit" class="btn btn-default"><i class="fas fa-search"></i></button>
                          </div>
                        </div>
                      </div>
                    </div>
                    <!-- /.card-header -->
                    <div class="card-body table-responsive p-0">
                      <table class="table table-hover text-nowrap">
                        <thead>
                          <tr>
                            <th>Task</th>
                            <th>Priority</th>
                            <th>Assigned To</th>
                            <th>Test Environment Date</th>
                            <th>Status</th>
                            <th>Action</th>
                          </tr>
                        </thead>
                        <tbody>
                            @if($tasks->count() > 0)
                            @foreach($tasks as $task)
                          <tr>
                            <td><a href="/tasks/{{$task->id}}">{{ $task->task_name }}</a></td>
                          <td>{{ $task->task_priority }}</td>
                          <td>{{ $task->task_assigned_to }}</td>
                            <td>{{$task->task_to_be_completed_date }}</td>
                            <td>{{ $task->task_status }}</td>
                            <td>
                                <a href="tasks/{{$task->id}}/edit" class="btn btn-primary btn-sm mr-2">
                                    <i class="fa fa-edit"></i> Edit
                                  </a>

                                  <form action="tasks/{{$task->id}}" method="POST" style="display: inline" class="">
                                    @csrf
                                    @method('DELETE')
                                  <button type="submit" class="btn btn-sm btn-danger ml-1 mr-1">
                                    <i class="fa fa-trash"></i> Delete
                                  </button>
                                </form>
                            </td>
                          </tr>
                         @endforeach
                         @else
                            <p class="ml-4 pt-2">No Tasks Found. Please Add one.</p>
                         @endif
                        </tbody>
                      </table>
                    </div>
                    <!-- /.card-body -->
                  </div>
                  <!-- /.card -->
                </div>
              </div>
        </div>
        {{ $tasks->links() }}
    </div>
</div>
@endsection

In my filesystem.php under the config folder, I have(just for storage):

 'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
            // 'root' => public_path('task-images'),
        ],

If I missed anything or codes, please let me know so I can edit my question. Thank you again in advance for helping me with this issue.

Advertisement

Answer

You are trying to access a property named images on your instance of Task but there isn’t an attribute named images and there isn’t a relationship named images, so null is being returned: $task->images == null. You named your relationship image not images, though images would be more correct since this relationship can return many; plural. Change the name of the relationship to images:

public function images()
{
    return $this->hasMany(Image::class);
}

Or reference it by its current name: $task->image

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement