前言
在原来的原生PHP上在用MVC来写项目复习一下。不过大家注意,自己写的MVC肯定是拿来学习的,不要想着拿来做项目。
项目介绍
学生管理(添加、查看、编辑、删除学生信息)
简单路由(通过 Router 类处理)。
基本的输入验证(防止 SQL 注入、XSS)。
使用 PDO 连接 MySQL。
简洁的 Bootstrap 界面。
文件结构
mvc-php-mysql/
├── app/
│ ├── Controllers/
│ │ └── StudentController.php
│ ├── Models/
│ │ └── Student.php
│ ├── Views/
│ │ ├── students/
│ │ │ ├── index.php
│ │ │ ├── create.php
│ │ │ └── edit.php
│ │ └── layout.php
│ └── Router.php
├── public/
│ ├── index.php
│ ├── .htaccess
│ └── assets/
│ └── bootstrap.min.css
├── vendor/
├── composer.json
└── .env
项目搭建
Composer初始化
{
"name": "yyf/mvc-php-mysql",
"description": "A simple PHP MVC project with MySQL for student management",
"autoload": {
"psr-4": {
"App\\": "app/"
}
},
"authors": [
{
"name": "yyf"
}
],
"require": {
"php": ">=7.4",
"ext-pdo_mysql": "*",
"vlucas/phpdotenv": "^5.5"
}
}
创建 .env 文件(根目录):
DB_HOST=localhost
DB_NAME=your_database_name
DB_USER=your_username
DB_PASS=your_password
这里大家换上自己的配置
创建数据库和表:
CREATE TABLE students (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(50) NOT NULL,
age int(11) NOT NULL,
gender enum('M','F') NOT NULL,
class varchar(20) NOT NULL,
email varchar(100) DEFAULT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY email (email)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO students (NAME, age, gender, class, email) VALUES
('张伟', 20, 'M', 'Class A', 'zhangwei@example.com'),
('李娜', 19, 'F', 'Class B', 'lina@example.com'),
('王强', 21, 'M', 'Class A', 'wangqiang@example.com'),
('赵丽', 20, 'F', 'Class C', 'zhaoli@example.com');
数据库
创建app/Database.php
<?php
namespace App;
use PDO;
use PDOException;
use Dotenv\Dotenv;
class Database {
private $pdo;
public function __construct() {
$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
$dotenv->load();
$dsn = "mysql:host={$_ENV['DB_HOST']};dbname={$_ENV['DB_NAME']};charset=utf8";
try {
$this->pdo = new PDO($dsn, $_ENV['DB_USER'], $_ENV['DB_PASS']);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Connection failed: " . $e->getMessage());
}
}
public function getPdo() {
return $this->pdo;
}
}
路由类
创建app/Router.php
<?php
namespace App;
class Router {
private $routes = [];
public function get($path, $callback) {
$this->routes['GET'][$path] = $callback;
}
public function post($path, $callback) {
$this->routes['POST'][$path] = $callback;
}
public function dispatch() {
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
foreach ($this->routes[$method] ?? [] as $path => $callback) {
$pattern = preg_replace('#\{[\w]+\}#', '(\d+)', $path);
$pattern = "#^" . $pattern . "$#";
if (preg_match($pattern, $uri, $matches)) {
array_shift($matches);
[$controller, $method] = $callback;
$instance = new $controller();
return call_user_func_array([$instance, $method], $matches);
}
}
http_response_code(404);
echo "404 Not Found";
}
}
入口文件
创建public/index,php
<?php
require __DIR__ . '/../vendor/autoload.php';
use App\Router;
use App\Controllers\StudentController;
$router = new Router();
$router->get('/', [StudentController::class, 'index']);
$router->get('/index', [StudentController::class, 'index']);
$router->get('/create', [StudentController::class, 'create']);
$router->post('/create', [StudentController::class, 'create']);
$router->get('/edit/{id}', [StudentController::class, 'edit']);
$router->post('/edit/{id}', [StudentController::class, 'edit']);
$router->get('/delete/{id}', [StudentController::class, 'delete']);
$router->dispatch();
学生管理
学生模型
创建app/Models/student.php
<?php
namespace App\Models;
use App\Database;
use PDO;
class Student {
private $db;
public function __construct() {
$this->db = (new Database())->getPdo();
}
public function getAll() {
$stmt = $this->db->query("SELECT * FROM students");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function create($data) {
$sql = "INSERT INTO students (name, age, gender, class, email) VALUES (:name, :age, :gender, :class, :email)";
$stmt = $this->db->prepare($sql);
return $stmt->execute($data);
}
public function find($id) {
$stmt = $this->db->prepare("SELECT * FROM students WHERE id = :id");
$stmt->execute(['id' => $id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
public function update($id, $data) {
$sql = "UPDATE students SET name = :name, age = :age, gender = :gender, class = :class, email = :email WHERE id = :id";
$data['id'] = $id;
$stmt = $this->db->prepare($sql);
return $stmt->execute($data);
}
public function delete($id) {
$stmt = $this->db->prepare("DELETE FROM students WHERE id = :id");
return $stmt->execute(['id' => $id]);
}
}
学生控制器
创建app/Controllers/StudentController.php
<?php
namespace App\Controllers;
use App\Models\Student;
class StudentController {
private $student;
public function __construct() {
$this->student = new Student();
}
public function index() {
$students = $this->student->getAll();
require_once __DIR__ . '/../Views/students/index.php';
}
public function create() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 简单输入验证
$data = [
'name' => filter_input(INPUT_POST, 'name', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
'age' => (int)$_POST['age'],
'gender' => $_POST['gender'] ?? '',
'class' => filter_input(INPUT_POST, 'class', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
'email' => filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL) ?: null
];
if (empty($data['name']) || $data['age'] <= 0 || !in_array($data['gender'], ['M', 'F']) || empty($data['class'])) {
$error = "请填写所有必填字段并确保输入有效";
require_once __DIR__ . '/../Views/students/create.php';
return;
}
if ($this->student->create($data)) {
header('Location: /');
exit;
} else {
$error = "创建学生失败";
require_once __DIR__ . '/../Views/students/create.php';
}
} else {
require_once __DIR__ . '/../Views/students/create.php';
}
}
public function edit($id) {
$student = $this->student->find($id);
if (!$student) {
http_response_code(404);
echo "Student not found";
return;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = [
'name' => filter_input(INPUT_POST, 'name', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
'age' => (int)$_POST['age'],
'gender' => $_POST['gender'] ?? '',
'class' => filter_input(INPUT_POST, 'class', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
'email' => filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL) ?: null
];
if (empty($data['name']) || $data['age'] <= 0 || !in_array($data['gender'], ['M', 'F']) || empty($data['class'])) {
$error = "请填写所有必填字段并确保输入有效";
require_once __DIR__ . '/../Views/students/edit.php';
return;
}
if ($this->student->update($id, $data)) {
header('Location: /');
exit;
} else {
$error = "更新学生失败";
require_once __DIR__ . '/../Views/students/edit.php';
}
} else {
require_once __DIR__ . '/../Views/students/edit.php';
}
}
public function delete($id) {
if ($this->student->find($id)) {
$this->student->delete($id);
}
header('Location: /');
exit;
}
}
布局文件
创建app/Views/layout.php
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>学生管理系统</title>
<link href="/assets/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<h1 class="mb-4">学生管理系统</h1>
<a href="/" class="btn btn-primary mb-3">学生列表</a>
<a href="/create" class="btn btn-success mb-3">添加学生</a>
<?php if (isset($error)): ?>
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<?php include $content; ?>
</div>
</body>
</html>
学生列表
创建app/views/students/index.php
<?php $content = __FILE__; ?>
<?php include __DIR__ . '/../layout.php'; ?>
<h2>学生列表</h2>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>班级</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($students)): ?>
<tr><td colspan="7" class="text-center">无数据</td></tr>
<?php else: ?>
<?php foreach ($students as $student): ?>
<tr>
<td><?= htmlspecialchars($student['id']) ?></td>
<td><?= htmlspecialchars($student['name']) ?></td>
<td><?= htmlspecialchars($student['age']) ?></td>
<td><?= htmlspecialchars($student['gender'] === 'M' ? '男' : '女') ?></td>
<td><?= htmlspecialchars($student['class']) ?></td>
<td><?= htmlspecialchars($student['email'] ?? '') ?></td>
<td>
<a href="/edit/<?= $student['id'] ?>" class="btn btn-sm btn-warning">编辑</a>
<a href="/delete/<?= $student['id'] ?>" class="btn btn-sm btn-danger" onclick="return confirm('确定删除?')">删除</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
学生添加
创建app/views/students/create.php
<?php $content = __FILE__; ?>
<?php include __DIR__ . '/../layout.php'; ?>
<h2>添加学生</h2>
<form method="POST" action="/create">
<div class="mb-3">
<label class="form-label">姓名</label>
<input type="text" name="name" class="form-control" value="<?= htmlspecialchars($_POST['name'] ?? '') ?>" required>
</div>
<div class="mb-3">
<label class="form-label">年龄</label>
<input type="number" name="age" class="form-control" value="<?= htmlspecialchars($_POST['age'] ?? '') ?>" required>
</div>
<div class="mb-3">
<label class="form-label">性别</label>
<select name="gender" class="form-select" required>
<option value="M" <?= ($_POST['gender'] ?? '') === 'M' ? 'selected' : '' ?>>男</option>
<option value="F" <?= ($_POST['gender'] ?? '') === 'F' ? 'selected' : '' ?>>女</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">班级</label>
<input type="text" name="class" class="form-control" value="<?= htmlspecialchars($_POST['class'] ?? '') ?>" required>
</div>
<div class="mb-3">
<label class="form-label">邮箱</label>
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($_POST['email'] ?? '') ?>">
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
编辑学生
创建app/views/students/edit.php
<?php $content = __FILE__; ?>
<?php include __DIR__ . '/../layout.php'; ?>
<h2>添加学生</h2>
<form method="POST" action="/create">
<div class="mb-3">
<label class="form-label">姓名</label>
<input type="text" name="name" class="form-control" value="<?= htmlspecialchars($_POST['name'] ?? '') ?>" required>
</div>
<div class="mb-3">
<label class="form-label">年龄</label>
<input type="number" name="age" class="form-control" value="<?= htmlspecialchars($_POST['age'] ?? '') ?>" required>
</div>
<div class="mb-3">
<label class="form-label">性别</label>
<select name="gender" class="form-select" required>
<option value="M" <?= ($_POST['gender'] ?? '') === 'M' ? 'selected' : '' ?>>男</option>
<option value="F" <?= ($_POST['gender'] ?? '') === 'F' ? 'selected' : '' ?>>女</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">班级</label>
<input type="text" name="class" class="form-control" value="<?= htmlspecialchars($_POST['class'] ?? '') ?>" required>
</div>
<div class="mb-3">
<label class="form-label">邮箱</label>
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($_POST['email'] ?? '') ?>">
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
完结,先发出来。我在瞅瞅有没有要完善的。有的话再来改,没有就准备下一个项目了。laravel!!!
`
`
`