Aplikasi E-Commerce Laravel 6 #12: Authorization & Customer Profile

Aplikasi E-Commerce Laravel 6 #12: Authorization & Customer Profile

Pendahuluan

Authorization memiliki peran yang penting dalam mengamankan informasi dari pengguna yang bukan pemiliknya, sehingga menerapkan batasan ini adalah sebuah keharusan. Ada banyak jalan dan cara untuk menerapkan permission, termasuk dengan menggunakan library role-permission, akan tetapi pada kesempatan kali ini kita akan menggunakan Authorization bawaan dari Laravel untuk mengenal bagaimana cara menggunakannya.

Selain itu kita juga akan belajar bagaimana membuat fitur untuk mengubah informasi profil customer berdasarkan customer yang sedang login.

Baca Juga: Aplikasi Ecommerce Laravel 6 #11: Dashboard & Management Orders

How to Use Laravel Authorization

Memberikan batasan bagi pengguna dalam mengakses informasi adalah bagian penting yang harus menjadi perhatian kita, misalnya saja, saat ini customer bisa mengakses dan melihat detail order customer lainnya hanya dengan me-replace InvoiceID pada URL (red: http://localhost:8000/member/orders/INVOICE-ID ), sehingga hal ini bisa menjadi sangat fatal terhadap privacy customer.

Pada sub-bab ini, kita akan membuat fungsi pembatasan tersebut ketika customer ingin melihat detail pesanannya, adapun fungsi pembatasan pada module lainnya silahkan disesuaikan jika diperlukan. Buka file app/Providers/AuthServiceProvider.php dan modifikasi method boot() menjadi

public function boot()
{
    $this->registerPolicies();

    //KITA MEMBUAT GATE DENGAN NAMA order-view, DIMANA DIA MEMINTA DUA PARAMETER YAKNI CUSTOMER DAN ORDER
    Gate::define('order-view', function($customer, $order) {
        //KEMUDIAN DICEK, JIKA CUSTOMER ID SAMA DENGAN CUSTOMER_ID YANG ADA PADA TABLE ORDER
        //MAKA RETURN-NYA TRUE
        //GATE INI HANYA AKAN ME-RETURN TRUE/FALSE SEBAGAI TANDA DIIZINKAN ATAU TIDAK
        return $customer->id == $order->customer_id;
    });
  
    //DEFINISIKAN GATE LAINNYA DISINI JIKA PERLU
}

Karena hanya akan kita gunakan ketika customer melihat detail pesanannya, maka buka file Ecommerce/OrderController.php dan modifikasi method view() menjadi

public function view($invoice)
{
    $order = Order::with(['district.city.province', 'details', 'details.product', 'payment'])
        ->where('invoice', $invoice)->first();

    //JADI KITA CEK, VALUE forUser() NYA ADALAH CUSTOMER YANG SEDANG LOGIN
    //DAN ALLOW NYA MEMINTA DUA PARAMETER
    //PERTAMA ADALAH NAMA GATE YANG DIBUAT SEBELUMNYA DAN YANG KEDUA ADALAH DATA ORDER DARI QUERY DI ATAS
    if (\Gate::forUser(auth()->guard('customer')->user())->allows('order-view', $order)) {
        //JIKA HASILNYA TRUE, MAKA KITA TAMPILKAN DATANYA
        return view('ecommerce.orders.view', compact('order'));
    }
    //JIKA FALSE, MAKA REDIRECT KE HALAMAN YANG DIINGINKAN
    return redirect(route('customer.orders'))->with(['error' => 'Anda Tidak Diizinkan Untuk Mengakses Order Orang Lain']);
}

Note: Code di atas sebagai contoh ketika digunakan pada satu module, jika ingin menggunakannya pada module lainnya maka caranya sama saja.

Bagian terakhir adalah menambahkan alert untuk menampilkan message error di atas, buka file resources/views/ecommerce/orders/index.blade.php dan tambahkan code berikut di atas tag <div class="table-responsive">

@if (session('error'))
<div class="alert alert-danger">{{ session('error') }}</div>
@endif

Untuk menguji cobanya apakah authorization di atas berhasil atau tidak, buat dua buah customer dan pesanan untuk masing-masing customer, kemudian replace InvoiceID pada URL dengan memasukkan InvoiceID yang bukan milik customer yang sedang login.

Hasil yang akan kita peroleh akan tampak seperti berikut

Customer Profile Update

Pada bagian sub-bab ini kita akan menyediakan fitur yang memungkinkan customer untuk mengubah informasi tentang dirinya. Adapun schema-nya adalah, semua informasi bisa di-edit kecuali email dan password jika dikosongkan maka tidak akan mengubah data ke database, apabila password diisi oleh customer maka password tersebut akan diperbaharui.

Buka file Ecommerce/FrontController.php dan tambahkan method berikut

public function customerSettingForm()
{
    //MENGAMBIL DATA CUSTOMER YANG SEDANG LOGIN
    $customer = auth()->guard('customer')->user()->load('district');
    //GET DATA PROPINSI UNTUK DITAMPILKAN PADA SELECT BOX
    $provinces = Province::orderBy('name', 'ASC')->get();
    //LOAD VIEW setting.blade.php DAN PASSING DATA CUSTOMER - PROVINCES
    return view('ecommerce.setting', compact('customer', 'provinces'));
}

Jangan lupa untuk menambahkan use statement:

use App\Province;

Fungsi load() cara kerjanya sama dengan eager loading, maka definisikan relationships customer ke district. Buka file Customer.php dan tambahkan method berikut

public function district()
{
    return $this->belongsTo(District::class);
}

Selanjutnya buat file setting.blade.php di dalam folder resources/views/ecommerce dan tambahkan potongan code di bawah ini

@extends('layouts.ecommerce')

@section('title')
    <title>Pengaturan - DW Ecommerce</title>
@endsection

@section('content')
    <!--================Home Banner Area =================-->
	<section class="banner_area">
		<div class="banner_inner d-flex align-items-center">
			<div class="container">
				<div class="banner_content text-center">
					<h2>Pengaturan</h2>
					<div class="page_link">
              <a href="{{ url('/') }}">Home</a>
              <a href="{{ route('customer.settingForm') }}">Pengaturan</a>
					</div>
				</div>
			</div>
		</div>
	</section>
	<!--================End Home Banner Area =================-->

	<!--================Login Box Area =================-->
	<section class="login_box_area p_120">
		<div class="container">
			<div class="row">
				<div class="col-md-3">
					@include('layouts.ecommerce.module.sidebar')
				</div>
				<div class="col-md-9">
          <div class="row">
						<div class="col-md-12">
							<div class="card">
                <div class="card-header">
                    <h4 class="card-title">Informasi Pribadi</h4>
                </div>
<div class="card-body">
                    @if (session('success'))
                        <div class="alert alert-success">{{ session('success') }}</div>
                    @endif

                    <form action="{{ route('customer.setting') }}" method="post">
                        @csrf
                        <div class="form-group">
                            <label for="">Nama Lengkap</label>
                            <input type="text" name="name" class="form-control" required value="{{ $customer->name }}">
                            <p class="text-danger">{{ $errors->first('name') }}</p>
                        </div>
                        <div class="form-group">
                            <label for="">Email</label>
                            <input type="email" name="email" class="form-control" required value="{{ $customer->email }}" readonly>
                            <p class="text-danger">{{ $errors->first('email') }}</p>
                        </div>
                        <div class="form-group">
                            <label for="">Password</label>
                            <input type="password" name="password" class="form-control" placeholder="******">
                            <p class="text-danger">{{ $errors->first('password') }}</p>
                            <p>Biarkan kosong jika tidak ingin mengganti password</p>
                        </div>
                        <div class="form-group">
                            <label for="">No Telp</label>
                            <input type="text" name="phone_number" class="form-control" required value="{{ $customer->phone_number }}">
                            <p class="text-danger">{{ $errors->first('phone_number') }}</p>
                        </div>
                        <div class="form-group">
                            <label for="">Alamat</label>
                            <input type="text" name="address" class="form-control" required value="{{ $customer->address }}">
                            <p class="text-danger">{{ $errors->first('address') }}</p>
                        </div>
                        <div class="form-group">
                            <label for="">Propinsi</label>
                            <select class="form-control" name="province_id" id="province_id" required>
                                <option value="">Pilih Propinsi</option>
                                @foreach ($provinces as $row)
                                <option value="{{ $row->id }}" {{ $customer->district->province_id == $row->id ? 'selected':'' }}>{{ $row->name }}</option>
                                @endforeach
                            </select>
                            <p class="text-danger">{{ $errors->first('province_id') }}</p>
                        </div>
                        <div class="form-group">
                            <label for="">Kabupaten / Kota</label>
                            <select class="form-control" name="city_id" id="city_id" required>
                                <option value="">Pilih Kabupaten/Kota</option>
                            </select>
                            <p class="text-danger">{{ $errors->first('city_id') }}</p>
                        </div>
                        <div class="form-group">
                            <label for="">Kecamatan</label>
                            <select class="form-control" name="district_id" id="district_id" required>
                                <option value="">Pilih Kecamatan</option>
                            </select>
                            <p class="text-danger">{{ $errors->first('district_id') }}</p>
                        </div>
                        <button class="btn btn-primary btn-sm">Simpan</button>
                    </form>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
	</section>
@endsection


@section('js')
    <script>
        //JADI KETIKA HALAMAN DI-LOAD
        $(document).ready(function(){
            //MAKA KITA MEMANGGIL FUNGSI LOADCITY() DAN LOADDISTRICT()
            //AGAR SECARA OTOMATIS MENGISI SELECT BOX YANG TERSEDIA
            loadCity($('#province_id').val(), 'bySelect').then(() => {
                loadDistrict($('#city_id').val(), 'bySelect');
            })
        })

        $('#province_id').on('change', function() {
            loadCity($(this).val(), '');
        })

        $('#city_id').on('change', function() {
            loadDistrict($(this).val(), '')
        })

        function loadCity(province_id, type) {
            return new Promise((resolve, reject) => {
                $.ajax({
                    url: "{{ url('/api/city') }}",
                    type: "GET",
                    data: { province_id: province_id },
                    success: function(html){
                        $('#city_id').empty()
                        $('#city_id').append('<option value="">Pilih Kabupaten/Kota</option>')
                        $.each(html.data, function(key, item) {
                            
                            // KITA TAMPUNG VALUE CITY_ID SAAT INI
                            let city_selected = {{ $customer->district->city_id }};
                           //KEMUDIAN DICEK, JIKA CITY_SELECTED SAMA DENGAN ID CITY YANG DOLOOPING MAKA 'SELECTED' AKAN DIAPPEND KE TAG OPTION
                            let selected = type == 'bySelect' && city_selected == item.id ? 'selected':'';
                            //KEMUDIAN KITA MASUKKAN VALUE SELECTED DI ATAS KE DALAM TAG OPTION
                            $('#city_id').append('<option value="'+item.id+'" '+ selected +'>'+item.name+'</option>')
                            resolve()
                        })
                    }
                });
            })
        }

        //CARA KERJANYA SAMA SAJA DENGAN FUNGSI DI ATAS
        function loadDistrict(city_id, type) {
            $.ajax({
                url: "{{ url('/api/district') }}",
                type: "GET",
                data: { city_id: city_id },
                success: function(html){
                    $('#district_id').empty()
                    $('#district_id').append('<option value="">Pilih Kecamatan</option>')
                    $.each(html.data, function(key, item) {
                        let district_selected = {{ $customer->district->id }};
                        let selected = type == 'bySelect' && district_selected == item.id ? 'selected':'';
                        $('#district_id').append('<option value="'+item.id+'" '+ selected +'>'+item.name+'</option>')
                    })
                }
            });
        }
    </script>
@endsection

Buka kembali file Ecommerce/FrontController.php, kemudian tambahkan method berikut untuk meng-handle proses update informasi customer.

public function customerUpdateProfile(Request $request)
{
    //VALIDASI DATA YANG DIKIRIM
    $this->validate($request, [
        'name' => 'required|string|max:100',
        'phone_number' => 'required|max:15',
        'address' => 'required|string',
        'district_id' => 'required|exists:districts,id',
        'password' => 'nullable|string|min:6'
    ]);

    //AMBIL DATA CUSTOMER YANG SEDANG LOGIN
    $user = auth()->guard('customer')->user();
    //AMBIL DATA YANG DIKIRIM DARI FORM
    //TAPI HANYA 4 COLUMN SAJA SESUAI YANG ADA DI BAWAH
    $data = $request->only('name', 'phone_number', 'address', 'district_id');
    //ADAPUN PASSWORD KITA CEK DULU, JIKA TIDAK KOSONG
    if ($request->password != '') {
        //MAKA TAMBAHKAN KE DALAM ARRAY
        $data['password'] = $request->password;
    }
    //TERUS UPDATE DATANYA
    $user->update($data);
    //DAN REDIRECT KEMBALI DENGAN MENGIRIMKAN PESAN BERHASIL
    return redirect()->back()->with(['success' => 'Profil berhasil diperbaharui']);
}

Kemudian definisikan routing dari kedua method yang sudah kita tambahkan, buka file routes/web.php dan tambahkan line berikut di dalam route group yang menggunakan middleware customer

Route::get('setting', 'FrontController@customerSettingForm')->name('customer.settingForm');
Route::post('setting', 'FrontController@customerUpdateProfile')->name('customer.setting');

Bagian terakhir adalah membuat link dari sidebar menu ke halaman pengaturan, buka file sidebar.blade.php dan modifikasi tag untuk pengaturan menjadi

<li class="icon-users"><a href="{{ route('customer.settingForm') }}">Pengaturan</a></li>

Baca Juga: Aplikasi Ecommerce Laravel 6 #10: Multiple Authentication

Kesimpulan

Artikel singkat tapi setidaknya kita belajar hal baru yakni cara menggunakan Authorization di Laravel, bagaimana cara membuat Gate di Laravel, cara menggunakan Gate di Laravel, dan lain sebagianya.

Adapun dokumentasi code dari artikel ini bisa dilihat di Github.

Category:
Share:

Comments