首页 > 解决方案 > Laravel 7 从数据库和磁盘中删除数组或单个图像 - 从数据库或磁盘中删除帖子但不关联图像

问题描述

在 Laravel 7 中,我有一个任务管理应用程序。我可以上传任务(如果是博客,则发布帖子)和图像。我有一个按预期工作的多张图片上传。当需要删除任务时,该任务删除得很好,但图像保留在数据库和磁盘中,该磁盘是公共的,位于名为 task-images 的文件夹中。作为 Laravel 的新手,我正在努力解决这个问题。我试图更改设置filesystem.php(我将使用注释掉的代码发布)但这并没有像我预期的那样改变位置。最后,我希望能够在删除帖子时删除多个图像,并在单个图像上单击删除并从数据库和磁盘中删除。我正在为我的所有任务路线使用资源控制器。我不知道该怎么做,而且我发现的教程并没有真正解决我的具体问题。任何帮助将不胜感激。先感谢您。

这是我的任务控制器TaskController.php

<?php

namespace App\Http\Controllers;

use App\Task;
use App\Image;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;

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);
        // $image = '/task-images/' . $task->image;
        Storage::delete($task->image);
        $task->delete();
        return redirect('home')->with('success', 'Task Deleted');
    }
}

filesystem.php(只是磁盘部分)

 'disks' => [

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

...

在我的个人表演模板show.blade.php中,如果出现代码冲突,请填写完整。

@extends('layouts.master')
@section('content')
<div class="container">
    <a href="/home" class="btn bg-purple mb-4">Go Back</a>
    <div class="card p-3">
        <div class="row">
            <div class="col-md-4 col-sm-12">
                <h3>Task</h3>
                <p>{{ $task->task_name }}</p>
                <h3>Assigned On:</h3>
                <p>{{ $task->created_at->format('m/d/Y') }}</p>
                <h3>Assigned To:</h3>
                <p>{{ $task->task_assigned_to }}</p>
            </div>
            <div class="col-md-4 col-sm-12">
                <h3>Task Description</h3>
                <p>{{ $task->task_description }}</p>
                <h3>Priority</h3>
            <p>{{ $task->task_priority }}</p>
                <h3>Status</h3>
                <p>{{ $task->task_status }}</p>
            </div>
            <div class="col-md-4 col-sm-12">
                <h3>Test Environment Date:</h3>
                <p>{{ $task->task_to_be_completed_date }}</p>
                <h3>Notes</h3>
                <p>{{ $task->task_notes }}</p>
                <h3>Action</h3>
                <div style="display: inline;">
                    <a href="/tasks/{{$task->id}}/edit" class="btn btn-sm btn-primary mr-2">
                        <i class="fa fa-edit"></i> Edit
                    </a>
                </div>
            <form style="display: inline;" action="/tasks/{{ $task->id }}" method="POST" class="">
                        @csrf
                        @method('DELETE')
                      <button type="submit" class="btn btn-danger btn-sm ml-1 mr-1">
                        <i class="fa fa-trash"></i> Delete
                      </button>
                    </form>

            </div>
            <div class="col-md-12">
                <h5>Images</h5>
                <hr />
                  <div class="row">
                      @if($task->image->count()>0)

                          @for($i=0; $i < count($images = $task->image()->get()); $i++)
                          <div class="col-lg-4 col-md-6 col-sm-12">
                            <a href="#" class="thumbnail" data-toggle="modal" data-target="#lightbox"><img class="w-50 mb-2" src="/task-images/{{ $images[$i]['name'] }}" alt=""></a>
                            <form style="display: inline;" action="/tasks/{{ $task->name }}" method="POST" class="">
                                @csrf
                                @method('DELETE')
                              <button type="submit" class="btn btn-danger btn-sm ml-1 mr-1">
                                <i class="fa fa-trash"></i> Delete
                              </button>
                            </form>
                        </div>

                          @endfor
                          @else
                              <p>No images found</p>
                      @endif
                  </div>
                <br />
              </div>
        </div>
    </div>
</div>
<!--Modal Start-->

<div id="lightbox" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <button type="button" class="close hidden" data-dismiss="modal" aria-hidden="true">×</button>
        <div class="modal-content">
            <div class="modal-body">
                <img class="w-100" src="" alt="" />
            </div>
        </div>
    </div>
</div>

<!--Modal End-->
@endsection
@section('scripts')
<script>
    $(document).ready(function() {
     var $lightbox = $('#lightbox');

     $('[data-target="#lightbox"]').on('click', function(event) {
         var $img = $(this).find('img'),
             src = $img.attr('src'),
             alt = $img.attr('alt'),
             css = {
                 'maxWidth': $(window).width() - 100,
                 'maxHeight': $(window).height() - 100
             };

         $lightbox.find('.close').addClass('hidden');
         $lightbox.find('img').attr('src', src);
         $lightbox.find('img').attr('alt', alt);
         $lightbox.find('img').css(css);
     });

     $lightbox.on('shown.bs.modal', function (e) {
         var $img = $lightbox.find('img');

         $lightbox.find('.modal-dialog').css({'width': $img.width()});
         $lightbox.find('.close').removeClass('hidden');
     });
 });
 </script>
@endsection

在我的任务模型中Task.php,我有:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use App\Image;

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('App\Image');
        return $this->hasMany(Image::class);
    }
}

最后是我的图像模型Image.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use App\Task;

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('App\Task', 'task_id');
        return $this->belongsTo(Task::class);
    }
}

如果我遗漏了什么,请告诉我,以便我编辑我的问题。再次,提前感谢您帮助我解决这个问题。我整个星期都在为此挠头。干杯。

编辑 如下建议在我的模型中实现引导功能后,我收到一个错误,即 foreach 使用了无效参数。我跑了一个 dd($task); 下图显示了结果。 删除时 $task 的 dd

最终编辑 下面的答案适用于我的情况。我确实必须编辑一些东西才能最终确定解决方案:在Task.php我将 foreach 更改为以下内容。

foreach($task->image ?: [] as $image) 

我在模型中声明了图像而不是图像,这导致了问题。添加三元运算符也有助于代码不会抛出任何错误。

在我的 TasksController.php 中,我使用相同的三元运算符更改了更新和创建函数,如下所示:

 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]);
            }
        }

我希望这可以帮助其他有同样问题的人。感谢@GrumpyCrouton 和@lagbox 帮助解决了这个问题以及@user3563950 没有他们,我仍然会再努力几个星期。

标签: phplaravelsql-delete

解决方案


在您的App\Image班级上,使用以下内容实现启动功能;

use Illuminate\Support\Facades\Storage;

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

App\Task还要在类中实现boot方法

use Illuminate\Support\Facades\Storage;

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

现在在你的TaskController实现destroy方法如下;

public function destroy($id)
    {
        $task = Task::findOrFail($id);
        $task->delete();
        return redirect('home')->with('success', 'Task Deleted');
    }

作为奖励,学习Laravel 模型绑定以减轻使用查找实例的痛苦findOrFail()


推荐阅读