PHP 应用开发:Markdown 格式处理与数据排序分页技巧
1. Markdown 格式处理
在处理笑话文本时,我们可以使用 Markdown 格式来增强文本的显示效果。首先,将显示笑话文本的代码替换为以下内容:
<?php
$markdown = new \Ninja\Markdown($joke->joketext);
echo $markdown->toHtml();
?>
这段代码将
joketext
的内容作为构造函数参数传递给
Markdown
类,并调用
toHtml
方法将文本转换为 HTML。不过,这种写法比原始方法更繁琐,我们可以使用更简洁的语法:
<?=(new \Ninja\Markdown($joke->joketext))->toHtml()?>
使用 Markdown 格式的好处在于,有很多开源代码可以帮助我们处理它。常见的 Markdown 库包括 ParseDown 和 cebe/markdown。
2. 数据排序
MySQL 支持按特定顺序检索记录。当前笑话列表页面按发布顺序显示笑话,我们希望将最新的笑话显示在前面。可以使用
SELECT
查询中的
ORDER BY
子句来指定排序的列,例如:
SELECT * FROM `joke` ORDER BY `jokedate` DESC
这个查询将选择所有笑话,并按日期降序排列,即最新的笑话在前。
为了在网站上实现这个功能,我们需要修改
DatabaseTable
类的
findAll
方法,添加一个可选的
$orderBy
参数:
public function findAll($orderBy = null) {
$query = 'SELECT * FROM ' . $this->table;
if ($orderBy != null) {
$query .= ' ORDER BY ' . $orderBy;
}
$result = $this->query($query);
return $result->fetchAll(\PDO::FETCH_CLASS, $this->className, $this->constructorArgs);
}
然后,修改
Joke
控制器的
list
方法,为
findAll
方法提供
$orderBy
参数:
public function list() {
if (isset($_GET['category'])) {
$category = $this->categoriesTable->findById($_GET['category']);
$jokes = $category->getJokes();
} else {
$jokes = $this->jokesTable->findAll('jokedate DESC');
}
// …
}
目前,主笑话列表页面按最新优先排序,但点击分类后,笑话仍按最早优先排序。我们可以考虑在
find
方法中添加相同的可选参数:
public function find($column, $value, $orderBy = null) {
$query = 'SELECT * FROM ' . $this->table . ' WHERE ' . $column . ' = :value';
$parameters = [
'value' => $value
];
if ($orderBy != null) {
$query .= ' ORDER BY ' . $orderBy;
}
$query = $this->query($query, $parameters);
return $query->fetchAll(\PDO::FETCH_CLASS, $this->className, $this->constructorArgs);
}
然而,这并不能解决分类内笑话排序的问题。因为
find
方法是在代表
joke_category
表的
DatabaseTable
实例上调用的,我们无法轻松按日期排序。可以使用 PHP 的
usort
函数来解决这个问题:
public function getJokes() {
$jokeCategories = $this->jokeCategoriesTable->find('categoryId', $this->id);
$jokes = [];
foreach ($jokeCategories as $jokeCategory) {
$joke = $this->jokesTable->findById($jokeCategory->jokeId);
if ($joke) {
$jokes[] = $joke;
}
}
usort($jokes, [$this, 'sortJokes']);
return $jokes;
}
private function sortJokes($a, $b) {
$aDate = new \DateTime($a->jokedate);
$bDate = new \DateTime($b->jokedate);
if ($aDate->getTimestamp() == $bDate->getTimestamp()) {
return 0;
}
return $aDate->getTimestamp() > $bDate->getTimestamp() ? -1 : 1;
}
使用
usort
函数会有轻微的性能开销,但在处理少量记录时,这种差异可以忽略不计。
3. 分页处理
随着网站的发展,笑话数量可能会增加,导致笑话列表页面加载时间过长。为了解决这个问题,我们可以使用分页来显示合理数量的笑话,例如每页显示 10 条。
首先,我们需要在
DatabaseTable
类的
findAll
和
find
方法中添加
$limit
和
$offset
可选参数:
public function find($column, $value, $orderBy = null, $limit = null, $offset = null) {
$query = 'SELECT * FROM ' . $this->table . ' WHERE ' . $column . ' = :value';
$parameters = [
'value' => $value
];
if ($orderBy != null) {
$query .= ' ORDER BY ' . $orderBy;
}
if ($limit != null) {
$query .= ' LIMIT ' . $limit;
}
if ($offset != null) {
$query .= ' OFFSET ' . $offset;
}
$query = $this->query($query, $parameters);
return $query->fetchAll(\PDO::FETCH_CLASS, $this->className, $this->constructorArgs);
}
public function findAll($orderBy = null, $limit = null, $offset = null) {
$query = 'SELECT * FROM ' . $this->table;
if ($orderBy != null) {
$query .= ' ORDER BY ' . $orderBy;
}
if ($limit != null) {
$query .= ' LIMIT ' . $limit;
}
if ($offset != null) {
$query .= ' OFFSET ' . $offset;
}
$result = $this->query($query);
return $result->fetchAll(\PDO::FETCH_CLASS, $this->className, $this->constructorArgs);
}
然后,在
Joke
控制器的
list
方法中提供
$limit
和
$offset
参数:
$page = $_GET['page'] ?? 1;
$offset = ($page - 1) * 10;
if (isset($_GET['category'])) {
$category = $this->categoriesTable->findById($_GET['category']);
$jokes = $category->getJokes();
} else {
$jokes = $this->jokesTable->findAll('jokedate DESC', 10, $offset);
}
为了显示分页链接,我们需要计算总页数,并在模板中生成链接:
<?php
$numPages = ceil($totalJokes / 10);
for ($i = 1; $i <= $numPages; $i++):
if ($i == $currentPage):
?>
<a class="currentpage" href="/joke/list?page=<?=$i?>"><?=$i?></a>
<?php else: ?>
<a href="/joke/list?page=<?=$i?>"><?=$i?></a>
<?php endif; ?>
<?php endfor; ?>
我们还可以通过添加 CSS 类来突出显示当前页面的链接:
.currentpage:before {
content: "[";
}
.currentpage:after {
content: "]";
}
以下是分页处理的流程图:
graph TD;
A[开始] --> B[获取当前页码];
B --> C[计算偏移量];
C --> D{是否有分类参数};
D -- 是 --> E[获取分类信息];
D -- 否 --> F[按日期降序获取笑话列表];
E --> G[获取分类下的笑话列表];
F --> H[获取指定偏移量和数量的笑话列表];
G --> I[显示笑话列表];
H --> I;
I --> J[计算总页数];
J --> K[生成分页链接];
K --> L[结束];
4. 分类内分页处理
目前的代码在分类内分页存在问题,点击分类时无法提供正确的偏移值。我们需要修改
Category
实体的
getJokes
方法,添加
$limit
和
$offset
参数:
public function getJokes($limit = null, $offset = null) {
$jokeCategories = $this->jokeCategoriesTable->find('categoryId', $this->id, null, $limit, $offset);
$jokes = [];
foreach ($jokeCategories as $jokeCategory) {
$joke = $this->jokesTable->findById($jokeCategory->jokeId);
if ($joke) {
$jokes[] = $joke;
}
}
usort($jokes, [$this, 'sortJokes']);
return $jokes;
}
同时,在
Joke
控制器的
list
方法中提供这些参数:
if (isset($_GET['category'])) {
$category = $this->categoriesTable->findById($_GET['category']);
$jokes = $category->getJokes(10, $offset);
}
另外,当前的分页链接存在两个问题:一是链接不包含分类变量,二是显示的页数是基于数据库中所有笑话的数量,而不是所选分类中的笑话数量。
为了解决第一个问题,我们需要在
list
方法中传递分类信息到模板,并在模板中修改链接:
return [
'template' => 'jokes.html.php',
'title' => $title,
'variables' => [
'totalJokes' => $totalJokes,
'jokes' => $jokes,
'user' => $author,
'categories' => $this->categoriesTable->findAll(),
'currentPage' => $page,
'category' => $_GET['category'] ?? null
]
];
<?php
$numPages = ceil($totalJokes / 10);
for ($i = 1; $i <= $numPages; $i++):
if ($i == $currentPage):
?>
<a class="currentpage" href="/joke/list?page=<?=$i?><?=!empty($categoryId) ? '&category=' . $categoryId : '' ?>"><?=$i?></a>
<?php else: ?>
<a href="/joke/list?page=<?=$i?><?=!empty($categoryId) ? '&category=' . $categoryId : '' ?>"><?=$i?></a>
<?php endif; ?>
<?php endfor; ?>
为了解决第二个问题,我们需要修改
DatabaseTable
类的
total
方法,支持按条件统计记录数量:
public function total($field = null, $value = null) {
$sql = 'SELECT COUNT(*) FROM `' . $this->table . '`';
$parameters = [];
if (!empty($field)) {
$sql .= ' WHERE `' . $field . '` = :value';
$parameters = ['value' => $value];
}
$query = $this->query($sql, $parameters);
$row = $query->fetch();
return $row[0];
}
然后,在
Category
实体类中添加一个新方法来返回该分类下的笑话数量:
public function getNumJokes() {
return $this->jokeCategoriesTable->total('categoryId', $this->id);
}
最后,在
Joke
控制器的
list
方法中调用这个方法:
$totalJokes = $category->getNumJokes();
以下是分类内分页处理的步骤列表:
1. 修改
Category
实体的
getJokes
方法,添加
$limit
和
$offset
参数。
2. 在
Joke
控制器的
list
方法中传递分类信息到模板。
3. 修改模板中的分页链接,包含分类变量。
4. 修改
DatabaseTable
类的
total
方法,支持按条件统计记录数量。
5. 在
Category
实体类中添加
getNumJokes
方法。
6. 在
Joke
控制器的
list
方法中调用
getNumJokes
方法。
通过以上步骤,我们可以实现分类内的正确分页。
PHP 应用开发:Markdown 格式处理与数据排序分页技巧
5. 总结与注意事项
在开发过程中,我们完成了 Markdown 格式处理、数据排序、分页处理以及分类内分页处理等功能。以下是一些总结和注意事项:
5.1 Markdown 格式处理
- 优点 :使用 Markdown 可以让文本内容更具格式,且有很多开源库可以帮助处理,如 ParseDown 和 cebe/markdown。
-
代码优化
:可以使用更简洁的语法
<?=(new \Ninja\Markdown($joke->joketext))->toHtml()?>来替代多行代码。
5.2 数据排序
-
数据库排序
:使用
ORDER BY子句可以让数据库对数据进行排序,如SELECT * FROMjokeORDER BYjokedateDESC。 -
PHP 排序
:当数据库排序无法满足需求时,可以使用 PHP 的
usort函数进行排序,但会有轻微性能开销。
5.3 分页处理
-
LIMIT 和 OFFSET
:使用
LIMIT和OFFSET子句可以实现分页功能,如SELECT * FROMjokeORDER BYjokedateLIMIT 10 OFFSET 10。 - 页面链接 :需要计算总页数并生成页面链接,同时可以通过 CSS 类突出显示当前页面链接。
5.4 分类内分页处理
-
参数传递
:需要在相关方法中添加
$limit和$offset参数,并传递分类信息到模板。 -
记录统计
:修改
DatabaseTable类的total方法,支持按条件统计记录数量。
以下是一个总结表格:
| 功能 | 实现方式 | 注意事项 |
| ---- | ---- | ---- |
| Markdown 格式处理 | 使用
\Ninja\Markdown
类 | 可使用简洁语法优化代码 |
| 数据排序 | 数据库使用
ORDER BY
,PHP 使用
usort
|
usort
有轻微性能开销 |
| 分页处理 | 使用
LIMIT
和
OFFSET
子句 | 计算总页数并生成页面链接 |
| 分类内分页处理 | 添加参数,传递分类信息,修改
total
方法 | 确保链接包含分类变量,统计分类内记录数量 |
6. 性能考虑
在处理大量数据时,性能是一个重要的考虑因素。以下是一些性能相关的建议:
6.1 排序性能
-
数据库排序
:使用
ORDER BY子句让数据库进行排序,性能较高,尤其是处理大量数据时。 -
PHP 排序
:
usort函数会有一定的性能开销,尽量在数据量较小时使用。
6.2 分页性能
-
LIMIT 和 OFFSET
:当偏移量较大时,
OFFSET会导致性能下降。可以考虑使用WHERE子句结合索引来优化。
以下是性能对比表格:
| 操作 | 数据库实现 | PHP 实现 | 性能对比 |
| ---- | ---- | ---- | ---- |
| 排序 |
ORDER BY
|
usort
| 数据库排序性能高 |
| 分页 |
LIMIT
和
OFFSET
| 无 | 偏移量较大时性能需优化 |
7. 扩展与优化
为了让应用更加完善,我们可以考虑以下扩展和优化:
7.1 动态分页数量
目前每页显示的笑话数量是固定的,可以通过用户选择或配置文件来动态调整每页显示的数量。
7.2 搜索功能
添加搜索功能,让用户可以根据关键词搜索笑话。可以在
DatabaseTable
类中添加相应的搜索方法。
7.3 缓存机制
对于一些频繁访问的数据,可以使用缓存机制来提高性能,如使用 Redis 缓存。
以下是扩展与优化的步骤列表:
1. 实现动态分页数量:在模板中添加选择框,根据用户选择调整
$limit
参数。
2. 添加搜索功能:在
DatabaseTable
类中添加搜索方法,在控制器中处理搜索请求。
3. 引入缓存机制:安装 Redis 并在代码中使用 Redis 缓存数据。
8. 总结
通过本文的介绍,我们学习了如何在 PHP 应用中处理 Markdown 格式、对数据进行排序和分页处理,以及解决分类内分页的问题。同时,我们也考虑了性能和扩展优化的方面。希望这些内容能帮助你开发出更加高效、完善的 PHP 应用。
以下是整个开发流程的 mermaid 流程图:
graph LR;
A[Markdown 格式处理] --> B[数据排序];
B --> C[分页处理];
C --> D[分类内分页处理];
D --> E[性能考虑];
E --> F[扩展与优化];
超级会员免费看
6341

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



