Magento 2数据库:模型,资源模型和集合

找出什么是ORM,并发现与数据库的连接以及每个Magento 2 ORM组件的功能。

Magento 2中与数据库的连接如何工作?

在Magento 2中,数据库连接设置包含在app / etc / env.php文件中:

该文件的路径存储在\ Magento \ Framework \ Config \ File \ ConfigFilePool类的私有数组$ applicationConfigFiles中

从资源模型到数据库有一条连接链:

  • 该方法 create()  的  \Magento\Framework\App\Bootstrap 类是所谓的的index.php文件。它调用自我公共静态方法createObjectManagerFactory()
public static function createObjectManagerFactory($rootDir, array $initParams)
        {
 
            $dirList = self::createFilesystemDirectoryList($rootDir, $initParams);
            $driverPool = self::createFilesystemDriverPool($initParams);
            $configFilePool = self::createConfigFilePool();
            return new ObjectManagerFactory($dirList, $driverPool, $configFilePool);
        }
  • 方法createObjectManagerFactory()调用自身方法createConfigFilePool(),该方法创建ConfigFilePool对象并返回其中已将ConfigFilePool设置为参数的ObjectManagerFactory对象。 (ObjectManagerFactory是具有复杂操作的对象。创建全局配置是此类操作之一。)
  • 所有自定义资源模型都应从\ Magento \ Framework \ Model \ ResourceModel \ Db \ AbstractDb类继承。它具有方法getConnection(),其中调用了\ Magento \ Framework \ App \ ResourceConnection对象。
 public function getConnection()
    {
        $fullResourceName = ($this->connectionName ? $this->connectionName : ResourceConnection::DEFAULT_CONNECTION);
        return $this->resource->getContent($fullResourceName);
    }

\Magento\Framework\App\ResourceConnection 包含从全局配置数据操作字段和方法。它涉及来自app / etc / env.php文件的数据。例如,ResourceConnection :: DEFAULT_CONNECTION是默认值,它对应于['db'=> ['connection'=> ['default'=> […]]]]

'db' => [
        'table_prefix' => '',
        'connection' => [
            'default' => [
                'host' => 'localhost',
                'dbname' => 'magento',
                'username' => 'root',
                'password' => '',
                'active' => '1'
            ]
        ]
    ],

(您可以在自定义资源模型中创建一个新的自定义连接并将其设置为$ connectionName。)

Magento 2资源模型

如前所述,通过资源模型实现与数据库的连接。资源模型是用于存储结构的数据映射器。它们用于对数据库进行事务处理。

资源模型扩展了\ Magento \ Framework \ Model \ ResourceModel \ Db \ AbstractDb(使用Magento 2,您不再需要在配置中声明模型,资源模型,适配器等。因此,此过程比使用的过程简单得多在Magento 1中)。

您需要为资源模型定义表名和主键字段名,因为有必要知道将模型状态保存在哪里。通过在受保护的_construct()方法中调用_init()方法来指定它们。请注意,您是通过单下划线构造方法而不是真正的双下划线PHP构造函数调用_init()的。这个单下划线_construct()方法也是Magento 1的遗留方法,并由从\ Magento \ Framework \ Model \ ResourceModel类继承的实际__construct()方法调用。

class Inbox extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
protected function _construct()
{
   $this->_init('adminnotification_inbox', 'notification_id');
}
….
}
---------------------------------------------------------------------
abstract class AbstractDb extends AbstractResource
{
….
protected function _init($mainTable, $idFieldName)
{
   $this->_setMainTable($mainTable, $idFieldName);
}
protected function _setMainTable($mainTable, $idFieldName = null)
{
   $this->_mainTable = $mainTable;
   if (null === $idFieldName) {
       $idFieldName = $mainTable . '_id';
   }
 
   $this->_idFieldName = $idFieldName;
   return $this;
}
…
}

与数据库的所有交互过程都存储在资源模型的方法中。他们使用\ Magento \ Framework \ DB \ Adapter \ Pdo \ Mysql方法访问数据库表。它扩展了Zend_Db_Adapter_Pdo_Mysql类,并实现了Magento \ Framework \ DB \ Adapter \ AdapterInterface。这是将这些类连接到资源模型的机制:

  • 所述 \Magento\Framework\DB\Adapter\Pdo\Mysql 对象是在创建
    Magento\Framework\Model\ResourceModel\Type\Db\Pdo\Mysql 在受保护的方法类getDbConnectionClassName() 
protected function getDbConnectionClassName()
    {
        return DB\Adapter\Pdo\Mysql::class;
    }
  • 在受保护的方法getDbConnectionInstance()中调用它,在公共方法getConnection()中调用它。
  • 全局配置文件app / etc / di.xml包含首选项:
<preference for="\Magento\Framework\App\ResourceConnection\ConnectionAdapterInterface" type="Magento\Framework\Model\ResourceModel\Type\Db\Pdo\Mysql"></preference>
  • 因此,\ Magento \ Framework \ Model \ ResourceModel \ Type \ Db \ Pdo \ MysqlConnectionAdapterInterface的实现。
  • ConnectionAdapterInterface被称为\Magento\Framework\Model\ResourceModel\Type\Db\ConnectionFactorycreate()  方法:

public function create(array $connectionConfig)
    {
        /** @var \Magento\Framework\App\ResourceConnection\ConnectionAdapterInterface $adapterInstance */
        $adapterInstance = $this->objectManager->create(
            \Magento\Framework\App\ResourceConnection\ConnectionAdapterInterface::class,
            ['config' => $connectionConfig]
        );
 
        return $adapterInstance->getConnection();
    }

\Magento\Framework\Model\ResourceModel\Type\Db\ConnectionFactory 是一个实现了的 \Magento\Framework\Model\ResourceModel\Type\Db\ConnectionFactoryInterface 类。在__construct()方法中的\ Magento \ Framework \ App \ ResourceConnection(在上面提到的资源模型中调用,并在用于连接数据库的方法中调用)中调用它:


public function __construct(
        ResourceConfigInterface $resourceConfig,
        ConnectionFactoryInterface $connectionFactory,
        DeploymentConfig $deploymentConfig,
    ) {
        var_dump($resourceConfig);
        $this->config = $resourceConfig;
        $this->connectionFactory = $connectionFactory;
        $this->deploymentConfig = $deploymentConfig;
        $this->tablePrefix = $tablePrefix ?: null;
    }

所有事务,带有外键,索引,列,表的操作都在\ Magento \ Framework \ DB \ Adapter \ Pdo \ Mysql中实现

Magento 2型号

模型是数据和行为,代表实体。数据库表所代表的任何内容都很可能成为模型。

要创建模型,只需创建一个类,然后从\ Magento \ Framework \ Model \ AbstractModel对其进行扩展。同样,请注意单下划线构造方法。不要将其与真正的PHP构造函数混淆。调用方法_init(),其中应设置资源模型。

class Inbox extends \Magento\Framework\Model\AbstractModel implements NotifierInterface, InboxInterface
{
protected function _construct()
{
   $this->_init('Magento\AdminNotification\Model\ResourceModel\Inbox');
}
….
}
-----------------------------------------------------------------------------------
abstract class AbstractModel extends \Magento\Framework\DataObject
{
...
protected function _init($resourceModel)
{
   $this->_setResourceModel($resourceModel);
   $this->_idFieldName = $this->_getResource()->getIdFieldName();
}
…
}

这是支持继承的AbstractModel方法getResource()getCollection()所必需的。

方法getCollection()已弃用。它不会在将来的Magento 2版本中使用。为了避免与代码冲突,需要使用collection factory。

该模型没有直接访问数据库的权限,只有资源模型。

模型仅包含用于管理具有数据的对象的方法,这些数据应该准备好保存到数据库或加载到前端。即使方法的负载已“移至”资源模型,在Magento 2的未来版本中也会从模型中弃用。它还包含不同的有用标志,这些标志用于在设置为数据库之前验证数据或防止在数据库中进行不必要的事务。对象中的数据未更改。根据不同的Magento 2实体,它在\ Magento \ Eav \ Model \ Entity类的save()方法中使用:

public function save(\Magento\Framework\Model\AbstractModel $object)
    {
        /**
         * Direct deleted items to delete method
         */
        if ($object->isDeleted()) {
            return $this->delete($object);
        }
        if (!$object->hasDataChanges()) {
            return $this;
        }
        ...
    }

除了load()方法之外,Magento2模型还包含已弃用的CRUD(“ CRUD”代表“创建,读取,更新,删除”。)方法save()delete()。动作已委托给资源模型。努力编写升级安全代码的开发人员不应在模型上使用CRUD方法,因为将来会在某些时候删除它们。相反,应将模型直接传递给资源模型上的CRUD方法。

abstract class AbstractModel extends \Magento\Framework\DataObject
{
….
public function save()
{
   $this->_getResource()->save($this);
   return $this;
}
….
}
 
abstract class AbstractDb extends AbstractResource
{
….
public function save(\Magento\Framework\Model\AbstractModel $object)
{
   if ($object->isDeleted()) {
       return $this->delete($object);
   }

实际上,该方法会同时执行以下两种操作:如果指定了$ isDeleted参数,则它将设置_isDeleted标志值;否则,返回当前标志值。validateBeforeSave()方法使用一个验证器,该验证器包含用于验证当前模型的所有规则。它由资源模型save()方法调用。

在资源模型的save()方法中,_dataSaveAllowed标志可以停止保存后beforeSave()方法被调用。如果某些数据验证失败,这将很有帮助。资源模型方法save()检查保存的对象并运行updateObject()saveNewObject(),其中的其中一个使用\ Magento \ Framework \ DB \ Adapter \ AdapterInterface对象来处理数据库。load()方法的机制类似:

public function load($modelId, $field = null)
{
   $this->_beforeLoad($modelId, $field);
   $this->_getResource()->load($this, $modelId, $field);
   $this->_afterLoad();
   $this->setOrigData();
   $this->_hasDataChanges = false;
   $this->updateStoredData();
   return $this;
}
….
abstract class AbstractDb extends AbstractResource
{
….
public function load(\Magento\Framework\Model\AbstractModel $object, $value, $field = null)
{
   if ($field === null) {
       $field = $this->getIdFieldName();
   }
 
   $connection = $this->getConnection();
   if ($connection && $value !== null) {
       $select = $this->_getLoadSelect($field, $value, $object);
       $data = $connection->fetchRow($select);
 
       if ($data) {
           $object->setData($data);
       }
   }
 
   $this->unserializeFields($object);
   $this->_afterLoad($object);
 
   return $this;
}
….
}

由于将来会在某个时候删除模型加载方法,因此最好将插件用于资源模型加载方法,而不要依赖于这些事件。

所述_afterLoad()方法用于装载数据后直接处理的对象。每次加载Magento模型时,您都可以通过观察事件model_load_beforemodel_load_after来拦截它,该事件带有一个简单的数据传输对象。模型实例本身就是关键的“对象”

Magento 2收藏

集合封装了模型和相关功能的集合,例如过滤,排序和分页。

Magento中的集合是一个实现IteratorAggregate和Countable PHP5 SPL接口的类。Magento中广泛使用集合来存储一组特定类型的对象。

该集合扩展了\ Magento \ Framework \ Model \ ResourceModel \ Db \ Collection \ AbstractCollection
创建资源集合时,需要指定它对应的模型,以便它可以在加载记录列表后实例化适当的类。还必须知道匹配的资源模型才能访问数据库。这就是为什么在受保护的_construct()方法中使用_init()方法指定模型和资源模型的类名的原因。

还有就是不要使用方法的最佳选择getCollection()
\Magento\Framework\Model\AbstractModel 用于获取实体的集合,因为它会在Magento 2未来的版本中删除。您应该使用收集工厂。这是在\ Magento \ Setup \ Fixtures \ Quote \ QuoteGenerator中获取产品集合的示例:

/**
  * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
  */
private $productCollectionFactory;
 
...
 
public function __construct(
        ...
        \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
        ...
    ) {
        ...
        $this->productCollectionFactory = $productCollectionFactory;
        ...
    }
 
...
 
private function getProductIds(\Magento\Store\Api\Data\StoreInterface $store, $typeId, $limit = null)
    {
        /** @var $productCollection \Magento\Catalog\Model\ResourceModel\Product\Collection */
        $productCollection = $this->productCollectionFactory->create();
        $productCollection->addStoreFilter($store->getId());
        $productCollection->addWebsiteFilter($store->getWebsiteId());
 
        // "Big%" should be replaced with a configurable value.
        if ($typeId === QuoteConfiguration::BIG_CONFIGURABLE_TYPE) {
            $productCollection->getSelect()->where(" type_id = '" . Configurable::TYPE_CODE . "' ");
            $productCollection->getSelect()->where(" sku LIKE 'Big%' ");
        } else {
            $productCollection->getSelect()->where(" type_id = '$typeId' ");
            $productCollection->getSelect()->where(" sku NOT LIKE 'Big%' ");
        }
 
        return $productCollection->getAllIds($limit);
    }

您针对集合编程的API是由\ Magento \ Framework \ Model \ ResourceModel \ Db \ Collection \ AbstractCollection实现的

集合的编程API由\ Magento \ Framework \ Model \ ResourceModel \ Db \ Collection \ AbstractCollection实现

// Collection provides several useful functions for working with a result
// set; here is a partial set
addFieldToFilter($field, $condition = null);
getConnection();
getSelect();
addBindParam($name, $value);
getIdFieldName();
distinct($flag);
addOrder($field, $direction = self::SORT_ORDER_DESC);
setOrder($field, $direction = self::SORT_ORDER_DESC);
unshiftOrder($field, $direction = self::SORT_ORDER_DESC);
getItems();
getSize();
getSelectCountSql();
load($printQuery = false, $logQuery = false);
resetData();
walk($callback, array $args = []);

集合提供了一些有用的功能来定义和使用结果集。如您所见,集合所公开的方法集与Magento 1相比并没有太大变化。

必须创建资源集合才能创建一组模型实例并对其进行操作。资源集合延迟加载它们表示的记录集。第一次访问项目列表时,将自动加载一个集合。之后,即使显式调用load()方法,也不会再次加载集合。

集合非常接近数据库层。他们的任务是从数据库中检索一组行,然后对其进行迭代,并为每一行实例化匹配的模型类。

它解决了这些主要问题:

  • 提供用于存储对象集合的容器。
  • 防止不必要的数据加载。
  • 在会话期间存储所有对象。
  • 提供用于对特定种类的实体进行过滤和排序的接口。

Magento 2服务合同中的Repository实现使用了Magento ORM。与Magento 1相比,这是一个重要的变化,因为一个模块不应再依赖使用特定ORM的其他模块,而应仅使用实体存储库。服务合同将在本文的第二部分中详细介绍。

赞(0)
微信
支付宝
微信二维码图片

微信扫描二维码打赏

支付宝二维码图片

支付宝扫描二维码打赏

相关文章

0 0 投票数
文章评分
订阅评论
提醒
2 评论
最旧
最新 最多投票
内联反馈
查看所有评论
111
111
1 年 前

1. 能不能把页面上显示的这只猫取消掉,或者加个关闭按钮,不然看起来总是会挡着文字内容,也可以在个人中心页加个配置,让用户可以自定义是否显示这只猫,
2. 另外我退出登录后,刷新页面,我还是可以继续修改这个评论,是不是评论这里没有加权限验证呀
3. 评论的保存按钮,有模糊重影
4. 注册的时候,邮箱没有做校验,你看我的账号,邮箱随便填的
5. 可以有一个联系作者的方式么,不然想反馈问题很难,比如邮箱或者留言给作者都可以,你可以在导航上加个联系我之类的