yakoffka.ru
    грабли, костыли и велосипеды php, css, html, js и прочего

    Создание интернет-витрины на laravel. Часть 3

    laravel.

    Капустин Яков

    оглавление

    Для отмены необходимости создавать пользователя каждый раз после полной перестройки базы данных создадим UsersTableSeeder и добавим класс UsersTableSeeder в файл 'database/seeds/DatabaseSeeder.php':

    bash:
    vagrant@homestead:~/projects/kk$ php artisan make:seeder UsersTableSeeder Seeder created successfully.
    database/seeds/UsersTableSeeder.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    <?php

    use Illuminate\Database\Seeder;

    class 
    UsersTableSeeder extends Seeder
    {
      
    /**
       * Run the database seeds.
       *
       * @return void
       */
      
    public function run()
      {
        
    DB::table('users')->insert([
          
    'name' => 'Admin Name',
          
    'email' => 'admin@gmail.com',
          
    'password' => bcrypt('111111'),
        ]);
      }
    }

    и добавим класс UsersTableSeeder в файл 'database/seeds/DatabaseSeeder.php':

    database/seeds/DatabaseSeeder.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    <?php

    use Illuminate\Database\Seeder;

    class 
    DatabaseSeeder extends Seeder
    {
      
    /**
       * Seed the application's database.
       *
       * @return void
       */
      
    public function run()
      {
        
    // $this->call(UsersTableSeeder::class);

        
    $this->call([
          
    ProductsTableSeeder::class,
          
    UsersTableSeeder::class,
        ]);
      }
    }

    Сделаем все атрибуты Product массово назначаемыми, определив свойство $guarded как пустой массив:

    app/Product.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12

    <?php

    namespace App;

    use 
    Illuminate\Database\Eloquent\Model;

    class 
    Product extends Model
    {
      protected 
    $guarded = [];
    }

    Создадим модель Comment вместе с фабрикой(?) и миграцией.

    bash:
    vagrant@homestead:~/projects/kk$ php artisan make:model Comment -m -f Model created successfully. Factory created successfully. Created Migration: 2019_05_20_010314_create_comments_table

    Опишем и выполним миграцию.

    database/migrations/2019_05_20_010314_create_comments_table.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37

    <?php

    use Illuminate\Support\Facades\Schema;
    use 
    Illuminate\Database\Schema\Blueprint;
    use 
    Illuminate\Database\Migrations\Migration;

    class 
    CreateCommentsTable extends Migration
    {
      
    /**
       * Run the migrations.
       *
       * @return void
       */
      
    public function up()
      {
        
    Schema::create('comments', function (Blueprint $table) {
          
    $table->bigIncrements('id');
          
    $table->unsignedInteger('product_id');
          
    $table->unsignedInteger('user_id')->nullable();
          
    $table->string('name_guest')->nullable();
          
    $table->text('comment_string');
          
    $table->timestamps();
        });
      }

      
    /**
       * Reverse the migrations.
       *
       * @return void
       */
      
    public function down()
      {
        
    Schema::dropIfExists('comments');
      }
    }
    bash:
    vagrant@homestead:~/projects/kk$ php artisan migrate Migrating: 2019_05_20_010314_create_comments_table Migrated: 2019_05_20_010314_create_comments_table vagrant@homestead:~/projects/kk$

    Опишем отношения в моделях Product и Comment.

    app/Product.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16

    <?php

    namespace App;

    use 
    Illuminate\Database\Eloquent\Model;

    class 
    Product extends Model
    {
      protected 
    $guarded = [];

      public function 
    comments() {
        return 
    $this->hasMany(Comment::class);
      }
    }
    app/Comment.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14

    <?php

    namespace App;

    use 
    Illuminate\Database\Eloquent\Model;

    class 
    Comment extends Model
    {
      public function 
    product() {
        return 
    $this->belongsTo(Product::class);
      }
    }

    Создадим контроллер ProductCommentsController и опишем в нем необходимые методы:

    bash:
    vagrant@homestead:~/projects/kk$ php artisan make:controller ProductCommentsController Controller created successfully. vagrant@homestead:~/projects/kk$
    app/Http/Controllers/ProductCommentsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75

    <?php

    namespace App\Http\Controllers;

    use 
    Illuminate\Http\Request;
    use 
    App\Comment;
    use 
    App\Product;
    use 
    Illuminate\Support\Facades\Validator;

    class 
    ProductCommentsController extends Controller
    {
      public function 
    index() { // testing deletion of product comments upon product removal
        
    return $comments Comment::all();
      }

      
    /**
       * Store a newly created resource in storage.
       *
       * @param  \Illuminate\Http\Request  $request
       * @return \Illuminate\Http\Response
       */
      
    public function store(Product $product) {

        
    // $validator = Validator::make($request->all(), [
        //   'product_id' => 'required|integer',
        //   'user_id' => 'required|integer',
        //   'guest_name' => 'nullable|string',
        //   'comment_string' => 'required|string',
        // ]);

        // if ($validator->fails()) {
        //   return back()->withErrors($validator)->withInput();
        // }

        
    $comment Comment::create([
          
    'product_id' => $product->id,
          
    'user_id' => request('user_id'),
          
    'guest_name' => request('guest_name'),
          
    'comment_string' => request('comment_string'),
        ]);

        
    // return back();
        
    return redirect('/products/' $product->id '#comment_' $comment->id);
      }

      public function 
    update(Comment $comment) {
        
    // dd($comment);
        // dd(request()->all());

        
    $comment->update([
          
    'comment_string' => request('comment_string'),
        ]);

        
    // return redirect()->route('productsShow', ['product' => $comment->product_id]);
        
    return redirect('/products/' $comment->product_id '#comment_' $comment->id);
      }

      
    /**
       * Remove the specified resource from storage.
       *
       * @param  \App\Comment  $comment
       * @return \Illuminate\Http\Response
       */
      
    public function destroy(Comment $comment)
      {
        
    // dd($comment);
        
    $product_id $comment->product_id;
        
    $comment->delete();

        return 
    redirect()->route('productsShow', ['product' => $product_id]);
      }

    }

    Дополним файл отображения для вывода комментариев:

    resources/views/products/show.blade.php:
    001
    002
    003
    004
    005
    006
    007
    008
    009
    010
    011
    012
    013
    014
    015
    016
    017
    018
    019
    020
    021
    022
    023
    024
    025
    026
    027
    028
    029
    030
    031
    032
    033
    034
    035
    036
    037
    038
    039
    040
    041
    042
    043
    044
    045
    046
    047
    048
    049
    050
    051
    052
    053
    054
    055
    056
    057
    058
    059
    060
    061
    062
    063
    064
    065
    066
    067
    068
    069
    070
    071
    072
    073
    074
    075
    076
    077
    078
    079
    080
    081
    082
    083
    084
    085
    086
    087
    088
    089
    090
    091
    092
    093
    094
    095
    096
    097
    098
    099
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190

    @extends('layouts.app')

    @
    section('title')
    {{ 
    $product->name }}
    @
    endsection

    @section('content')
    <
    div class="container">
      
      <
    h1>{{ $product->name }}</h1>
      
      <
    div class="row">

        <
    div class="col-md-4 wrap_b_image">

          @if(
    $product->image)
          <
    div class="card-img-top b_image" style="background-image: url({{ asset('storage') }}/images/products/{{$product->id}}/{{$product->image}});">
          @else
          <
    div class="card-img-top b_image" style="background-image: url({{ asset('storage') }}/images/default/no-img.jpg);">
          @endif

            <
    div class="dummy"></div><div class="element"></div>
          </
    div>
        </
    div>

        <
    div class="col-md-8">
          <
    h2>specification product</h2>

          <
    span class="grey">manufacturer: </span>{{ $product->manufacturer }}<br>
          <
    span class="grey">materials: </span>{{ $product->materials }}<br>
          <
    span class="grey">year_manufacture: </span>{{ $product->year_manufacture }}<br>
          <
    span class="grey">артикул: </span>{{ $product->id }}<br>

          @if(
    $product->price)
            <
    span class="grey">price: </span>{{ $product->price }} &#8381;
          
    @else
            <
    span class="grey">priceless</span>
          @endif

          <
    div class="row product_buttons">

            <
    div class="col-sm-4">
              <
    a href="#" class="btn btn-outline-primary">
                <
    class="fas fa-shopping-cart"></ibuy now
              
    </a>
            </
    div>

            <
    div class="col-sm-4">
              <
    a href="{{ route('productsEdit', ['product' => $product->id]) }}" class="btn btn-outline-success">
                <
    class="fas fa-pen-nib"></iedit
              
    </a>
            </
    div>

            <
    div class="col-sm-4">
              <!-- 
    form delete product -->
              <
    form action="{{ route('productsDestroy', ['product' => $product->id]) }}" method='POST'>
                @
    csrf

                
    @method('DELETE')

                <
    button type="submit" class="btn btn-outline-danger">
                  <
    class="fas fa-trash"></idelete
                
    </button>
              </
    form>
            </
    div>

          </
    div>

          
        </
    div>
      </
    div><br>

      <
    div class="row">
        <
    div class="col-md-12">
          <
    h2>description {{ $product->name }}</h2>
          <
    p>{{ $product->description }}</p>
        </
    div>
      </
    div>

      <
    div class="row">
        <
    div class="col-md-12">
          <
    h2>comments for {{ $product->name }}</h2>

          @if(
    $product->comments->count())
            <
    ul class='content list-group'>

            @foreach (
    $product->comments as $comment)
              <
    li class="list-group-item" id="comment_{{ $comment->id }}" >
                <
    div class="comment_header">

                  @if(
    $comment->guest_name)
                    
    Guest {{ $comment->guest_name }}
                  @else
                    
    User #{{ $comment->user_id }}
                  
    @endif


                  <!-- 
    created_at/updated_at -->
                  @if(
    $comment->updated_at == $comment->created_at)
                    
    wrote {{ $comment->created_at }}:
                  @else
                    
    wrote {{ $comment->created_at }} (edited: {{ $comment->updated_at }}):
                  @endif

                  <
    div class="comment_buttons">

                    <
    div class="comment_num">#{{ $comment->id }}</div>

                    
    <!-- button edit -->
                    <
    button type="button" class="btn btn-outline-success" data-toggle="collapse" 
                      
    data-target="#collapse_{{ $comment->id }}" aria-expanded="false" aria-controls="coll" class="edit"
                    
    >
                      <
    class="fas fa-pen-nib"></i>
                    </
    button>

                    <!-- 
    delete comment -->
                    <!-- <
    form action='/comments/destroy/{{ $comment->id }}' method='POST'> -->
                    <
    form action="{{ route('commentsDestroy', ['comment' => $comment->id]) }}" method="POST">
                      @
    csrf

                      
    @method('DELETE')

                      <
    button type="submit" class="btn btn-outline-danger"><class="fas fa-trash"></i></button>
                    </
    form>
                  </
    div>

                </
    div>

                <
    div class="comment_str">{{$comment->comment_string}}</div>
                    
                <!-- 
    form edit -->
                <
    form action="/comments/{{ $comment->id }}" 
                  
    method="POST" class="collapse" id="collapse_{{ $comment->id }}"
                
    >
                  @
    method('PATCH')

                  @
    csrf

                  
    <textarea id="comment_string_{{ $comment->id }}" name="comment_string" 
                    
    cols="30" rows="4" class="form-control card" placeholder="Add a comment"
                  
    >{{$comment->comment_string}}</textarea>
                  <
    button type="submit" class="btn btn-success">edit comment</button>
                </
    form>

              </
    li>
            @endforeach

            </
    ul>
          @else
            <
    class="grey">no comments for this product.</p>
          @endif

        </
    div>
      </
    div>


      <
    div class="row">
        <
    div class="col-md-12">

          <
    h2>leave your comment</h2>

          <
    form method="POST" action="/products/{{ $product->id }}/comments">
            @
    csrf
            
    <!-- <input type="hidden" name="product_id" value="{{ $product->id }}"> -->

            @
    auth
              
    <input type="hidden" name="user_id" value="{{ Auth::user()->id }}">
              <
    input type="hidden" name="guest_name" value="">
            @else
              <
    input type="hidden" name="user_id" value="0">
              <
    div class="form-group">
                <!-- <
    label for="guest_name">Your name</label> -->
                <
    input type="text" id="guest_name" name="guest_name" class="form-control" placeholder="Your name" value="{{ old('guestName') }}" required>
              </
    div>
            @
    endauth

            
    <div class="form-group">
              <!-- <
    label for="comment_string">Add a comment</label> -->
              <
    textarea id="comment_string" name="comment_string" cols="30" rows="4" class="form-control" placeholder="Add a your comment" required>{{ old('comment_string') }}</textarea>             
            </
    div>
            <
    button type="submit" class="btn btn-primary">comment on</button>
          </
    form>

        </
    div>
      </
    div>

    </
    div>
    @
    endsection

    Изменим существующие и добавим роуты для комментариев:

    routes/web.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    ...

    /* products*/
    Route::get('/products''ProductsController@index')->name('products');
    Route::get('/products/create''ProductsController@create')->name('productsCreate');
    Route::get('/products/{product}''ProductsController@show')->name('productsShow');
    Route::post('/products''ProductsController@store')->name('productsStore');
    Route::get('/products/edit/{product}''ProductsController@edit')->name('productsEdit');
    Route::patch('/products/{product}''ProductsController@update')->name('productsUpdate');
    Route::delete('/products/{product}''ProductsController@destroy')->name('productsDestroy');

    /* comments*/
    Route::get('/comments''ProductCommentsController@index');
    Route::get('/comments/create''ProductCommentsController@create');
    Route::get('/comments/{comment}''ProductCommentsController@show');
    // Route::post('/comments', 'ProductCommentsController@store');
    Route::post('/products/{product}/comments''ProductCommentsController@store');
    Route::get('/comments/edit/{comment}''ProductCommentsController@edit');
    Route::patch('/comments/{comment}''ProductCommentsController@update');
    // Route::delete('/comments/destroy/{comment}', 'ProductCommentsController@destroy');
    Route::delete('/comments/{comment}''ProductCommentsController@destroy')->name('commentsDestroy');

    Добавим методы storeImage() и update(), изменим return, используя именованные маршруты и допишем удаление комментариев к удаляемому товару.

    app/Http/Controllers/ProductsController.php:
    001
    002
    003
    004
    005
    006
    007
    008
    009
    010
    011
    012
    013
    014
    015
    016
    017
    018
    019
    020
    021
    022
    023
    024
    025
    026
    027
    028
    029
    030
    031
    032
    033
    034
    035
    036
    037
    038
    039
    040
    041
    042
    043
    044
    045
    046
    047
    048
    049
    050
    051
    052
    053
    054
    055
    056
    057
    058
    059
    060
    061
    062
    063
    064
    065
    066
    067
    068
    069
    070
    071
    072
    073
    074
    075
    076
    077
    078
    079
    080
    081
    082
    083
    084
    085
    086
    087
    088
    089
    090
    091
    092
    093
    094
    095
    096
    097
    098
    099
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197

    <?php

    namespace App\Http\Controllers;

    use 
    Illuminate\Http\Request;
    use 
    Illuminate\Support\Facades\DB;
    use 
    Illuminate\Support\Facades\Validator;
    use 
    Illuminate\Support\Facades\Storage;

    use 
    App\Product;

    class 
    ProductsController extends Controller
    {
      
    /**
       * Display a listing of the resource.
       *
       * @return \Illuminate\Http\Response
       */
      
    public function index() {
        
    $products DB::table('products')->orderBy('id''desc')->simplePaginate(6);
        return 
    view('products.index'compact('products'));
      }

      
    /**
       * Display the specified resource.
       *
       * @param  int  $id
       * @return \Illuminate\Http\Response
       */
      
    public function show($id) {
        
    $product Product::find($id);
        return 
    view('products.show'compact('product'));
      }

      
    /**
       * Show the form for creating a new resource.
       *
       * @return \Illuminate\Http\Response
       */
      
    public function create()
      {
        return 
    view('products.create');
      }

      
    /**
       * Show the form for creating a new resource.
       *
       * @return \Illuminate\Http\Response
       */
      
    public function edit(Product $product)
      {
        return 
    view('products.edit'compact('product'));
      }

      
    /**
       * Store a newly created resource in storage.
       *
       * @param  \Illuminate\Http\Request  $request
       * @return \Illuminate\Http\Response
       */
      
    public function store(Request $request)
      {
        
    $validator Validator::make($request->all(), [
          
    'name' => 'required|max:255',
          
    'manufacturer' => 'nullable|string',
          
    'materials' => 'nullable|string',
          
    'description' => 'nullable|string',
          
    'image' => 'image',
          
    'year_manufacture' => 'nullable|integer',
          
    'price' => 'nullable|integer',
          
    'added_by_user_id' => 'required|integer',
        ]);

        if (
    $validator->fails()) {
          return 
    back()->withErrors($validator)->withInput();
        }
        
        
    $product = new Product;

        
    $product->name $request->get('name');
        
    $product->manufacturer $request->get('manufacturer') ?? '';
        
    $product->materials $request->get('materials') ?? '';
        
    $product->description $request->get('description') ?? '';
        
    $product->year_manufacture $request->get('year_manufacture') ?? 0;
        
    $product->price $request->get('price') ?? 0;
        
    $product->added_by_user_id $request->get('added_by_user_id');    

        if (!
    $product->save()) {
          return 
    back()->withErrors(['something wrong!'])->withInput();
        }

        if (
    request()->file('image')) {

          
    $product->image $this->storeImage(request()->file('image'), $product->id);

          if (!
    $product->image or !$product->update()) {
            return 
    back()->withErrors(['something wrong. err' __line__])->withInput();
          }
        }

        return 
    redirect()->route('productsShow', ['product' => $product->id]);
      }

      
    /**
       * Update the specified resource in storage.
       *
       * @param  \Illuminate\Http\Request  $request
       * @param  int  $id
       * @return \Illuminate\Http\Response
       */
      
    public function update(Product $product)
      {
        
    $validator Validator::make(request()->all(), [
          
    'name' => 'required|max:255',
          
    'manufacturer' => 'nullable|string',
          
    'materials' => 'nullable|string',
          
    'description' => 'nullable|string',
          
    'image' => 'image',
          
    'year_manufacture' => 'nullable|integer',
          
    'price' => 'nullable|integer',
          
    'edited_by_user_id' => 'required|integer',
        ]);

        if (
    $validator->fails()) {
          return 
    back()->withErrors($validator)->withInput();
        }

        
    $product->update([
          
    'name' => request('name'),
          
    'manufacturer' => request('manufacturer'),
          
    'materials' => request('materials'),
          
    'description' => request('description'),
          
    'year_manufacture' => request('year_manufacture'),
          
    'edited_by_user_id' => request('edited_by_user_id'),
        ]);


        if (
    request()->file('image')) {

          
    $product->image $this->storeImage(request()->file('image'), $product->id);

          if (!
    $product->image or !$product->update()) {
            return 
    back()->withErrors(['something wrong. err' __line__])->withInput();
          }
        }

        return 
    redirect()->route('productsShow', ['product' => $product->id]);
      }

      
    /**
       * Remove the specified resource from storage.
       *
       * @param  int  $id
       * @return \Illuminate\Http\Response
       */
      
    public function destroy($id)
      {
        
    $product Product::find($id);

        
    // destroy product images
        
    if ($product->image) {
          
    $directory 'public/images/products/' $product->id;
          
    Storage::deleteDirectory($directory);
        }

        
    // destroy product comments
        
    $product->comments()->delete();

        
    // destroy product
        
    $product->delete();

        return 
    redirect()->route('products');
      }

      
    /**
       * Store image product.
       *
       * @param  \Illuminate\Http\Request  $request
       * @return string $filename or false
       */
      
    private function storeImage($image$product_id) {

        
    $directory 'public/images/products/' $product_id;
        
    $filename $image->getClientOriginalName();

        if (
    Storage::makeDirectory($directory)) {
          if (
    Storage::putFileAs($directory$image$filename)) {
            return 
    $filename;
          }
        }

        return 
    FALSE;
      }

    }

    Добавим поле 'edited_by_user_id' в таблицу 'products':

    database/migrations/2019_05_17_163750_create_products_table.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18

    ...

    Schema::create('products', function (Blueprint $table) {
      
    $table->bigIncrements('id');
      
    $table->string('name');
      
    $table->string('manufacturer')->nullable();
      
    $table->string('materials')->nullable();
      
    $table->text('description')->nullable();
      
    $table->string('image')->nullable()->charset('utf8');
      
    $table->integer('year_manufacture')->nullable();
      
    $table->float('price'82)->nullable();
      
    $table->unsignedInteger('added_by_user_id');
      
    $table->unsignedInteger('edited_by_user_id')->nullable(); // upd
      
    $table->timestamps();
    });
    ...

    и опишем файлы отображения для создания и редактирования товаров:

    resources/views/products/create.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71

    @extends('layouts.app')

    @
    section('title')
    Creating new product
    @endsection

    @section('content')
    <
    div class="container">

      <
    div class="row justify-content-center">
        <
    h1>Creating new product</h1>
      </
    div>

      <
    div class="row">

        <
    div class="col-sm-12 product_card_bm">
          <
    div class="card">
            <
    form method="POST" action="{{ route('productsStore') }}" enctype="multipart/form-data">
              @
    csrf

              
    <input type="hidden" name="added_by_user_id" value="0">

              <
    div class="form-group">
                <!-- <
    input type="file" id="image" name="image" accept="image/png, image/jpeg, jpg, pdf"> -->
                <
    input type="file" name="image" accept=".jpg, .jpeg, .png" value="{{ old('image') }}">
              </
    div>

              <
    div class="form-group">
                <!-- <
    label for="name">name</label> -->
                <
    input type="text" id="name" name="name" class="form-control" placeholder="Name Product" value="{{ old('name') }}" required>
              </
    div>

              <
    div class="form-group">
                <!-- <
    label for="manufacturer">manufacturer</label> -->
                <
    input type="text" id="manufacturer" name="manufacturer" class="form-control" placeholder="manufacturer" value="{{ old('manufacturer') }}">
              </
    div>

              <
    div class="form-group">
                <!-- <
    label for="materials">materials</label> -->
                <
    input type="text" id="materials" name="materials" class="form-control" placeholder="materials" value="{{ old('materials') }}">
              </
    div>

              <
    div class="form-group">
                <!-- <
    label for="year_manufacture">year_manufacture</label> -->
                <
    input type="number" id="year_manufacture" name="year_manufacture" class="form-control"  placeholder="year_manufacture" value="{{ old('year_manufacture') }}">
              </
    div>

              <
    div class="form-group">
                <!-- <
    label for="price">price</label> -->
                <
    input type="number" id="price" name="price" class="form-control" placeholder="price" value="{{ old('price') }}">
              </
    div>

              <!-- <
    input type="hidden" name="added_by_user_id" value=""> -->

              <
    div class="form-group">
                <!-- <
    label for="description">Add a comment</label> -->
                <
    textarea id="description" name="description" cols="30" rows="10" class="form-control" placeholder="description">{{ old('description') }}</textarea>             
              </
    div>

              <
    button type="submit" class="btn btn-primary form-control">Create new product!</button>

            </
    form>
          </
    div>
        </
    div>


      </
    div>
    </
    div>
    @
    endsection
    resources/views/products/edit.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95

    @extends('layouts.app')

    @
    section('title')
    Creating new product
    @endsection

    @section('content')
    <
    div class="container">

      <
    div class="row justify-content-center">
        <
    h1>edit {{ $product->name }}</h1>
      </
    div>

      <
    div class="row">

        <
    div class="col-sm-12 product_card_bm">
          <
    div class="card">
            <
    form method="POST" action="{{ route('productsUpdate', ['product' => $product->id]) }}" enctype="multipart/form-data">
              @
    csrf

              
    @method('PATCH')

              <
    input type="hidden" name="edited_by_user_id" value="0">

              @if(
    $product->image)

                <
    div class="card-img-top b_image col-sm-4" style="background-image: url({{ asset('storage') }}/images/products/{{$product->id}}/{{$product->image}});">
                  <
    div class="dummy"></div><div class="element"></div>
                </
    div>

                <
    div class="form-group"заменить изображение
                  
    <input type="file" name="image" accept=".jpg, .jpeg, .png"
                    
    value="{{ old('image') }}">
                </
    div>

              @else

                <
    div class="form-group">
                  <
    input type="file" name="image" accept=".jpg, .jpeg, .png"
                    
    value="{{ old('image') }}">
                </
    div>

              @endif

              <
    div class="form-group">
                <!-- <
    label for="name">name</label> -->
                <
    input type="text" id="name" name="name" class="form-control" placeholder="Name Product"
                  
    value="{{ old('name') ?? $product->name }}" required>
              </
    div>


              <
    div class="form-group">
                <!-- <
    label for="manufacturer">manufacturer</label> -->
                <
    input type="text" id="manufacturer" name="manufacturer" class="form-control"
                  
    placeholder="manufacturer" value="{{ old('manufacturer') ?? $product->manufacturer }}">
              </
    div>

              <
    div class="form-group">
                <!-- <
    label for="materials">materials</label> -->
                <
    input type="text" id="materials" name="materials" class="form-control"
                  
    placeholder="materials" value="{{ old('materials') ?? $product->materials }}">
              </
    div>

              <
    div class="form-group">
                <!-- <
    label for="year_manufacture">year_manufacture</label> -->
                <
    input type="number" id="year_manufacture" name="year_manufacture" class="form-control"
                  
    placeholder="year_manufacture" value="{{ old('year_manufacture') ?? $product->year_manufacture }}">
              </
    div>

              <
    div class="form-group">
                <!-- <
    label for="price">price</label> -->
                <
    input type="number" id="price" name="price" class="form-control" placeholder="price"
                  
    value="{{ old('price') ?? $product->price }}">
              </
    div>

              <!-- <
    input type="hidden" name="added_by_user_id" value=""> -->

              <
    div class="form-group">
                <!-- <
    label for="description">Add a comment</label> -->
                <
    textarea id="description" name="description" cols="30" rows="10" class="form-control"
                  
    placeholder="description">{{ old('description') ?? $product->description }}</textarea>             
              </
    div>

              <
    button type="submit" class="btn btn-primary form-control">edit  product!</button>

            </
    form>
          </
    div>
        </
    div>


      </
    div>
    </
    div>
    @
    endsection

    Слегка подправим файлы отображения и стили; подключим fontawesome.

    resources/views/layouts/app.blade.php:
    001
    002
    003
    004
    005
    006
    007
    008
    009
    010
    011
    012
    013
    014
    015
    016
    017
    018
    019
    020
    021
    022
    023
    024
    025
    026
    027
    028
    029
    030
    031
    032
    033
    034
    035
    036
    037
    038
    039
    040
    041
    042
    043
    044
    045
    046
    047
    048
    049
    050
    051
    052
    053
    054
    055
    056
    057
    058
    059
    060
    061
    062
    063
    064
    065
    066
    067
    068
    069
    070
    071
    072
    073
    074
    075
    076
    077
    078
    079
    080
    081
    082
    083
    084
    085
    086
    087
    088
    089
    090
    091
    092
    093
    094
    095
    096
    097
    098
    099
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115

    <!DOCTYPE html>
    <
    html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <
    head>
      <
    meta charset="utf-8">
      <
    meta name="viewport" content="width=device-width, initial-scale=1">

      <!-- 
    CSRF Token -->
      <
    meta name="csrf-token" content="{{ csrf_token() }}">

      <
    title>@yield('title') {{ config('app.name''Laravel') }}</title>

      <!-- 
    Scripts -->
      <
    script src="{{ asset('js/app.js') }}" defer></script>

      <!-- 
    Fonts -->
      <
    link rel="dns-prefetch" href="//fonts.gstatic.com">
      <
    link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">
      <
    link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
      <!-- 
    Styles -->
      <
    link href="{{ asset('css/app.css') }}" rel="stylesheet">
      <
    link href="{{ asset('css/yo.css') }}" rel="stylesheet">

      <!-- 
    Favicon -->
      <
    link rel="icon" type="image/png" sizes="128x128" href="/favicon.png">
    </
    head>
    <
    body>
      <
    div id="app">
        <
    nav class="navbar navbar-expand-md navbar-dark">
          <
    div class="container">
            <
    class="navbar-brand grey" href="{{ url('/') }}">
              {{ 
    config('app.name''Laravel') }}
            </
    a>
            <
    button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
              <
    span class="navbar-toggler-icon"></span>
            </
    button>

            <
    div class="collapse navbar-collapse" id="navbarSupportedContent">
              <!-- 
    Left Side Of Navbar -->
              <
    ul class="navbar-nav mr-auto">

              </
    ul>

              <!-- 
    Right Side Of Navbar -->
              <
    ul class="navbar-nav ml-auto">
                <!-- 
    Authentication Links -->
                @
    guest
                  
    <li class="nav-item">
                    <
    class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                  </
    li>
                  @if (
    Route::has('register'))
                    <
    li class="nav-item">
                      <
    class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                    </
    li>
                  @endif
                @else
                  <
    li class="nav-item dropdown">
                    <
    a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                      {{ 
    Auth::user()->name }} <span class="caret"></span>
                    </
    a>

                    <
    div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                      <
    class="dropdown-item" href="{{ route('logout') }}"
                         
    onclick="event.preventDefault();
                               document.getElementById('logout-form').submit();"
    >
                        {{ 
    __('Logout') }}
                      </
    a>

                      <
    form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                        @
    csrf
                      
    </form>
                    </
    div>
                  </
    li>
                @
    endguest
              
    </ul>
            </
    div>
          </
    div>
        </
    nav>

        @include(
    'menu.main')

        @if (
    $errors->any())
          <
    div class="container">        
            <
    div class="alert alert-danger alert-dismissible fade show" role="alert">
              <
    strong>Holy guacamole!</strongYou should check in on some of those fields below.
              <
    ol>
              @foreach (
    $errors->all() as $error)
                <
    li>{{ $error }}</li>
              @endforeach
              </
    ol>
              <
    button type="button" class="close" data-dismiss="alert" aria-label="Close">
                <
    span aria-hidden="true">&times;</span>
              </
    button>
            </
    div>
          </
    div>
        @endif

        <
    main class="py-4">
          @yield(
    'content')
        </
    main>

        @include(
    'menu.main')

        <
    div class="container">
          <
    div class="footer">
            <
    a aria-label="Homepage" title="GitHub" class="footer-octicon d-none d-lg-block mx-lg-4" href="https://github.com/yakoffka/kk">
              <
    svg height="24" class="octicon octicon-mark-github" viewBox="0 0 16 16" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path></svg>
            </
    a>
          </
    div>
        </
    div>
      </
    div>
      
    </
    body>
    </
    html>
    resources/views/products/index.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94

    @extends('layouts.app')

    @
    section('title')
    catalog
    @endsection

    @section('content')
    <
    div class="container">

      <
    h1>Products</h1>

      <
    div class="row">

        <!-- 
    pagination block -->
        @if(
    $products->links())
          <
    div class="row col-sm-12 pagination">{{ $products->links() }}</div>
        @endif

        @foreach(
    $products as $product)

        <
    div class="col-lg-4 product_card_bm">
          <
    div class="card">

            <
    h5><a href="{{ route('productsShow', ['product' => $product->id]) }}">{{ $product->name }}</a></h5>

            <
    a href="{{ route('productsShow', ['product' => $product->id]) }}">

              @if(
    $product->image)
              <
    div class="card-img-top b_image" style="background-image: url({{ asset('storage') }}/images/products/{{$product->id}}/{{$product->image}});">
              @else
              <
    div class="card-img-top b_image" style="background-image: url({{ asset('storage') }}/images/default/no-img.jpg);">
              @endif

                <
    div class="dummy"></div><div class="element"></div>
              </
    div>

            </
    a>

            <
    div class="card-body">
              <
    class="card-text col-sm-12">
                <
    span class="grey">
                  @if(
    $product->price)
                    
    price: {{ $product->price }} &#8381;
                  
    @else
                    
    priceless
                  
    @endif
                </
    span><br>
              </
    p>

              <
    div class="row product_buttons">

                <
    div class="col-sm-4">
                  <
    a href="{{ route('productsShow', ['product' => $product->id]) }}" class="btn btn-outline-primary">
                    <
    class="fas fa-eye"></i>
                  </
    a>
                </
    div>

                <
    div class="col-sm-4">
                  <
    a href="{{ route('productsEdit', ['product' => $product->id]) }}" class="btn btn-outline-success">
                    <
    class="fas fa-pen-nib"></i>
                  </
    a>
                </
    div>

                <
    div class="col-sm-4">
                  <!-- 
    form delete product -->
                  <
    form action="{{ route('productsDestroy', ['product' => $product->id]) }}" method='POST'>
                    @
    csrf

                    
    @method('DELETE')

                    <
    button type="submit" class="btn btn-outline-danger">
                    <
    class="fas fa-trash"></i>
                    </
    button>
                  </
    form>
                </
    div>

              </
    div>
              
            </
    div>
          </
    div>
        </
    div>

        @endforeach

        <!-- 
    pagination block -->
        @if(
    $products->links())
          <
    div class="row col-sm-12 pagination">{{ $products->links() }}</div>
        @endif

      </
    div>
    </
    div>
    @
    endsection
    public/css/yo.css:
    001
    002
    003
    004
    005
    006
    007
    008
    009
    010
    011
    012
    013
    014
    015
    016
    017
    018
    019
    020
    021
    022
    023
    024
    025
    026
    027
    028
    029
    030
    031
    032
    033
    034
    035
    036
    037
    038
    039
    040
    041
    042
    043
    044
    045
    046
    047
    048
    049
    050
    051
    052
    053
    054
    055
    056
    057
    058
    059
    060
    061
    062
    063
    064
    065
    066
    067
    068
    069
    070
    071
    072
    073
    074
    075
    076
    077
    078
    079
    080
    081
    082
    083
    084
    085
    086
    087
    088
    089
    090
    091
    092
    093
    094
    095
    096
    097
    098
    099
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204


    h1
    {
      
    text-align:center;
      
    color#636b6f;
    }
    h2{
      
    color#09c;
    }
    .
    center{
      
    text-align:center;
    }

    .
    grey {
      
    color#636b6f;
    }

    .
    row {
      
    margin:1em 0;
    }


    /* /app.blade */
    nav.navbar.navbar-dark {
      
    background-color#09c;
    }
    a.navbar-brand.grey, .navbar-dark .navbar-nav .nav-link{
      
    /* color: rgba(0,0,0,.7); */
      
    colorhsla(0,0%,100%,.75);
      
    font-size.9rem;
    }
    a.navbar-brand.grey:hover, .navbar-dark .navbar-nav .nav-link:hover{
      
    /* color: rgba(0,0,0,.7); */
      
    colorhsla(0,0%,100%,.95);
      
    font-size.9rem;
    }
    /* /app.blade */


    /* main menu */
    .main_menu {
      
    text-aligncenter;
      
    margin1em 0;
    }
    .
    main_menu {
      
    color#636b6f;
      
    padding0 25px;
      
    font-size13px;
      
    font-weight600;
      
    letter-spacing.1rem;
      
    text-decorationnone;
      
    text-transformuppercase;
    }
    /* /main menu */


    /* products */

    .card .row.product_buttons .col-sm-4{
      
    padding2%;
    }

    .
    product_card_bm {
      
    margin-bottom2em;
    }
    .
    product_card_bm .card{
      
    padding1em;
      
    min-height5em;
    }
    .
    product_card_bm .card h5{
      
    text-align:center;
    }
    .
    product_card_bm .card h5 a:hover {
      
    color#3490dc;
      
    text-decorationnone;
    }

    .
    pagination {
      
    padding0 1em;
    }

    .
    row.product_buttons a.btn,
    .
    row.product_buttons form button
    {
      
    width100%;
    }

    /* /products */


    /* product */
    .card img.img_lr {
      
    width:auto;
      
    max-height:280px;
    }

    .
    row.product_buttons .col-sm-4{
      
    padding-left0;
    }
    .
    card .product_buttons a.btn,
    .
    card .product_buttons form button
    {
      
    width100%;
      
    padding-left0;
      
    padding-right0;
    }

    .
    comment_str {
      
    padding-top.75rem;
    }
    .
    comment_header {
      
    positionrelative;
      
    background-color#e1e4e8;
      
    margin: -.75rem -1.25rem 0;
      
    padding.75rem 1.25rem;
    }
      .
    comment_header .comment_buttons {
        
    positionabsolute;
        
    right0;
        
    top0;
        
    margin.26em 0;
      }
      .
    comment_header .comment_buttons button,
      .
    comment_header .comment_buttons form,
      .
    comment_header .comment_buttons .comment_num
      
    {
        
    floatleft;
        
    margin0 .25em;
        
    /* background-color: #fff; */
      
    }
      .
    comment_header .comment_buttons form {
        
    margin-left0;
        
    margin-right0;
      }
      .
    comment_header .comment_buttons .comment_num {
        
    padding.375rem 0rem;
        
    font-size.9rem;
        
    line-height1.6;
      }
      .
    comment_header .comment_buttons button{
        
    background-color#fff;
      
    }
        .
    comment_header .comment_buttons button.btn-outline-info:hover {
          
    color#212529;
          
    background-color#6cb2eb;
          
    border-color#6cb2eb;
        
    }
        .
    comment_header .comment_buttons button.btn-outline-danger:hover {
          
    color#fff;
          
    background-color#e3342f;
          
    border-color#e3342f;
        
    }
      
    form.collapse textarea {
        
    margin-bottom.25rem;
      }

    .
    wrap_b_image {
      
    background-color#fff;
      
    border1px solid rgba(0,0,0,.125);
      
    border-radius.25rem;
      
    padding:1em;
    }
    /* /product */


    /* b_image https://htmler.ru/2016/06/07/css-height-dynamic-width/*/
    .b_image{
      
    width:100%;
      
    background-repeat:no-repeat;
      
    background-position:center center;
      
    background-size:contain;
      
    displayinline-block;
      
    positionrelative;
    }
      .
    b_image .dummy {
        
    margin-top100%; /* соотношение сторон*/
      
    }
      .
    b_image .element {
        
    positionabsolute;
        
    top0;
        
    bottom0;
        
    left0;
        
    right0;
      }
    /* b_image */


    /* footer */
    .footer {
      
    margin1em 0;
      
    padding1em 0;
      
    color#e1e4e8;
      
    border-top1px solid #e1e4e8 !important;
      
    text-aligncenter;
    }
      .
    footer .footer-octicon {
        
    color#c6cbd1;
      
    }
        .
    footer .footer-octicon .octicon {
          
    displayinline-block;
          
    fillcurrentColor;
        }
    /* /footer */

    перестроим базу данных.

    bash:
    vagrant@homestead:~/projects/kk$ php artisan migrate:refresh --seed Rolling back: 2019_05_20_010314_create_comments_table Rolled back: 2019_05_20_010314_create_comments_table Rolling back: 2019_05_17_163750_create_products_table Rolled back: 2019_05_17_163750_create_products_table Rolling back: 2014_10_12_100000_create_password_resets_table Rolled back: 2014_10_12_100000_create_password_resets_table Rolling back: 2014_10_12_000000_create_users_table Rolled back: 2014_10_12_000000_create_users_table Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table Migrating: 2019_05_17_163750_create_products_table Migrated: 2019_05_17_163750_create_products_table Migrating: 2019_05_20_010314_create_comments_table Migrated: 2019_05_20_010314_create_comments_table Seeding: ProductsTableSeeder Seeding: UsersTableSeeder Database seeding completed successfully. vagrant@homestead:~/projects/kk$

    Напишем три комментария (два к товару с id=24).

    Рис. 1 Вывод комментариев до удаления товара с id=24
    Рис. 1 Вывод комментариев до удаления товара с id=24

    Удалим товар с id=24 и проверим результат.

    Рис. 2 Вывод комментариев после удаления товара с id=24
    Рис. 2 Вывод комментариев после удаления товара с id=24
    Рис. 3 index
    Рис. 3 index
    Рис. 4 show
    Рис. 4 show

    Исходники можно скачать здесь.