验证应该在哪里进行?

时间:2014-04-28 23:23:58

标签: php validation rest model-view-controller dao

我想在我的PHP项目中使用MVC模式。我还想在模型层使用Dao-Service模式,因为它使数据库引擎可以轻松互换,并使业务逻辑不受数据库交互的影响。

现在,I heard验证应该在模型层中进行,因为控制器只负责传输数据。这很合理。

是否应该在服务层或实体本身实现?

方法1:实体验证

class Post extends Entity
{
    protected $title;

    public function getTitle()
    {
        return $this->title;
    }

    public function setTitle($newTitle)
    {
        if (strlen($newTitle) == 0)
            throw new ValidationException('Title cannot be empty.');
        $this->title = $newTitle;
    }
}

class PostService
{
    public static function saveOrUpdate(Post $post)
    {
        PostDao::saveOrUpdate($post);
    }
}

优点:

  • 如果我做错了,我马上就知道了,
  • 一切都在一个地方,这似乎是一件好事。

缺点:

  • 由于花哨的设置者和吸气剂,许多样板代码,
  • 为实体添加了一些业务逻辑,这似乎是一件坏事(特别是如果验证非常复杂 - 例如需要查询数据库/其他服务),
  • 序列化和反序列化可能会变得困难。

方法2:服务验证

class Post extends Entity
{
    public $title;
}

class PostService
{
    public static function saveOrUpdate(Post $post)
    {
        if (strlen($post->title) == 0)
            throw new ValidationException('Title cannot be empty.');
        PostDao::saveOrUpdate($post);
    }
}

优点:

  • 保持业务逻辑服务,这似乎是一件好事,
  • 样板保持最小。

缺点:

  • 当出现问题并且 确实出错时,我立即不知道。
  • 我无法保证在将其保存到数据库之前实际进行验证。示例:我必须在两个例程中保存帖子,忘记在其中一个例程中使用PostService::saveOrUpdate代理,并通过PostDao::saveOrUpdate直接执行。噗,验证并没有在那个例程和项目中发生,现在唯一的希望就是单元测试或者我自己在代码中发现它。因此,代码在这方面更难维护。

你有任何提示吗?我错过了什么吗?到目前为止,该项目正在绘图板上,所以我已经做好了准备。

2 个答案:

答案 0 :(得分:4)

  

注意:控制器负责“传输数据”。控制器的职责是改变模型(在特殊情况下 - 当前视图实例的状态)。

实际上有第三种方法:domain object有一个单独的isValid()方法(你称之为“实体”)。当您有多个数据条目的验证规则时,setter验证会变得混乱。

示例:验证用户注册表单的重复密码。

在setter中进行验证会变得非常混乱。特别是如果您选择为每个失败的验证使用例外。

此外,我建议您使用data access objects而不是data mappers。代码基本上看起来像这样:

$post = new Model\Domain\Post;
$mapper = new Model\Mappers\Post($pdo);

$post->setId(42);
$mapper->fetch($post);

$post->setTitle($title);
if ($post->isValid()) {
    $mapper->store($post);
}

如果你想重用一些验证规则,这种方法还允许你通过构造函数在Validator实例中注入某种Model\Domain\Post实例来外化验证。

但是,在制作更大的应用时,您可能会注意到,很少有重复检查,这超出了现有的php filters

  

注意:请不要使用静态类。这迫使你使用不需要的程序范例。

你必须注意的另一件事是:你在做什么样的验证?

应在域对象中验证业务规则,数据完整性检查(例如:“此电子邮件地址是唯一的”)是持久性逻辑的一部分。持久性逻辑应该(根据SRP由一个单独的实例处理。在给定的例子中,该部分由数据映射器管理。

答案 1 :(得分:2)

我认为你应该有两层验证,你的“模型”验证可以验证全局适用于数据模型的不变条件。例如,由于数据库中的非空字段,因此需要“名称”。在这种情况下,方法2是合适的。

另一层验证是“表单”验证,这将在您的表单模型中进行,这将测试特定于表单的条件。例如,您的用户模型上有一个is_admin字段,在管理面板更新用户表单中设置此字段可能有效,但对用户“更改密码”表单无效。这种方法可能更接近第一种方法。

关于实现它,我可能不会在setter中对它进行验证,除非你想对每一个“set”进行测试,或者你很乐意只设置和保存有效的字段。如果其中一个字段无效,通常你会拒绝整个更新,因此拥有一个isValid()函数更有意义,它可以在保存之前自动调用,或者测试它是否有效而不保存。