Обновление CRUD Laravel в отношении базы данных один ко многим

#laravel #crud

#laravel #crud

Вопрос:

У меня есть приложение CRUD с автомобилями, и для каждого автомобиля у меня есть поля для создания автомобиля и изображений для загрузки. Все в порядке, у меня отношение «один ко многим», и для автомобиля у меня может быть несколько изображений. Когда я создаю автомобиль и загружаю фотографии, эти фотографии правильно сохраняются в базе данных и в моем директоре public / images. Проблема в том, что когда я хочу сделать ОБНОВЛЕНИЕ равным, я не знаю, как обновить фотографии в базе данных.

Вот мой код, у меня есть функция обновления, где я точно не знаю, как выполнить обновление.

 My cars table:

    Schema::create('cars', function (Blueprint $table) {
            $table->id();
            $table->string('model');
            $table->integer('seats');
            $table->string('fuel');
            $table->integer('year');
            $table->string('color');
            $table->string('gearbox');
            $table->integer('price');
            $table->string('coinType');
            $table->timestamps();
        });


My images table

    Schema::create('images', function (Blueprint $table) {
            $table->id();
            $table->integer('car_id');
            $table->string('file_name');
            $table->timestamps();
        });


My Car model:

    class Car extends Model
{
    protected $fillable = [
        'model',
        'seats',
        'fuel',
        'year',
        'color',
        'gearbox',
        'price',
        'coinType',
    ];

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

My Image model:

    class Image extends Model
{
    protected $fillable = [
        'car_id',
        'file_name'
    ];
    
    public function car()
    {
        return $this->belongsTo(Car::class);
    }
    use HasFactory;
}


My edit.blade:

    @extends('layouts.app')

@section('content')
<div class="row">
    <div class="col-sm-8 offset-sm-2">
        <h2 class="display-3">Edit a car</h2>
        <div>
            @if ($errors->any())
            <div class="alert alert-danger">
                <ul>
                    @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                    @endforeach
                </ul>
            </div><br />
            @endif
            <form method="post" action="{{ url('cars/'. $res->id) }}" enctype="multipart/form-data">
                @csrf
                @method('PUT')
                <div class="form-group">
                    <legend>
                        <label for="model" class="col-form-label">Model :</label>
                        <input type="text" class="form-control" id="model" name="model" value="{{ $res->model }}">
                    </legend>
                </div>

                <div class="form-group ">
                    <legend>
                        <label for="seats" class="col-form-label">Seats :</label>
                    </legend>
                    <select name="seats" id="seats" value="{{ $res->seats }}">
                        <option value="2">2</option>
                        <option value="4" selected="selected">4</option>
                        <option value="5">5</option>
                        <option value="6">6</option>
                        <option value="7">7</option>
                        <option value="8">8</option>
                    </select>
                </div>

                <div class="form-group ">
                    <legend>
                        <label for="fuel" class="col-form-label">Fuel :</label>
                    </legend>
                    <select name="fuel" id="fuel" value="{{ $res->fuel }}">
                        <option value="benzine gpl">benzine gpl</option>
                        <option value="gpl" selected="selected">diesel</option>
                        <option value="diesel">gpl</option>
                        <option value="diesel gpl">diesel gpl</option>
                        <option value="benzine">benzine</option>
                    </select>
                </div>

                <div class="form-group ">
                    <legend>
                        <label for="year" class="col-form-label">Year :</label>
                    </legend>
                    <input type="text" name="year" id="year" value="{{ $res->year }}">
                </div>

                <div class="form-group">
                    <legend>
                        <label for="color" class="col-form-label">Color :</label>
                    </legend>
                    <input type="text" class="form-control" id="color" name="color" value="{{ $res->color }}">
                </div>

                <div class="form-group ">
                    <legend>
                        <label for="gearbox" class="col-form-label">Gearbox :</label>
                    </legend>
                    <select name="gearbox" id="gearbox" value="{{ $res->gearbox }}">
                        <option value="manual">manual</option>
                        <option value="automatic" selected="selected">automatic</option>
                        <option value="semiautomatic">semiautomatic</option>
                    </select>
                </div>

                <div class="form-group ">
                    <legend>
                        <label for="price" class="col-form-label">Price :</label>
                    </legend>
                    <input type="text" class="form-control" id="price" name="price" value="{{ $res->price }}">
                </div>

                <div class="form-group ">
                    <legend>
                        <label for="coinType" class="col-form-label">CoinType :</label>
                    </legend>
                    <select name="coinType" id="coinType" value="{{ $res->coinType }}">
                        <option value="EUR">EUR</option>
                        <option value="LEI" selected="selected">LEI</option>
                        <option value="USD">USD</option>
                    </select>
                </div>

                <div class="form-group ">
                    <legend>
                        <label for="image" class="col-form-label">Upload images :</label>
                    </legend>
                    <input type="file" class="form-control" name="images[]" multiple />
                </div>


                <hr style="height:2px;border-width:0;color:gray;background-color:gray">

                <div class="col-xs-12 col-sm-12 col-md-12 ">
                    <button type="submit" class="btn btn-primary">Add car</button>
                    <a class="btn btn-primary" href="{{ route('cars.index') }}"> Back</a>
                </div>
        </div>
        </form>

        @endsection
  

Мой контроллер:

 public function store(Request $request)
{
    $request->validate([
        'model' => 'required',
        'year' => 'required',
        'color' => 'required',
        'price'=> 'required',
        'file_name.*' => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048'
    ]);

    $destionationPath = public_path('/images');
    $images = [];

    $car = new Car();
    $car->model = $request->model;
    $car->seats = $request->seats;
    $car->fuel = $request->fuel;
    $car->year = $request->year;
    $car->color = $request->color;
    $car->gearbox = $request->gearbox;
    $car->price = $request->price;
    $car->coinType = $request->coinType;
  

    $car->save();

    
    if ($files = $request->file('images')) {
        foreach ($files as $file) {
            $fileName = $file->getClientOriginalName();
            $file->move($destionationPath, $fileName);
            $images[] = $fileName;
        }
    }

    foreach ($images as $imag) {
        $image = new Image();
        $image->car_id = $car->id;
        $image->file_name = $imag;
        $image->save();
    }


    return redirect()->route('cars.index')->with('success', 'Car saved !');
}

public function update(Request $request, $id)
{
    $request->validate([
        'model' => 'required',
        'year' => 'required',
        'color' => 'required',
        'price'=> 'required',
        'file_name.*' => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048'
    ]);

    $destionationPath = public_path('/images');
    $images = [];

    $car = Car::find($id);
    $car->model = $request->model;
    $car->seats = $request->seats;
    $car->fuel = $request->fuel;
    $car->year = $request->year;
    $car->color = $request->color;
    $car->gearbox = $request->gearbox;
    $car->price = $request->price;
    $car->coinType = $request->coinType;
  

    $car->update();

    
    if ($files = $request->file('images')) {
        foreach ($files as $file) {
            $fileName = $file->getClientOriginalName();
            $file->move($destionationPath, $fileName);
            $images[] = $fileName;
        }
    }

    foreach ($images as $imag) {
        //here I don't know what to do
    }


    return redirect()->route('cars.index')->with('success', 'Car updated !');
}
  

Спасибо.

Ответ №1:

Просматривая ваш файл блейда, вы на самом деле не показываете исходные изображения, поэтому пользователь не может обновлять изображения, он может вставлять новые изображения, а старые должны быть удалены, верно?

Если вы хотите обновить изображение, вы должны показать исходные изображения в файле блейда с его идентификатором, чтобы в бэкэнде вы могли найти изображение на основе идентификатора

Но теперь это должно сработать для вас:

вы можете просто проверить, загрузил ли пользователь какой-либо файл

 if ($files = $request->files('images')) {
...
  

удалите все старые изображения

 $car->images->each(function($image) {
    // You probabily should be using Storage facade for this
    // this should be the full path, I didn't test it, but if you need add extra methods so it returns the full path to the file
    if (file_exists($destionationPath . '/' . $image->file_name) {
        unset($destionationPath . '/' . $image->file_name);
    }

    $image->delete();
});
  

А затем воссоздайте изображения, как вы делаете в магазине

 foreach ($files as $file) {
    $fileName = $file->getClientOriginalName();
    $file->move($destionationPath, $fileName);
    $images[] = $fileName;
}

foreach ($images as $imag) {
    $image = new Image();
    $image->car_id = $car->id;
    $image->file_name = $imag;
    $image->save();
}
  

Комментарии:

1. Еще одна вещь, попробуйте реализовать transactions ( DB::transaction ), чтобы, если что-то не сработает в середине кода, вы ничего не потеряли в базе данных (вы не можете быть уверены в файлах, чтобы убедиться, что вы можете сохранить пути и попытаться удалить файлы в конце кода)