cusc_sanpham
và cusc_hinhanh
cusc_hinhanh
cusc_hinhanh
sử dụng cặp khóa chính gồm 2 cột (columns) là sp_ma
và ha_stt
nên chúng ta cần hiệu chỉnh model HinhAnh
để có thể đáp ứng được việc lưu trữ cặp khóa chính thông qua Laravel như sau:
app\HinhAnh.php
, bổ sung các hàm xử lý với trường hợp sử dụng cặp khóa chính (Multi PrimaryKey) model HinhAnh:<?php namespace App; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class HinhAnh extends Model { public $timestamps = false; //created_at, updated_at protected $table = 'cusc_hinhanh'; protected $fillable = ['ha_ten']; protected $guarded = ['sp_ma', 'ha_stt']; protected $primaryKey = ['sp_ma', 'ha_stt']; public $incrementing = false; /** * Set the keys for a save update query. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ protected function setKeysForSaveQuery(Builder $query) { $keys = $this->getKeyName(); if(!is_array($keys)){ return parent::setKeysForSaveQuery($query); } foreach($keys as $keyName){ $query->where($keyName, '=', $this->getKeyForSaveQuery($keyName)); } return $query; } /** * Get the primary key value for a save query. * * @param mixed $keyName * @return mixed */ protected function getKeyForSaveQuery($keyName = null) { if(is_null($keyName)){ $keyName = $this->getKeyName(); } if (isset($this->original[$keyName])) { return $this->original[$keyName]; } return $this->getAttribute($keyName); } }
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Carbon\Carbon; class SanPham extends Model { const CREATED_AT = 'sp_taoMoi'; const UPDATED_AT = 'sp_capNhat'; protected $table = 'cusc_sanpham'; protected $fillable = ['sp_ten', 'sp_giaGoc', 'sp_giaBan', 'sp_hinh', 'sp_thongTin', 'sp_danhGia', 'sp_taoMoi', 'sp_capNhat', 'sp_trangThai', 'l_ma']; protected $guarded = ['sp_ma']; protected $primaryKey = 'sp_ma'; protected $dates = ['sp_taoMoi', 'sp_capNhat']; protected $dateFormat = 'Y-m-d H:i:s'; public function loaisanpham() { return $this->belongsTo('App\Loai', 'l_ma', 'l_ma'); } public function hinhanhlienquan() { return $this->hasMany('App\HinhAnh', 'sp_ma', 'sp_ma'); } }
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> <div class="form-group"> <div class="file-loading"> <label>Hình ảnh liên quan sản phẩm</label> <input id="sp_hinhanhlienquan" type="file" name="sp_hinhanhlienquan[]" multiple> </div> </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 }); // Ô nhập liệu cho phép chọn nhiều hình ảnh cùng lúc (các hình ảnh liên quan đến sản phẩm) $("#sp_hinhanhlienquan").fileinput({ theme: 'fas', showUpload: false, showCaption: false, browseClass: "btn btn-primary btn-lg", fileType: "any", previewFileIcon: "<i class='glyphicon glyphicon-king'></i>", overwriteInitial: false, allowedFileExtensions: ["jpg", "gif", "png", "txt"] }); }); </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
store
của Backend\SanPhamController
use App\HinhAnh; // Chúng ta cần sử dụng model HinhAnh để truy vấn dữ liệu /** * 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', // Cú pháp dùng upload nhiều file 'sp_hinhanhlienquan.*' => '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(); // Lưu hình ảnh liên quan if($request->hasFile('sp_hinhanhlienquan')) { $files = $request->sp_hinhanhlienquan; // duyệt từng ảnh và thực hiện lưu foreach ($request->sp_hinhanhlienquan as $index => $file) { $file->storeAs('public/photos', $file->getClientOriginalName()); // Tạo đối tưọng HinhAnh $hinhAnh = new HinhAnh(); $hinhAnh->sp_ma = $sp->sp_ma; $hinhAnh->ha_stt = ($index + 1); $hinhAnh->ha_ten = $file->getClientOriginalName(); $hinhAnh->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'); }
resources/views/backend/sanpham/edit.blade.php
@extends('backend.layouts.master') @section('title') Hiệu chỉnh 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.update', ['id' => $sp->sp_ma]) }}" enctype="multipart/form-data"> <input type="hidden" name="_method" value="PUT" /> {{ 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($loai->l_ma == $sp->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', $sp->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', $sp->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', $sp->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', $sp->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', $sp->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', $sp->sp_taoMoi) }}" data-mask-datetime> </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', $sp->sp_capNhat) }}" data-mask-datetime> </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', $sp->sp_trangThai) == 1 ? "selected" : "" }}>Khóa</option> <option value="2" {{ old('sp_trangThai', $sp->sp_trangThai) == 2 ? "selected" : "" }}>Khả dụng</option> </select> </div> <div class="form-group"> <div class="file-loading"> <label>Hình ảnh liên quan sản phẩm</label> <input id="sp_hinhanhlienquan" type="file" name="sp_hinhanhlienquan[]" multiple> </div> </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", append: false, showRemove: false, autoReplace: true, previewFileIcon: "<i class='glyphicon glyphicon-king'></i>", overwriteInitial: false, initialPreviewShowDelete: false, initialPreviewAsData: true, initialPreview: [ "{{ asset('storage/photos/' . $sp->sp_hinh) }}" ], initialPreviewConfig: [ { caption: "{{ $sp->sp_hinh }}", size: {{ Storage::exists('public/photos/' . $sp->sp_hinh) ? Storage::size('public/photos/' . $sp->sp_hinh) : 0 }}, width: "120px", url: "{$url}", key: 1 }, ] }); // Ô nhập liệu cho phép chọn nhiều hình ảnh cùng lúc (các hình ảnh liên quan đến sản phẩm) $("#sp_hinhanhlienquan").fileinput({ theme: 'fas', showUpload: false, showCaption: false, browseClass: "btn btn-primary btn-lg", fileType: "any", append: false, showRemove: false, autoReplace: true, previewFileIcon: "<i class='glyphicon glyphicon-king'></i>", overwriteInitial: false, allowedFileExtensions: ["jpg", "gif", "png", "txt"], initialPreviewShowDelete: false, initialPreviewAsData: true, initialPreview: [ @foreach($sp->hinhanhlienquan()->get() as $hinhAnh) "{{ asset('storage/photos/' . $hinhAnh->ha_ten) }}", @endforeach ], initialPreviewConfig: [ @foreach($sp->hinhanhlienquan()->get() as $index=>$hinhAnh) { caption: "{{ $hinhAnh->ha_ten }}", size: {{ Storage::exists('public/photos/' . $hinhAnh->ha_ten) ? Storage::size('public/photos/' . $hinhAnh->ha_ten) : 0 }}, width: "120px", url: "{$url}", key: {{ ($index + 1) }} }, @endforeach ] }); }); </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
update
của Backend\SanPhamController
/** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { // Bổ sung ràng buộc Validate $validation = $request->validate([ 'sp_hinh' => 'file|image|mimes:jpeg,png,gif,webp|max:2048', // Cú pháp dùng upload nhiều file 'sp_hinhanhlienquan.*' => 'image|mimes:jpeg,png,gif,webp|max:2048' ]); // Tìm object Sản phẩm theo khóa chính $sp = SanPham::where("sp_ma", $id)->first(); $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')) { // Xóa hình cũ để tránh rác Storage::delete('public/photos/' . $sp->sp_hinh); // Upload hình mới // Lưu tên hình vào column sp_hinh $file = $request->sp_hinh; $sp->sp_hinh = $file->getClientOriginalName(); // Chép file vào thư mục "photos" $fileSaved = $file->storeAs('public/photos', $sp->sp_hinh); } // Lưu hình ảnh liên quan if ($request->hasFile('sp_hinhanhlienquan')) { // DELETE các dòng liên quan trong table `HinhAnh` foreach ($sp->hinhanhlienquan()->get() as $hinhAnh) { // Xóa hình cũ để tránh rác Storage::delete('public/photos/' . $hinhAnh->ha_ten); // Xóa record $hinhAnh->delete(); } $files = $request->sp_hinhanhlienquan; // duyệt từng ảnh và thực hiện lưu foreach ($request->sp_hinhanhlienquan as $index => $file) { $file->storeAs('public/photos', $file->getClientOriginalName()); // Tạo đối tưọng HinhAnh $hinhAnh = new HinhAnh(); $hinhAnh->sp_ma = $sp->sp_ma; $hinhAnh->ha_stt = ($index + 1); $hinhAnh->ha_ten = $file->getClientOriginalName(); $hinhAnh->save(); } } $sp->save(); // Hiển thị câu thông báo 1 lần (Flash session) Session::flash('alert-info', 'Cập nhật thành công ^^~!!!'); // Điều hướng về trang index return redirect()->route('admin.sanpham.index'); }
despoy
của Backend\SanPhamController
/** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { // Tìm object Sản phẩm theo khóa chính $sp = SanPham::where("sp_ma", $id)->first(); // Nếu tìm thấy được sản phẩm thì tiến hành thao tác DELETE if (empty($sp) == false) { // DELETE các dòng liên quan trong table `HinhAnh` foreach ($sp->hinhanhlienquan()->get() as $hinhAnh) { // Xóa hình cũ để tránh rác Storage::delete('public/photos/' . $hinhAnh->ha_ten); // Xóa record $hinhAnh->delete(); } // Xóa hình cũ để tránh rác Storage::delete('public/photos/' . $sp->sp_hinh); } $sp->delete(); // Hiển thị câu thông báo 1 lần (Flash session) Session::flash('alert-info', 'Xóa sản phẩm thành công ^^~!!!'); // Điều hướng về trang index return redirect()->route('admin.sanpham.index'); }
Cùng nhau học tập, khám phá các kiến thức nền tảng về Lập trình web, mobile, database nhé.
Nền tảng kiến thức - Hành trang tới tương lai hân hạnh phục vụ Quý khách!
Khám phá, trải nghiệm ngay
Vui lòng đăng nhập để gởi bình luận!
Đăng nhậpChưa có bình luận nào!