Konco mengikuti sejumlah konvensi kode yang kuat. Bagian ini wajib dibaca engineer baru sebelum menulis kode pertama — banyak pola tidak tertulis di dokumentasi framework tapi konsisten di seluruh codebase.
Service Layer pattern
Business logic kompleks wajib di app/Services/, bukan di controller. Controller tetap thin — hanya validasi, delegasi ke service, dan return response.
Services yang ada
| Service | Lokasi | Tanggung jawab |
|---|---|---|
OrderService | app/Services/OrderService.php | Proses order: subscribe & topup |
PaymentService | app/Services/PaymentService.php | Integrasi payment gateway |
MonitpayService | app/Services/MonitpayService.php | Spesifik MonitPay integration |
AdminRequestService | app/Services/AdminRequestService.php | Workflow approval admin request |
TeamService | app/Services/TeamService.php | Manajemen tim multi-user |
PermissionService | app/Services/PermissionService.php | Role & permission handling |
Pola controller → service
// Controller — thin
class SubscribeController extends Controller
{
public function store(Request $request, OrderService $orderService)
{
$validated = $request->validate([...]);
$order = $orderService->createSubscribe($validated);
return redirect()->route('member.subscribe.index');
}
}Model traits wajib
Setiap model baru wajib memakai kombinasi trait ini:
| Trait | Fungsi |
|---|---|
App\Traits\UUID | Auto-generate UUIDv4 primary key saat creating |
App\Traits\WithAuthor | Auto-isi created_by / updated_by dari Auth::id() saat saving |
App\Traits\DateTimeTrait | (opsional) Utility date/time formatting |
Blueprint model baru
<?php
namespace App\Models;
use App\Traits\UUID;
use App\Traits\WithAuthor;
use Illuminate\Database\Eloquent\Model;
class YourModel extends Model
{
use UUID, WithAuthor;
protected $keyType = 'string';
public $incrementing = false;
protected $fillable = [
'field1',
'field2',
];
// ...
}Migration blueprint
Schema::create('your_models', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('field1');
$table->uuid('created_by')->nullable();
$table->uuid('updated_by')->nullable();
$table->timestamps();
});Bahasa Indonesia
| Area | Bahasa | Catatan |
|---|---|---|
| Komentar kode | Indonesia | Bukan Inggris |
| Nama field database | Indonesia | e.g. expired, is_active, created_by (pengecualian: stock_ads_account_id) |
| Label UI | Indonesia | e.g. “Sedang diproses”, “Akun Aktif” |
| Nama class/method | Inggris | Konvensi PSR-12 |
// Benar
// cek apakah akun sudah di-share
if ($account->isShared()) { ... }
// Salah
// check if account is shared
if ($account->isShared()) { ... }PSR-12
| Aspek | Konvensi |
|---|---|
| Styling | PSR-12 |
| Linter | laravel/pint (dev dependency) — jalankan ./vendor/bin/pint |
| Formatting | 4 spaces indent, opening brace same line untuk class/method, next line untuk control structures |
Livewire 2.x
Real-time UI memakai Livewire — components di app/Http/Livewire/, views di resources/views/livewire/.
Struktur Livewire Konco
app/Http/Livewire/
├── Admin/ # Livewire admin portal
│ ├── BusinessManagerSelectWire.php
│ └── ...
└── Member/ # Livewire member portal
├── AdsAccountMemberWire.php
├── AdsBalanceIndexWire.php
├── AdsManagerIndexWire.php
├── SubscribeAdsList.php
├── WithdrawIndexWire.php
└── ...Pola Livewire
class AdsAccountMemberWire extends Component
{
public $search;
public $filterStatus;
public function render()
{
$accounts = AdsAccount::query()
->where('member_bm_id', auth()->user()->member->id)
->when($this->search, fn($q, $v) => $q->where('name', 'like', "%{$v}%"))
->when($this->filterStatus, fn($q, $v) => $q->where('status', $v))
->paginate(10);
return view('livewire.member.ads-account-member-wire', compact('accounts'));
}
}Observer pattern
Model observer di app/Observers/:
| Observer | Di-observe model | Fungsi |
|---|---|---|
AdminRequestObserver | AdminRequest | Track lifecycle admin request (created, updated, status change) |
Registrasi observer
Observer di-register di App\Providers\EventServiceProvider:
protected $observers = [
AdminRequest::class => [AdminRequestObserver::class],
];Events & broadcasting
Events di app/Events/:
| Event | Trigger | Channel |
|---|---|---|
AdminRequestCreated | Admin request baru dibuat | Private channel admin |
TicketMessageCreated | Pesan tiket dikirim | Private channel tiket |
TicketStatusUpdated | Status tiket berubah | Private channel tiket |
Broadcasting via Laravel Reverb (WebSocket) — config di config/reverb.php. Frontend memakai Laravel Echo + Pusher JS (kompatibel Reverb).
Helper functions
Helper global di-auto-load via composer.json autoload.files:
| File | Isi |
|---|---|
app/helper/helpers.php | Utility umum — date formatting, text limiting, decode(), send_email(), send_report() |
app/helper/role_helper.php | Role & permission utilities |
app/helper/asset_version.php | Asset versioning untuk cache busting |
// composer.json
"autoload": {
"files": [
"app/helper/helpers.php",
"app/helper/role_helper.php",
"app/helper/asset_version.php"
]
}Helper tersedia global di seluruh aplikasi — tidak perlu use atau import.
Middleware custom
| Middleware | Alias | Fungsi |
|---|---|---|
TwoFactorAuthentication | 2fa | Enforce 2FA verification |
SandboxAccessMiddleware | sandbox | Restrict route ke environment non-production |
EnsureUserHasTeamMiddleware | — | Validasi team membership |
RedirectIfCannotAccessDashboard | — | Dashboard access control |
LanguageMiddleware | — | Multi-language switching |
cekAuth:member | — | Custom guard check untuk member |
Registrasi di app/Http/Kernel.php $routeMiddleware.
Config custom
| File | Isi |
|---|---|
config/facebook.php | Facebook API credentials lokal (web app — meski Graph API di API engine) |
config/monitpay.php | MonitPay payment gateway config |
config/myconfig.php | Application-specific settings |
config/google2fa.php | 2FA config |
config/reverb.php | WebSocket broadcasting config |
config/passport.php | Passport OAuth2 config |
Routing convention
| Area | File | Prefix | Guard |
|---|---|---|---|
| Admin | routes/_admin.php | /admin | auth:admin, 2fa |
| Member | routes/_member.php | /member | cekAuth:member, permission:* |
| Profile | routes/_profile.php | (included in admin/member) | auth |
| Public + auth | routes/web.php | / | (mixed) |
| API/webhook | routes/api.php | /api | (mixed) |
| Broadcast channels | routes/channels.php | — | (broadcast auth) |
| Console | routes/console.php | — | — |
Naming convention
// Route name pattern: <scope>.<resource>.<action>
Route::get('/', [UserController::class, 'index'])->name('admin.user.index');
Route::post('/', [UserController::class, 'store'])->name('admin.user.store');
Route::get('/{id}/edit', [UserController::class, 'edit'])->name('admin.user.edit');Langkah berikutnya
- Detail RSA handshake? Baca Auth & Security.
- Database & model? Lihat Database.
- Service eksternal? Lihat External Services.