Kategori: Laravel

  • Pengalaman Membangun Backend Manajemen Gudang Menggunakan Laravel


    Pendahuluan

    Banyak pengelolaan gudang yang masih sering kali menggunakan metode pencatatan manual dalam mengelola gudang, seperti melalui buku catatan atau spreadsheet sederhana. Metode ini memiliki banyak keterbatasan, di antaranya rentan terhadap kesalahan pencatatan, sulit dalam pelacakan barang, serta lambat dalam proses pembaruan data. Akibatnya, sering terjadi ketidaksesuaian antara stok fisik dengan catatan administrasi, yang dapat menyebabkan pemborosan, keterlambatan distribusi, hingga potensi kerugian akibat kehilangan barang atau kelebihan persediaan.

    Sebagai solusi kami mengembangkan sistem backend yang memungkinkan pemantauan stok secara real-time, otomatisasi pencatatan barang masuk dan keluar, integrasi dengan sistem lain, serta analisis data untuk mendukung pengambilan keputusan yang lebih akurat. Implementasi sistem ini tidak hanya meningkatkan efisiensi dan efektivitas dalam pengelolaan gudang, tetapi juga mengurangi risiko kesalahan serta meningkatkan transparansi dalam manajemen persediaan.


    Mengapa Laravel untuk Backend Aplikasi?

    Laravel dipilih karena kami sebagai mahasiswa TI memilih tools yang sudah sangat populer dan ekosistem yang lengkap sehingga waktu atau cost saat development menjadi lebih efisien karena kami tidak perlu banyak belajar lagi untuk mempelajari teknologi baru.

    Dari segi teknis, untuk membuat routing REST API itu sangat mudah, simpel, dan juga kodenya konsisten, kemudian setup Authentication dan Authorization yang mudah, beserta mempercepat pengembangan backend dengan adanya Eloquent ORM, mudah menambah middleware untuk melakukan validasi otomatis untuk menjaga keamanan atau sanitasi pada input, serta dokumentasi laravel yang jelas dan komunitas yang sudah besar.


    Gambaran Arsitektur Backend

    • Controller: Menerima request
    • Service: Mengolah logika
    • Model: Mengelola data dan relasi antar model
    • Middleware: Otorisasi role super admin dan admin gudang
    • Resources/Response JSON: Output JSON yang standar dan rapi

    Fitur Utama dalam Sistem Manajemen Gudang

    FiturKonsep Laravel yang DigunakanManfaat
    Login Multi-RoleAuth, Middleware, PolicyPembatasan akses per jabatan
    CRUD Data Barang & KategoriEloquent ORM, Form Request ValidationManajemen stok lebih akurat
    Riwayat Pengiriman & PenerimaanRelasi Many-to-Many / One-to-ManyTracking distribusi barang
    Dashboard Ringkasan StokAggregation QueryKeputusan cepat berdasarkan data

    Pengelolaan relasi antar-entitas merupakan bagian paling penting dalam memastikan setiap transaksi terekam dengan benar.


    Contoh Implementasi API

    Route API (api.php):

    Route::middleware(['role:SuperAdmin'])->group(function () {
            Route::resource('gudangs', GudangController::class);
            Route::patch('gudangs/{id}/activate', [GudangController::class, 'activate'])->name('gudangs.activate');
            Route::patch('gudangs/{id}/deactivate', [GudangController::class, 'deactivate'])->name('gudangs.deactivate');
        });
    PHP

    Controller method untuk CRUD:

    <?php
    
    namespace App\Http\Controllers;
    
    use App\Http\Resources\GudangIndexResource;
    use Illuminate\Http\Request;
    use App\Models\GudangDanToko;
    use Illuminate\Support\Facades\DB;
    use Illuminate\Validation\ValidationException;
    use Illuminate\Database\Eloquent\ModelNotFoundException;
    
    class GudangController extends Controller
    {
        public function index()
        {
            try {
                $GudangDanToko = GudangDanToko::where('kategori_bangunan', 0)
                    ->orderBy('id')
                    ->get([
                        'id',
                        'nama_gudang_toko',
                        'alamat',
                        'no_telepon',
                        'flag',
                    ]);
    
                $headings = [
                    "NO",
                    "Nama Gudang",
                    "Alamat",
                    "No telepon",
                    "Status"
                ];
    
                return response()->json([
                    'status' => true,
                    'message' => 'Data Gudang',
                    'data' => [
                        'gudangs' => GudangIndexResource::collection($GudangDanToko),
                        /** @var array<int, string> */
                        'headings' => $headings,
                    ],
                ]);
            } catch (\Exception $e) {
                return response()->json([
                    'status' => false,
                    'message' => 'Terjadi kesalahan saat mengambil data gudang.',
                    'error' => $e->getMessage(),
                ], 500);
            }
        }
    
        public function create()
        {
            try {
                return response()->json([
                    'status' => true,
                    'message' => 'Form Tambah Gudang',
                ]);
            } catch (\Exception $e) {
                return response()->json([
                    'status' => false,
                    'message' => 'Terjadi kesalahan saat menyiapkan form tambah gudang.',
                    'error' => $e->getMessage(),
                ], 500);
            }
        }
    
        public function store(Request $request)
        {
            $gudang = GudangDanToko::all();
            try {
                $validated = $request->validate([
                    'nama_gudang_toko' => 'required|string|max:255',
                    'alamat' => 'nullable|string',
                    'no_telepon' => 'nullable|string|max:20',
                ]);
    
                $gudang = array_merge($validated, ['kategori_bangunan' => 0]);
    
                DB::transaction(function () use ($gudang) {
                    GudangDanToko::create($gudang);
                }, 3);
    
                return response()->json([
                    'status' => true,
                    'message' => "Berhasil menambahkan data gudang.",
                ], 201);
            } catch (ValidationException $e) {
                return response()->json([
                    'status' => false,
                    'message' => 'Data yang diberikan tidak valid.',
                    'errors' => $e->errors()
                ], 422);
            } catch (\Exception $e) {
                return response()->json([
                    'status' => false,
                    'message' => 'Terjadi kesalahan saat menyimpan gudang.',
                    'error' => $e->getMessage(),
                ], 500);
            }
        }
    
        public function show(string $id)
        {
            try {
                $gudang = GudangDanToko::findOrFail($id, [
                    'id',
                    'nama_gudang_toko',
                    'alamat',
                    'no_telepon',
                    'flag'
                ]);
    
                return response()->json([
                    'status' => true,
                    'message' => "Detail Data {$gudang->nama_gudang_toko}",
                    'data' => new GudangIndexResource($gudang),
                ]);
            } catch (ModelNotFoundException $e) {
                return response()->json([
                    'status' => false,
                    'message' => "Data gudang tidak ditemukan.",
                    'error' => $e->getMessage(), 
                ], 404);
            } catch (\Exception $e) {
                return response()->json([
                    'status' => false,
                    'message' => "Terjadi kesalahan saat mengambil detail data gudang.",
                    'error' => $e->getMessage(),
                ], 500);
            }
        }
    
        public function edit(string $id)
        {
            try {
                $gudang = GudangDanToko::findOrFail($id, [
                    'id',
                    'nama_gudang_toko',
                    'alamat',
                    'no_telepon'
                ]);
    
                return response()->json([
                    'status' => true,
                    'message' => "Data untuk Form Edit Gudang",
                    'data' => new GudangIndexResource($gudang),
                ]);
            } catch (ModelNotFoundException $e) {
                return response()->json([
                    'status' => false,
                    'message' => "Data gudang tidak ditemukan.",
                    'error' => $e->getMessage(),
                ], 404);
            } catch (\Exception $e) {
                return response()->json([
                    'status' => false,
                    'message' => "Terjadi kesalahan saat mengambil data untuk form edit Gudang.",
                    'error' => $e->getMessage(),
                ], 500);
            }
        }
    
        public function update(Request $request, string $id)
        {
            try {
    
                $validated = $request->validate([
                    'nama_gudang_toko' => 'required|string|max:255',
                    'alamat' => 'nullable|string',
                    'no_telepon' => 'nullable|string|max:20',
                ]);
    
                $gudang = GudangDanToko::findOrFail($id);
    
                DB::transaction(function () use ($gudang, $validated) {
                    $gudang->update($validated);
                }, 3);
    
                return response()->json([
                    'status' => true,
                    'message' => "{$gudang->nama_gudang_toko} berhasil diperbarui.",
                    'data' => new GudangIndexResource($gudang)
                ]);
            } catch (ValidationException $e) {
                return response()->json([
                    'status' => false,
                    'message' => 'Data yang diberikan tidak valid.',
                    'errors' => $e->errors(),
                ], 422);
            } catch (ModelNotFoundException $e) {
                return response()->json([
                    'status' => false,
                    'message' => "Data gudang tidak ditemukan.",
                    'error' => $e->getMessage(),
                ], 404);
            } catch (\Exception $e) {
                return response()->json([
                    'status' => false,
                    'message' => "Terjadi kesalahan saat memperbarui data gudang.",
                    'error' => $e->getMessage(),
                ], 500);
            }
        }
    
        public function deactivate(string $id)
        {
            try {
                $gudang = GudangDanToko::findOrFail($id);
    
                if ($gudang->flag == 0) {
                    return response()->json([
                        'status' => false,
                        'message' => "{$gudang->nama_gudang_toko} sudah dinonaktifkan.",
                    ], 409);
                }
    
                DB::transaction(function () use ($gudang) {
                    $gudang->update(['flag' => 0]);
                }, 3);
    
                return response()->json([
                    'status' => true,
                    'message' => "{$gudang->nama_gudang_toko} berhasil dinonaktifkan.",
                    'data' => new GudangIndexResource($gudang),
                ]);
            } catch (ModelNotFoundException $e) {
                return response()->json([
                    'status' => false,
                    'message' => "Data gudang tidak ditemukan.",
                    'error' => $e->getMessage(),
                ], 404);
            } catch (\Exception $e) {
                return response()->json([
                    'status' => false,
                    'message' => "Terjadi kesalahan saat menonaktifkan data gudang.",
                    'error' => $e->getMessage(),
                ], 500);
            }
        }
    
        public function activate(string $id)
        {
            try {
                $gudang = GudangDanToko::findOrFail($id);
    
                if ($gudang->flag == 1) {
                    return response()->json([
                        'status' => false,
                        'message' => " {$gudang->nama_gudang_toko} sudah diaktifkan.",
                    ], 400);
                }
    
                DB::transaction(function () use ($gudang) {
                    $gudang->update(['flag' => 1]);
                }, 3);
    
                return response()->json([
                    'status' => true,
                    'message' => "{$gudang->nama_gudang_toko} berhasil diaktifkan.",
                    'data' => new GudangIndexResource($gudang),
                ], 201);
            } catch (ModelNotFoundException $e) {
                return response()->json([
                    'status' => false,
                    'message' => "Data gudang tidak ditemukan.",
                    'error' => $e->getMessage(),
                ], 404);
            } catch (\Exception $e) {
                return response()->json([
                    'status' => false,
                    'message' => "Terjadi kesalahan saat mengaktifkan data gudang.",
                    'error' => $e->getMessage(),
                ], 500);
            }
        }
    }
    PHP

    Response JSON terstruktur:

    <?php
    
    namespace App\Http\Resources;
    
    use Carbon\Carbon;
    use App\Helpers\TimeHelpers;
    use Illuminate\Http\Request;
    use Illuminate\Http\Resources\Json\JsonResource;
    
    class GudangIndexResource extends JsonResource
    {
        /**
         * Transform the resource into an array.
         *
         * @return array<string, mixed>
         */
        public function toArray(Request $request): array
        {
            return [
                'id' => (int) $this->id,
                'nama_gudang' => $this->nama_gudang_toko,
                'alamat' => $this->alamat,
                'no_telepon' => $this->no_telepon,
                'status' => $this->flag ? 'Aktif' : 'Nonaktif',
            ];
        }
    }
    PHP

      Hasil yang Didapat

      Dengan ini backend untuk manajemen gudang sudah siap digunakan dengan client manapun (website, mobile) dan mudah untuk diintegrasikan dengan menambahkan dokumentasi API yang rapi dan mudah untuk dibaca dan dimengerti bagi developer frontend web maupun mobile. Dokumentasi API bisa melalui Swagger OpenAPI atau bisa membuat dokumentasi manual melalui fitur yang dihadirkan di Postman.


      Kesimpulan

      Laravel sangat cocok untuk backend REST API maupun dibuat fullstack dan untuk pemula yang ingin belajar menyelami dunia backend, disini teman-teman bisa banyak belajar mengenai arsitektur server-side dan keamanan akses sehingga teman-teman bisa menghasilkan backend yang cepat untuk deliver sehingga bisa dipakai segera oleh aplikasi client. Jadi jika teman-teman baru mulai belajar backend, Laravel adalah framework yang tepat untuk memulai sekaligus membangun proyek nyata yang bermanfaat bagi masyarakat luas.