تکمیل کلاس BaseController


همان‌طور که از نام این پروژه (rest-api-blog) مشخص است، با استفاده از این وب سرویس قصد داریم تا یک سیستم وبلاگ ساده را پیاده‌سازی نماییم به طوری که عمده‌ترین قابلیت‌های این RESTful API عبارتند از:

- ثبت‌نام کاربر
- لاگین
- فراخوانی کلیهٔ‌ مقالات
- فراخوانی مقاله بر اساس شناسه
- ثبت مقالهٔ جدید
- ویرایش مقالات
- حذف مقالات

برای این منظور، نیاز به یکسری کدهای زیرساختی داریم که در همین راستا در این آموزش خواهیم دید که به چه شکل کلاس BaseController را تکمیل خواهیم نمود به طوری که تا این لحظه کلاس مذکور به صورت زیر است:

<?php
namespace Core;

class BaseController
{

}

ابتدا کدهای تکمیلی را مد نظر قرار داده سپس به تفسیر کدهای جدید که داخل این کلاس نوشته‌ شده‌اند خواهیم پرداخت:

<?php
namespace Core;

class BaseController
{
    public $requestMethod;
    public $request;
    public $requestParams;

    public function __construct()
    {
        $this->requestMethod = $_SERVER["REQUEST_METHOD"];
        $this->request = (array) json_decode(file_get_contents('php://input'), true);
    }

    public function throwError(int $statusCode, string $message)
    {
        header('Content-Type: application/json');
        echo json_encode([
            'error' => [
                'status' => $statusCode,
                'message' => $message,
            ],
        ]);
        die;
    }

    public function validateRequest()
    {
        if ($_SERVER['CONTENT_TYPE'] != 'application/json') {
            $this->throwError(403, 'The only acceptable content type is application/json.');
        }
        if (!isset($this->request['request_params']) || !is_array($this->request['request_params'])) {
            $this->throwError(400, 'The parameters of the request are not defined.');
        } else {
            $this->requestParams = $this->request['request_params'];
        }
    }

    public function validateParams($fieldName, $value, $isRequired = false)
    {
        if ($isRequired == true && $value == "") {
            $this->throwError(400, "The $fieldName field is required.");
        }
        return $value;
    }

    public function returnResponse($statusCode, $message)
    {
        header('Content-Type: application/json');
        header("Status: $statusCode");
        $http = [
            100 => '100 Continue',
            101 => '101 Switching Protocols',
            200 => '200 OK',
            201 => '201 Created',
            202 => '202 Accepted',
            203 => '203 Non-Authoritative Information',
            204 => '204 No Content',
            205 => '205 Reset Content',
            206 => '206 Partial Content',
            300 => '300 Multiple Choices',
            301 => '301 Moved Permanently',
            302 => '302 Found',
            303 => '303 See Other',
            304 => '304 Not Modified',
            305 => '305 Use Proxy',
            307 => '307 Temporary Redirect',
            400 => '400 Bad Request',
            401 => '401 Unauthorized',
            402 => '402 Payment Required',
            403 => '403 Forbidden',
            404 => '404 Not Found',
            405 => '405 Method Not Allowed',
            406 => '406 Not Acceptable',
            407 => '407 Proxy Authentication Required',
            408 => '408 Request Time-out',
            409 => '409 Conflict',
            410 => '410 Gone',
            411 => '411 Length Required',
            412 => '412 Precondition Failed',
            413 => '413 Request Entity Too Large',
            414 => '414 Request-URI Too Large',
            415 => '415 Unsupported Media Type',
            416 => '416 Requested Range Not Satisfiable',
            417 => '417 Expectation Failed',
            500 => '500 Internal Server Error',
            501 => '501 Not Implemented',
            502 => '502 Bad Gateway',
            503 => '503 Service Unavailable',
            504 => '504 Gateway Time-out',
            505 => '505 HTTP Version Not Supported',
        ];
        echo json_encode([
            'response' => [
                'code' => $http[$statusCode],
                'message' => $message,
            ],
        ]);
        die;
    }

    public function notfound()
    {
        $this->returnResponse(404, 'The endpoint you are looking for is not found.');
    }
}

همان‌طور که می‌بینیم، در بدنهٔ این کلاس سه پراپرتی تعریف کرده‌ایم تحت عناوین requestMethod$ و request$ و همچنین requestParams$ و داخل کانستراکتور، که به محض استفاده از این کلاس فراخوانی خواهد شد، نیز دستور داده‌ایم تا مقدار پراپرتی requestMethod$ برابر با ["SERVER["REQUEST_METHOD_$ قرار گیرد که این وظیفه را دارا است تا نوع متد اچ‌تی‌تی‌پی مورد استفاده در حین ارسال ریکوئست را برگرداند. به طور مثال، اگر درخواستی فرضی همچون مورد زیر را مد نظر قرار دهیم:

GET https://example.com/api/v1/users

با فراخوانی آرایهٔ سوپرگلوبال SERVER_$ به همراه کلید REQUEST_METHOD به استرینگ GET دست خواهیم یافت به طوری که داریم:

<?php 
echo $_SERVER["REQUEST_METHOD"]; // return GET

در ادامه و داخل کانستراکتور این کلاس مقدار پراپرتی request$ را مشخص کرده‌ایم بدین صورت که با استفاده از قابلیت Type Hinting زبان پی‌اچ‌پی و درج دستور (array) پس از علامت مساوی گفته‌ایم که نوع دیتایی که در این پراپرتی ذخیره می‌گردد حتماً می‌باید آرایه باشد (برای کسب اطلاعات بیشتر در این رابطه، به مقالهٔ‌ آشنایی با مفهوم Type Hinting در زبان PHP مراجعه نمایید.)

با توجه به اینکه کلیهٔ درخواست‌هایی که برای این وب سرویس ارسال می‌شوند می‌باید از جنس جیسون باشند، مسلماً نیاز داریم تا برای پردازش آن‌ها محتوای دریافتی را از حالت جیسون خارج کنیم و به یک آرایه مبدل سازیم که برای این منظور از متدی در زبان پی‌اچ‌پی تحت عنوان ()json_decode استفاده کرده‌ایم بدین صورت که پارامتر دوم آن را برابر با true قرار داده‌ایم که این تضمین را ایجاد می‌کند که آبجکت‌ها را تبدیل به یک اصطلاحاً Associative Array می‌کند.

نکته
گاهی آرایه‌ای که کلیدهای آن از جنس عدد باشد نیاز ما را مرتفع نمی‌سازد که در چنین مواقعی نیاز به استفاده از آرایه‌های اصطلاحاً Associative داریم بدین شکل که کلید‌ها در این نوع آرایه‌ها استرینگ هستند. به طور مثال، آرایهٔ  ['names = ['first_name' => 'ali$ یک Associative Array است.

گفتیم که در نظر گرفتن مقدار true به عنوان پارامتر دوم ()json_decode، این تضمین را ایجاد می‌کند که آبجکت‌ها تبدیل به آرایه شوند که برای درک بهتر این موضوع، مثال زیر را مد نظر قرار می‌دهیم:

$this->request = (array) json_decode(file_get_contents('php://input'));

حال فرض کنیم درخواستی از جنس POST به صورت زیر دریافت می‌کنیم:

array(1) {
    ["request_params"]=>
    object(stdClass)#7 (4) {
      ["firstName"]=>
      string(6) "Behzad"
      ["lastName"]=>
      string(6) "Moradi"
      ["mail"]=>
      string(14) "hi@example.com"
      ["pass"]=>
      string(6) "123456"
    }

همان‌طور که می‌بینیم، کلید request_params حاوی یک آبجکت است اما این در حالی است که اگر مقدار true را به عنوان پارامتر دوم به صورت زیر در نظر بگیریم:

$this->request = (array) json_decode(file_get_contents('php://input'), true);

به عنوان خروجی خواهیم داشت:

array(1) {
    ["request_params"] =>
    array(4) {
        ["firstName"] =>
        string(6)
        "Behzad" ["lastName"] =>
        string(6)
        "Moradi" ["mail"] =>
        string(14)
        "hi@example.com" ["pass"] =>
        string(6)
        "123456"
    }
}

می‌بینیم که آبجکت مذکور به یک Associative Array تبدیل شده است. در ادامه می‌رسیم به بررسی عملکرد فانکشن ()file_get_contents که اساساً این وظیفه را دارا است تا محتوایات یک فایل را در قالب استرینگ بازگرداند. زمانی که پارامتر ورودی php://input را برای این فانکشن در نظر می‌گیریم، این امکان در اختیارمان قرار می‌گیرد تا دیتایی که در قالب متد POST ارسال می‌شود و به اصطلاح Raw (خام) است را به صورت یک استرینگ به دست آوریم و همان‌طور که پیش از این توضیح دادیم، با استفاده از فانکشن ()json_decode استرینگ مذکور را می‌توان به یک آرایه یا آبجکت مبدل ساخت. برای درک بهتر این موضوع، کد زیر را مد نظر قرار می‌دهیم:

$this->request = file_get_contents('php://input');

حال می‌توانیم یک خروجی فرضی به صورت زیر داشته باشیم:

string(123) "{
    "request_params": {
        "firstName": "Behzad",
        "lastName": "Moradi",
        "mail": "hi@example.com",
        "pass": "123456"
    }
}"

اما اگر همان کد اولیه به صورت زیر را مد نظر قرار دهیم:

$this->request = (array) json_decode(file_get_contents('php://input'), true);

خروجی ما به صورت زیر خواهد بود:

array(1) {
    ["request_params"] =>
    array(4) {
        ["firstName"] =>
        string(6)
        "Behzad" ["lastName"] =>
        string(6)
        "Moradi" ["mail"] =>
        string(14)
        "hi@example.com" ["pass"] =>
        string(6)
        "123456"
    }
}

می‌بینیم دیتای جیسونی که پیش از این به صورت استرینگ خام بود، حال به صورت یک آرایه درآمده است.

این بخش از محتوا مخصوص کاربرانی است که ثبت‌نام کرده‌اند.
جهت مشاهدهٔ این بخش از محتوا لاگین نمایید.

جمع‌بندی

در این پروژه قصد داریم تا علاوه بر کنترلر اصلی که DefaultController نام دارد، کنترلر دیگری تحت عنوان UserController نیز داشته باشیم که این وظیفه را خواهد داشت تا کلیهٔ‌ تَسک‌های مرتبط با کاربران مثل ثبت‌نام و لاگین در آن جای گیرد. از همین روی، برخی متدهایی که مابین کنترلرهای مختلف یکسان هستند را داخل BaseController تعریف نمودیم تا از داخل همهٔ کنترلرهای پروژه به آن‌ها دسترسی داشته باشیم.


لیست نظرات
کاربر میهمان
دیدگاه شما چیست؟
کاربر میهمان