验证外键的最佳方法


Best way to validate foreign keys?

我在验证引用其他模型的模型时遇到了问题。

我有一个"用户"模型和一个"配置文件"模型。我想创建配置文件,但我需要检查用户是否确实存在。

在我的配置文件模型中,我有一个方法"validateUser",但我必须将查询写入数据库中的特定表,或者我必须创建一个用户模型对象并调用 exists(id)。

这两种选择似乎都有很多缺点。表名称可能会更改,我必须检查使用它的所有模型。使用用户模式对象,我必须在配置文件模型中创建一个对象或注入它。

解决这个问题的最佳方法是什么?

末日领域 ..

在现实世界中,验证很少是一个简单的过程。相反,您有几个不相关的问题:

  • 检查实例内的数据是否与业务规则一致
  • 确保实例的状态不与其他域结构冲突
  • 根据存储中的现有数据和约束验证信息的完整性

如果您使用活动记录来表示您的域模型(不要与 MVC 中的 M 混淆),那么这三个方面都将成为单个对象的责任。

这就是你现在拥有的。

但是有希望..

最好的选择是将所有这些责任分开(所有冰雹SRP)。基本上,您要做的是将当前设置划分为不同的结构组:

  • 对象:用于处理域实体的特定规则

  • 数据映射器:用于存储抽象

  • 服务:用于域对象和映射器(或其他域对象)之间的交互

由于您的问题有些令人困惑(用户和配置文件以及保存和验证,然后不存在某些内容),我不确定我是否正确理解了它,但这里有一个小例子:

public function createProfile( $id, $email, $name )
{
    $account = new Account;
    $account->setId( $id );
    $accountMapper = new AccountMapper( $pdo ); // explained below
    if ( $accountMapper->fetch( $account ) === false )
    {
        $this->errors[] = .. something about missing account
        return;
    }
    $profile = new Profile;
    $profile->setEmail( $email )
            ->setName( $name );
    if ( $profile->isValid() === false )
    {
        $this->errors[] = .. something about invalid profile
        return;
    }
    
    try
    {
        $profileMapper = new ProfileMapper( $pdo ); // explained below
        $profileMapper->store( $profile );
    }
    catch ( Exception $e )
    {
        $this->errrors[] = .. something about failing to create profile
        return;
    }
    $account->addProfile( $profile );
    $accountMapper->store( $account );
}

这是一个极其简化的示例。特别是在初始化映射器的情况下,因为在现实世界中,该部分将由某个工厂处理。有点像这篇文章中描述的。

这里的要点是,域数据的验证和数据库完整性的保险是分开完成的。用于与数据库交互的底层 API 实际上会返回错误代码,即您违反了UNIQUE KEYFOREIGN KEY或任何其他约束,然后您可以使用这些约束来确定出了什么问题。

该方法本身将是服务的一部分(在本例中为管理用户帐户的某些服务)。

注意:如果您的应用程序需要为每个操作执行多个 SQL 交互,并且这些交互需要作为具有回滚功能的事务来完成,那么,您应该考虑实现工作单元,而不是直接使用数据映射器。要了解UoW,您必须阅读企业应用程序架构模式,因为它确实是广泛的主题。