Skip to content

Meeting 10

Pada pertemuan kali ini, kita akan membahas Paradigma Baru dalam PHP. Dunia pengembangan PHP terus berkembang, dan dengan penambahan fitur-fitur baru dalam PHP, kita juga akan memakai pengelola dependensi PHP yang biasa kita kenal dengan Composer.

Composer

Composer adalah alat manajemen paket (package management tool) untuk bahasa pemrograman PHP. digunakan oleh pengembang PHP untuk mengelola dependensi (library, framework, atau komponen lainnya) untuk pengembangan lebih terstruktur dan mudah dikelola.

Autoloading

Autoloading adalah teknik yang digunakan untuk memuat kelas PHP secara otomatis tanpa perlu menggunakan pernyataan require atau include secara manual. Dalam PHP, Anda dapat menggunakan PSR-4 (PHP-FIG Standard Recommendation 4) untuk mengatur autoloading kelas. Composer akan membantu untuk mengatur autoloading dengan mudah.

Pembuatan Proyek PHP Dengan PSR-4 Autoload

Pada kali ini kita akan membuat aplikasi dari PHP dengan cara yang lebih yang paling baru yaitu dengan Autoload, kita akan membuat mini proyek dan akan kita beri nama MiniMarkPlace. untuk langkah pertama memulai pembuatan aplikasi kita buatlah folder baru dengan nama mini-mark-place.

Inisiasi Autoload

  • masuk ke folder mini-mark-place dan buka terminal lalu ketikan perintah berikut:
    sh
    composer init
    composer init
    Detail Inisiasi
    • makan akan keluar beberapa pengaturan untuk pengerjaan dari aplikasi yang akan di kerjakan.
    • Package name (<vendor>/<name>): masukan nama proyek yang akan di kerjakan contoh latihan/mini-mark-place.
    • Description []: masukan sedikit penjelasan tentang aplikasi yang akan di buat, atau boleh di kosongkan.
    • Author: masukan nama pembuat, atau boleh di kosongkan.
    • Minimum Stability []: Bisa dikosongkan, namun jika dimasukan akan menjadi minimal versi dari dependensi yang akan digunakan.
    • Package Type (e.g. library, project, metapackage, composer-plugin) []: lewati saja langkah ini, untuk menggunakan pengaturan defult dari composer.
    • License []: dapat dikosongkan, atau jika kita memiliki lisensi atas produk kita sendiri bisa diisi.
    • Add PSR-4 autoload mapping: tambahkan /src untuk menentukan autoload, mengeksekusi path aplikasi
  • Jika sudah menginisiasi composer dan mengukuti langkahnya, maka akan menghasilkan folder dan file baru seperti di bawah ini:
├── src
├── vendor
└── composer.json
├── src
├── vendor
└── composer.json
  • untuk membuat prefix yang lebih bagus dalam pemanggilan class ubah sintaks di composer.json pada bagian autoload menjadi
json
"autoload": {
    "psr-4": {
        "MiniMarkPlace\\": "src/"
    }
},
"autoload": {
    "psr-4": {
        "MiniMarkPlace\\": "src/"
    }
},
  • buat file baru di dalam folder src dengan nama index.php dengan sintaks di bawah ini.
php
<?php

require __DIR__.'/../vendor/autoload.php';

echo "Hello world";
<?php

require __DIR__.'/../vendor/autoload.php';

echo "Hello world";
Penjelasan Code
  • index.php file ini akan menjadi pintu masuk untuk aplikasi yang akan kita buat
  • require __DIR__.'/../vendor/autoload.php': menggunakan autoloader yang sudah terinstall
  • __DIR__ merupakan function dari php mengembalikan letak path dari file.
  • echo "Hello World": menampilkan teks hello world
  • Jalan kan aplikasi di terminal dengan sintaks
sh
php -S localhost:2000 -t src
php -S localhost:2000 -t src

Atau jika ingin menjalan kan melalui laragon, buat file baru di folder proyek dengan nama .htaccess, dan tambahkan sintaks di bawah ini

txt
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/src/
RewriteRule ^(.*)$ /src/$1 [L]
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/src/
RewriteRule ^(.*)$ /src/$1 [L]

dan struktur akhir proyek kita akan menjadi seperti ini:

├── src
│   ├── index.php
├── vendor
├── .htaccess
└── composer.json
├── src
│   ├── index.php
├── vendor
├── .htaccess
└── composer.json

Route

Untuk membuat route, kita harus mempunyai class dasar yang berfungsi sebagai routing, seperti yang kita tahu, routing dasar dari php sesuai dengna penamaan folder dan nama file, namun disini kita akan mengubah itu, agar php bisa menghasilkan url yang gampang di baca.

Basic Route Class

  • Pertama - tama buatlah folder baru di dalam src folder, dengan nama Libraries
  • buatlah file baru di dalam folder Libraries dengan nama Routing.php
Code Routing.php
php
<?php

namespace MiniMarkPlace\Libraries;

class Routing
{
    private $routes = [];

    public function add(string $path, $action)
    {
        $this->routes[$path] = $action;
    }

    public function run()
    {
        $path = $_SERVER['PATH_INFO'] ?? '/';

        if (!isset($this->routes[$path])) {
            header('HTTP/1.1 404 Not Found');
            die('404 Not Found');
        }

        $action = $this->routes[$path];
        return call_user_func($action);
    }
}
<?php

namespace MiniMarkPlace\Libraries;

class Routing
{
    private $routes = [];

    public function add(string $path, $action)
    {
        $this->routes[$path] = $action;
    }

    public function run()
    {
        $path = $_SERVER['PATH_INFO'] ?? '/';

        if (!isset($this->routes[$path])) {
            header('HTTP/1.1 404 Not Found');
            die('404 Not Found');
        }

        $action = $this->routes[$path];
        return call_user_func($action);
    }
}
Penjelasan Code
  • Namespace Diawali dengan namespace, konsep ini ada sejak php versi 5.3 di karnakan banyaknya konflik karena penamaan class yang sama, dan dapat memudahkan kita untuk memanggil atau mengimport file yang berbeda.
  • Class Routing memiliki property $routes, yang merupakan array asosiatif antara path dan tindakan yang harus dilakukan saat route itu diakses.
  • Method add(string $path, $action): method ini digunakan untuk menambahkan route baru ke dalam daftar routes
  • Method run(): method menjalankan aplikasi, dan memeriksa alamat route yang sedang diakses, Jika route yang diminta tidak ada dalam daftar route, aplikasi akan memberi tahu pengguna bahwa halaman tidak ditemukan (404 Not Found)
  • lalu kita akan mendaftarkan beberapa route, Ubah sintaks file index.php menjadi seperti di bawah ini:
php
<?php

require __DIR__.'/../vendor/autoload.php';

use MiniMarkPlace\Libraries\Routing;

$router = new Routing();

// Home page route
$router->add('/', function() {
    return 'Hello world';
});

// About page route
$router->add('/about', function() {
    return 'About page';
});

// Run the router
echo $router->run();
<?php

require __DIR__.'/../vendor/autoload.php';

use MiniMarkPlace\Libraries\Routing;

$router = new Routing();

// Home page route
$router->add('/', function() {
    return 'Hello world';
});

// About page route
$router->add('/about', function() {
    return 'About page';
});

// Run the router
echo $router->run();
  • coba jalankan aplikasi dan akses route yang sudah kita buat / dan /about

Route With Method

class Routing yang kita buat sudah bisa berjalan, namun pada kali ini kita akan menambahkan agar class Routing yang sudah kita buat bisa menerima untuk perbedaan method, seperti yang kita pelajari sebelumnya terdapat lebih dari satu metode untuk http

php
$router->add('GET', '/', function() {
    return 'Hello world';
});

$router->add('GET', '/product-category', function() {
    return 'data produk kategori';
});

$router->add('POST', '/product-category', function() {
    return 'tambah produk kategori';
});
$router->add('GET', '/', function() {
    return 'Hello world';
});

$router->add('GET', '/product-category', function() {
    return 'data produk kategori';
});

$router->add('POST', '/product-category', function() {
    return 'tambah produk kategori';
});
php
<?php

namespace MiniMarkPlace\Libraries;

class Routing
{
    private $routes = [];

    public function add(string $method, string $path, $callback)
    {
        $this->routes[] = [
            'method'   => $method,
            'path'     => $path,
            'callback' => $callback
        ];
    }

    public function run()
    {
        $method = $_SERVER['REQUEST_METHOD'];
        $uri    = $_SERVER['REQUEST_URI'];

        foreach ($this->routes as $route) {
            if ($route['method'] != $method) {
                continue;
            }

            if ($route['path'] == $uri) {
                return call_user_func($route['callback']);
            }
        }

        header('HTTP/1.1 404 Not Found');
        die('404 Not Found');
    }
}
<?php

namespace MiniMarkPlace\Libraries;

class Routing
{
    private $routes = [];

    public function add(string $method, string $path, $callback)
    {
        $this->routes[] = [
            'method'   => $method,
            'path'     => $path,
            'callback' => $callback
        ];
    }

    public function run()
    {
        $method = $_SERVER['REQUEST_METHOD'];
        $uri    = $_SERVER['REQUEST_URI'];

        foreach ($this->routes as $route) {
            if ($route['method'] != $method) {
                continue;
            }

            if ($route['path'] == $uri) {
                return call_user_func($route['callback']);
            }
        }

        header('HTTP/1.1 404 Not Found');
        die('404 Not Found');
    }
}
Penjelasan Code
  • Method add(): kita melakukan perubahan pada method ini, yang sebelumnya lebih sederhana hanya menerima dua parameter saja, pada update ini kita menambah satu parameter lagi untuk kebutuh method http yang akan di terima.
  • Method run(): kita juga perlu memperbaharui Method run(), dan menambahkan logika untuk mencari route yang berdasarkan method http yang sudah di daftarkan.
  • Pada file index.php kita menambahkan 2 route baru dengan method http yang berbeda GET dan POST

Route With Paramater

Agar class Routing kita dapat menerima paramater, kita akan sedikit mengubah lagi sintaks dari class Routing. Parameter di route cukup penting untuk kebutuhan UPDATE, DELETE dan Detail Data(SHOW), prefix url parameter yang akan kita buat adalah seperti ini /product-category/:id

php
$router->add('GET', '/product-category/:id', function($id) {
    return 'detail data produk kategori dengan id = ' . $id;
});
$router->add('GET', '/product-category/:id', function($id) {
    return 'detail data produk kategori dengan id = ' . $id;
});
php
$regexPattern = preg_replace_callback('/:\w+/', function ($params) {
        return '([^/]+)';
    },
    $route['path']
);

if (preg_match("#^{$regexPattern}$#", $uri, $params)) {
    array_shift($params);
    return call_user_func_array($route['callback'], $params);
}
$regexPattern = preg_replace_callback('/:\w+/', function ($params) {
        return '([^/]+)';
    },
    $route['path']
);

if (preg_match("#^{$regexPattern}$#", $uri, $params)) {
    array_shift($params);
    return call_user_func_array($route['callback'], $params);
}
Penjelasan Code
  • untuk perbaharuan kali ini pada class Routing hanya pada method run():
    php
        $regexPattern = preg_replace_callback('/:\w+/', function ($params) {
                return '([^/]+)';
            },
            $route['path']
        );
    
        if (preg_match("#^{$regexPattern}$#", $uri, $params)) {
            array_shift($params);
            return call_user_func_array($route['callback'], $params);
        }
        $regexPattern = preg_replace_callback('/:\w+/', function ($params) {
                return '([^/]+)';
            },
            $route['path']
        );
    
        if (preg_match("#^{$regexPattern}$#", $uri, $params)) {
            array_shift($params);
            return call_user_func_array($route['callback'], $params);
        }
    • menggunakan pattern dari regex untuk mengambil parameter yang diisi di uri.
    • melakukan cek apakah uri ditemukan di dalam regexPattern dan jika ditemukan maka akan mengembalikan hasilnya ke callback terkait dengan parameter yang dikirim sebagai argumen.
  • Menambahkan route baru di index.php untuk mencoba apakah penerapan perbaharuan yang kita lakukan dapat bekerja.
php
$router->add('GET', '/product-category/:id', function($id) {
    return 'detail data produk kategori dengan id = ' . $id;
});
$router->add('GET', '/product-category/:id', function($id) {
    return 'detail data produk kategori dengan id = ' . $id;
});

Kita juga bisa memakai lebih dari satu parameter sebagai contoh:

php
$router->add('GET', '/url/:param1/:param2/:param3', function($param1, $param2, $param3) {
   return "{$param1}, {$param2}, {$param3}";
});
$router->add('GET', '/url/:param1/:param2/:param3', function($param1, $param2, $param3) {
   return "{$param1}, {$param2}, {$param3}";
});

Route With Controller

class Routing yang sudah kita buat sudah berjalan dengan baik, namun kita tidak bisa langsung manaruh logika pemrograman kita pada file index.php, setiap sintaks dari route akan sangat panjang, apalagi kalau aplikasi yang kita buat lebih dari satu module, seperti kategori produk dan produk.

  • Buatlah folder baru dengan nama folder Controllers
  • lalu buat file baru di dalam folder tersebut dengan nama file ProductCategoryController.php
php
<?php

namespace MiniMarkPlace\Controllers;

class ProductCategoryController
{
    public function index()
    {
        return "data produk kategori";
    }

    public function store()
    {
        return "tambah produk kategori";
    }

    public function show($id)
    {
        return 'detail data produk kategori dengan id = ' . $id;
    }

    public function update($id)
    {
        return 'update data produk kategori dengan id = ' . $id;
    }

    public function delete($id)
    {
        return 'menghapus data produk kategori dengan id = ' . $id;
    }
}
<?php

namespace MiniMarkPlace\Controllers;

class ProductCategoryController
{
    public function index()
    {
        return "data produk kategori";
    }

    public function store()
    {
        return "tambah produk kategori";
    }

    public function show($id)
    {
        return 'detail data produk kategori dengan id = ' . $id;
    }

    public function update($id)
    {
        return 'update data produk kategori dengan id = ' . $id;
    }

    public function delete($id)
    {
        return 'menghapus data produk kategori dengan id = ' . $id;
    }
}
  • Perbaharui sintaks dari class Routing agar dapat meneruskan aksi ke class controller yang sudah kita buat yaitu class ProductCategoryController
php
if (preg_match("#^{$regexPattern}$#", $uri, $params)) {
    array_shift($params);

    if (is_callable($route['callback'])) {
        return call_user_func_array($route['callback'], $params);
    } else {
        list($controller, $method) = $route['callback'];
        $instance = new $controller();
        return $instance->$method(...$params);
    }
}
if (preg_match("#^{$regexPattern}$#", $uri, $params)) {
    array_shift($params);

    if (is_callable($route['callback'])) {
        return call_user_func_array($route['callback'], $params);
    } else {
        list($controller, $method) = $route['callback'];
        $instance = new $controller();
        return $instance->$method(...$params);
    }
}
  • dan terkahir perbaharui cara pemanggilan route dari file index.php, sintaks akan menjadi seperti dibawah ini.
php
$router->add('GET', '/product-category', [ProductCategoryController::class, 'index']);
$router->add('GET', '/product-category/:id', [ProductCategoryController::class, 'show']);
$router->add('POST', '/product-category', [ProductCategoryController::class, 'store']);
$router->add('PUT', '/product-category/:id', [ProductCategoryController::class, 'update']);
$router->add('DELETE', '/product-category/:id', [ProductCategoryController::class, 'delete']);
$router->add('GET', '/product-category', [ProductCategoryController::class, 'index']);
$router->add('GET', '/product-category/:id', [ProductCategoryController::class, 'show']);
$router->add('POST', '/product-category', [ProductCategoryController::class, 'store']);
$router->add('PUT', '/product-category/:id', [ProductCategoryController::class, 'update']);
$router->add('DELETE', '/product-category/:id', [ProductCategoryController::class, 'delete']);

Dengan begini base untuk class Routing kita dapat menangani segala kondisi, dan url pada aplikasi yang kita buat menjadi lebih bagus, dan struktur aplikasi yang kita buat akan menjadi seperti:

├── src
│   ├── Controllers
│   │   ├── ProductCategoryController.php
│   ├── Libraries
│   │   ├── Routing.php
│   ├── index.php
├── vendor
├── .htaccess
└── composer.json
├── src
│   ├── Controllers
│   │   ├── ProductCategoryController.php
│   ├── Libraries
│   │   ├── Routing.php
│   ├── index.php
├── vendor
├── .htaccess
└── composer.json