Magento 中有两种持久化模型,简单类型和 **Entity**–**attribute**–**value** (**EAV**) 类型。术语 entity (后面统一译做实体) 可以表示其中的任意一种模型,我们可以将 entity 看成一种持久化模型。
<br />Magento_Newsletter 模块中的 Subscriber 实体是简单模型的一个例子,我们可以观察到它包含以下内容:
> - 一个继承自 `Magento\Framework\Model\AbstractModel` 的模型类 `Magento\Newsletter\Model\Subscriber`
> - 一个继承自 `Magento\Framework\Model\ResourceModel\Db\AbstractDb` 的资源类 `Magento\Newsletter\Model\ResourceModel\Subscriber`
> - 一个继承自 `Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection` 的 collection 类 `Magento\Newsletter\Model\ResourceModel\Subscriber\Collection`
<br />Magento_Customer 模块的 Customer 实体是 EAV 模型的一个例子,它由以下几部分组成。
> - 一个继承自 `Magento\Framework\Model\AbstractModel` 类的模型类 `Magento\Customer\Model\Customer`
> - 一个继承自 `Magento\Eav\Model\Entity\VersionControl\AbstractEntity` 类的 resource model 类 `Magento\Customer\Model\ResourceModel\Customer`
> - 一个继承自 `Magento\Eav\Model\Entity\Collection\VersionControl\AbstractCollection` 类的 collection 类 `Magento\Customer\Model\ResourceModel\Customer\Collection`
<br />EAV 模型和普通模型的不同之处本质上在于 resource model 和 collection 类的不同,resource model 用来连接数据库,或者称作持久化器,如果你愿意的话。<br />
<br />当一个订阅者(subscriber)被保存时,对应的数据被水平地保存在数据库中,从订阅者的模型中获取的数据直接输出到单张 `newsletter_subscriber` 表中。<br />
<br />当保存顾客(customer)信息时,数据被水平地保存在数据库中,从顾客模型中获取的数据被输出到一下几张表中:
> - customer_entity
> - customer_entity_datetime
> - customer_entity_decimal
> - customer_entity_int
> - customer_entity_text
> - customer_entity_varchar
<br />决定个体属性存储在什么地方是由包含在 `eav_attribute.backend_type` 的列决定的,`SELECT DISTINCT backend_type FROM eav_attribute;` 查询语句获取到的数据展示如下:
> - 存储在 `<entityName>_entity` 表中的 `static` 属性值
> - 存储在 `<entityName>_entity_varchar` 表中的 `varchar` 属性值
> - 存储在 `<entityName>_entity_int` 表中的 `int` 属性值
> - 存储在 `<entityName>_entity_text` 表中的 `text` 属性值
> - 存储在 `<entityName>_entity_datetime` 表中的 `datetime` 属性值
> - 存储在 `<entityName>_entity_decimal` 表中的 `decimal` 属性值
紧挨着 `eav_attribute` 表,其他相关信息散落在其他 `eav_*` 表中,最重要的就是 `eav_attribute_*` 表,主要有:
> - eav_attribute
> - eav_attribute_group
> - eav_attribute_label
> - eav_attribute_option
> - eav_attribute_option_swatch
> - eav_attribute_option_value
> - eav_attribute_set
<br />`SELECT entity_type_code, entity_model FROM eav_entity_type;` 查询表明下面的几个 Magento 实体采用的是 EAV 模型:
> - `customer`: `Magento\Customer\Model\ResourceModel\Customer`
> - `customer_address`: `Magento\Customer\Model\ResourceModel\Address`
> - `catalog_category`: `Magento\Catalog\Model\ResourceModel\Category`
> - `catalog_product`: `Magento\Catalog\Model\ResourceModel\Product`
> - `order`: `Magento\Sales\Model\ResourceModel\Order`
> - `invoice`: `Magento\Sales\Model\ResourceModel\Order\Invoice`
> - `creditmemo`: `Magento\Sales\Model\ResourceModel\Order\Creditmemo`
> - `shipment`: `Magento\Sales\Model\ResourceModel\Order\Shipment`
但是,以上的几个模型并没有全部使用 EAV 模型,SELECT DISTINCT entity_type_id FROM eav_attribute; 查询出来的只有下面几个:
> - customer
> - customer_address
> - catalog_category
> - catalog_product
<br />
<br />EAV 模型本身就更加复杂,它的主要使用场景是动态创建属性,最理想的是通过管理接口创建属性,就像产品属性的创建一样。但是在绝大多数时候,简单模型就够用了。<br />
### 创建一个简单模型
<br />和 EAV 模型不同,创建一个简单模型十分直接。下面就一起来创建一个 Log 实体的 model、resource model 和 collection。<br />
<br />我们首先创建 <MAGELICIOUS_DIR>/Core/Model/Log.php 并填充以下内容:
```php
class Log extends \Magento\Framework\Model\AbstractModel {
protected $_eventPrefix = 'magelicious_core_log';
protected $_eventObject = 'log';
protected function _construct() {
$this->_init(\Magelicious\Core\Model\ResourceModel\Log::class);
}
}
```
$_eventPrefix 和 $_eventObject 的使用并不是必须的,但是推荐使用。这些值会在 Magento\Framework\Model\AbstractModel 的事件派发中被使用,而且也给我们的模块添加了可扩展性。尽管 Magento 使用 <ModuleName>_<ModelName> 惯例来给 $_eventPrefix 赋值,但是使用 <VendorName>_<ModuleName>_<ModelName> 会更加安全。 $_eventObject 的名称按照惯例是模型本身的名称。<br />
<br />我们接下来创建 <MAGELICIOUS_DIR>/Core/Model/ResourceModel/Log.php 文件并填充以下内容:
```php
class Log extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb {
protected function _construct() {
$this->_init('magelicious_core_log', 'entity_id');
}
}
```
_init 方法有 2 个参数:magelicious_core_log 赋值给 $mainTable ,entity_id 赋值给 $idFieldName 参数 ,$idFieldName 的值是对应表的主键名称。现在 magelicious_core_log 表还不存在,我们将在后面创建它。<br />
<br />接下来我们创建 <MAGELICIOUS_DIR>/Core/Model/ResourceModel/Log/Collection.php 并填充以下内容:
```php
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection {
protected function _construct() {
$this->_init(
\Magelicious\Core\Model\Log::class,
\Magelicious\Core\Model\ResourceModel\Log::class
);
}
}
```
_init 方法有 2 个参数:$model 和 $resourceModel 的字符串表示,Magento 用 <FULLY_QUALIFIED_CLASS_NAME>::class 这种语法来表示,这种方式相比直接传递类名更好。 <br />
<br />
### 需要记住的方法
EAV 模型和简单模型都继承自 Magento\Framework\Model\AbstractModel 类,Magento\Framework\Model\AbstractModel 又继承自 Magento\Framework\DataObject ,DataObject 又几个简洁的值得记住的方法。<br />
<br />下面的一组方法和数据转换相关:
> - toArray: 将对象转换为数组,数组中的键为请求的键
> - toXml: 将对象转换为
> - toJson: 将对象转换为 JSON
> - toString: 将对象数据按照预定义格式转换为字符串
> - serialize: 将对象按照定义好的键和值转换为字符串
<br />下面的这些方法都是通过魔术方法 __call 实现的,主要有以下几种语法:
> - get<AttributeName>, 例如 $object->getPackagingOption()
> - set<AttributeName>, 例如 $object->setPackagingOption('plastic_bag')
> - uns<AttributeName>, 例如 $object->unsPackagingOption()
> - has<AttributeName>, 例如 $object->hasPackagingOption()
<br />为了尝试上面的几个方法,我们手动创建 magelicious_core_log 表,SQL 如下:
```sql
CREATE TABLE `magelicious_core_log` (
`entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`severity_level` varchar(24) NOT NULL,
`note` text NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`entity_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```
因为继承自 DataObject ,所以即使 Magelicious\Core\Model\Log 模型是空的,也可以利用下面的方法来保存数据:
```php
$log->setCreatedAt(new \DateTime());
$log->setSeverityLevel('info');
$log->setNote('Just Some Note');
$log->save();
```
上面的例子能正常运行,但是模型类远远不止这些。在创建模型的过程中手动创建数据表并不可行。Magento 中采用了 setup 脚本的机制来创建数据表。<br />
- Magento 基本概念
- Magento 中的 Plugin
- Magento 中的 Events 和 observers
- Magento 中的 Areas
- Magento 中的请求处理流程
- Magento 中的模块(Modules)
- Magento 中的 Cache
- Magento 中的依赖注入
- Magento 中的 Console commands
- Magento 中的 Cron jobs
- 掌握 Entities
- 理解 model 的类型
- 理解 setup scripts
- 实体扩展
- 初入 web API
- users 的几种不同类型
- 授权的不同类型
- APIs 的不同类型
- 使用 Magento 现有的 API
- 创建自定义 web APIs
- 理解 search criteria
- Magento 后台开发
- 使用 listing 组件
- 使用 form 组件
- 开发前台功能
- 搭建前台开发环境
- 初始化 & 调用 JS 组件
- 开始使用 RequireJS
- 替换 jQuery widget 组件
- 扩展 jQuery widget 组件
- 创建 jQuery widgets 组件
- 创建 UI/KnockoutJS 组件
- 扩展 UI/KnockoutJS 组件
- 自定义 Catalog
- 创建参考尺寸
- 创建当天发货
- 标识新产品
- Magento 性能最佳实践
- Magento 硬件推荐配置
- 软件推荐
- 架构参考
- 配置项最佳实践
- 高级设置
- 其他
- 如何通过 Burp Suite 和 Xdebug 来调试 Magento
- Mageno checkout 必知必会
- Magento 安装(新手必看)
- 安装流程
- 系统要求