PHP 网站开发:从关系管理到性能优化
在网站开发过程中,实现对象化的网站架构是一个重要的目标,而其中涉及到的关系管理、性能优化以及功能拓展都是关键环节。下面将详细介绍如何在网站中进行这些操作。
编辑笑话功能
编辑笑话时,会根据笑话的作者 ID 进行权限判断。如果笑话的 ID 为空或者当前用户 ID 与笑话的作者 ID 相同,用户可以编辑笑话。以下是相应的代码:
<?php if (empty($joke->id) || $userId == $joke->authorId):?>
<form action="" method="post">
<input type="hidden" name="joke[id]" value="<?=$joke->id ?? ''?>">
<label for="joketext">Type your joke here:</label>
<textarea id="joketext" name="joke[joketext]" rows="3" cols="40"><?=$joke->joketext ?? ''?></textarea>
<input type="submit" name="submit" value="Save">
</form>
<?php else:?>
<p>You may only edit jokes that you posted.</p>
<?php endif; ?>
这个代码片段实现了一个简单的权限控制,只有笑话的作者才能对其进行编辑。
缓存优化
在 Joke 实体类的
getAuthor
方法中,存在潜在的性能问题。原始的
getAuthor
方法每次调用都会向数据库发送相同的查询请求,导致不必要的性能开销。例如:
public function getAuthor() {
return $this->authorsTable->findById($this->authorId);
}
当多次调用该方法时,会多次查询数据库:
echo $joke->getAuthor()->name;
echo $joke->getAuthor()->email;
echo $joke->getAuthor()->password;
为了避免这个问题,可以使用透明缓存技术。具体步骤如下:
1. 在 Joke 实体类中添加一个私有属性
$author
用于存储作者信息。
class Joke {
// …
public $joketext;
private $authorsTable;
private $author;
// …
}
-
修改
getAuthor方法,在方法中添加逻辑判断,如果$author为空,则从数据库中获取作者信息并存储在$author中,否则直接返回$author。
public function getAuthor() {
if (empty($this->author)) {
$this->author = $this->authorsTable->findById($this->authorId);
}
return $this->author;
}
这样,无论调用多少次
getAuthor
方法,数据库只会被查询一次,大大提高了性能。
笑话分类功能
随着网站上笑话数量的增加,为了方便用户查看特定类型的笑话,需要添加笑话分类功能。以下是实现该功能的详细步骤:
创建分类表
使用 SQL 代码创建分类表:
CREATE TABLE `ijdb_sample`.`category` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NULL,
PRIMARY KEY (`id`)
);
也可以使用 MySQL Workbench 来创建这个表,确保
id
是自增的主键,
name
类型为 VARCHAR。
创建分类控制器
在
Ijdb/Controllers/Category.php
中创建一个新的控制器
Category
,并在构造函数中传入
DatabaseTable
实例,用于与分类表进行交互。
<?php
namespace Ijdb\Controllers;
class Category {
private $categoriesTable;
public function __construct(\Ninja\DatabaseTable $categoriesTable) {
$this->categoriesTable = $categoriesTable;
}
}
创建分类编辑模板
创建
editcategory.html.php
模板,用于显示添加或编辑分类的表单。
<form action="" method="post">
<input type="hidden" name="category[id]" value="<?=$category->id ?? ''?>">
<label for="categoryname">Enter category name:</label>
<input type="text" id="categoryname" name="category[name]" value="<?=$category->name ?? ''?>" />
<input type="submit" name="submit" value="Save">
</form>
添加编辑方法
在
Category
类中添加
edit
方法,用于处理分类的编辑操作。
public function edit() {
if (isset($_GET['id'])) {
$category = $this->categoriesTable->findById($_GET['id']);
}
$title = 'Edit Category';
return [
'template' => 'editcategory.html.php',
'title' => $title,
'variables' => [
'category' => $category ?? null
]
];
}
配置路由
在
IjdbRoutes
中创建
DatabaseTable
实例,并配置分类编辑页面的路由。
class IjdbRoutes implements \Ninja\Routes {
private $authorsTable;
private $jokesTable;
private $categoriesTable;
private $authentication;
public function __construct() {
include __DIR__ . '/../../includes/DatabaseConnection.php';
$this->jokesTable = new \Ninja\DatabaseTable($pdo, 'joke', 'id', '\Ijdb\Entity\Joke', [&$this->authorsTable]);
$this->authorsTable = new \Ninja\DatabaseTable($pdo, 'author', 'id', '\Ijdb\Entity\Author', [&$this->jokesTable]);
$this->categoriesTable = new \Ninja\DatabaseTable($pdo, 'category', 'id');
// …
}
// …
$categoryController = new \Ijdb\Controllers\Category($this->categoriesTable);
$routes = [
// …
'category/edit' => [
'POST' => [
'controller' => $categoryController,
'action' => 'saveEdit'
],
'GET' => [
'controller' => $categoryController,
'action' => 'edit'
],
'login' => true
],
// …
];
}
添加保存方法
在
Category
类中添加
saveEdit
方法,用于处理表单提交并将分类信息保存到数据库。
public function saveEdit() {
$category = $_POST['category'];
$this->categoriesTable->save($category);
header('location: /category/list');
}
创建分类列表页面
创建
categories.html.php
模板,用于显示分类列表,并为每个分类提供编辑和删除按钮。
<h2>Categories</h2>
<a href="/category/edit">Add a new category</a>
<?php foreach ($categories as $category): ?>
<blockquote>
<p>
<?=htmlspecialchars($category->name, ENT_QUOTES, 'UTF-8')?>
<a href="/category/edit?id=<?=$category->id?>">Edit</a>
<form action="/category/delete" method="post">
<input type="hidden" name="id" value="<?=$category->id?>">
<input type="submit" value="Delete">
</form>
</p>
</blockquote>
<?php endforeach; ?>
添加列表方法和路由
在
Category
类中添加
list
方法,用于获取所有分类信息并返回给模板。
public function list() {
$categories = $this->categoriesTable->findAll();
$title = 'Joke Categories';
return [
'template' => 'categories.html.php',
'title' => $title,
'variables' => [
'categories' => $categories
]
];
}
在
IjdbRoutes
中配置分类列表页面的路由。
'category/list' => [
'GET' => [
'controller' => $categoryController,
'action' => 'list'
],
'login' => true
];
添加删除方法和路由
在
Category
类中添加
delete
方法,用于删除指定的分类。
public function delete() {
$this->categoriesTable->delete($_POST['id']);
header('location: /category/list');
}
在
IjdbRoutes
中配置分类删除的路由。
'category/delete' => [
'POST' => [
'controller' => $categoryController,
'action' => 'delete'
],
'login' => true
];
为笑话分配分类
为了实现笑话与分类的关联,需要创建一个关联表
joke_category
,并对相关代码进行修改。
创建关联表
使用 SQL 代码创建
joke_category
表:
CREATE TABLE `ijdb_sample`.`joke_category` (
`jokeId` INT NOT NULL,
`categoryId` INT NOT NULL,
PRIMARY KEY (`jokeId`, `categoryId`)
);
这个表的两个字段
jokeId
和
categoryId
共同作为主键,防止同一个笑话被重复添加到同一个分类中。
修改 Joke 控制器
在
Joke
控制器的构造函数中添加
$categoriesTable
参数,并在实例化时传入该参数。
class Joke {
private $authorsTable;
private $jokesTable;
private $categoriesTable;
private $authentication;
public function __construct(DatabaseTable $jokesTable, DatabaseTable $authorsTable, DatabaseTable $categoriesTable, Authentication $authentication) {
$this->jokesTable = $jokesTable;
$this->authorsTable = $authorsTable;
$this->categoriesTable = $categoriesTable;
$this->authentication = $authentication;
}
// …
}
修改编辑方法
在
Joke
控制器的
edit
方法中,获取所有分类信息并传递给模板。
public function edit() {
$author = $this->authentication->getUser();
$categories = $this->categoriesTable->findAll();
if (isset($_GET['id'])) {
$joke = $this->jokesTable->findById($_GET['id']);
}
$title = 'Edit joke';
return [
'template' => 'editjoke.html.php',
'title' => $title,
'variables' => [
'joke' => $joke ?? null,
'userId' => $author->id ?? null,
'categories' => $categories
]
];
}
修改编辑模板
在
editjoke.html.php
模板中,使用一系列复选框让用户选择笑话所属的分类。
<form action="" method="post">
<input type="hidden" name="joke[id]" value="<?=$joke->id ?? ''?>">
<label for="joketext">Type your joke here:</label>
<textarea id="joketext" name="joke[joketext]" rows="3" cols="40"><?=$joke->joketext ?? ''?></textarea>
<p>Select categories for this joke:</p>
<?php foreach ($categories as $category): ?>
<input type="checkbox" name="category[]" value="<?=$category->id?>" />
<label><?=$category->name?></label>
<?php endforeach; ?>
<input type="submit" name="submit" value="Save">
</form>
修改 CSS 样式
为了让页面显示更美观,在
jokes.css
中添加以下 CSS 样式:
form p {clear: both;}
input[type="checkbox"] {float: left; clear: left; width: auto; margin-right: 10px;}
input[type="checkbox"] + label {clear: right;}
修改保存方法
在
Joke
控制器的
saveEdit
方法中,处理分类信息并将其保存到
joke_category
表中。
public function saveEdit() {
$author = $this->authentication->getUser();
$joke = $_POST['joke'];
$joke['jokedate'] = new \DateTime();
$jokeEntity = $author->addJoke($joke);
foreach ($_POST['category'] as $categoryId) {
$jokeEntity->addCategory($categoryId);
}
header('location: /joke/list');
}
修改
addJoke
方法
在
Author
类的
addJoke
方法中,返回一个
Joke
实体实例。
public function addJoke($joke) {
$joke['authorId'] = $this->id;
return $this->jokesTable->save($joke);
}
修改
DatabaseTable
类的
save
方法
在
DatabaseTable
类的
save
方法中,创建并返回一个实体实例,并将数据写入该实例。
public function save($record) {
$entity = new $this->className(...$this->constructorArgs);
try {
if ($record[$this->primaryKey] == '') {
$record[$this->primaryKey] = null;
}
$this->insert($record);
} catch (\PDOException $e) {
$this->update($record);
}
foreach ($record as $key => $value) {
if (!empty($value)) {
$entity->$key = $value;
}
}
return $entity;
}
通过以上步骤,我们实现了一个功能较为完善的网站,包括笑话的编辑、缓存优化、分类管理以及笑话与分类的关联。这些操作不仅提高了网站的性能,还增强了网站的功能和用户体验。
PHP 网站开发:从关系管理到性能优化
总结与回顾
我们来总结一下前面实现的各项功能及其关键步骤,以下是一个简要的表格梳理:
| 功能模块 | 关键步骤 |
| — | — |
| 编辑笑话 | 1. 判断用户权限,仅作者可编辑
2. 提供编辑表单并处理提交 |
| 缓存优化 | 1. 在 Joke 类添加
$author
属性
2. 修改
getAuthor
方法实现缓存 |
| 笑话分类功能 | 1. 创建分类表
2. 创建分类控制器、模板和方法
3. 配置路由实现分类的增删改查 |
| 为笑话分配分类 | 1. 创建关联表
joke_category
2. 修改 Joke 控制器及相关方法
3. 处理分类信息保存到关联表 |
性能优化的重要性
在整个网站开发过程中,性能优化是一个不可忽视的环节。就像前面提到的缓存优化,数据库查询是一个相对耗时的操作,如果不进行优化,多次重复查询会严重影响网站的响应速度。透明缓存技术通过在类内部进行优化,使得外部使用时无需关心具体实现,只需调用方法即可,大大提高了性能。
例如,在没有使用缓存时,多次调用
getAuthor
方法会多次查询数据库:
echo $joke->getAuthor()->name;
echo $joke->getAuthor()->email;
echo $joke->getAuthor()->password;
而使用缓存后,数据库只会被查询一次:
echo $joke->getAuthor()->name;
echo $joke->getAuthor()->email;
echo $joke->getAuthor()->password;
这个过程可以用以下 mermaid 流程图表示:
graph TD;
A[调用 getAuthor 方法] --> B{缓存是否存在};
B -- 是 --> C[返回缓存中的作者信息];
B -- 否 --> D[查询数据库获取作者信息];
D --> E[将作者信息存入缓存];
E --> C;
功能拓展的灵活性
我们实现的笑话分类功能展示了网站功能拓展的灵活性。随着网站的发展,用户可能有更多的需求,比如查看特定类型的笑话。通过创建分类表和关联表,我们可以轻松地为笑话添加分类,并且支持一个笑话属于多个分类。
整个笑话分类功能的实现流程可以概括为以下步骤:
1.
创建分类表
:定义分类的基本信息。
2.
创建控制器和模板
:实现分类的增删改查操作。
3.
配置路由
:将不同的操作映射到相应的控制器方法。
4.
创建关联表
:建立笑话与分类的多对多关系。
5.
修改控制器和方法
:处理分类信息的保存和关联。
这个流程可以用以下 mermaid 流程图表示:
graph LR;
A[创建分类表] --> B[创建控制器和模板];
B --> C[配置路由];
C --> D[创建关联表];
D --> E[修改控制器和方法];
代码复用与框架的优势
在开发过程中,我们使用的类和框架体现了代码复用的优势。例如,
DatabaseTable
类的
save
方法经过修改后,可以在不同的实体类中复用,每次保存数据时都能返回一个实体实例,方便后续操作。
public function save($record) {
$entity = new $this->className(...$this->constructorArgs);
try {
if ($record[$this->primaryKey] == '') {
$record[$this->primaryKey] = null;
}
$this->insert($record);
} catch (\PDOException $e) {
$this->update($record);
}
foreach ($record as $key => $value) {
if (!empty($value)) {
$entity->$key = $value;
}
}
return $entity;
}
通过这样的设计,我们避免了重复编写大量的数据库操作代码,提高了开发效率。而且,当需要对数据库操作进行修改时,只需要在
DatabaseTable
类中进行修改,就可以影响到所有使用该类的地方。
未来展望
虽然我们已经实现了网站的基本功能,但仍然有很多可以改进和拓展的地方。例如,可以添加用户对笑话的点赞、评论功能,进一步丰富用户体验;可以对分类进行更细致的管理,如添加分类的描述信息等。
在性能方面,可以考虑使用更高级的缓存技术,如 Redis 缓存,进一步提高网站的响应速度。同时,对数据库进行优化,如创建合适的索引,也可以提高查询效率。
总之,通过不断地优化和拓展,我们可以让网站更加完善,满足用户日益增长的需求。
通过以上的开发和优化过程,我们可以看到,一个功能完善、性能良好的网站需要在多个方面进行精心设计和实现。从权限控制到性能优化,从功能拓展到代码复用,每一个环节都对网站的质量有着重要的影响。希望这些经验和方法能对大家的网站开发有所帮助。
超级会员免费看

被折叠的 条评论
为什么被折叠?



