Kodokon kodokon.com

हाथ से बनाया गया एक MVC आर्किटेक्चर

एक संपूर्ण माइक्रो MVC फ़्रेमवर्क लिखें - एक regex राउटर, इंजेक्ट किए गए कंट्रोलर, बफ़र्ड व्यू - ताकि समझ सकें कि फ़्रेमवर्क आपकी ओर से क्या करते हैं।

11 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

किसी फ़्रेमवर्क को समझने का अर्थ है यह जानना कि एक छोटा फ़्रेमवर्क कैसे लिखा जाए। हर आधुनिक MVC आर्किटेक्चर एक फ़्रंट कंट्रोलर पर टिका होता है: एक ही index.php सभी अनुरोध प्राप्त करता है (सर्वर हर URL को उस पर पुनर्निर्देशित करता है), फिर एक राउटर HTTP मेथड और पथ को एक हैंडलर से मिलाता है। हमारा राउटर /posts/{id} जैसे पैटर्न को नामित कैप्चर समूहों वाले नियमित व्यंजकों में बदल देता है: फिर preg_match सीधे $found['id'] को भर देता है, URL को हाथ से विभाजित किए बिना।

PHP
<?php
declare(strict_types=1);

final class Router
{
    private array $routes = [];

    public function add(
        string $method,
        string $pattern,
        callable $handler
    ): void {
        $regex = preg_replace(
            '#\{(\w+)\}#',
            '(?P<$1>[^/]+)',
            $pattern
        );
        $this->routes[] = [
            $method, "#^$regex$#", $handler
        ];
    }

    public function dispatch(
        string $method,
        string $uri
    ): mixed {
        foreach ($this->routes as $route) {
            [$m, $re, $handler] = $route;
            if ($m !== $method) {
                continue;
            }
            if (preg_match($re, $uri, $found) === 1) {
                return $handler($found);
            }
        }
        http_response_code(404);
        return 'Page not found';
    }
}
पैटर्न /posts/{id} बन जाता है #^/posts/(?P<id>[^/]+)$#।

कंट्रोलर समन्वय करता है: यह पैरामीटर पढ़ता है, मॉडल से क्वेरी करता है - वह एकमात्र परत जिसे डेटाबेस से बात करने की अनुमति है - और एक व्यू चुनता है। निर्भरताएँ कंस्ट्रक्टर के माध्यम से आती हैं: यह बिना किसी कंटेनर के डिपेंडेंसी इंजेक्शन है, और readonly प्रॉपर्टी को प्रोमोट करना गारंटी देता है कि निर्माण के बाद वे कभी नहीं बदलेंगी। संसाधन के मौजूद न होने पर मॉडल false के बजाय एक स्पष्ट null लौटाता है: तब कंट्रोलर 404 पर निर्णय ले सकता है।

PHP
<?php
declare(strict_types=1);

final class PostModel
{
    public function __construct(
        private readonly PDO $pdo
    ) {}

    public function find(int $id): ?array
    {
        $stmt = $this->pdo->prepare(
            'SELECT * FROM posts WHERE id = :id'
        );
        $stmt->execute(['id' => $id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row === false ? null : $row;
    }
}

final class PostController
{
    public function __construct(
        private readonly PostModel $posts
    ) {}

    public function show(array $params): string
    {
        $id = (int) $params['id'];
        $post = $this->posts->find($id);
        if ($post === null) {
            http_response_code(404);
            return render('not-found');
        }
        return render('post', ['post' => $post]);
    }
}
render फ़ंक्शन अगले ब्लॉक में परिभाषित किया गया है।

व्यू एक साधारण PHP फ़ाइल है जो केवल उन वेरिएबल को प्रदर्शित करती है जो पहले से इसके लिए तैयार किए गए हैं। दो आंतरिक तंत्र इसे संभव बनाते हैं: extract डेटा ऐरे को स्थानीय वेरिएबल में बदल देता है, और आउटपुट बफ़रिंग (ob_start फिर ob_get_clean) आउटपुट को तुरंत भेजने के बजाय उसे एक स्ट्रिंग के रूप में लौटाने के लिए कैप्चर करती है - नेस्टेड टेम्पलेट बनाने या रेंडरिंग के बाद हेडर तय करने के लिए अपरिहार्य।

PHP
<?php
declare(strict_types=1);

function render(
    string $view,
    array $data = []
): string {
    extract($data, EXTR_SKIP);
    ob_start();
    require __DIR__ . "/views/$view.php";
    return (string) ob_get_clean();
}

$pdo = new PDO('sqlite:blog.db');
$controller = new PostController(
    new PostModel($pdo)
);

$router = new Router();
$router->add(
    'GET',
    '/posts/{id}',
    fn (array $p): string => $controller->show($p)
);

$path = parse_url(
    $_SERVER['REQUEST_URI'],
    PHP_URL_PATH
);

echo $router->dispatch(
    $_SERVER['REQUEST_METHOD'],
    (string) $path
);
संपूर्ण फ़्रंट कंट्रोलर: रेंडरिंग, वायरिंग और डिस्पैच।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. इस राउटर में, पैटर्न /posts/{id} को बदलने से क्या उत्पन्न होता है?
    • एक नामित कैप्चर समूह वाला एक नियमित व्यंजक, जो फिर $found['id'] के माध्यम से सुलभ होता है
    • URL खंडों का एक साहचर्य ऐरे
    • एक साधारण स्ट्रिंग तुलना, खंड-दर-खंड
  2. render फ़ंक्शन ob_start का उपयोग क्यों करता है?
    • HTML आउटपुट को भेजने से पहले संपीड़ित करने के लिए
    • व्यू के आउटपुट को कैप्चर करने और उसे तुरंत ब्राउज़र को भेजने के बजाय एक स्ट्रिंग के रूप में लौटाने के लिए
    • व्यू को PHP कोड निष्पादित करने से रोकने के लिए
    • व्यू की रेंडरिंग को डिस्क पर कैश करने के लिए
  3. फ़्रंट कंट्रोलर की भूमिका क्या है?
    • स्थैतिक फ़ाइलों को तेज़ी से परोसना
    • हर पेज से पहले उपयोगकर्ता की अनुमतियाँ जाँचना
    • एप्लिकेशन का एकल प्रवेश बिंदु होना: हर अनुरोध उस पर पुनर्निर्देशित होता है, फिर राउटर को सौंप दिया जाता है