Results 1 to 5 of 5

Thread: [PHP] Project Architecture

  1. #1

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,711

    [PHP] Project Architecture

    Hi every, thank you for taking the time to visit this thread. Here is the background, I am wanting to eventually start my own software development company and right now I have secured a contract job to build out a ticket maintenance system for an offshore company. What I'm wanting to do is develop a base stack so that I can use it going forward on future projects.

    I have written a simple console application that takes the following arguments so that it automatically generates the code that I'm displaying below:
    • Table name
    • Column names
    • File type (model, database, service, api)


    In my next several posts, I will provide the code that I am currently using to represent my Menu table in my database.

    For visibility, this is what the file structure looks like:
    Code:
    root
    ├── pages
    ├── server
    │   ├── api
    │   ├── database
    │   ├── model
    │   ├── service
    │   └── utilities
    └── vendor
    What I am hoping to achieve is direction on:
    1. Does this code structure make sense to other developers. I.e. if you were to inherit this project with no knowledge of who I am or how the project was developed, would it take too much effor to maintain
    2. Can the code be simplified?
    3. What suggestions do y'all have?
    Last edited by dday9; Mar 8th, 2021 at 08:23 PM.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  2. #2

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,711

    Re: [PHP] Project Architecture

    The first piece of code represents a POCO representation of the database table, stored in root/server/model, named [table-name].php:
    Code:
    <?php
    class MenuModel {
        public static $TableName ='Menu';
    
        public static $ColumnNames = [
            'MenuId',
            'MenuName',
            'MenuSlug',
            'CreatedBy',
            'CreatedOn',
            'ModifiedBy',
            'ModifiedOn',
            'DeletedOn'
        ];
    
        public static $RequiredColumnNames = [
            'MenuId',
            'MenuName',
            'MenuSlug'
        ];
    
        /**
         * property definition for MenuId
         */
        public $MenuId;
    
        /**
         * getter for MenuId
         */
        private function getMenuId() {
            return $this->MenuId;
        }
    
        /**
         * setter for MenuId
         */
        private function setMenuId($menuId) {
            $this->MenuId = $menuId;
        }
    
        /**
         * property definition for MenuName
         */
        public $MenuName;
    
        /**
         * getter for MenuName
         */
        private function getMenuName() {
            return $this->MenuName;
        }
    
        /**
         * setter for MenuName
         */
        private function setMenuName($menuName) {
            $this->MenuName = $menuName;
        }
    
        /**
         * property definition for MenuSlug
         */
        public $MenuSlug;
    
        /**
         * getter for MenuSlug
         */
        private function getMenuSlug() {
            return $this->MenuSlug;
        }
    
        /**
         * setter for MenuSlug
         */
        private function setMenuSlug($menuSlug) {
            $this->MenuSlug = $menuSlug;
        }
    
        /**
         * property definition for CreatedBy
         */
        public $CreatedBy;
    
        /**
         * getter for CreatedBy
         */
        private function getCreatedBy() {
            return $this->CreatedBy;
        }
    
        /**
         * setter for CreatedBy
         */
        private function setCreatedBy($createdBy) {
            $this->CreatedBy = $createdBy;
        }
    
        /**
         * property definition for CreatedOn
         */
        public $CreatedOn;
    
        /**
         * getter for CreatedOn
         */
        private function getCreatedOn() {
            return $this->CreatedOn;
        }
    
        /**
         * setter for CreatedOn
         */
        private function setCreatedOn($createdOn) {
            $this->CreatedOn = $createdOn;
        }
    
        /**
         * property definition for ModifiedBy
         */
        public $ModifiedBy;
    
        /**
         * getter for ModifiedBy
         */
        private function getModifiedBy() {
            return $this->ModifiedBy;
        }
    
        /**
         * setter for ModifiedBy
         */
        private function setModifiedBy($modifiedBy) {
            $this->ModifiedBy = $modifiedBy;
        }
    
        /**
         * property definition for ModifiedOn
         */
        public $ModifiedOn;
    
        /**
         * getter for ModifiedOn
         */
        private function getModifiedOn() {
            return $this->ModifiedOn;
        }
    
        /**
         * setter for ModifiedOn
         */
        private function setModifiedOn($modifiedOn) {
            $this->ModifiedOn = $modifiedOn;
        }
    
        /**
         * property definition for DeletedOn
         */
        public $DeletedOn;
    
        /**
         * getter for DeletedOn
         */
        private function getDeletedOn() {
            return $this->DeletedOn;
        }
    
        /**
         * setter for DeletedOn
         */
        private function setDeletedOn($deletedOn) {
            $this->DeletedOn = $deletedOn;
        }
    
        /**
         * property definition for CreatedByUsername
         */
        public $CreatedByUsername;
    
        /**
         * getter for CreatedByUsername
         */
        private function getCreatedByUsername() {
            return $this->CreatedByUsername;
        }
    
        /**
         * setter for CreatedByUsername
         */
        private function setCreatedByUsername($createdByUsername) {
            $this->CreatedByUsername = $createdByUsername;
        }
    
        /**
         * property definition for ModifiedByUsername
         */
        public $ModifiedByUsername;
    
        /**
         * getter for ModifiedByUsername
         */
        private function getModifiedByUsername() {
            return $this->ModifiedByUsername;
        }
    
        /**
         * setter for ModifiedByUsername
         */
        private function setModifiedByUsername($modifiedByUsername) {
            $this->ModifiedByUsername = $modifiedByUsername;
        }
    
        /**
         * magic getter
         */
        public function __set($name, $value) {
            switch($name) {
                case 'MenuId':
                    return $this->setMenuId($value);
                case 'MenuName':
                    return $this->setMenuName($value);
                case 'MenuSlug':
                    return $this->setMenuSlug($value);
                case 'CreatedBy':
                    return $this->setCreatedBy($value);
                case 'CreatedOn':
                    return $this->setCreatedOn($value);
                case 'ModifiedBy':
                    return $this->setModifiedBy($value);
                case 'ModifiedOn':
                    return $this->setModifiedOn($value);
                case 'DeletedOn':
                    return $this->setDeletedOn($value);
                case 'CreatedByUsername':
                    return $this->setCreatedByUsername($value);
                case 'ModifiedByUsername':
                    return $this->setModifiedByUsername($value);
            }
        }
    
        /**
         * magic setter
         */
        public function __get($name) {
            switch($name) {
                case 'MenuId':
                    return $this->getMenuId();
                case 'MenuName':
                    return $this->getMenuName();
                case 'MenuSlug':
                    return $this->getMenuSlug();
                case 'CreatedBy':
                    return $this->getCreatedBy();
                case 'CreatedOn':
                    return $this->getCreatedOn();
                case 'ModifiedBy':
                    return $this->getModifiedBy();
                case 'ModifiedOn':
                    return $this->getModifiedOn();
                case 'DeletedOn':
                    return $this->getDeletedOn();
                case 'CreatedByUsername':
                    return $this->getCreatedByUsername();
                case 'ModifiedByUsername':
                    return $this->getModifiedByUsername();
            }
        }
    }
    ?>
    This next piece of code represents the methods to perform the CRUD operations against the database, stored in root/server/database, named [table-name].php:
    Code:
    <?php
    require_once(__DIR__ . '/../model/menu.php');
    require_once(__DIR__ . '/../utilities/database.php');
    
    class MenuDatabase extends DatabaseRoot {
    
        public static function create($menu, $createdBy = null) {
            if (!($menu instanceof MenuModel)) {
                throw new Exception('menu argument is not a MenuModel');
            }
    
            $sql = '
                INSERT INTO Menu (
                    MenuName,
                    MenuSlug,
                    CreatedBy,
                    CreatedOn
                ) VALUES (
                    :menuName,
                    :menuSlug,
                    :createdBy,
                    CURRENT_TIMESTAMP()
                );
            ';
    
            $database = new DatabaseUtility();
            $connection = $database->getConnection();
            $statement = $connection->prepare($sql);
    
            $statement->bindParam(':menuName', $menu->MenuName);
            $statement->bindParam(':menuSlug', $menu->MenuSlug);
            if (is_null($createdBy)) {
                $statement->bindValue(':createdBy', null, PDO::PARAM_NULL);
            } else {
                $statement->bindParam(':createdBy', $createdBy);
            }
    
            $statement->execute();
    
            $recordId = $connection->lastInsertId();
            $filter = array();
            $filter['MenuId'] = $recordId;
            $records = MenuDatabase::query($filter);
    
            if ($records['total'] !== 1) {
                throw new Exception('Something went wrong creating the record.');
            }
    
            return $records['records'][0];
        }
    
        public static function delete($menuId, $modifiedBy = null) {
            if (!(is_numeric($menuId) && ctype_digit(strval($menuId)))) {
                throw new Exception('$menuId argument is not a valid id.');
            }
    
            $filter = array();
            $filter['MenuId'] = $menuId;
            $records = MenuDatabase::query($filter);
    
            if ($records['total'] !== 1) {
                throw new Exception('The record does not exist with Id: ' . $menuId);
            }
    
            $sql = '
                UPDATE
                    Menu
                SET
                    ModifiedBy = :modifiedBy,
                    DeletedOn = CURRENT_TIMESTAMP()
                WHERE
                    MenuId = :menuId;
            ';
    
            $database = new DatabaseUtility();
            $connection = $database->getConnection();
            $statement = $connection->prepare($sql);
    
            if (is_null($modifiedBy)) {
                $statement->bindValue(':modifiedBy', null, PDO::PARAM_NULL);
            } else {
                $statement->bindParam(':modifiedBy', $modifiedBy);
            }
            $statement->bindParam(':menuId', $menuId);
    
            $statement->execute();
    
            $filter = array();
            $filter['MenuId'] = $menuId;
            $records = MenuDatabase::query($filter);
    
            if ($records['total'] !== 1) {
                throw new Exception('Something went wrong deleting the record.');
            }
    
            return $records['records'][0];
        }
    
        public static function query($filters) {
            $parameters = array();
            $where = array();
    
            $orderBy = ' ORDER BY Menu.MenuName';
    
            $sql = '
                SELECT
                    Menu.*,
                    UserCreatedBy.Username AS CreatedByUsername,
                    UserModifiedBy.Username AS CreatedByUsername
                FROM
                    Menu
                    LEFT OUTER JOIN
                        `User` AS UserCreatedBy ON Menu.CreatedBy = UserCreatedBy.UserId
                    LEFT OUTER JOIN
                        `User` AS UserModifiedBy ON Menu.ModifiedBy = UserModifiedBy.UserId';
    
            if ($filters) {
                foreach ($filters as $key => $value) {
                    $filters[ucfirst($key)] = $value;
                    unset($filters[lcfirst($key)]);
                }
    
                if (array_key_exists('IncludeDeleted', $filters) && $filters['IncludeDeleted'] == 'false') {
                    unset($filters['IncludeDeleted']);
                    $filters['DeletedOn_Null'] = true;
                }
    
                DatabaseUtility::processFilter($filters, MenuModel::$TableName, MenuModel::$ColumnNames, $parameters, $where);
                if ($where) {
                    $sql .= ' WHERE ' . join(' AND ', $where);
                }
            }
    
            if ($orderBy) {
                $sql .= $orderBy . ';';
            }
    
            $database = new DatabaseUtility();
            $connection = $database->getConnection();
            $statement = $connection->prepare($sql);
    
            $statement->execute($parameters);
    
            $rowCount = 0;
    
            $result = array();
            $result['records'] = array();
            while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
                // MySQL will return a blank row if no rows were returned,
                // If every property is null, then skip this row
                $continue = false;
                foreach (MenuModel::$ColumnNames as $value) {
                    if (!is_null($row[$value])) {
                        $continue = true;
                    }
                }
    
                if (!$continue) {
                    continue;
                }
    
                $rowCount++;
                extract($row);
    
                $record = array(
                    'MenuId' => $MenuId,
                    'MenuName' => $MenuName,
                    'MenuSlug' => $MenuSlug,
                    'CreatedBy' => $CreatedBy,
                    'CreatedOn' => $CreatedOn,
                    'ModifiedBy' => $ModifiedBy,
                    'ModifiedOn' => $ModifiedOn,
                    'DeletedOn' => $DeletedOn
                );
    
                array_push($result['records'], $record);
            }
    
            $result['total'] = $rowCount;
            return $result;
        }
    
        public static function undelete($menuId, $modifiedBy = null) {
            if (!(is_numeric($menuId) && ctype_digit(strval($menuId)))) {
                throw new Exception('$menuId argument is not a valid id.');
            }
    
            $filter = array();
            $filter['MenuId'] = $menuId;
            $records = MenuDatabase::query($filter);
    
            if ($records['total'] !== 1) {
                throw new Exception('The record does not exist with Id: ' . $menuId);
            }
    
            $sql = '
                UPDATE
                    Menu
                SET
                    ModifiedBy = :modifiedBy,
                    DeletedOn = NULL
                WHERE
                    MenuId = :menuId;
            ';
    
            $database = new DatabaseUtility();
            $connection = $database->getConnection();
            $statement = $connection->prepare($sql);
    
            if (is_null($modifiedBy)) {
                $statement->bindValue(':modifiedBy', null, PDO::PARAM_NULL);
            } else {
                $statement->bindParam(':modifiedBy', $modifiedBy);
            }
            $statement->bindParam(':menuId', $menuId);
    
            $statement->execute();
    
            $filter = array();
            $filter['MenuId'] = $menuId;
            $records = MenuDatabase::query($filter);
    
            if ($records['total'] !== 1) {
                throw new Exception('Something went wrong restoring the record.');
            }
    
            return $records['records'][0];
        }
    
        public static function update($menuId, $menu, $modifiedBy = null) {
            if (!(is_numeric($menuId) && ctype_digit(strval($menuId)))) {
                throw new Exception('$menuId argument is not a valid id.');
            }
            if (!($menu instanceof MenuModel)) {
                throw new Exception('menu argument is not a MenuModel');
            }
    
            $filter = array();
            $filter['MenuId'] = $menuId;
            $records = MenuDatabase::query($filter);
    
            if ($records['total'] !== 1) {
                throw new Exception('The record does not exist with Id: ' . $menuId);
            }
    
            // MenuSlug is not included, slugs are immutable
            $sql = '
                UPDATE
                    Menu
                SET
                    MenuName = :menuName,
                    ModifiedBy = :modifiedBy,
                    ModifiedOn = CURRENT_TIMESTAMP()
                WHERE
                    MenuId = :menuId;
            ';
    
            $database = new DatabaseUtility();
            $connection = $database->getConnection();
            $statement = $connection->prepare($sql);
    
            $statement->bindParam(':menuName', $menu->MenuName);
            if (is_null($modifiedBy)) {
                $statement->bindValue(':modifiedBy', null, PDO::PARAM_NULL);
            } else {
                $statement->bindParam(':modifiedBy', $modifiedBy);
            }
            $statement->bindParam(':menuId', $menuId);
    
            $statement->execute();
    
            $filter = array();
            $filter['MenuId'] = $menuId;
            $records = MenuDatabase::query($filter);
    
            if ($records['total'] !== 1) {
                throw new Exception('Something went wrong updating the record.');
            }
    
            return $records['records'][0];
        }
    }
    ?>
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  3. #3

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,711

    Re: [PHP] Project Architecture

    This next piece of code represents a middle layer between the API request and the database operation, stored in root/server/service, named [table-name].php:

    Any business logic specific to the model gets applied here. E.g. look at how the create method is creating a slug for MenuSlug in MenuService::create
    Code:
    <?php
    require_once(__DIR__ . '/../model/menu.php');
    require_once(__DIR__ . '/../database/menu.php');
    require_once(__DIR__ . '/../utilities/text.php');
    
    class MenuService {
    
        public static function create($menu, $createdBy = null) {
            $menu->MenuSlug = TextUtility::slugify($menu->MenuName);
            $record = MenuDatabase::create($menu, $createdBy);
            return $record;
        }
    
        public static function delete($menuId, $modifiedBy = null) {
            $record = MenuDatabase::delete($menuId, $modifiedBy);
            return $record;
        }
    
        public static function get($menuId) {
            $filters = array();
            $filters['MenuId'] = $menuId;
            $records = MenuService::query($filters);
    
            if ($records['total'] === 0) {
                throw new Exception('No record with id: ' . $menuId, 404);
            }
            if ($records['total'] > 1) {
                throw new Exception('Multiple records with id: ' . $menuId);
            }
    
            return $records['records'][0];
        }
    
        public static function query($filters) {
            $records = MenuDatabase::query($filters);
            return $records;
        }
    
        public static function undelete($menuId, $modifiedBy = null) {
            $record = MenuDatabase::undelete($menuId, $modifiedBy);
            return $record;
        }
    
        public static function update($menuId, $menu, $modifiedBy = null) {
            $existing = MenuService::Get($menuId);
            $updating = new MenuModel();
    
            foreach (MenuModel::$ColumnNames as $value) {
                if ($value === 'MenuId') {
                    continue;
                }
                $property = $menu->$value;
                if (!$property) {
                    $updating->$value = $existing[$value];
                } else {
                    $updating->$value = $property;
                }
            }
    
            $record = MenuDatabase::update($menuId, $updating, $modifiedBy);
            return $record;
        }
    }
    ?>
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  4. #4

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,711

    Re: [PHP] Project Architecture

    This next piece of code represents the API request to create a record, stored in root/api/[table-name], named create.php:
    Code:
    <?php
    // imports
    require_once(__DIR__ . '/../../model/menu.php');
    require_once(__DIR__ . '/../../service/menu.php');
    require_once(__DIR__ . '/../../utilities/auth.php');
    require_once(__DIR__ . '/../../utilities/controller.php');
    
    // return headers/body
    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/json; charset=UTF-8');
    $returnedJson = array();
    
    $url = 'server/api/menu/create.php';
    $currentUserId = AuthUtility::assertUserIdInHeaders(apache_request_headers());
    
    // check for unauthorized request
    if (!ControllerUtility::assertSystemActionPermission($url, $currentUserId)) {
        return;
    }
    
    // check for bad method
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        http_response_code(405);
        $returnedJson['error'] = 'The supplied request method is not supported for the requested resource. This resource expects a POST request.';
        echo json_encode($returnedJson);
        return;
    }
    if (!$_POST) {
        $rest_json = file_get_contents("php://input");
        $_POST = json_decode($rest_json, true);
    }
    
    // check for bad request
    $errors = array();
    $menu = new MenuModel();
    foreach (MenuModel::$RequiredColumnNames as $property) {
        $success = ControllerUtility::isValueInRequest($_POST, $property);
        if (!$success) {
            array_push($errors, $property);
            continue;
        }
        $menu->$property = $_POST[$property];
    }
    if (count($errors) > 0) {
        http_response_code(400);
        $returnedJson['error'] = 'Malformed request syntax. The following properties are missing from the request: ' . join(', ', $errors);
        echo json_encode($returnedJson);
        return;
    }
    
    // create the record
    try {
        $record = MenuService::create($menu, $currentUserId);
    
        http_response_code(201);
        $returnedJson = $record;
    }
    catch (exception $e) {
        $statusCode = (!($e->getCode()) ? 500 : $e->getCode());
        http_response_code($statusCode);
        $returnedJson['error'] = $e->getMessage();
    }
    
    echo json_encode($returnedJson);
    ?>
    This next piece of code represents the API request to delete a record, stored in root/api/[table-name], named delete.php:
    Code:
    <?php
    // imports
    require_once(__DIR__ . '/../../model/menu.php');
    require_once(__DIR__ . '/../../service/menu.php');
    require_once(__DIR__ . '/../../utilities/auth.php');
    require_once(__DIR__ . '/../../utilities/controller.php');
    
    // return headers/body
    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/json; charset=UTF-8');
    $returnedJson = array();
    
    $url = 'server/api/menu/delete.php';
    $currentUserId = AuthUtility::assertUserIdInHeaders(apache_request_headers());
    
    // check for unauthorized request
    if (!ControllerUtility::assertSystemActionPermission($url, $currentUserId)) {
        return;
    }
    
    // check for bad method
    if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
        http_response_code(405);
        $returnedJson['error'] = 'The supplied request method is not supported for the requested resource. This resource expects a GET request.';
        echo json_encode($returnedJson);
        return;
    }
    
    // check for bad request
    $errors = array();
    $idInRequest = ControllerUtility::isValueInRequest($_GET, 'MenuId');
    if (!$idInRequest) {
        http_response_code(400);
        $returnedJson['error'] = 'Malformed request syntax. The following property is missing from the request: MenuId';
        echo json_encode($returnedJson);
        return;
    }
    $id = $_GET['MenuId'];
    
    // delete the record
    try {
        MenuService::delete($id, $currentUserId);
    
        http_response_code(204);
    }
    catch (exception $e) {
        $statusCode = (!($e->getCode()) ? 500 : $e->getCode());
        http_response_code($statusCode);
        $returnedJson['error'] = $e->getMessage();
    }
    
    echo json_encode($returnedJson);
    ?>
    This next piece of code represents the API request to get a single record, stored in root/api/[table-name], named get.php:
    Code:
    <?php
    // imports
    require_once(__DIR__ . '/../../model/menu.php');
    require_once(__DIR__ . '/../../service/menu.php');
    require_once(__DIR__ . '/../../utilities/auth.php');
    require_once(__DIR__ . '/../../utilities/controller.php');
    
    // return headers/body
    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/json; charset=UTF-8');
    $returnedJson = array();
    
    $url = 'server/api/menu/get.php';
    $currentUserId = AuthUtility::assertUserIdInHeaders(apache_request_headers());
    
    // check for unauthorized request
    if (!ControllerUtility::assertSystemActionPermission($url, $currentUserId)) {
        return;
    }
    
    // check for bad method
    if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
        http_response_code(405);
        $returnedJson['error'] = 'The supplied request method is not supported for the requested resource. This resource expects a GET request.';
        echo json_encode($returnedJson);
        return;
    }
    
    // check for bad request
    $errors = array();
    $idInRequest = ControllerUtility::isValueInRequest($_GET, 'MenuId');
    if (!$idInRequest) {
        http_response_code(400);
        $returnedJson['error'] = 'Malformed request syntax. The following property is missing from the request: MenuId';
        echo json_encode($returnedJson);
        return;
    }
    $id = $_GET['MenuId'];
    
    // get the record
    try {
        $record = MenuService::get($id);
    
        http_response_code(200);
        $returnedJson = $record;
    }
    catch (exception $e) {
        $statusCode = (!($e->getCode()) ? 500 : $e->getCode());
        http_response_code($statusCode);
        $returnedJson['error'] = $e->getMessage();
    }
    
    echo json_encode($returnedJson);
    ?>
    This next piece of code represents the API request to get a zero or more records, stored in root/api/[table-name], named query.php:
    Code:
    <?php
    // imports
    require_once(__DIR__ . '/../../model/menu.php');
    require_once(__DIR__ . '/../../service/menu.php');
    require_once(__DIR__ . '/../../utilities/auth.php');
    require_once(__DIR__ . '/../../utilities/controller.php');
    
    // return headers/body
    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/json; charset=UTF-8');
    $returnedJson = array();
    
    $url = 'server/api/menu/query.php';
    $currentUserId = AuthUtility::assertUserIdInHeaders(apache_request_headers());
    
    // check for unauthorized request
    if (!ControllerUtility::assertSystemActionPermission($url, $currentUserId)) {
        return;
    }
    
    // check for bad method
    if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
        http_response_code(405);
        $returnedJson['error'] = 'The supplied request method is not supported for the requested resource. This resource expects a GET request.';
        echo json_encode($returnedJson);
        return;
    }
    
    // query the database for zero or more records
    try {
        $records = MenuService::query($_GET);
    
        http_response_code(200);
        $returnedJson = $records;
    }
    catch (exception $e) {
        $statusCode = (!($e->getCode()) ? 500 : $e->getCode());
        http_response_code($statusCode);
        $returnedJson['error'] = $e->getMessage();
    }
    
    echo json_encode($returnedJson);
    ?>
    This next piece of code represents the API request to restore a single record, stored in root/api/[table-name], named undelete.php:
    Code:
    <?php
    // imports
    require_once(__DIR__ . '/../../model/menu.php');
    require_once(__DIR__ . '/../../service/menu.php');
    require_once(__DIR__ . '/../../utilities/auth.php');
    require_once(__DIR__ . '/../../utilities/controller.php');
    
    // return headers/body
    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/json; charset=UTF-8');
    $returnedJson = array();
    
    $url = 'server/api/menu/undelete.php';
    $currentUserId = AuthUtility::assertUserIdInHeaders(apache_request_headers());
    
    // check for unauthorized request
    if (!ControllerUtility::assertSystemActionPermission($url, $currentUserId)) {
        return;
    }
    
    // check for bad method
    if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
        http_response_code(405);
        $returnedJson['error'] = 'The supplied request method is not supported for the requested resource. This resource expects a GET request.';
        echo json_encode($returnedJson);
        return;
    }
    
    // check for bad request
    $errors = array();
    $idInRequest = ControllerUtility::isValueInRequest($_GET, 'MenuId');
    if (!$idInRequest) {
        http_response_code(400);
        $returnedJson['error'] = 'Malformed request syntax. The following property is missing from the request: MenuId';
        echo json_encode($returnedJson);
        return;
    }
    $id = $_GET['MenuId'];
    
    // restore the record
    try {
        $record = MenuService::undelete($id, $currentUserId);
    
        http_response_code(200);
        $returnedJson = $record;
    }
    catch (exception $e) {
        $statusCode = (!($e->getCode()) ? 500 : $e->getCode());
        http_response_code($statusCode);
        $returnedJson['error'] = $e->getMessage();
    }
    
    echo json_encode($returnedJson);
    ?>
    This next piece of code represents the API request to update a single record, stored in root/api/[table-name], named update.php:
    Code:
    <?php
    // imports
    require_once(__DIR__ . '/../../model/menu.php');
    require_once(__DIR__ . '/../../service/menu.php');
    require_once(__DIR__ . '/../../utilities/auth.php');
    require_once(__DIR__ . '/../../utilities/controller.php');
    
    // return headers/body
    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/json; charset=UTF-8');
    $returnedJson = array();
    
    $url = 'server/api/menu/update.php';
    $currentUserId = AuthUtility::assertUserIdInHeaders(apache_request_headers());
    
    // check for unauthorized request
    if (!ControllerUtility::assertSystemActionPermission($url, $currentUserId)) {
        return;
    }
    
    // check for bad method
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        http_response_code(405);
        $returnedJson['error'] = 'The supplied request method is not supported for the requested resource. This resource expects a POST request.';
        echo json_encode($returnedJson);
        return;
    }
    if (!$_POST) {
        $rest_json = file_get_contents("php://input");
        $_POST = json_decode($rest_json, true);
    }
    
    // check for bad request
    $errors = array();
    $menu = new MenuModel();
    foreach (MenuModel::$RequiredColumnNames as $property) {
        $success = ControllerUtility::isValueInRequest($_POST, $property);
        if (!$success) {
            array_push($errors, $property);
            continue;
        }
        $menu->$property = $_POST[$property];
    }
    if (count($errors) > 0) {
        http_response_code(400);
        $returnedJson['error'] = 'Malformed request syntax. The following properties are missing from the request: ' . join(', ', $errors);
        echo json_encode($returnedJson);
        return;
    }
    
    // update the record
    try {
        $record = MenuService::update($menu->MenuId, $menu, $currentUserId);
    
        http_response_code(200);
        $returnedJson = $record;
    }
    catch (exception $e) {
        $statusCode = (!($e->getCode()) ? 500 : $e->getCode());
        http_response_code($statusCode);
        $returnedJson['error'] = $e->getMessage();
    }
    
    echo json_encode($returnedJson);
    ?>
    Last edited by dday9; Mar 8th, 2021 at 08:22 PM.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  5. #5
    Smooth Moperator techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,531

    Re: [PHP] Project Architecture

    Slick. MY only real gripe is the [table-name] nomenclature ... I would adopt a [object-name] instead, but that's just me.
    I haven't spent too much time with the code you posted, but I don't see a whole lot wrong with it. The select * gives some pause... I see that it allows for the model to be flexible, but the DBA in me wants to throw the BS flag at that and say list out the fields, you know what's in the table.

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width