kominfogusit/laravel-helper

helper package untuk laravel project di Diskominfo Kota Gunungsitoli

2.0.25 2023-08-01 13:48 UTC

README

[instalasi]

composer require kominfogusit/laravel-helper

composer update kominfogusit/laravel-helper

perhatian: tidak perlu lagi menambahkan apapun di config/app.php, baik di providers maupun di aliases.

lalu publish file-file lang, css dan js dengan cara:

php artisan vendor:publish --tag=kominfo-helper

php artisan vendor:publish --tag=kominfo-helper --force

perhatian: sebaiknya hindari penggunaan --force. sebelum publish, pilih dulu satu atau beberapa file di bawah ini yang akan di baca ulang dari repo package. baru publish dengan tanpa --force. publish tidak akan menimpa file-file yang sudah ada.

file-file yang dihasilkan:

config/kominfohelper.php
lang/id/error.php
lang/id/tooltip.php
lang/id/validation.php
public/js/global.js

[component]

untuk blade:

// -----
// form control
// -----

<x-form-input
    name="nama_lengkap"
    lang-context="views.pegawai.edit">
    </x-form-input>

<x-form-textarea
    name="nama_lengkap"
    lang-context="views.pegawai.edit">
    </x-form-textarea>

<x-form-select
    name="jenis_kelamin"
    lang-context="views.pegawai.edit"
    :options="['L' => 'Laki-laki', 'P' => 'Perempuan']">
    </x-form-select>

<x-form-file
    name="file_upload"
    lang-context="views.pegawai.edit"
    accept=".docx,.pdf"
    multiple="true">
    </x-form-file>

<x-form-recaptcha2
    name="pegawai_captcha"
    site-key="{{ config('recaptcha.api_site_key') }}"
    :modal-form="true">
    </x-form-recaptcha2>

// -----
// collection control
// -----

// menampilkan filter button dan data count
// kalau filter-control-nya hanya 1, gunakan syntax: "(bool) request()->filter1" pada :is-tersaring
<x-collection-filtering
    :collection="$objectList"
    :is-tersaring="request()->filter1 || request()->filter2 || [...]"
    :post-url="route('route_name')">
        <x-form-input
            name="nama_lengkap"
            old-value="{{ request()->filter1 }}"
            lang-context="views.pegawai.index">
            </x-form-input>
        <x-form-input
            name="alamat"
            old-value="{{ request()->filter2 }}"
            lang-context="views.pegawai.index">
            </x-form-input>
    </x-collection-filtering>

// menampilkan hanya data count (misalnya kalau tidak butuh fitur filter)
<x-collection-info
    :collection="$objectList">
    </x-collection-info>

// menampilkan dropdown untuk memilih jumlah baris data per halaman (row per page)
// tidak membutuhkan parameter
<x-collection-rowperpage />

// supaya row per page persist, di controller function
$rowsPerPage = intval(request()->query('rowsperpage', request()->cookie('rowsperpage', config('kominfohelper.rows_per_page'))));
Cookie::queue('rowsperpage', $rowsPerPage, 360);
// ...
$objectList = ObjectModel::paginate($rowsPerPage)->withQueryString();

// -----
// partial view
// -----

// gunakan partial view ini untuk me-render pagination (bootstrap 5)
// $objectList harus hasil pagination.
// kalau tidak ada page (!$objectList->hasPages()), tidak ada yang di render.
{{ $objectList->links('kominfo-helper::partials.pagination-numbers') }}

untuk lang/id/views/pegawai.php:

return [
    'edit.nama_lengkap' => 'Nama lengkap',
    'edit.jenis_kelamin' => 'Jenis kelamin',
    'index.filter1' => 'Filter A',
    'index.filter2' => 'Filter B',
];

[validator]

di controller function yang menjalankan validasi:

$validator = Validator::make($request->all(), [
    // ...
    'tanggal' => 'required|tglblnthn',
    'nik' => 'required|nik',
    'nomorkk' => 'required|nomorkk',
    // ...
    'g-recaptcha-response' => 'recaptcha',
    // atau bisa juga
    recaptchaFieldName() => recaptchaRuleName(),
    // ...
]);

[menyimpan data ke database]

di controller function yang outputnya full view:

// ganti $object dengan model eloquent
// ganti table.operation dengan nama tabel dan nama operasi, cth: pegawai.create, atau pegawai.update
try {
    $object->save();
} catch (\Throwable $th) {
    Log::error('pesan_error', ['th' => $th, 'db' => AppHelper::attrToArray($object)]);
    return redirect()->back()->withErrors(AppHelper::validatorErrorTh($th, 'pesan_error'))->withInput();
}

di controller function yang outputnya json:

// ganti $object dengan model eloquent
// ganti table.operation dengan nama tabel dan nama operasi, cth: pegawai.create, atau pegawai.update
try {
    $object->save();
} catch (\Throwable $th) {
    Log::error('pesan_error', ['th' => $th, 'db' => AppHelper::attrToArray($object)]);
    return AppHelper::jsonResponseErrorTh($th, 'pesan_error');
}

[redirect back, untuk non-ajax]

di controller function yang outputnya full view:

// redirect back dengan Throwable
return redirect()->back()->withErrors(AppHelper::validatorErrorTh($th))->withInput();

// redirect back, ada form input salah
// contoh: menampilkan validasi error di form control #nama_lengkap
return redirect()->back()->withErrors(AppHelper::validatorErrorForm('nama_lengkap', 'Nama ini tidak boleh digunakan.'))->withInput();

// redirect back, menampilkan sweetalert
// contoh: menampilkan pesan khusus lewat sweetalert
return redirect()->back()->withErrors(AppHelper::validatorErrorMessage('Maaf, pendaftaran sudah tutup.'))->withInput();

[json response, untuk ajax]

di controller function yang outputnya json:

// success
// ganti $redirect_url dengan alamat tujuan redirect. kalau kosong, refresh.
return AppHelper::jsonResponseSuccess($redirect_url);

// error dengan Throwable
// ganti $redirect_url dengan alamat tujuan redirect. kalau kosong, refresh.
return AppHelper::jsonResponseErrorTh($th, $message, $redirect_url);

// error dengan menampilkan pesan
// ganti $redirect_url dengan alamat tujuan redirect. kalau kosong, refresh.
return AppHelper::jsonResponseErrorMessage($message, $redirect_url);

[DateTime di model]

class PegawaiModel
{
    protected function tanggalLahir(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => AppHelper::showTanggal($value, "d-m-Y"),
            set: fn ($value) => AppHelper::parseTanggal($value, "d-m-Y"),
        );
    }
}

[layout]

untuk blade template:

<script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="{{ asset('js/global.js') }}"></script>
@stack('scripts')

@error('error_alert')
    <script>
        $(function() {
            Swal.fire('', '{{ $message }}', 'error');
        });
    </script>
@enderror

untuk blade content:

// -----
// buttons
// -----

<a href="javascript:void(0);" class="btn btn-danger"
    data-deleteurl="{{ route('route_delete',['id'=> $id]) }}"
    onclick="sweetDeleteData(this)">
    Hapus
    </a>

<a href="javascript:void(0);"
    data-deleteurl="{{ route('route_delete',['id'=> $id]) }}"
    onclick="sweetDeleteData(this)"
    title="{{ __('tooltip.btn_delete') }}">
    <i class='fa-solid fa-trash'></i>
    </a>

// -----
// table-index
// -----

<div class="table-responsive">
    <table class="table table-index">
        <thead>
            <tr>
                <th>Kolom 1</th>
                <th>Kolom 2</th>
                <th>Kolom 3</th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Data 1</td>
                <td>Data 2</td>
                <td>Data 3</td>
                <td class="col-btn1 || col-btn2 || col-btn3"> ... </td>
            </tr>
        </tbody>
    </table>
</div>
<div class="mt-3">
    {{ $objectList->links('kominfo-helper::partials.pagination-numbers') }}
</div>

// -----
// table-detail
// -----

<table class="table table-detail">
    <tbody>
        <tr>
            <th width="80px">Label</th>
            <td>Data</td>
        </tr>
        <tr>
            <th>Label</th>
            <td>Data</td>
        </tr>
    </tbody>
</table>

[modal form]

frontend blade yang akan menampilkan form:

<a href="javascript:void(0);"
    class="btn btn-sm btn-primary"
    data-mf-id="modalCreate"
    data-mf-url="{{ route('data.create') }}"
    onclick="modalFormShow(this)">
    Tambah
    </a>

<div class="modal fade" id="modalCreate" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="true" role="dialog" aria-hidden="true">
    <div class="modal-dialog modal-dialog-scrollable modal-dialog-centered" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title fs-5">Tambah Data</h5>
            </div>
            <div class="modal-body">
            </div>
        </div>
    </div>
</div>

backend blade yang akan me-render form:

<form action="{{ route('data.create_post') }}" method="POST">
    @csrf
    <x-form-input name="id" lang-context="views.data.create"></x-form-input>
    <x-form-input name="nama" lang-context="views.data.create"></x-form-input>
    <x-form-input name="kategori" lang-context="views.data.create"></x-form-input>
    <div class="form-buttons">
        <a href="javascript:void(0);" class="btn btn-sm btn-outline btn-outline-primary" id="closeButton">Batal</a>
        <a href="javascript:void(0);" class="btn btn-sm btn-primary" id="submitButton">Tambah</a>
    </div>
</form>

backend controller yang akan menerima post data:

public function createPost(Request $request)
{
    $validator = Validator::make($request->all(), [
        'id' => 'required',
        'nama' => 'required',
        'kategori' => 'required',
    ]);
    if ($validator->fails()) {
        return AppHelper::jsonResponseErrorValidator($validator);
    }
    $validated = $validator->validated();

    // ...
    // proses form disini
    // kalau masih ada error, lempar dengan:
    AppHelper::jsonResponseErrorMessage(__('pesan_error'));
    // atau...
    return AppHelper::jsonResponseErrorValidator(AppHelper::validatorErrorForm('nama','Nama ini tidak boleh digunakan.'));
    // ...

    return AppHelper::jsonResponseSuccess();
} // function createPost

[pemanggilan ajax]

ajaxShowLoading();
$.ajax({
  type: "GET",
  url: url,
  success: function (resultData) {
    if (resultData.status) {
      // ...
      // proses data disini
      // ...
      ajaxReturnJsonTrue(resultData.redirect);
    } else {
      ajaxReturnJsonFalse(resultData.desc);
    }
  },
  error: function () {
    ajaxReturnError();
  },
});

[recaptcha v2]

install dan publish package biscolab/laravel-recaptcha

composer require biscolab/laravel-recaptcha
php artisan vendor:publish --provider="Biscolab\ReCaptcha\ReCaptchaServiceProvider"

update version di file config/recaptcha.php:

return [
    // ...
    'version' => 'v2',
    'default_language' => 'id',
    // ...
];

tambahkan di file .env:

RECAPTCHA_SITE_KEY=<YOUR_API_SITE_KEY>
RECAPTCHA_SECRET_KEY=<YOUR_API_SECRET_KEY>
RECAPTCHA_SKIP_IP=<YOUR_IP_LIST>

RECAPTCHA_SKIP_IP boleh kosong.

di file blade yang akan menampilkan recaptcha, masukkan script ini di html HEAD:

<html>
    <head>
    // ...
    {!! htmlScriptTagJsApi($configuration) !!}
    // ...
    </head>

lalu tampilkan recaptcha melalui component:

<x-form-recaptcha2 name="pegawai_captcha" site-key="{{ config('recaptcha.api_site_key') }}" :modal-form="true"> </x-form-recaptcha2>

atribut modal-form digunakan hanya bila recaptcha di-render di modal form.

kemudian di controller yang akan menerima form post, recaptcha divalidasi dengan cara:

$validator = Validator::make($request->all(), [
    // ...
    'g-recaptcha-response' => 'recaptcha',
    // atau bisa juga
    recaptchaFieldName() => recaptchaRuleName(),
    // ...
]);