【问题标题】:Zend Framework: Models, Mappers; Default Fields in Mappers & Field Operations in Models?Zend 框架:模型、映射器;映射器中的默认字段和模型中的字段操作?
【发布时间】:2012-09-02 14:38:19
【问题描述】:

我正在 Zend Framework 中创建一个简单的 ORM,以使用 DbTable/Mapper/Model 方法粗略地封装一个公共库应用程序。不过,我不确定我执行与用户相关的课程的方式是否正确,因为我在Mapper_User 中有一些逻辑,在Model_User 中有一些逻辑。

Mapper_User

<?php
class Mapper_Users {

/*
createModelObject would be called by a Controller handling a Form_Regsiter's
data, to create a new Model_User object. This object'd then be saved by the
same Controller by calling Mapper_Users->save();
*/
    public function createModelObject(array $fields) {
        if(!isset($fields['date_registered']))
            $fields['date_registered'] = date('Y-m-d H:i:s');
        if(!isset($fields['max_concurrent_rentals']))
            $fields['max_concurrent_rentals'] = 3;
        return new Model_User($fields);
    }
}
?>

在从头开始创建新的Model_User 对象的方法中(例如,不是从数据库中提取记录,而是注册一个新用户),我使用提供的名称/用户名/密码实例化一个新的Model_User一个表格,然后设置一些对象属性,例如注册日期,“一次允许的最大书籍”等。该数据由Mapper_User 填充到Model_User 中,然后在调用Mapper_User-&gt;save(); 时写入数据库。 Mapper 感觉是放置这个的正确位置 - 保持模型光亮。

这是对的吗,还是应该在Model_User 内部设置这样的默认字段?

模型_用户

<?php
class Model_User {

    public function setPassword($value) {
        $this->password = md5($value);
    }
}
?>

在设置用户对象的密码时,正如您所料,我在Model_User-&gt;setPassword($value); 中执行此操作,并在此方法中执行$this-&gt;password = md5($value);。同样,这感觉正确 - 如果 Model_User 是从数据库中提取的,尝试在 Mapper_User-&gt;save(); 方法中执行 md5 步骤会导致问题,因为密码字段显然已经被散列。

这就是我困惑的地方。在我看来,与“与用户有关的字段”有关的所有逻辑都应该存在于它的模型或映射器中,但是在这里我在映射器中有一些逻辑(默认字段),还有一些(字段操作)在模型。 这是对的,还是我应该尝试以某种方式获取模型中的默认字段,或映射器中的字段操作?

感谢您抽出宝贵时间阅读本文!


编辑@RockyFord:

Mapper_User 实际上 扩展了我写的摘要,因为我不喜欢在 500 个Mapper_*.php 文件中编写相同的基本代码,因此有一些官僚作风,但它有效 __construct() 非常简单:

<?php
class Mapper_Users {

    public function __construct() {
        $this->_db = new DbTable_Users();
        if(!$this->_db instanceof Zend_Db_Table_Abstract)
            throw new Exception('Invalid table data gateway provided');
    }
}
?>

【问题讨论】:

  • 如果您发布一些代码来演示会有所帮助,我想我明白了,但我不确定。
  • @RockyFord 认为没有它会更整洁,但我会添加一些!
  • 我正在努力寻找答案,但如果不了解您的想法和编码方式,我不知道从哪里开始。
  • 谢谢@RockyFord,我真的很感激 - 添加了两个有问题的代码块的一些代码示例,这是否有助于了解我在做什么正确/错误?
  • 我现在就告诉你,setPassword() 中的散列不会长时间工作。你如何 __construct() 你的映射器?

标签: zend-framework orm model datamapper


【解决方案1】:

DataMapper 负责用对象的数据填充对象,并将其持久化。当您调用$user-&gt;save() 时,您似乎在混合事物,因为您将持久性逻辑放在域对象中。当您使用 ActiveRecord 模式而不是 DataMappers 时,这是一种常见的方法,这是一件坏事。

您的DataMapper 应该负责保存对象$mapper-&gt;save($user);,它只需要更新更改的属性。因此,只有您设置了新的哈希值,密码才会更新。

更新:

你说:

[...] 尝试在Mapper_User-&gt;save(); 方法中执行 md5 步骤会导致 如果Model_User 是从数据库中提取的一个问题,则作为密码 字段显然已经被散列了。

创建一个名为setPasswordHash() 的方法并在从数据库中提取时使用它。

记住:Don't look for things!

您应该请求它,而不是在映射器中寻找数据库。

public __construct(Zend_Db_Table $dbTable) {
    $this->dbTable = $dbTable;
}

这都是依赖注入。

【讨论】:

  • 嗨@Keyne 感谢您的回复。 save(); 方法实际上是在Mapper_User 中,一个Model_User 对象被传递给它,数据被提取并通过DbTable_User 保存到数据库中。所以我想我至少有一点是对的! :)
  • @SteveGriffiths 啊!没看到那个。抱歉 =) 另请注意,最好将 crypt() 与河豚一起使用,而不是 md5()
  • 别担心,是的,一旦所有其他问题都解决了,我将从 md5 切换到更少冲突的东西!再次感谢
  • 回复:您的编辑;这是否意味着任何希望实例化给定 Mapper 的给定控制器也需要了解相应的 DbTable?例如在某些控制器中的某些操作中:$mapper = new Mapper_User(new DbTable_User());?这种方式似乎更复杂......伙计,这一切都太令人困惑了。
  • 忘记在我的回复中标记你@Keyne
【解决方案2】:

这可能需要一段时间才能完全回答,但我将从setPassword 问题开始。

你现在的:

public function setPassword($value) {
        $this->password = md5($value);
    }

现在这与惯例或最佳实践无关,而是实用性。

问问自己:

当您检索用户对象的数据库记录并且该数据库记录包含散列密码时会发生什么?

答案:当您构造用户对象并调用$this-&gt;setPassword($password); 或等效项时,您会将哈希应用于哈希。

因此,您几乎有义务在映射器的 save() 方法或用于更新密码的方法中对密码进行哈希处理。将数据库表中的哈希值视为密码,将输入到表单字段中的值视为该密码的占位符。

下一部分:

在我看来,与“与用户有关的字段”相关的所有逻辑都应该存在于其模型或映射器中

这基本上是正确的。

属于对象域 (Model_User) 的所有内容在域模型类 (Model_User) 中处理。

映射器仅将(map)数据对象(数据库行、json 字符串、xml 文件、平面文件、csv 文件 ...)转换为表单可以实例化域对象 (Model_User)。

因此,您最终可能会为给定的域对象提供多个映射器,或者一个映射器可能映射到多个数据源。

如果您不再将数据视为“字段”,这可能会对您有所帮助,这可能会使您将注意力集中在数据库中,而是根据 属性 来考虑对象>特征

因为当您深入到最基本的级别时,Model_User 对象只是:

class Model_User {
    protected $id;
    protected $name;
    protected $password;
    //continue....
}

所有的 getter、setter、构造函数和其他方法都差不多,所以我们可以将值放入这些变量中。

【讨论】:

  • +1 用于模型属性而不是字段。但它是protected,而不是$protected,对吗?无论如何,我会将其设为私有以正确封装和隐藏类详细信息。如果出于某种原因我需要扩展它,我会使用 setter/getter 而不是直接访问属性。它与OPEN/CLOSED 原则有关。
  • 这看起来很棒@RockyFord,非常感谢。现在尝试解析它,但经过今天的 Zend 斗争可能需要一段时间......
  • @Keyne 当然你是对的,这就是我在不支持自动完成的情况下编写代码所得到的。 :)
猜你喜欢
  • 2022-08-03
  • 1970-01-01
  • 1970-01-01
  • 2015-09-05
  • 1970-01-01
  • 2018-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多