微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Eloquents 的 Mutators/Accessors、Model 的内部属性和 Casts 如何相互关联?

如何解决Eloquents 的 Mutators/Accessors、Model 的内部属性和 Casts 如何相互关联?

我的问题可以用标题中的问题来概括。 然而,这个看似无害的问题引发了几个详细的后续问题。 不幸的是,官方文档 Eloquent: Mutators & Casting 远非完整和有用。

在我的脑海里有 Retrofit(Java ORM 框架)和 Doctrine(另一个 PHP ORM 框架)的知识,我最初开发了以下 Eloquent 架构的心理图:

+--------+                 +--------------------------------------------------+                  +---------------+
|        |      ---get-->  |                       Model                      |  ---Accessor-->  | Application's |
|  RDBM  | sql    Casts    | $this->attributes is an associative array        |                  |    Business   |
|        |      <--set---  | with values typed as required by the model space |  <--Mutator----  |     Logic     |
+--------+                 +--------------------------------------------------+                  +---------------+

本质上,我假设

  1. 演员表在 RDBM 和模型之间进行转换
    • sql 一侧的值的可接受类型为 boolintfloatdoublestring,因为它很复杂类型(如 sql DATETIME)总是由 RDBM 输入/输出sql 字符串
    • 模型侧面允许的类型是模型需要的任何类型
  2. 访问器/修改器是模型和应用程序其余部分之间的经典 getter/setter
  3. 模型类的内部关联数组 attributes 将字符串键映射到类型特定于模型的值和转换的结果

稍微正式一点,我认为界面应该如下所示。 请注意,我添加了注释和 PHP 类型提示,以提高我的假设的清晰度。 我知道这行不通,因为父类方法没有类型提示。 类型 CustomType 是模型空间中属性类型的占位符。

class MyCast extends AttributeCast
{
  /*
   * Hydrates an sql response into the model
   * 
   * @param Model  $model      the associated model
   * @param string $key        the name of the sql column/attribute
   * @param mixed  $value      the sql representation of the value
   * @param array  $attributes an associative array of (sql column,value)-pairs of the current row/entity,*                           in particular $attributes[$key] === $value holds
   *
   * @return CustomType|null  The converted value
   */
  public function get(Model $model,string $key,$value,array $attributes): ?CustomType {
    // do some conversion
  }



  /*
   * Dehydrates/persists a model's attribute to sql
   *
   * @param Model            $model       the associated model
   * @param string           $key         the name of the sql column/attribute
   * @param CustomType|null  $value       the value of the model
   * @param array            $attributes  all internal attributes of the model,*                                    in particular $attributes[$key] === $value holds
   *
   * @return array  An associative map of sql columns and their values
   */
  public function set(Model $model,?CustomType $value,array $attributes): array {
    // do some conversion
  }

}

class MyModel extends Model {

  /**
   * Getter for $this->attributes['some']
   *
   * Assumption 1: $this->attributes['some'] === $value holds,i.e.
   *               it does not matter if this code uses the passed $value
   *               or `$this->attributes['some']`
   * Assumption 2: Internal array of attributes stores value using the
   *               the type of the model space
   *
   * @param ?CustomType $value  the $value to get from the model; the
   *                            parameter is not actually required (cp.
   *                            assumption 1) but only passed by the
   *                            framework for convenience
   * @return ?CustomType  The requested value
   */
  protected function getSomeAttribute(?CustomType $value): ?CustomType {
    return $this->attributes['some']; // assumed default behavior
  }
  
  /**
   * Setter for $this->attributes['some']
   *
   * Assumption: Internal array of attributes stores value using the
   *             the type of the model space
   *
   * @param ?CustomType $value  the $value to set
   */
  protected function setSomeAttribute(?CustomType $value): void {
    $this->attributes['some'] = $value; // assumed default behavior
  }
}

然而,正如我发现的那样,Eloquent 框架似乎一方面认为访问器/修改器,另一方面将强制转换视为相互排斥。 特别是,在某些情况下(我找不到经验法则),当我同时定义两者时,我最终陷入了无限循环。 我还注意到,如果没有定义 mutator/accessor,则在访问属性时框架会调用强制转换。 (例如,cp.Model::__get() --> HasAttributes::getAttribute --> HasAttributes::getAttributeValue --> HasAttributes::transformModelValue)。 此外,我从实验中了解到,我所做的一些假设根本不正确。 例如:

  • 传递给访问器 $value 的参数 getSomeAttribute 不是多余的。 特别是,在调用访问器时假设始终定义 $this->attributes['some'] 是不安全的。 如果尝试访问访问器内部的 $this->attributes['some'],可能会收到索引未定义错误
  • 传递给mutator $value 的参数getSomeAttribute 并不总是使用假定的“模型空间”类型,即使定义了属性的强制转换。 有时框架会调用 mutator 并传递一个字符串。
  • 自定义类型转换的 setget 方法也是如此:传递给 get 的值并不总是来自 sql 查询,传递给 {{1} } 并不总是使用模式空间类型。

我回到我的主要问题:

Eloquents 的 Mutators/Accessors、Model 的内部属性和 Casts 如何相互关联?

我的好答案也应该解决我上面的假设并澄清所有提出的问题。 这些后续问题可能有助于构建答案:

  1. 框架对模型内部数组 set 中值的类型有什么期望?它们应该是“与 sql 兼容的”还是应该在“模型空间”中?

  2. 框架何时调用访问器、修改器、强制转换的设置方法或强制转换的获取方法

    请注意,仅在一种情况下从应用程序的业务逻辑访问模型的属性。 显然,该框架还在从/向 DB 层进行加水/脱水期间调用其中一些方法,并且可能在更多情况下我仍然不知道。

  3. 如果为同一个属性定义了访问器、修改器和/或强制转换的组合,在 2. 的所有情况下会发生什么?

  4. 模型的内部数组 attribute 何时填充,特别是在调用访问器、修改器和强制转换方面?

  5. 如果传递给 $attributes 方法get 已经具有正确的类型或者是简单地返回传递的 $value 是否安全?

    注意:我提出这个问题的原因是我注意到 Eloquents get 是这样做的。如果传入 $value 对象,则该方法不会返回相同的 asDateTime 对象,而是返回它的深层副本。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。