Overview

Haryadi Framework adalah micro-MVC PHP.

Format
HTML CSS PHP

# Persyaratan

  • PHP >= 8.1
  • Composer
  • Laragon dan Xampp

# Instalasi

Mulai dari repository kosong.

Dari Git
git clone https://github.com/Captzuzo/haryadi.git
cd haryadi
composer install
cp .env.example .env
php haryadi layanan
              

# Struktur Proyek

Ringkasan direktori standar:

haryadi/
├─ app/
│  ├─ Controllers/
│  ├─ Models/
│  ├─ Middleware/
│  ├─ Providers/
│  └─ Services/
├─ core/
│  ├─ Router.php
│  ├─ View.php
│  ├─ Database.php
│  └─ ...
├─ public/
│  └─ index.php
├─ resources/
│  └─views/
│     └─welcome.haryadi.php
├─ database/
│  ├─ migrations/
│  └─ seeders/
├─ routes.php
└─ haryadi (CLI entry)

# Routing

Routing di Haryadi Framework mengatur hubungan antara URL dan aksi (controller atau callback) yang akan dijalankan. Sistem routing ini ringan, cepat, dan mendukung berbagai metode HTTP seperti GET, POST, PUT, PATCH, dan DELETE.

1. Contoh Dasar

use Haryadi\Core\Router;
use App\Controllers\WelcomeController;

Router::get('/', [WelcomeController::class, 'index'])->name('home');
Router::post('/user', [UserController::class, 'store']);
Router::delete('/user/{id}', [UserController::class, 'destroy']);

Masing-masing route dapat diberikan name() untuk memudahkan referensi.

2. Struktur Kelas Route

Kelas Route merepresentasikan satu definisi rute tunggal.

namespace Haryadi\Core;

class Route
{
    public string $method;
    public string $path;
    public $handler;
    public ?string $name = null;

    public function __construct(string $method, string $path, $handler)
    {
        $this->method = strtoupper($method);
        $this->path = $path;
        $this->handler = $handler;
    }

    public function name(string $name): self
    {
        $this->name = $name;
        return $this;
    }
}

3. Kelas Router

Kelas Router bertanggung jawab mendaftarkan, menyimpan, dan mengeksekusi rute. Ia juga mengelola instance tunggal (singleton) agar semua route berada dalam satu konteks.

Metode Deskripsi Contoh
Router::get($path, $handler) Menangani request HTTP GET. Router::get('/', [HomeController::class, 'index']);
Router::post($path, $handler) Menangani request HTTP POST. Router::post('/login', [AuthController::class, 'store']);
Router::put($path, $handler) Menangani request HTTP PUT (update data). Router::put('/user/{id}', [UserController::class, 'update']);
Router::patch($path, $handler) Menangani request HTTP PATCH (partial update). Router::patch('/user/{id}', [UserController::class, 'patch']);
Router::delete($path, $handler) Menangani request HTTP DELETE. Router::delete('/user/{id}', [UserController::class, 'destroy']);
$route->name('nama') Memberi nama pada route untuk kemudahan referensi. Router::get('/dashboard', [DashController::class, 'index'])->name('dashboard');

4. Dispatching Request

Fungsi dispatch() digunakan untuk mencocokkan request pengguna dengan daftar route yang telah didaftarkan.

public static function dispatch(Request $request): Response
{
    $uri = rtrim(strtok($_SERVER['REQUEST_URI'], '?'), '/') ?: '/';
    $method = $_SERVER['REQUEST_METHOD'];
    $router = self::getInstance();

    foreach ($router->getRoutes() as $route) {
        if ($route->method === $method && $route->path === $uri) {
            if (is_array($route->handler)) {
                [$controller, $func] = $route->handler;
                $response = (new $controller())->$func();
                return $response instanceof Response ? $response : new Response($response);
            } elseif (is_callable($route->handler)) {
                $response = call_user_func($route->handler, $request);
                return $response instanceof Response ? $response : new Response($response);
            }
        }
    }

    // Route tidak ditemukan
    return new Response('404 Not Found', 404);
}

⚠️ Catatan: Jika tidak ada rute yang cocok, framework akan mengembalikan 404 Not Found.

5. Helper Fungsi Terkait

Beberapa helper berguna untuk mendukung sistem routing:

  • env($key, $default = null) — Membaca konfigurasi dari file .env.
  • view($view, $data = []) — Render tampilan dari direktori views/.
  • config($key, $default = null) — Mengambil nilai dari file konfigurasi.
  • throw_if($condition, $exception) — Melempar exception jika kondisi terpenuhi.
  • base_path($path = '') — Mendapatkan path absolut project.

💡 Tips: Helper dapat diperluas di file src/Core/helpers.php untuk fungsi-fungsi tambahan.

# Controllers

Controller standar di Haryadi Framework adalah kelas yang berisi method untuk menangani request dan mengembalikan response (view, redirect, atau JSON).

namespace App\Controllers;

use Haryadi\Core\Controller;

class UserController extends Controller
{
    public function index()
    {
        $users = \App\Models\User::all();
        return $this->view('users.index', compact('users'));
    }

    public function store()
    {
        $data = $this->input();
        $this->validate([
            'nama' => 'required',
            'email' => 'required|email',
        ], $data);

        \App\Models\User::create($data);
        return $this->redirect('/users');
    }
}

Kelas Dasar: Haryadi\Core\Controller

Kelas Controller adalah fondasi utama yang digunakan oleh semua controller dalam framework Haryadi. Menyediakan method bantu seperti view(), redirect(), input(), validate(), dan json().

Metode Deskripsi Contoh
view(string $view, array $data = []) Render view dengan data tertentu. $this->view('welcome', ['title' => 'Halo Dunia']);
redirect(string $url) Mengarahkan pengguna ke URL lain. $this->redirect('/login');
input(?string $key = null, $default = null) Ambil data dari $_GET atau $_POST. $username = $this->input('username');
validate(array $rules, array $data) Validasi sederhana dengan aturan seperti required dan email. $this->validate(['email' => 'required|email'], $data);
json(array|object $data, int $status = 200) Mengirim response dalam format JSON. $this->json(['success' => true]);
namespace Haryadi\Core;

class Controller
{
    protected function view(string $view, array $data = [])
    {
        return View::make($view, $data);
    }

    protected function redirect(string $url)
    {
        header("Location: {$url}");
        exit;
    }

    protected function input(?string $key = null, $default = null)
    {
        $input = array_merge($_GET ?? [], $_POST ?? []);
        return $key ? ($input[$key] ?? $default) : $input;
    }

    protected function validate(array $rules, array $data): void
    {
        foreach ($rules as $field => $rule) {
            $value = $data[$field] ?? null;
            $parts = explode('|', $rule);

            foreach ($parts as $r) {
                if ($r === 'required' && empty($value)) {
                    throw new \Exception("Field '{$field}' wajib diisi.");
                }
                if ($r === 'email' && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
                    throw new \Exception("Field '{$field}' harus berupa email valid.");
                }
            }
        }
    }

    protected function json(array|object $data, int $status = 200): void
    {
        http_response_code($status);
        header('Content-Type: application/json');
        echo json_encode($data);
        exit;
    }
}

💡 Tips: Semua controller di aplikasi sebaiknya mewarisi (extends) kelas ini agar memiliki akses ke seluruh utilitas built-in framework.

Models

Model di Haryadi Framework digunakan untuk berinteraksi dengan database menggunakan pendekatan ORM sederhana berbasis PDO. Setiap model mewarisi Haryadi\Core\Model dan otomatis terhubung ke konfigurasi database melalui Database::connect().

1. Contoh Model

namespace App\Models;

use Haryadi\Core\Model;
use Haryadi\Core\Factories\HasFactory;

class User extends Model
{
    use HasFactory;

    protected static string $table = 'users';

    protected array $fillable = [
        'name', 'email', 'password'
    ];

    public bool $timestamps = true;
}

2. Properti Penting

  • $table → Nama tabel database
  • $primaryKey → Default id, bisa diubah
  • $fillable → Field yang dapat diisi massal (mass assignment)
  • $hidden → Field yang disembunyikan saat serialisasi
  • $casts → Konversi otomatis tipe data (int, bool, datetime, dll)
  • $timestamps → Aktifkan kolom created_at dan updated_at

3. Metode Utama

Metode Deskripsi Contoh
all() Ambil semua data dari tabel User::all();
find($id) Cari data berdasarkan primary key User::find(1);
where($column, $value) Ambil 1 data sesuai kondisi User::where('email', 'mail@test.com');
create($data) Simpan data baru ke tabel $user->create($_POST);
update($id, $data) Perbarui data berdasarkan ID $user->update(1, ['name' => 'Deni']);
delete($id) Hapus data berdasarkan ID User::delete(3);

4. Contoh Lengkap

$user = new User();

// Simpan data baru
$user->create([
  'name' => 'Deni Agus Haryadi',
  'email' => 'deni@example.com',
  'password' => password_hash('123456', PASSWORD_BCRYPT)
]);

// Ambil semua user
$users = User::all();

// Cari user
$single = User::find(1);

// Update data
$user->update(1, ['name' => 'Deni A. Haryadi']);

// Hapus data
User::delete(2);

5. CLI Generator

Untuk membuat model baru dengan cepat, gunakan perintah CLI bawaan:

php haryadi make:model NamaModel

📁 File akan otomatis dibuat di app/Models/NamaModel.php.

💡 Tip: Gunakan protected $timestamps = true; agar otomatis menambahkan created_at dan updated_at.

Views (Templating)

Sistem View di Haryadi Framework menggunakan pendekatan sederhana: file PHP murni yang berada di resources/views dan dapat dipanggil melalui helper view().

📁 Struktur View

resources/
└── views/
    ├── users/
    │   └── index.haryadi.php
    └── layout/
        └── main.haryadi.php

1. Contoh Penggunaan

// Controller
namespace App\Controllers;

use Haryadi\Core\Controller;
use App\Models\User;

class UserController extends Controller
{
    public function index()
    {
        $users = User::all();
        return view('users.index', compact('users'));
    }
}

2. File View

// resources/views/users/index.haryadi.php
<?php foreach($users as $user): ?>
  <div class="user">
    <?= e($user['name']) ?>
  </div>
<?php endforeach; ?>

Setiap file .haryadi.php dapat berisi HTML, PHP, atau kombinasi keduanya. Framework akan melakukan render dengan output buffering sehingga hasilnya dikembalikan sebagai string.

3. Kelas View

namespace Haryadi\Core;

class View
{
    public static function make($view, $data = [])
    {
        $file = BASE_PATH . "/resources/Views/" . str_replace('.', '/', $view) . ".haryadi.php";

        if (!file_exists($file)) {
            throw new \Exception("View '{$view}.haryadi.php' tidak ditemukan!");
        }

        extract($data); // Konversi array ke variabel
        ob_start();
        include $file;
        return ob_get_clean(); // Kembalikan hasil render
    }
}

4. Helper Fungsi

  • view($view, $data = []) — Render file view
  • redirect($path) — Redirect ke URL lain
  • e($value) — Escape output agar aman dari XSS

5. Contoh Lengkap

// Controller
public function showProfile()
{
    $user = User::find(1);
    return view('profile.show', ['user' => $user]);
}

// resources/views/profile/show.haryadi.php
<h1>Profil Pengguna</h1>
<p>Nama: <?= e($user->name) ?></p>
<p>Email: <?= e($user->email) ?></p>
💡
Tips: Kamu dapat membuat struktur folder view yang dalam menggunakan dot notation. Contoh: view('dashboard.user.profile') akan mencari resources/views/dashboard/user/profile.haryadi.php.

Middleware

Middleware dapat didefinisikan di app/Middleware dan diregistrasi di provider router.

// app/Middleware/AuthMiddleware.php
class AuthMiddleware
{
    public function handle($request, $next)
    {
        if(empty($_SESSION['user'])){
            return redirect('/login');
        }
        return $next($request);
    }
}
💡
Tips: Kamu dapat membuat struktur folder view yang dalam menggunakan dot notation. Contoh: view('dashboard.user.profile') akan mencari resources/views/dashboard/user/profile.haryadi.php.

CLI — php haryadi

CLI tool sederhana untuk menjalankan server, migrasi, make commands.


php haryadi layanan           : Menjalankan server lokal                      
php haryadi route:list        : Menampilkan semua route                       
php haryadi buat:model        : Membuat model baru                            
php haryadi buat:controller   : Membuat controller baru                       
php haryadi buat:view         : Membuat view baru                             
php haryadi buat:migration    : Membuat migration baru                        
php haryadi buat:seeder       : Menjalankan seeder baru                       
php haryadi migrasi           : Menjalankan migrasi database                  
php haryadi db:seed           : Menjalankan seeder database                   
php haryadi bantuan           : Menampilkan daftar command

Migrations & Seeders

Migrations adalah file PHP di database/migrations yang digunakan untuk membuat dan mengubah struktur tabel database. Setiap migration memiliki method up() untuk membuat tabel atau kolom, dan down() untuk rollback.

class CreateUsersTable {
  public function up() {
    // buat tabel users
  }

  public function down() {
    // drop tabel
  }
}
💡
Tips: Gunakan nama migration dengan format php haryadi buat:migration create_nama_table untuk kemudahan generate nama tabel.

1. Contoh Migration

// database/migrations/2025_10_29_123456_create_users_table.php
return new class {
    public function up() {
        Tabel::create('users', function (Bagan $tabel) {
            $tabel->id();
            $tabel->string('name');
            $tabel->timestamps();
        });
    }

    public function down() {
        Tabel::dropIfExists('users');
    }
};

2. Seeders

Seeders digunakan untuk mengisi data awal database. File disimpan di database/seeders dengan method run().

// database/seeders/UserSeeder.php
namespace Database\Seeders;

use Haryadi\Core\Model;

class UserSeeder extends Seeder
{
    public function run(): void
    {
        Model::factory(10)->create();
    }
}
💡
Tips: Jalankan seeder dengan command php haryadi make:seeder UserSeeder dan panggil call(UserSeeder::class) untuk mengeksekusi.

Authentication & Authorization

Kerangka menyediakan helper auth sederhana: auth()->user(), guest(), dan trait role-based basic.

// contoh register
$user = App\Models\User::create([ 
'name'=>'Deni', 
'email'=>'d@example.com', 
'password'=>password_hash('secret', PASSWORD_DEFAULT) ]);

// middleware pengecekan role
if(!in_array('admin', $user->roles ?? [])) abort(403);

Configuration & Environment

Gunakan file .env untuk konfigurasi. Config di config/ sebagai array PHP.

// .env
APP_ENV=local
DB_HOST=127.0.0.1
DB_DATABASE=haryadi
DB_USERNAME=root
DB_PASSWORD=

Error Handling & Debug

Core menyediakan handler error yang bisa ditoggle via APP_DEBUG. Untuk page error khusus, letakkan file di resources/errors.

// contoh menangani exception
try{
  // kode
}catch(\Exception $e){
  if(config('app.debug')){
    dd($e->getMessage(), $e->getTrace());
  }
  view('errors.500');
}

Testing

Tidak terikat ke framework testing tertentu. Direkomendasikan menggunakan PHPUnit.

composer require --dev phpunit/phpunit
./vendor/bin/phpunit --configuration phpunit.xml

Deployment

Langkah umum:

  1. Set environment production pada file .env
  2. Upload kode ke server (Git/FTP)
  3. Install composer: composer install --no-dev --optimize-autoloader
  4. Jalankan migrasi: php haryadi migrate --force
  5. Set webroot ke folder public/
  6. Pastikan permission storage & uploads writable

Troubleshooting & FAQ

Error Composer autoload (PSR-4)

Pastikan namespace & path sesuai composer.json; jalankan composer dump-autoload -o.

View not found

Periksa path file view di resources/views dan helper view('path.name') menggunakan tanda titik.

Database connection

Cek kredensial di .env dan coba php -r "new PDO('mysql:host=...')" untuk quick test.

Contribute & Development

Panduan singkat untuk kontributor:

  • Fork repo, buat branch feature/nama
  • Tulis test untuk fitur baru
  • Submit PR dengan deskripsi perubahan
  • Ikuti code style PSR-12