问题描述
我的问题可以用标题中的问题来概括。 然而,这个看似无害的问题引发了几个详细的后续问题。 不幸的是,官方文档 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 |
+--------+ +--------------------------------------------------+ +---------------+
本质上,我假设
- 演员表在 RDBM 和模型之间进行转换
- 访问器/修改器是模型和应用程序其余部分之间的经典 getter/setter
- 模型类的内部关联数组
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 并传递一个字符串。 - 自定义类型转换的
set
和get
方法也是如此:传递给get
的值并不总是来自 sql 查询,传递给 {{1} } 并不总是使用模式空间类型。
我回到我的主要问题:
Eloquents 的 Mutators/Accessors、Model 的内部属性和 Casts 如何相互关联?
我的好答案也应该解决我上面的假设并澄清所有提出的问题。 这些后续问题可能有助于构建答案:
-
框架对模型内部数组
set
中值的类型有什么期望?它们应该是“与 sql 兼容的”还是应该在“模型空间”中? -
框架何时调用访问器、修改器、强制转换的设置方法或强制转换的获取方法?
请注意,仅在一种情况下从应用程序的业务逻辑访问模型的属性。 显然,该框架还在从/向 DB 层进行加水/脱水期间调用其中一些方法,并且可能在更多情况下我仍然不知道。
-
如果传递给
$attributes
方法的get
已经具有正确的类型或者是简单地返回传递的$value
是否安全?注意:我提出这个问题的原因是我注意到 Eloquents
get
是这样做的。如果传入$value
对象,则该方法不会返回相同的asDateTime
对象,而是返回它的深层副本。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)