git commit -m "✨ refactor(helper): dokumentasi & peningkatan fungsi helper LPJ
## Ringkasan Refaktor besar pada helper `Modules/Lpj/app/Helpers/Lpj.php` untuk meningkatkan keterbacaan, keamanan, dan maintainability melalui dokumentasi, logging, dan validasi input. ## Perubahan Utama - Tambah `declare(strict_types=1)` dan type declarations di seluruh fungsi - Tambah PHPDoc lengkap pada `formatTanggalIndonesia` & `formatRupiah` - Tambah logging detail (awal fungsi, validasi, keberhasilan, dan kegagalan) - Validasi input null/kosong serta error handling yang lebih aman - Bungkus fungsi query DB dalam `DB::transaction` untuk konsistensi data - Tambah fungsi baru: - `parsePembandingMigration()` → membersihkan & memformat data pembanding migrasi - `getFilePath()` → resolve path file internal/eksternal - `parseTimestamp()` → robust timestamp parser multi-format - Fix minor linter issue: `strtotime(now())`, `pow(10,3)` → `(int) str_pad(...)` ## Dampak - Semua fungsi kini memiliki dokumentasi dan validasi lengkap - Logging terstruktur untuk memudahkan debugging di production - Peningkatan keamanan dan kestabilan dengan type safety & transaksi DB - Output lebih konsisten dan mudah dilacak"
This commit is contained in:
@@ -1,7 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Modules\Location\Models\City;
|
use Modules\Location\Models\City;
|
||||||
use Modules\Location\Models\District;
|
use Modules\Location\Models\District;
|
||||||
use Modules\Location\Models\Province;
|
use Modules\Location\Models\Province;
|
||||||
@@ -15,29 +18,112 @@
|
|||||||
use Modules\Lpj\Models\Penilaian;
|
use Modules\Lpj\Models\Penilaian;
|
||||||
use Modules\Lpj\Models\TeamsUsers;
|
use Modules\Lpj\Models\TeamsUsers;
|
||||||
use Modules\Usermanagement\Models\User;
|
use Modules\Usermanagement\Models\User;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
function formatTanggalIndonesia($date, $time = false)
|
/**
|
||||||
|
* Format tanggal ke dalam format Bahasa Indonesia
|
||||||
|
*
|
||||||
|
* Mengubah tanggal menjadi format yang lebih mudah dibaca dalam Bahasa Indonesia.
|
||||||
|
* Contoh: "15 Januari 2024" atau "15 Januari 2024 pukul 14.30 WIB"
|
||||||
|
*
|
||||||
|
* @param string|mixed $date Tanggal yang akan diformat (string tanggal atau null)
|
||||||
|
* @param bool $time Apakah akan menampilkan waktu juga (default: false)
|
||||||
|
* @return string Tanggal yang sudah diformat dalam Bahasa Indonesia
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* formatTanggalIndonesia('2024-01-15') // "15 Januari 2024"
|
||||||
|
* formatTanggalIndonesia('2024-01-15 14:30:00', true) // "15 Januari 2024 pukul 14.30 WIB"
|
||||||
|
* formatTanggalIndonesia(null) // ""
|
||||||
|
* formatTanggalIndonesia('invalid-date') // "invalid-date" (return as-is jika error)
|
||||||
|
*/
|
||||||
|
function formatTanggalIndonesia($date, $time = false): string
|
||||||
{
|
{
|
||||||
Carbon::setLocale('id');
|
Log::debug('Memulai format tanggal Indonesia', [
|
||||||
try {
|
'date' => $date,
|
||||||
$waktu = Carbon::parse($date);
|
'time' => $time
|
||||||
if (!$time) {
|
]);
|
||||||
return $waktu->translatedFormat('d F Y');
|
|
||||||
}
|
// Validasi input null atau kosong
|
||||||
return $waktu->translatedFormat('d F Y') . ' pukul ' . $waktu->format('H.i') . ' WIB';
|
if (empty($date)) {
|
||||||
} catch (Throwable $e) {
|
Log::debug('Tanggal kosong, return empty string');
|
||||||
return $date;
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Carbon::setLocale('id');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$waktu = Carbon::parse($date);
|
||||||
|
|
||||||
|
if (!$time) {
|
||||||
|
$result = $waktu->translatedFormat('d F Y');
|
||||||
|
Log::debug('Format tanggal berhasil', ['result' => $result]);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $waktu->translatedFormat('d F Y') . ' pukul ' . $waktu->format('H.i') . ' WIB';
|
||||||
|
Log::debug('Format tanggal dengan waktu berhasil', ['result' => $result]);
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
Log::warning('Gagal parse tanggal', [
|
||||||
|
'date' => $date,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
// Return input as-is jika gagal parse
|
||||||
|
return (string) $date;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
function formatRupiah($number, $decimals = 0)
|
* Format angka ke dalam format mata uang Rupiah Indonesia
|
||||||
|
*
|
||||||
|
* Mengubah angka menjadi format mata uang Rupiah dengan pemisah ribuan
|
||||||
|
* dan menggunakan koma sebagai pemisah desimal sesuai standar Indonesia.
|
||||||
|
*
|
||||||
|
* @param int|float|string $number Angka yang akan diformat (bisa negatif)
|
||||||
|
* @param int $decimals Jumlah digit desimal (default: 0)
|
||||||
|
* @return string Angka yang sudah diformat dalam format Rupiah
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* formatRupiah(1500000) // "Rp 1.500.000"
|
||||||
|
* formatRupiah(1500000.50, 2) // "Rp 1.500.000,50"
|
||||||
|
* formatRupiah(-500000) // "Rp -500.000"
|
||||||
|
* formatRupiah(0) // "Rp 0"
|
||||||
|
* formatRupiah(null) // "Rp 0"
|
||||||
|
*/
|
||||||
|
function formatRupiah($number, $decimals = 0): string
|
||||||
{
|
{
|
||||||
$number = (float) $number;
|
Log::debug('Memulai format Rupiah', [
|
||||||
return 'Rp ' . number_format($number, $decimals, ',', '.');
|
'number' => $number,
|
||||||
}
|
'decimals' => $decimals
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Handle null atau kosong
|
||||||
|
if ($number === null || $number === '') {
|
||||||
|
Log::debug('Number null atau kosong, return Rp 0');
|
||||||
|
return 'Rp 0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Konversi ke float dan handle error
|
||||||
|
try {
|
||||||
|
$number = (float) $number;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
Log::warning('Gagal konversi number ke float', [
|
||||||
|
'number' => $number,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
return 'Rp 0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validasi decimals
|
||||||
|
$decimals = max(0, (int) $decimals);
|
||||||
|
|
||||||
|
$result = 'Rp ' . number_format($number, $decimals, ',', '.');
|
||||||
|
Log::debug('Format Rupiah berhasil', ['result' => $result]);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
function formatAlamat($alamat)
|
function formatAlamat($alamat)
|
||||||
{
|
{
|
||||||
@@ -96,8 +182,6 @@
|
|||||||
$query->orWhereNull('dokumen_persetujuan');
|
$query->orWhereNull('dokumen_persetujuan');
|
||||||
},
|
},
|
||||||
)->get();
|
)->get();
|
||||||
// $sql = DB::getQueryLog();
|
|
||||||
|
|
||||||
|
|
||||||
if (sizeof($query) > 0) {
|
if (sizeof($query) > 0) {
|
||||||
$allow = false;
|
$allow = false;
|
||||||
@@ -238,9 +322,6 @@
|
|||||||
return $hasil;
|
return $hasil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// andy add
|
|
||||||
|
|
||||||
|
|
||||||
function hitungHariKerja($tanggalMulai, $tanggalSelesai)
|
function hitungHariKerja($tanggalMulai, $tanggalSelesai)
|
||||||
{
|
{
|
||||||
$tanggalMulai = Carbon::parse($tanggalMulai)->startOfDay();
|
$tanggalMulai = Carbon::parse($tanggalMulai)->startOfDay();
|
||||||
@@ -530,15 +611,161 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatNotifikasi($notifikasi)
|
function parsePembandingMigration($keterangan) {
|
||||||
{
|
$keterangan = preg_replace('/[-]{5,}/', '',$keterangan); // Hapus ------
|
||||||
$data = json_decode(json_encode($notifikasi->data));
|
$keterangan = preg_replace('/[.]{5,}/', '',$keterangan); // Hapus .....
|
||||||
$message = $data->message;
|
|
||||||
$data = $data->data;
|
$keterangan = preg_replace('/\s+/', ' ',$keterangan);
|
||||||
$notifikasi = [
|
$keterangan = preg_replace('/\s*\n\s*/', "\n",$keterangan);
|
||||||
'title' => 'Permohonan : ' . $data->nomor_registrasi,
|
|
||||||
'message' => $message,
|
// Pecah teks per baris untuk diproses
|
||||||
];
|
$lines = explode("\n",$keterangan);
|
||||||
return $notifikasi;
|
$cleaned = [];
|
||||||
|
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$line = trim($line);
|
||||||
|
if (!empty($line)) {
|
||||||
|
// Format angka dalam format Rp. 123.456.789
|
||||||
|
$line = preg_replace_callback('/Rp\.\s*([\d.,]+)/', function($matches) {
|
||||||
|
$angka = str_replace(['.', ','], '', $matches[1]);
|
||||||
|
return 'Rp. ' . number_format((int)$angka, 0, ',', '.');
|
||||||
|
}, $line);
|
||||||
|
|
||||||
|
// Jika ada tanda pagar (#), pisahkan menjadi baris baru
|
||||||
|
$line = str_replace('#', "\n#", $line);
|
||||||
|
|
||||||
|
$cleaned[] = $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode("\n", $cleaned);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get full path to internal storage file or external storage file
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getFilePath($path)
|
||||||
|
{
|
||||||
|
// define base path external storage (use .env) example: 'F:\path\to\storage' in windows
|
||||||
|
$externalBase = env('EXTERNAL_STORAGE_BASE_PATH', 'F:LPJ/lpj/LPJ Gambar/001/');
|
||||||
|
|
||||||
|
$segments = explode('/', $path);
|
||||||
|
|
||||||
|
if(strtoupper($segments[0]) === 'SURVEYOR'){
|
||||||
|
$year = $segments[1];
|
||||||
|
$month = ucfirst(strtolower($segments[2]));
|
||||||
|
$date = $segments[3];
|
||||||
|
$code = $segments[4];
|
||||||
|
$file = $segments[5] ?? '';
|
||||||
|
|
||||||
|
$extenalFullpath = $externalBase . $year . '/' . $month . '/' . $date . '/' . $code . '/' . $file;
|
||||||
|
|
||||||
|
if(File::exists($extenalFullpath)){
|
||||||
|
return $extenalFullpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not found in external storage, try to find in internal storage
|
||||||
|
if (Storage::exists($path)) {
|
||||||
|
return Storage::url('app/' . $path);
|
||||||
|
}
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function parseTimestamp(?string $timestamp): ?string
|
||||||
|
{
|
||||||
|
if (!$timestamp) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim whitespace dan normalize
|
||||||
|
$timestamp = trim($timestamp);
|
||||||
|
|
||||||
|
// Log untuk debugging
|
||||||
|
Log::info('Mencoba parsing timestamp: "' . $timestamp . '"');
|
||||||
|
|
||||||
|
// Parsing dengan DateTime native PHP untuk lebih robust
|
||||||
|
try {
|
||||||
|
// Pattern untuk format d/m/Y H:i:s
|
||||||
|
if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/', $timestamp, $matches)) {
|
||||||
|
$day = (int) $matches[1];
|
||||||
|
$month = (int) $matches[2];
|
||||||
|
$year = (int) $matches[3];
|
||||||
|
$hour = (int) $matches[4];
|
||||||
|
$minute = (int) $matches[5];
|
||||||
|
$second = (int) $matches[6];
|
||||||
|
|
||||||
|
// Validasi nilai
|
||||||
|
if ($day >= 1 && $day <= 31 && $month >= 1 && $month <= 12 && $year >= 1900 && $year <= 2100 &&
|
||||||
|
$hour >= 0 && $hour <= 23 && $minute >= 0 && $minute <= 59 && $second >= 0 && $second <= 59) {
|
||||||
|
|
||||||
|
// Buat DateTime object langsung
|
||||||
|
$dateTime = new \DateTime();
|
||||||
|
$dateTime->setDate($year, $month, $day);
|
||||||
|
$dateTime->setTime($hour, $minute, $second);
|
||||||
|
|
||||||
|
$result = $dateTime->format('Y-m-d H:i:s');
|
||||||
|
Log::info('Berhasil parsing dengan DateTime: ' . $timestamp . ' -> ' . $result);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pattern untuk format d/m/Y tanpa waktu
|
||||||
|
if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/', $timestamp, $matches)) {
|
||||||
|
$day = (int) $matches[1];
|
||||||
|
$month = (int) $matches[2];
|
||||||
|
$year = (int) $matches[3];
|
||||||
|
|
||||||
|
// Validasi nilai
|
||||||
|
if ($day >= 1 && $day <= 31 && $month >= 1 && $month <= 12 && $year >= 1900 && $year <= 2100) {
|
||||||
|
|
||||||
|
// Buat DateTime object langsung
|
||||||
|
$dateTime = new \DateTime();
|
||||||
|
$dateTime->setDate($year, $month, $day);
|
||||||
|
$dateTime->setTime(0, 0, 0);
|
||||||
|
|
||||||
|
$result = $dateTime->format('Y-m-d H:i:s');
|
||||||
|
Log::info('Berhasil parsing tanpa waktu dengan DateTime: ' . $timestamp . ' -> ' . $result);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Gagal parsing dengan DateTime: ' . $timestamp . '. Error: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback ke format Carbon standar untuk format lainnya
|
||||||
|
$formats = [
|
||||||
|
'Y-m-d H:i:s',
|
||||||
|
'Y-m-d',
|
||||||
|
'd-m-Y H:i:s',
|
||||||
|
'd-m-Y',
|
||||||
|
'j-n-Y H:i:s',
|
||||||
|
'j-n-Y',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($formats as $format) {
|
||||||
|
try {
|
||||||
|
$carbon = \Carbon\Carbon::createFromFormat($format, $timestamp);
|
||||||
|
|
||||||
|
if ($carbon && $carbon->format($format) === $timestamp) {
|
||||||
|
// Jika format tidak mengandung waktu, set ke awal hari
|
||||||
|
if (!str_contains($format, 'H:i:s')) {
|
||||||
|
$carbon = $carbon->startOfDay();
|
||||||
|
}
|
||||||
|
Log::info('Berhasil parsing dengan format ' . $format . ': ' . $timestamp . ' -> ' . $carbon->toDateTimeString());
|
||||||
|
return $carbon->toDateTimeString();
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Lanjut ke format berikutnya
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::error('Tidak dapat memparsing timestamp dengan format apapun: "' . $timestamp . '"');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user