[翻译]使用MySQL和PHP PDO创建简单的PHP CRUD REST API

使用MySQL和PHP PDO创建简单的PHP CRUD REST API

原文地址

在这篇教程中我将与你分享如何一步一步的使用MySQL数据库创建一个PHP 8 REST风格的api。

如果你想对PHP框架有一个基本的理解,那么你可以看看我们之前的教程How to Create a PHP Laravel 6 CRUD Web App with MySQL

这个教程涉及如何使用PHP和MySQL构建简单的CRUD REST API,用Postmen测试API以及在本地配置PHP开发环境。

什么是API

API的全称是应用编程接口,是一套构建软件应用的规范,协议和工具的集合。
一个API接口使多个不同软件组件之间的通信成为可能。

在web开发中,API是一个URL,web应用使用这个URL通过GET,POST,UPDATE和DELETE等HTTP请求处理数据以及管理CRUD操作。

什么是REST API

wikipedia:

Representational state transfer (REST)是一种软件设计风格,是为创建web服务而制定的一套约束规范。遵从REST设计风格的web服务,被称为REST风格的web服务(RESTful),这些web服务在互联网上使计算机系统之间能够互联互通。RESTful web服务允许请求系统使用一组统一的、预定义的无状态操作访问和操作web资源。其他类型的Web服务公开他们自己专门的操作集,比如SOAP web服务。

php8 API项目文件结构

下面是PHP 8 REST API 项目的文件结构。
创建apiclassconfig文件夹保存API和MySQL数据库相关的配置文件。
01
你可以通过MAMP或者WAMP运行你的PHP项目。但是 我们准备使用命令行工具启动PHP项目。

在终端定位到你的PHP项目的根目录(这里是举例):

cd php-project-name

然后使用下面命令启动项目:

php -S 127.0.0.1:8080

启动成功会显示下面内容:

PHP 7.3.11 Development Server started at Thu May  7 13:21:35 2020
Listening on http://127.0.0.1:8080
Document root is /Users/digamber/Desktop/php-rest-api

可以通过连接:http://127.0.0.1:8080 访问项目。

MySQL数据库配置

在开始之前我们需要创建一个phpapidb数据库。你可以通过PhpMyAdmin可视化工具创建,也可以通过MySQL root 用户使用命令行工具创建。

我们之前写过一篇详细的文章Installing and Setting up MySQL in Mac’s Terminal app

下面通过root用户使用命令行工具创建数据库:

mysql -u root -p

创建phpapidb数据库:

CREATE DATABASE phpapidb;

检查数据库:

SHOW DATABASES;

+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| phpapidb           |
| sys                |
+--------------------+

使用phpapidb

use phpapidb;

数据库创建完成后运行下面的SQL脚本创建数据表:

CREATE TABLE IF NOT EXISTS `Employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(256) NOT NULL,
  `email` varchar(50),
  `age` int(11) NOT NULL,
  `designation` varchar(255) NOT NULL,
  `created` datetime NOT NULL,
  PRIMARY KEY (`id`)
)ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=19;

确认数据表已创建好:

SHOW tables;

+--------------------+
| Tables_in_phpapidb |
+--------------------+
| Employee           |
+--------------------+

Employee表中插入数据:

INSERT INTO `Employee` (`id`, `name`, `email`, `age`, `designation`, `created`) VALUES 
(1, 'John Doe', 'johndoe@gmail.com', 32, 'Data Scientist', '2012-06-01 02:12:30'),
(2, 'David Costa', 'sam.mraz1996@yahoo.com', 29, 'Apparel Patternmaker', '2013-03-03 01:20:10'),
(3, 'Todd Martell', 'liliane_hirt@gmail.com', 36, 'Accountant', '2014-09-20 03:10:25'),
(4, 'Adela Marion', 'michael2004@yahoo.com', 42, 'Shipping Manager', '2015-04-11 04:11:12'),
(5, 'Matthew Popp', 'krystel_wol7@gmail.com', 48, 'Chief Sustainability Officer', '2016-01-04 05:20:30'),
(6, 'Alan Wallin', 'neva_gutman10@hotmail.com', 37, 'Chemical Technician', '2017-01-10 06:40:10'),
(7, 'Joyce Hinze', 'davonte.maye@yahoo.com', 44, 'Transportation Planner', '2017-05-02 02:20:30'),
(8, 'Donna Andrews', 'joesph.quitz@yahoo.com', 49, 'Wind Energy Engineer', '2018-01-04 05:15:35'),
(9, 'Andrew Best', 'jeramie_roh@hotmail.com', 51, 'Geneticist', '2019-01-02 02:20:30'),
(10, 'Joel Ogle', 'summer_shanah@hotmail.com', 45, 'Space Sciences Teacher', '2020-02-01 06:22:50');

如果你使用MySQL 8+可能会得到一下错误:The server requested authentication method unknown to the client

MySQL 8+默认使用auth_socket 插件,所以你需要使用密码登录数据库。
可以使用下面的SQL脚本登录:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
  1. 你需要把上面脚本中的password替换为root用户的密码;
  2. 如果你不是使用root用户登录的数据库,就把脚本中的root替换为你正在用的用户。

连接数据库

下面的代码中包含MySQL数据库的详细信息,比如数据库名字,用户名,密码等。使用PHP的PDO扩展连接数据库。

php.net中关于PDO:
PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口。实现 PDO 接口的每个数据库驱动可以公开具体数据库的特性作为标准扩展功能。 注意利用 PDO 扩展自身并不能实现任何数据库功能;必须使用一个 具体数据库的 PDO 驱动 来访问数据库服务。

在项目根目录创建config文件夹,并在文件夹下创建database.php文件,包含以下代码:

class Database {
    private $host = "127.0.0.1";
    private $database_name = "phpapidb";
    private $username = "root";
    private $password = "xxxxxxxx";

    public $conn;

    public function getConnection(){
        $this->conn = null;
        try{
            $this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->database_name, $this->username, $this->password);
            $this->conn->exec("set names utf8");
        }catch(PDOException $exception){
            echo "Database could not be connected: " . $exception->getMessage();
        }
        return $this->conn;
    }
}

创建PHP类

现在开始创建Employee类。
在PHP中类被用于面向对象编程。本例中PHP类封装了数据库表的值,并且还给SQL脚本中的变量赋值。

SQL脚本中的表属性通过类构造函数与数据库连接类联系起来。

每一个对象类包含CRUD方法,这些方法通过PHP函数对数据表中的行执行CREATE,READ,UPDATE和DELETE操作。

创建class/employees.php文件并在Employee类中定义CRUD方法:

class Employee{

    // Connection
    private $conn;

    // Table
    private $db_table = "Employee";

    // Columns
    public $id;
    public $name;
    public $email;
    public $age;
    public $designation;
    public $created;

    // Db connection
    public function __construct($db){
        $this->conn = $db;
    }

    // GET ALL
    public function getEmployees(){
        $sqlQuery = "SELECT id, name, email, age, designation, created FROM " . $this->db_table . "";
        $stmt = $this->conn->prepare($sqlQuery);
        $stmt->execute();
        return $stmt;
    }

    // CREATE
    public function createEmployee(){
        $sqlQuery = "INSERT INTO
                    ". $this->db_table ."
                SET
                    name = :name, 
                    email = :email, 
                    age = :age, 
                    designation = :designation, 
                    created = :created";
    
        $stmt = $this->conn->prepare($sqlQuery);
    
        // sanitize
        // htmlspecialchars — 将特殊字符转换为 HTML 实体,比如&转换成& https://www.php.net/htmlspecialchars
        // strip_tags() 函数剥去字符串中的 HTML、XML 以及 PHP 的标签
        $this->name=htmlspecialchars(strip_tags($this->name));
        $this->email=htmlspecialchars(strip_tags($this->email));
        $this->age=htmlspecialchars(strip_tags($this->age));
        $this->designation=htmlspecialchars(strip_tags($this->designation));
        $this->created=htmlspecialchars(strip_tags($this->created));
    
        // bind data
        $stmt->bindParam(":name", $this->name);
        $stmt->bindParam(":email", $this->email);
        $stmt->bindParam(":age", $this->age);
        $stmt->bindParam(":designation", $this->designation);
        $stmt->bindParam(":created", $this->created);
    
        if($stmt->execute()){
            return true;
        }
        return false;
    }

    // READ single
    public function getSingleEmployee(){
        $sqlQuery = "SELECT
                    id, 
                    name, 
                    email, 
                    age, 
                    designation, 
                    created
                    FROM
                    ". $this->db_table ."
                WHERE 
                    id = ?
                LIMIT 0,1";

        $stmt = $this->conn->prepare($sqlQuery);

        $stmt->bindParam(1, $this->id);

        $stmt->execute();

        $dataRow = $stmt->fetch(PDO::FETCH_ASSOC);
        
        $this->name = $dataRow['name'];
        $this->email = $dataRow['email'];
        $this->age = $dataRow['age'];
        $this->designation = $dataRow['designation'];
        $this->created = $dataRow['created'];
    }        

    // UPDATE
    public function updateEmployee(){
        $sqlQuery = "UPDATE
                    ". $this->db_table ."
                SET
                    name = :name, 
                    email = :email, 
                    age = :age, 
                    designation = :designation, 
                    created = :created
                WHERE 
                    id = :id";
    
        $stmt = $this->conn->prepare($sqlQuery);
    
        $this->name=htmlspecialchars(strip_tags($this->name));
        $this->email=htmlspecialchars(strip_tags($this->email));
        $this->age=htmlspecialchars(strip_tags($this->age));
        $this->designation=htmlspecialchars(strip_tags($this->designation));
        $this->created=htmlspecialchars(strip_tags($this->created));
        $this->id=htmlspecialchars(strip_tags($this->id));
    
        // bind data
        $stmt->bindParam(":name", $this->name);
        $stmt->bindParam(":email", $this->email);
        $stmt->bindParam(":age", $this->age);
        $stmt->bindParam(":designation", $this->designation);
        $stmt->bindParam(":created", $this->created);
        $stmt->bindParam(":id", $this->id);
    
        if($stmt->execute()){
            return true;
        }
        return false;
    }

    // DELETE
    function deleteEmployee(){
        $sqlQuery = "DELETE FROM " . $this->db_table . " WHERE id = ?";
        $stmt = $this->conn->prepare($sqlQuery);
    
        $this->id=htmlspecialchars(strip_tags($this->id));
    
        $stmt->bindParam(1, $this->id);
    
        if($stmt->execute()){
            return true;
        }
        return false;
    }

}

Employee类管理CRUD操作:

  • __construct() 连接数据库
  • getEmployees() 获取所有数据
  • getSingleEmployee() 获取单个数据
  • createEmployee() 添加一条数据
  • updateEmployee() 更新数据
  • deleteEmployee()删除数据

使用PHP REST API获取MySQL数据

下面的代码会检索所有MySQL表。
api文件夹下创建read.php文件,包含如下代码:

header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

include_once '../config/database.php';
include_once '../class/employees.php';

$database = new Database();
$db = $database->getConnection();

$items = new Employee($db);

$stmt = $items->getEmployees();
$itemCount = $stmt->rowCount();


echo json_encode($itemCount);

if($itemCount > 0){
    
    $employeeArr = array();
    $employeeArr["body"] = array();
    $employeeArr["itemCount"] = $itemCount;

    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
        extract($row);
        $e = array(
            "id" => $id,
            "name" => $name,
            "email" => $email,
            "age" => $age,
            "designation" => $designation,
            "created" => $created
        );

        array_push($employeeArr["body"], $e);
    }
    echo json_encode($employeeArr);
}

else{
    http_response_code(404);
    echo json_encode(
        array("message" => "No record found.")
    );
}

下面使用Postman测试这个API。
打开Postman,输入下面的url,点击Send按钮,查看输出:

method endpoint
GET http://localhost:8080/api/read.php

02

从MySQL数据库获取单行数据

下面的代码从数据库获取单个数据。
api文件夹下创建single_read.php文件,包含以下代码

header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

include_once '../config/database.php';
include_once '../class/employees.php';

$database = new Database();
$db = $database->getConnection();

$item = new Employee($db);

$item->id = isset($_GET['id']) ? $_GET['id'] : die();

$item->getSingleEmployee();

if($item->name != null){
    // create array
    $emp_arr = array(
        "id" =>  $item->id,
        "name" => $item->name,
        "email" => $item->email,
        "age" => $item->age,
        "designation" => $item->designation,
        "created" => $item->created
    );
    
    http_response_code(200);
    echo json_encode($emp_arr);
}
    
else{
    http_response_code(404);
    echo json_encode("Employee not found.");
}
method endpoint
GET http://localhost:8080/api/single_read.php/?id=2

03

向MySQL表中添加单个数据

api文件夹下创建create.php文件,包含以下代码:

header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

include_once '../config/database.php';
include_once '../class/employees.php';

$database = new Database();
$db = $database->getConnection();

$item = new Employee($db);

$data = json_decode(file_get_contents("php://input"));

$item->name = $data->name;
$item->email = $data->email;
$item->age = $data->age;
$item->designation = $data->designation;
$item->created = date('Y-m-d H:i:s');

if($item->createEmployee()){
    echo 'Employee created successfully.';
} else{
    echo 'Employee could not be created.';
}
method endpoint
GET http://localhost:8080/api/create.php

04

编辑MySQL表中的数据

api文件夹下创建update.php文件,包含如下代码:

header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

include_once '../config/database.php';
include_once '../class/employees.php';

$database = new Database();
$db = $database->getConnection();

$item = new Employee($db);

// 关于php://input  -- https://www.cnblogs.com/lxwphp/p/11316144.html
$data = json_decode(file_get_contents("php://input"));

$item->id = $data->id;

// employee values
$item->name = $data->name;
$item->email = $data->email;
$item->age = $data->age;
$item->designation = $data->designation;
$item->created = date('Y-m-d H:i:s');

if($item->updateEmployee()){
    echo json_encode("Employee data updated.");
} else{
    echo json_encode("Data could not be updated");
}
method endpoint
POST http://localhost:8080/api/update.php

05

从MySQL数据库中删除单个数据

api文件夹下创建delete.php文件,包含以下代码:

header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

include_once '../config/database.php';
include_once '../class/employees.php';

$database = new Database();
$db = $database->getConnection();

$item = new Employee($db);

$data = json_decode(file_get_contents("php://input"));

$item->id = $data->id;

if($item->deleteEmployee()){
    echo json_encode("Employee deleted.");
} else{
    echo json_encode("Data could not be deleted");
}
method endpoint
DELETE http://localhost:8080/api/delete.php

06

总结

在这个教程中我们学习了如何使用PHP 8 & MySQL 8构建CRUD RESTful API。
并且了解了一些有用的PHP方法,比如:htmlspecialchars(), bindParam(), execute(), PHP PDO 和 json_encode()

原文地址:https://www.cnblogs.com/fogwind/p/15218809.html