Создание интернет-витрины на laravel. Часть 2
.
Капустин Яков
оглавление
- 01 Изменения, касающиеся предыдущей части
- 02 Создание символьной ссылки на storage
- 03 Пагинация
- 04 Отображение страницы товара
- 05 Страница создания нового товара
- 06 Отображение ошибок
- 07 Результат
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', 8, 2)->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.
02Создание символьной ссылки на storage
По умолчанию диск 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'.
03Пагинация
Изменим метод index() в контроллере, добавив в него сортировку в обратном порядке и простую пагинацию:
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'));
}
...
Добавим в вид блок пагинации и немного подробностей в карточку товара.
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">
<p class="card-text">
<span class="grey">
@if($product->price)
price: {{ $product->price }} ₽
@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
...
04Отображение страницы товара
Для вывода страницы товара создаём роут, описываем метод show() в контроллере, добавляем файл отображения и ссылку на страницу товара:
2
3
4
...
Route::get('/products/{product}', 'ProductsController@show');
2
3
4
5
6
7
8
9
10
...
public function show($id) {
$product = Product::find($id);
return view('products.show', compact('product'));
}
...
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 }} ₽
@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
2
3
4
5
...
<h5><a href="/products/{{ $product->id }}">{{ $product->name }}</a></h5>
...
05Страница создания нового товара
Приступим к созданию заготовки админки. Добавляем методы create(), update(), store() и destroy() в ProductsController (upd: в 3 части все методы изменятся).
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 части они тоже изменятся):
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');
и создаём файл отображения:
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
06Отображение ошибок
Для вывода пользователю сообщений о возникающих в контроллере ошибках добавим следующий блок кода:
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!</strong> You 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">×</span>
</button>
</div>
</div>
@endif
...
Капустин Яков (2019.05.19 22:31)