Step 1: tạo ánh xạ thư mục lưu trữ storage
với thư mục public
Do cơ chế bảo mật của Laravel, người dùng (End user) chỉ được phép truy cập vào thư mục public
, trong khi các tập file (files) được người dùng upload lên sẽ được lưu trữ trong thư mục storage
. Do đó, để người dùng (End user) có thể truy cập (hiển thị hình ảnh, download tập tin, ...) chúng ta cần ánh xạ giữa thư mục storage
và thư mục public
Thực thi câu lệnh sau:
php artisan storage:linkSau khi thực thi, mặc định Laravel sẽ ánh xạ thư mục sau:
storage/app/public
-> public/storage
Step 2: chuẩn bị thư viện Jascript
Thư viện Upload file
Để giao diện trực quan cho người dùng dễ sử dụng khi upload file (có chức năng xem trước hình ảnh và kéo thả). Chúng ta sẽ sử dụng thư việnbootstrap-file-input
- Trang chủ thư viện:
- Download tại: https://github.com/kartik-v/bootstrap-fileinput/zipball/master
- Giải nén file, đổi tên thư mục thành
bootstrap-fileinput
- Chép vào trong thư mục
public/vendor/bootstrap-fileinput
Thư viện mặt nạ nhập liệu (Input mask)
Để tránh người dùng nhập liệu sai sót, chúng ta sẽ bổ sung các thư viện ràng buộc trên giao diện để người dùng nhập liệu theo đúng định dạng chúng ta mong muốn. Có nhiều thư viện Javascript hỗ trợ các định dạng nhập liệu (hay còn gọi là mặt nạ nhập liệu - Input Mask). Ở đây chúng ta sử dụng thư viện "Jquery Input Mask"- Trang chủ thư viện: https://github.com/RobinHerbots/Inputmask
- Download tại: https://github.com/RobinHerbots/Inputmask/archive/5.0.5.zip
- Giải nén file, copy thư mục
dist
, đổi tên thànhinput-mask
- Chép vào trong thư mục
public/vendor/input-mask
Step 3: thực hiện tạo màn hình thêm mới Sản phẩm (create)
Mô hình hoạt động của create:
Viết code cho action create()
:
- Action
create()
thường dùng để hiển thị màn hình bao gồm form và các ô nhập liệu (input), dùng để nhập thông tin Sản phẩm mới. - Thông thường các ô nhập liệu (input) thường không có giá trị ban đầu, để người dùng tự nhập thông tin vào.
- Trên màn hình sẽ có nút
Lưu
Hiệu chỉnh file app\Http\Controllers\Backend\SanphamController.php
use App\Loai; // Chúng ta cần sử dụng model Loai để truy vấn dữ liệu public function create() { // Sử dụng Eloquent Model để truy vấn dữ liệu $ds_loai = Loai::all(); // SELECT * FROM loai // Đường dẫn đến view được quy định như sau: <FolderName>.<ViewName> // Mặc định đường dẫn gốc của method view() là thư mục `resources/views` // Hiển thị view `backend.sanpham.create` return view('backend.sanpham.create') // với dữ liệu truyền từ Controller qua View, được đặt tên là `danhsachloai` ->with('danhsachloai', $ds_loai); }
Tạo view create.blade.php
- Để dễ dàng quản lý các view, ta sẽ tạo 1 thư mục tương ứng với tên Controller, mỗi action sẽ tương ứng với tên view.
- Tạo folder
resources/views/backend/sanpham
- Tạo file
resources/views/backend/sanpham/create.blade.php
@extends('backend.layouts.master') @section('title') Thêm mới sản phẩm @endsection @section('custom-css') <!-- Các css dành cho thư viện bootstrap-fileinput --> <link href="{{ asset('vendor/bootstrap-fileinput/css/fileinput.css') }}" media="all" rel="stylesheet" type="text/css" /> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" crossorigin="anonymous"> <link href="{{ asset('vendor/bootstrap-fileinput/themes/explorer-fas/theme.css') }}" media="all" rel="stylesheet" type="text/css" /> @endsection @section('content') @if ($errors->any()) <div class="alert alert-danger"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form method="post" action="{{ route('admin.sanpham.store') }}" enctype="multipart/form-data"> {{ csrf_field() }} <div class="form-group"> <label for="l_ma">Loại sản phẩm</label> <select name="l_ma" class="form-control"> @foreach($danhsachloai as $loai) @if(old('l_ma') == $loai->l_ma) <option value="{{ $loai->l_ma }}" selected>{{ $loai->l_ten }}</option> @else <option value="{{ $loai->l_ma }}">{{ $loai->l_ten }}</option> @endif @endforeach </select> </div> <div class="form-group"> <label for="sp_ten">Tên sản phẩm</label> <input type="text" class="form-control" id="sp_ten" name="sp_ten" value="{{ old('sp_ten') }}"> </div> <div class="form-group"> <label for="sp_giaGoc">Giá gốc</label> <input type="text" class="form-control" id="sp_giaGoc" name="sp_giaGoc" value="{{ old('sp_giaGoc') }}"> </div> <div class="form-group"> <label for="sp_giaGoc">Giá bán</label> <input type="text" class="form-control" id="sp_giaBan" name="sp_giaBan" value="{{ old('sp_giaBan') }}"> </div> <div class="form-group"> <div class="file-loading"> <label>Hình đại diện</label> <input id="sp_hinh" type="file" name="sp_hinh"> </div> </div> <div class="form-group"> <label for="sp_thongTin">Thông tin</label> <input type="text" class="form-control" id="sp_thongTin" name="sp_thongTin" value="{{ old('sp_thongTin') }}"> </div> <div class="form-group"> <label for="sp_danhGia">Đánh giá</label> <input type="text" class="form-control" id="sp_danhGia" name="sp_danhGia" value="{{ old('sp_danhGia') }}"> </div> <div class="form-group"> <label for="sp_taoMoi">Ngày tạo mới</label> <input type="text" class="form-control" id="sp_taoMoi" name="sp_taoMoi" value="{{ old('sp_taoMoi') }}"> </div> <div class="form-group"> <label for="sp_capNhat">Ngày cập nhật</label> <input type="text" class="form-control" id="sp_capNhat" name="sp_capNhat" value="{{ old('sp_capNhat') }}"> </div> <div class="form-group"> <label for="sp_trangThai">Trạng thái</label> <select name="sp_trangThai" class="form-control"> <option value="1" {{ old('sp_trangThai') == 1 ? "selected" : "" }}>Khóa</option> <option value="2" {{ old('sp_trangThai') == 2 ? "selected" : "" }}>Khả dụng</option> </select> </div> <button type="submit" class="btn btn-primary">Lưu</button> </form> @endsection @section('custom-scripts') <!-- Các script dành cho thư viện bootstrap-fileinput --> <script src="{{ asset('vendor/bootstrap-fileinput/js/plugins/sortable.js') }}" type="text/javascript"></script> <script src="{{ asset('vendor/bootstrap-fileinput/js/fileinput.js') }}" type="text/javascript"></script> <script src="{{ asset('vendor/bootstrap-fileinput/js/locales/fr.js') }}" type="text/javascript"></script> <script src="{{ asset('vendor/bootstrap-fileinput/themes/fas/theme.js') }}" type="text/javascript"></script> <script src="{{ asset('vendor/bootstrap-fileinput/themes/explorer-fas/theme.js') }}" type="text/javascript"></script> <script> $(document).ready(function() { $("#sp_hinh").fileinput({ theme: 'fas', showUpload: false, showCaption: false, browseClass: "btn btn-primary btn-lg", fileType: "any", previewFileIcon: "<i class='glyphicon glyphicon-king'></i>", overwriteInitial: false }); }); </script> <!-- Các script dành cho thư viện Mặt nạ nhập liệu InputMask --> <script src="{{ asset('vendor/input-mask/jquery.inputmask.min.js') }}"></script> <script src="{{ asset('vendor/input-mask/bindings/inputmask.binding.js') }}"></script> <script> $(document).ready(function() { // Gắn mặt nạ nhập liệu cho các ô nhập liệu Giá gốc $('#sp_giaGoc').inputmask({ alias: 'currency', positionCaretOnClick: "radixFocus", radixPoint: ".", _radixDance: true, numericInput: true, groupSeparator: ",", suffix: ' vnđ', min: 0, // 0 ngàn max: 100000000, // 1 trăm triệu autoUnmask: true, removeMaskOnSubmit: true, unmaskAsNumber: true, inputtype: 'text', placeholder: "0", definitions: { "0": { validator: "[0-9\uFF11-\uFF19]" } } }); // Gắn mặt nạ nhập liệu cho các ô nhập liệu Giá bán $('#sp_giaBan').inputmask({ alias: 'currency', positionCaretOnClick: "radixFocus", radixPoint: ".", _radixDance: true, numericInput: true, groupSeparator: ",", suffix: ' vnđ', min: 0, // 0 ngàn max: 100000000, // 1 trăm triệu autoUnmask: true, removeMaskOnSubmit: true, unmaskAsNumber: true, inputtype: 'text', placeholder: "0", definitions: { "0": { validator: "[0-9\uFF11-\uFF19]" } } }); // Gắn mặt nạ nhập liệu cho các ô nhập liệu Ngày tạo mới $('#sp_taoMoi').inputmask({ alias: 'datetime', inputFormat: 'yyyy-mm-dd' // Định dạng Năm-Tháng-Ngày }); // Gắn mặt nạ nhập liệu cho các ô nhập liệu Ngày cập nhật $('#sp_capNhat').inputmask({ alias: 'datetime', inputFormat: 'yyyy-mm-dd' // Định dạng Năm-Tháng-Ngày }); }); </script> @endsection
Viết code cho action store()
:
- Action
store()
thường dùng để thực thi câu lệnh INSERT dữ liệu vào database. - Sẽ có kiểm tra ràng buộc ngoại lệ Validation.
- Sau khi Lưu dữ liệu thành công sẽ chuyển trang qua
index
- Hiệu chỉnh file
app\Http\Controllers\SanphamController.php
use Session; /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { // Bổ sung ràng buộc Validate $validation = $request->validate([ 'sp_hinh' => 'required|file|image|mimes:jpeg,png,gif,webp|max:2048', ]); // Tạo mới object SanPham $sp = new SanPham(); $sp->sp_ten = $request->sp_ten; $sp->sp_giaGoc = $request->sp_giaGoc; $sp->sp_giaBan = $request->sp_giaBan; $sp->sp_thongTin = $request->sp_thongTin; $sp->sp_danhGia = $request->sp_danhGia; $sp->sp_taoMoi = $request->sp_taoMoi; $sp->sp_capNhat = $request->sp_capNhat; $sp->sp_trangThai = $request->sp_trangThai; $sp->l_ma = $request->l_ma; // Kiểm tra xem người dùng có upload hình ảnh Đại diện hay không? if ($request->hasFile('sp_hinh')) { $file = $request->sp_hinh; // Lưu tên hình vào column sp_hinh $sp->sp_hinh = $file->getClientOriginalName(); // Chép file vào thư mục "storate/public/photos" $fileSaved = $file->storeAs('public/photos', $sp->sp_hinh); } $sp->save(); // Hiển thị câu thông báo 1 lần (Flash session) Session::flash('alert-info', 'Them moi thanh cong ^^~!!!'); // Điều hướng về route index return redirect()->route('admin.sanpham.index'); }
Chương trình học
Các bài học
Bài học trước Bài học tiếp theo
Chương trình học
Bao gồm Module, Chương, Bài học, Bài tập, Kiểm tra...Chương trình học
Bài học trước Bài học tiếp theo