DEV Community

Imam Ali Mustofa
Imam Ali Mustofa

Posted on • Updated on • Originally published at darkterminal.prose.sh

Create a Dynamic Modal using PHP and HTMX #1

Hello Punk! This is the index #1

Well, I am still at the point where I left before. Yes! The next part about:

The Head Voice!

How can I create a modal that display form to create a new supplier in my app and load the content from backend (Yes! PHP) that generate the content, also doing validation and stuff, and removing the modal after the create operation is done!.

Jd Headless Jd GIF - https://tenor.com/view/jd-headless-jd-john-dorian-bye-funny-gif-14134807


After I create a the modal and displaying form with awesome CSS transition (copy & paste) from htmx docs. Then what I need is make that form work with insert action.

The Routing #2 POST

To make the form work with backend I need to create 1 more route that handle POST request from client to server.

<?php
// Filename: /home/darkterminal/workspaces/fck-htmx/routes/web.php
use Fckin\core\Application;

/** @var Application $app  */

$app->router->get('suppliers/create', 'Suppliers@create');
$app->router->post('suppliers/create', 'Suppliers@create');
Enter fullscreen mode Exit fullscreen mode

Myself: Why you don't use match routing method? Like:

$app-router->match(['GET','POST'], 'suppliers/create', 'Suppliers@create')
Enter fullscreen mode Exit fullscreen mode

Aaaah... I won't! If you want, create for yourself! (btw, it's not available in my fck PHP Framework, cz I am too egoist). Don't ask about the my fck PHP Framework documentation, they doesn't exists.

The Controller #2 POST

Still in the same file, but need lil tweak! ✨

The controller before

<?php
// Filename: /home/darkterminal/workspaces/fck-htmx/controllers/Suppliers.php

namespace App\controllers;

use App\config\helpers\Utils;
use App\models\Suppliers as ModelsSuppliers;
use Fckin\core\Controller;
use Fckin\core\Request;
use Fckin\core\Response;

class Suppliers extends Controller
{
    protected $suppliers;

    public function __construct()
    {
        $response = new Response();
        if (!isAuthenticate()) {
            $response->setStatusCode(401);
            exit();
        }
        $this->suppliers = new ModelsSuppliers();
    }

public function create(Request $request)
    {
        $params = []; // <-- I will do it something here when make POST request

        return Utils::addComponent('suppliers/modals/modal-create', $params);
    }
}
Enter fullscreen mode Exit fullscreen mode

The controller after

<?php
// Filename: /home/darkterminal/workspaces/fck-htmx/controllers/Suppliers.php

namespace App\controllers;

use App\config\helpers\Utils;
use App\models\Suppliers as ModelsSuppliers;
use Fckin\core\Controller;
use Fckin\core\Request;
use Fckin\core\Response;

class Suppliers extends Controller
{
    protected $suppliers;

    public function __construct()
    {
        $response = new Response();
        if (!isAuthenticate()) {
            $response->setStatusCode(401);
            exit();
        }
        $this->suppliers = new ModelsSuppliers();
    }

    public function create(Request $request)
    {
        $params = [
            'errors' => []
        ];
        if ($request->isPost()) {
            $formData = $request->getBody();
            $formData['supplierCoordinate'] = empty($formData['latitude']) || empty($formData['latitude']) ? null : \implode(',', [$formData['latitude'], $formData['longitude']]);
            $formData = Utils::remove_keys($formData, ['latitude', 'longitude']);
            $this->suppliers->loadData($formData);
            if ($this->suppliers->validate() && $this->suppliers->create($formData)) {
                \header('HX-Trigger: closeModal, loadTableSupplier');
                 exit();
            } else {
                $params['errors'] = $this->suppliers->errors;
            }
        }

        return Utils::addComponent('suppliers/modals/modal-create', $params);
    }
}
Enter fullscreen mode Exit fullscreen mode

Emmm.... deez neat! How the App\models\Suppliers as ModelsSuppliers look like? Did you mean the Model? Sure whatever you want! But before going further... I need to breakdown deez controller first.

$this->suppliers = new ModelsSuppliers();
Enter fullscreen mode Exit fullscreen mode

Initialized the supplier model that accessible in the entire Class Controller from the __constructor method. Then I modify the $params variable

$params = [
    'errors' => []
];
Enter fullscreen mode Exit fullscreen mode

I add the errors key that store the error messages from validation. The validation part I will explain in the next paragraph, so the story will be inline as possible. Trust me!

if ($request->isPost())
Enter fullscreen mode Exit fullscreen mode

Check if the client request to POST method, if yes then collect the payload from that request using

$formData = $request->getBody();
Enter fullscreen mode Exit fullscreen mode

and store into $formData variable. Because I am handsome and stupid I create field for latitude and longitude separately and don't comment about the ternary if else statement! Pleaaaaseee....

$formData['supplierCoordinate'] = empty($formData['latitude']) || empty($formData['latitude']) ? null : \implode(',', [$formData['latitude'], $formData['longitude']]);
Enter fullscreen mode Exit fullscreen mode

So I can combine the latitude and longitude as a supplierCoordinate value. Then remove the latitude and longitude from payload cz I don't need anymore.

$formData = Utils::remove_keys($formData, ['latitude', 'longitude']);
Enter fullscreen mode Exit fullscreen mode

Also I load that payload into the model

$this->suppliers->loadData($formData);
Enter fullscreen mode Exit fullscreen mode

So the model can read all the payload and validate the payload

$this->suppliers->validate()
Enter fullscreen mode Exit fullscreen mode

In the background I have the rules for the input. They look like this:

public function rules(): array
{
    return [
        'supplierName' => [self::RULE_REQUIRED],
        'supplierCompanyName' => [self::RULE_REQUIRED],
        'supplierAddress' => [self::RULE_REQUIRED],
        'supplierPhoneNumber' => [self::RULE_REQUIRED],
        'supplierEmail' => [self::RULE_EMAIL],
        'supplierCoordinate' => []
    ];
}
Enter fullscreen mode Exit fullscreen mode

If validation passed! Then create the new supplier from the payload

$this->suppliers->create($formData)
Enter fullscreen mode Exit fullscreen mode

If both of them is passed then send the trigger event to the client from the backend

\header('HX-Trigger: closeModal, loadTableSupplier');