34、数据库表类中实体类的使用与优化

数据库表类中实体类的使用与优化

在开发过程中,我们常常需要从数据库中获取数据并进行处理。传统方式下,数据库返回的数据通常是数组形式,这在处理数据时可能会带来一些不便。本文将介绍如何使用实体类来优化数据处理,提高代码的可读性和可维护性。

1. 从数组到实体类的转变

数据库返回的数据为数组形式,例如在 getUser() 方法返回的 $author 数组,我们需要将其数据复制到新创建的 Author 类的实例中。这种复制操作显然效率不高,我们可以通过在 saveEdit 方法外部构造 Author 对象,并让 getUser 返回构造好的对象来避免这个问题。

public function saveEdit() {
    $authorObject = $this->authentication->getUser();
    $joke = $_POST['joke'];
    $joke['jokedate'] = new \DateTime();
    $authorObject->addJoke($joke);
    header('location: /joke/list');
}

目前, getUser 方法调用了 DatabaseTable 类的 findById 方法,该方法返回的是一个数组。不过,我们可以使用 fetchObject 方法来返回指定类的实例,例如 Author 类。

return $query->fetchObject('Author', [$jokesTable]);

这里有两个参数:
1. 要实例化的类的名称。
2. 创建对象时提供给构造函数的参数数组。

2. 改进 DatabaseTable

为了更灵活地使用不同的实体类,我们可以修改 DatabaseTable 类的构造函数,使其接受两个可选参数:要创建的类的名称和提供给它的任何参数。

class DatabaseTable {
    private $pdo;
    private $table;
    private $primaryKey;
    private $className;
    private $constructorArgs;

    public function __construct(\PDO $pdo, string $table, string $primaryKey, string $className = '\stdClass', array $constructorArgs = []) {
        $this->pdo = $pdo;
        $this->table = $table;
        $this->primaryKey = $primaryKey;
        $this->className = $className;
        $this->constructorArgs = $constructorArgs;
    }
}

同时,修改 findById 方法以使用新的类变量:

public function findById($value) {
    $query = 'SELECT * FROM `' . $this->table . '` WHERE `' . $this->primaryKey . '` = :value';
    $parameters = [
        'value' => $value
    ];
    $query = $this->query($query, $parameters);
    return $query->fetchObject($this->className, $this->constructorArgs);
}

PDO fetchAll 方法也可以通过提供相应参数来返回对象:

return $result->fetchAll(\PDO::FETCH_CLASS, $this->className, $this->constructorArgs);
3. 修改 IjdbRoutes

IjdbRoutes 类中,我们需要更新 DatabaseTable 类的实例化,为 $authorsTable 实例提供类名和参数。

$this->jokesTable = new \Ninja\DatabaseTable($pdo, 'joke', 'id');
$this->authorsTable = new \Ninja\DatabaseTable($pdo, 'author', 'id', '\Ijdb\Entity\Author', [$this->jokesTable]);

这样,当使用 $authorsTable 实例检索记录时,返回的 $author 变量将是 Author 类的实例,我们可以直接调用该类的方法。

4. 解决 saveEdit 方法的问题

由于 getUser 现在返回的是 Author 类的实例,我们可以直接使用该实例,避免手动创建和设置属性。

public function saveEdit() {
    $author = $this->authentication->getUser();
    $joke = $_POST['joke'];
    $joke['jokedate'] = new \DateTime();
    $author->addJoke($joke);
    header('location: /joke/list');
}

同时,我们还需要修改 Authentication 类,以使用对象而不是数组。在 isLoggedIn 方法和 login 方法中,将数组访问语法改为对象访问语法。

// isLoggedIn 方法修改
$passwordColumn = $this->passwordColumn;
if (!empty($user) && $user[0]->$passwordColumn === $_SESSION['password']) {

// login 方法修改
public function login($username, $password) {
    $user = $this->users->find($this->usernameColumn, strtolower($username));
    if (!empty($user) && password_verify($password, $user[0]->{$this->passwordColumn})) {
        session_regenerate_id();
        $_SESSION['username'] = $username;
        $_SESSION['password'] = $user[0]->{$this->passwordColumn};
        return true;
    } else {
        return false;
    }
}
5. 修复笑话列表页面的问题

当前笑话列表页面显示错误,原因是 $author $joke 不再是数组,而是对象。我们需要将数组访问语法改为对象访问语法。

$author = $this->authorsTable->findById($joke->authorId);
$jokes[] = [
    'id' => $joke->id,
    'joketext' => $joke->joketext,
    'jokedate' => $joke->jokedate,
    'name' => $author->name,
    'email' => $author->email
];

同时,修改方法的返回语句和 delete 方法以使用对象语法。

return ['template' => 'jokes.html.php',
    'title' => $title,
    'variables' => [
        'totalJokes' => $totalJokes,
        'jokes' => $jokes,
        'userId' => $author->id ?? null
    ]
];

// delete 方法修改
if ($joke->authorId != $author->id)
6. 创建 Joke 实体类

为了更好地处理笑话数据,我们创建 Joke 实体类。

<?php
namespace Ijdb\Entity;

class Joke
{
    public $id;
    public $authorId;
    public $jokedate;
    public $joketext;
    private $authorsTable;

    public function __construct(\Ninja\DatabaseTable $authorsTable)
    {
        $this->authorsTable = $authorsTable;
    }

    public function getAuthor()
    {
        return $this->authorsTable->findById($this->authorId);
    }
}
7. 使用 Joke 类并解决依赖问题

要使用新的 Joke 类,我们需要更新 IjdbRoutes 类,将 authorsTable 实例作为构造函数参数传递。但这会导致一个问题,即 authorsTable 实例的构造函数需要 jokesTable 实例,而 jokesTable 实例的构造函数又需要 authorsTable 实例,形成了一个死循环。

我们可以使用引用(reference)来解决这个问题。

$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]);
8. 简化列表控制器操作

现在,我们可以直接将整个 Joke 对象传递到模板中,并在模板中读取作者信息。

// 移除冗余代码
$jokes = [];
foreach ($result as $joke) {
    $author = $this->authorsTable->findById($joke->authorId);
    $jokes[] = [
        'id' => $joke->id,
        'joketext' => $joke->joketext,
        'jokedate' => $joke->jokedate,
        'name' => $author->name,
        'email' => $author->email
    ];
}

// 替换代码
$result = $this->jokesTable->findAll();
$jokes = $this->jokesTable->findAll();
9. 更新模板

更新 jokes.html.php 模板,使用新的对象语法。

<p><?=$totalJokes?> jokes have been submitted to the Internet Joke Database.</p>
<?php foreach ($jokes as $joke): ?>
<blockquote>
    <p>
        <?=htmlspecialchars($joke->joketext, ENT_QUOTES, 'UTF-8')?>
        (by <a href="mailto:<?=htmlspecialchars($joke->getAuthor()->email, ENT_QUOTES, 'UTF-8'); ?>">
            <?=htmlspecialchars($joke->getAuthor()->name, ENT_QUOTES, 'UTF-8'); ?></a> on
        <?php
            $date = new DateTime($joke->jokedate);
            echo $date->format('jS F Y');
        ?>)
        <?php if ($userId == $joke->authorId): ?>
            <a href="/joke/edit?id=<?=$joke->id?>">Edit</a>
            <form action="/joke/delete" method="post">
                <input type="hidden" name="id" value="<?=$joke->id?>">
                <input type="submit" value="Delete">
            </form>
        <?php endif; ?>
    </p>
</blockquote>
<?php endforeach; ?>
10. 整理编辑笑话页面

由于我们改变了 DatabaseTable 类的工作方式,“编辑笑话”页面出现了问题。我们需要打开 editjoke.html.php 模板,将数组语法替换为对象语法来访问笑话的属性。

通过以上步骤,我们成功地将数据库返回的数组数据转换为实体类对象,提高了代码的可读性和可维护性,同时也优化了数据处理的流程。

流程图

graph TD;
    A[开始] --> B[修改DatabaseTable类构造函数];
    B --> C[修改findById和fetchAll方法];
    C --> D[更新IjdbRoutes类实例化];
    D --> E[修改saveEdit方法];
    E --> F[修改Authentication类];
    F --> G[修复笑话列表页面];
    G --> H[创建Joke实体类];
    H --> I[解决依赖问题];
    I --> J[简化列表控制器操作];
    J --> K[更新模板];
    K --> L[整理编辑笑话页面];
    L --> M[结束];

总结

本文详细介绍了如何使用实体类来优化数据库数据处理,包括从数组到实体类的转变、改进 DatabaseTable 类、解决依赖问题以及更新模板等步骤。通过这些优化,我们可以提高代码的质量和可维护性,使开发过程更加高效。

操作步骤总结

  1. 修改 DatabaseTable 类的构造函数,添加类名和构造函数参数。
  2. 修改 findById fetchAll 方法,使用 fetchObject fetchAll 返回对象。
  3. 更新 IjdbRoutes 类,提供类名和参数。
  4. 修改 saveEdit 方法,使用对象而不是数组。
  5. 修改 Authentication 类,使用对象访问语法。
  6. 修复笑话列表页面,将数组语法改为对象语法。
  7. 创建 Joke 实体类。
  8. 使用引用解决依赖问题。
  9. 简化列表控制器操作,直接传递对象到模板。
  10. 更新模板,使用对象语法。
  11. 整理编辑笑话页面,替换数组语法为对象语法。

数据库表类中实体类的使用与优化(续)

11. 深入理解引用的作用

在前面的步骤中,我们使用了引用(reference)来解决 authorsTable jokesTable 实例化时的依赖问题。引用就像是 Windows 中的快捷方式,或者 macOS 或 Linux 中的符号链接。它本身不存储具体的数据,而是指向另一个变量。

以下是一个简单的引用示例:

$originalVariable = 1;
$reference = &$originalVariable;
$originalVariable = 2;
echo $reference;

在这个示例中, $reference $originalVariable 的引用。当 $originalVariable 的值被修改为 2 时, $reference 的值也会变为 2。这是因为 $reference 指向的是 $originalVariable 的内存地址,读取 $reference 的值时,实际上是读取 $originalVariable 的值。

在我们的应用中,通过在 IjdbRoutes 类中使用引用,确保了 authorsTable jokesTable 实例在需要时已经被创建,避免了依赖循环的问题。

12. 优化后的代码优势分析

通过将数据库返回的数组数据转换为实体类对象,我们获得了许多优势。

优势 说明
提高代码可读性 实体类对象的属性和方法具有明确的含义,使得代码更易于理解。例如, $joke->getAuthor()->name $joke['email'] 更直观地表达了我们要获取的信息。
增强可维护性 如果数据库表结构发生变化,我们只需要在实体类中进行相应的修改,而不需要在整个代码库中查找和修改数组访问的地方。
更好的扩展性 可以在实体类中添加新的方法和属性,以满足不断变化的业务需求。例如,我们可以在 Joke 类中添加一个 getCategory 方法,用于获取笑话的类别。
模板灵活性 控制器只需要提供一个笑话列表,模板可以根据需要读取任何信息,包括作者信息。如果数据库中添加了新的列,我们可以直接在模板中显示该值,而不需要修改控制器。
13. 可能遇到的问题及解决方案

在使用实体类优化数据库数据处理的过程中,可能会遇到一些问题。以下是一些常见问题及解决方案:

问题 解决方案
依赖循环 使用引用(reference)来解决 authorsTable jokesTable 实例化时的依赖问题。
模板语法错误 确保在模板中使用正确的对象访问语法,例如将 $joke['joketext'] 改为 $joke->joketext
方法调用错误 确保实体类中定义了所需的方法,并且在调用时使用正确的方法名和参数。
14. 进一步优化建议

虽然我们已经对代码进行了优化,但仍然有一些可以进一步改进的地方。

  • 缓存机制 :在获取作者信息时,可以考虑使用缓存机制,避免频繁的数据库查询。例如,使用 Redis 缓存作者信息,当需要获取作者信息时,先从缓存中查找,如果缓存中不存在,再从数据库中查询。
  • 错误处理 :在实体类的方法中添加更完善的错误处理机制,例如在 getAuthor 方法中,如果数据库查询失败,应该返回一个默认值或者抛出一个自定义的异常。
  • 代码复用 :可以将一些通用的方法提取到基类中,提高代码的复用性。例如,创建一个 Entity 基类,将一些通用的方法(如 getProperty setProperty 等)放在基类中,让 Author Joke 类继承该基类。
15. 总结与展望

通过本文的介绍,我们学习了如何使用实体类来优化数据库数据处理,从数组到实体类的转变,以及如何解决依赖问题和更新模板。这些优化措施提高了代码的可读性、可维护性和扩展性,使开发过程更加高效。

在未来的开发中,我们可以继续探索更多的优化方法,如使用缓存机制、完善错误处理和提高代码复用性等。同时,我们也可以将这些优化方法应用到其他项目中,提高项目的整体质量。

流程图

graph TD;
    A[开始] --> B[理解引用作用];
    B --> C[分析优化后代码优势];
    C --> D[解决可能遇到的问题];
    D --> E[提出进一步优化建议];
    E --> F[总结与展望];
    F --> G[结束];

操作步骤总结

  1. 理解引用的概念和作用,使用引用解决依赖循环问题。
  2. 分析优化后代码的优势,包括提高可读性、可维护性和扩展性等。
  3. 识别可能遇到的问题,并采取相应的解决方案。
  4. 提出进一步的优化建议,如缓存机制、错误处理和代码复用等。
  5. 总结优化过程,展望未来的开发方向。

通过以上步骤,我们可以更好地掌握使用实体类优化数据库数据处理的方法,提高代码的质量和开发效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值