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

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

    laravel.

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

    оглавление

    Исправим допущенную ранее неточность в файле миграции:

    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
    19
    20

    ...

    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->integer('image_id')->nullable();
      
    $table->string('image')->nullable(); // upd!
      
    $table->integer('year_manufacture')->nullable();
      
    $table->float('price'82)->nullable();
      
    $table->integer('added_by_user_id');
      
    $table->integer('edited_by_user_id')->nullable(); // upd!
      
    $table->timestamps();
    });

    ...

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

    bash:
    vagrant@homestead:~/projects/kk$ php artisan migrate:refresh --seed 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 Seeding: ProductsTableSeeder Database seeding completed successfully.

    По умолчанию диск public использует драйвер local и хранит файлы в storage/app/public. Чтобы сделать их доступными через веб, необходимо создать символьную ссылку из public/storage на storage/app/public.

    bash:
    vagrant@homestead:~/projects/kk$ php artisan storage:link The [public/storage] directory has been linked. vagrant@homestead:~/projects/kk$

    Теперь к файлу storage/app/public/img.png в файлах *.blade.php можно обратиться через следующую конструкцию: '{{ asset('storage') }}/img.png'.

    Изменим метод index() в контроллере, добавив в него сортировку в обратном порядке и простую пагинацию:

    app/Http/Controllers/ProductsController.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14

    ...
    use 
    Illuminate\Support\Facades\DB;
    ...

    public function 
    index() {
      
    // $products = Product::all();
      // return view('products.index', compact('products'));
      
    $products DB::table('products')->orderBy('id''desc')->simplePaginate(6);
      return 
    view('products.index'compact('products'));
    }

    ...

    Добавим в вид блок пагинации и немного подробностей в карточку товара.

    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

    ...

    @foreach(
    $products as $product)

    <!-- <
    div class="col-sm-4 product_card_bm">
      <
    div class="card">
        <
    h5><a href="/products/{{ $product->id }}">{{ $product->name }}</a></h5>
      </
    div>
    </
    div> -->

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

        <
    h5><a href="/products/{{ $product->id }}">{{ $product->name }}</a></h5>

        <
    div class="center">
          <
    a href="products/{{ $product->id }}" >
            @if(
    $product->image_id)
              <
    img class="card-img-top img_lr" src="{{ asset('storage') }}/images/products/{{$product->id}}/{{$product->image_id}}" alt="{{ $product->name }}" style="">
            @else
              <
    img class="card-img-top img_lr" src="{{ asset('storage') }}/images/default/no-img.jpg" alt="no image">
            @endif
          </
    a>
        </
    div>

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

          <
    a href="products/{{ $product->id }}" class="btn btn-outline-primary">description</a>
          <
    a href="products/destroy/{{ $product->id }}" class="btn btn-outline-danger">destroy</a>
      
        </
    div>
      </
    div>
    </
    div>

    @endforeach

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

    ...

    Для вывода страницы товара создаём роут, описываем метод show() в контроллере, добавляем файл отображения и ссылку на страницу товара:

    routes/web.php:
    1
    2
    3
    4

    ...
    Route::get('/products/{product}''ProductsController@show');
    app/Http/Controllers/ProductsController.php:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ...

    public function 
    show($id) {
      
    $product Product::find($id);
      return 
    view('products.show'compact('product'));
    }

    ...
    resources/views/products/show.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

    @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">

          @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</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
          
          <
    br><br>
          <
    button type="button" class="btn btn-outline-success">buy now</button>
          <
    a href="products/destroy/{{ $product->id }}" class="btn btn-outline-danger">destroy</a>
          <
    br>

        </
    div>
      </
    div><br>

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

    </
    div>
    @
    endsection
    resources/views/products/index.blade.php:
    1
    2
    3
    4
    5

    ...
    <
    h5><a href="/products/{{ $product->id }}">{{ $product->name }}</a></h5>
    ...

    Приступим к созданию заготовки админки. Добавляем методы create(), update(), store() и destroy() в ProductsController (upd: в 3 части все методы изменятся).

    app/Http/Controllers/ProductsController.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

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

    /**
     * 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')) {
        
        
    $directory 'public/images/products/' $product->id;
        
    Storage::makeDirectory($directory);

        
    $file $request->file('image');
        
    $filename $file->getClientOriginalName();

        
    Storage::putFileAs($directory$file$filename);

        
    $product->image $filename;

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

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

    /**
     * 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

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

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

    добавляем роуты (upd: в 3 части они тоже изменятся):

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

    ...

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

    и создаём файл отображения:

    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="/products" 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/layouts/app.blade.php:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    ...
    @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
    ...
    Рис. 1 Пагинация на странице отображения товаров
    Рис. 1 Пагинация на странице отображения товаров
    Рис. 2 Страница товара
    Рис. 2 Страница товара
    Рис. 3 Вывод ошибок пользователю
    Рис. 3 Вывод ошибок пользователю

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