# Magento2 CRUD模型,资源集合,工厂对象详解\[教程\]

**URL:** https://forum.magentochina.org/t/magento2-crud%E6%A8%A1%E5%9E%8B%E8%B5%84%E6%BA%90%E9%9B%86%E5%90%88%E5%B7%A5%E5%8E%82%E5%AF%B9%E8%B1%A1%E8%AF%A6%E8%A7%A3%E6%95%99%E7%A8%8B/43
**Category:** Magento 2.X
**Tags:** crud, 开发, 后端, 数据, 数据库
**Created:** [2017 年10 月 17 日 15:51 UTC](https://forum.magentochina.org/t/magento2-crud%E6%A8%A1%E5%9E%8B%E8%B5%84%E6%BA%90%E9%9B%86%E5%90%88%E5%B7%A5%E5%8E%82%E5%AF%B9%E8%B1%A1%E8%AF%A6%E8%A7%A3%E6%95%99%E7%A8%8B/43 "2017-10-17T15:51:45Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### 作者： ![Shuai](https://forum.magentochina.org/user_avatar/forum.magentochina.org/shuai/32/3_2.png) [@Shuai](https://forum.magentochina.org/u/Shuai)
#### 发布日期： [2017 年10 月 17 日 15:51 UTC](https://forum.magentochina.org/t/magento2-crud%E6%A8%A1%E5%9E%8B%E8%B5%84%E6%BA%90%E9%9B%86%E5%90%88%E5%B7%A5%E5%8E%82%E5%AF%B9%E8%B1%A1%E8%AF%A6%E8%A7%A3%E6%95%99%E7%A8%8B/43/1 "2017-10-17T15:51:45Z")

</div>

Magento 2中的CRUD模型可以很 **容易管理数据库中的数据** ，您不需要编写许多代码来创建CRUD。  
CRUD是 **用于创建、读取、更新和删除数据** 。我们将学习一些主要内容:如何 **设置数据库、模型、资源模型和集合** ，收集并进行数据库相关操作。在之前的文章中，我们讨论了[创建Hello World模块](https://forum.magentochina.org/t/Magento-2%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E6%A8%A1%E5%9D%97%E6%95%99%E7%A8%8B/38)  
在学习这篇文章之前，让我们先来学习一下表是如何工作的。我将 **创建** 一个表 **magentochina\_post** ，并使用以下列:

- post\_id - 帖子ID
- title - 帖子标题
- content - 帖子的内容
- creation\_time - 创建时间

##创建 Magento 2 模型

- Step 1: 安装脚本
- Step 2: 模型
- Step 3: 资源模型
- Step 4: 资源模型集合
- Step 5: 工厂对象

####Step 1: 安装脚本  
首先，我们将为CRUD模型创建数据库表。要做到这一点，我们需要创建安装文件:

> app/code/Magentochina/HelloWorld/Setup/InstallSchema.php

此文件只在安装模块时执行一次。让我们把这个内容放到这个文件中，创建表:

```
<?php
namespace Magentochina\HelloWorld\Setup;
class InstallSchema implements \Magento\Framework\Setup\InstallSchemaInterface
{
/**
 * 创建表
 *
 * @param \Magento\Framework\Setup\SchemaSetupInterface $setup
 * @param \Magento\Framework\Setup\ModuleContextInterface $context
 * @return void
 * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
 */
public function install(\Magento\Framework\Setup\SchemaSetupInterface $setup, \Magento\Framework\Setup\ModuleContextInterface $context)
{
    $installer = $setup;
    $installer->startSetup();
    if (!$installer->tableExists('magentochina_helloworld_post')) {
        $table = $installer->getConnection()->newTable(
            $installer->getTable('magentochina_helloworld_post')
        )
        ->addColumn(
            'post_id',
            \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
            null,
            [
                'identity' => true,
                'nullable' => false,
                'primary' => true,
                'unsigned' => true,
            ],
            'Post ID'
        )
        ->addColumn(
            'name',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            ['nullable => false'],
            'Post Name'
        )
        ->addColumn(
            'url_key',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            [],
            'Post URL Key'
        )
        ->addColumn(
            'post_content',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            '64k',
            [],
            'Post Post Content'
        )
        ->addColumn(
            'tags',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            [],
            'Post Tags'
        )
        ->addColumn(
            'status',
            \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
            1,
            [],
            'Post Status'
        )
        ->addColumn(
            'featured_image',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            [],
            'Post Featured Image'
        )
       
        ->addColumn(
            'created_at',
            \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
            null,
            [],
            'Post Created At'
        )
        ->addColumn(
            'updated_at',
            \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
            null,
            [],
            'Post Updated At'
        )
        ->setComment('Post Table');
        $installer->getConnection()->createTable($table);

        $installer->getConnection()->addIndex(
            $installer->getTable('magentochina_helloworld_post'),
            $setup->getIdxName(
                $installer->getTable('magentochina_helloworld_post'),
                ['name','url_key','post_content','tags','featured_image'],
                \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_FULLTEXT
            ),
            ['name','url_key','post_content','tags','featured_image'],
            \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_FULLTEXT
        );
    }
    $installer->endSetup();
}
}

```

这个内容展示了表是如何创建的，您可以编辑它来创建您自己的表。请注意，[Magento](https://www.magentochina.org)将在安装该模块时首次自动运行该文件。如果您之前安装了该模块，您将需要升级模块，并将表创建为 **UpgradeSchema.php** 文件夹中。

接着运行[Magento命令行](https://doc.magentochina.org/guides/v2.1/config-guide/cli/config-cli-subcommands.html):

> php bin/magento setup:upgrade

现在检查您的数据库，您将看到一个名为“magentochina\_post”的表。如果这个表没有创建，它可能因为您在将内容添加到 **InstallSchema.php** 之前运行了上面的命令行。要解决这个问题，您需要删除让Magento知道您的模块已经安装在系统中的信息。请打开表 **setup\_module** ，查找并删除一行的名为 **magentochina\_post** 的模块。在此之后，再次运行该命令来安装该表。

这个 **InstallSchema.php** 用于创建数据库结构。如果要将数据安装到创建的表中，则需要使用 **InstallData.php** 文件:

> app/code/Magentochina/HelloWorld/Setup/InstallData.php

请参照Magento的核心数据文件中查看如何使用它。这是你能看到的文件:

> - vendor/magento/module-tax/Setup/InstallData.php
> - vendor/magento/module-customer/Setup/InstallData.php
> - vendor/magento/module-catalog/Setup/InstallData.php

如前所述，这些安装文件将首次用于安装模块。如果您想在升级模块时更改数据库，请尝试使用  
**UpgradeSchema.php** 和 **UpgradeData.php** 。

####Step 2: 创建模型  
模型是MVC架构中最庞大的体系,在Magento 2 CRUD中，模型有许多不同的功能，如管理数据、安装或升级模块。在[本教程](https://forum.magentochina.org/t/Magento2%E6%A8%A1%E5%9D%97%E5%BC%80%E5%8F%91%E4%B8%AD%E6%96%87%E6%95%99%E7%A8%8B%E7%B4%A2%E5%BC%95/36)中，我只讨论数据管理CRUD。我们必须创建模型、资源模型和资源模型，以管理 **magentochina\_pos** 表中的数据。

在创建模型之前，我们需要为它创建 **PostInterface** 接口:

> app/code/Magentochina/HelloWorld/Model/Api/Data/PostInterface.php

内容:

```
<?php
namespace Magentochina\HelloWorld\Model\Api\Data;
interface PostInterface
 {
   public function getId();
   public function setId();
   public function getName();
   public function setName();
   public function getPostContent();
   public function setPostContent();
 }

```

这个接口已经定义了set和get方法，我们将在与模型交互时使用它。当将CRUD模型导出到基于API的Magento服务基础模型时，这个接口起着重要的作用。

现在我们将创建模型文件:

> app/code/Magentochina/HelloWorld/Model/Post.php

内容:

```
<?php 
namespace Magentochina\HelloWorld\Model;
class Post extends \Magento\Framework\Model\AbstractModel implements 
\Magento\Framework\DataObject\IdentityInterface,
\Magentochina\HelloWorld\Model\Api\Data\PostInterface
{
const CACHE_TAG = 'magentochina_helloworld_post';

protected $_cacheTag = 'magentochina_helloworld_post';

protected $_eventPrefix = 'magentochina_helloworld_post';

protected function _construct()
{
    $this->_init('Magentochina\HelloWorld\Model\ResourceModel\Post');
}

public function getIdentities()
{
    return [self::CACHE_TAG . '_' . $this->getId()];
}

public function getDefaultValues()
{
    $values = [];

    return $values;
}
}

```

这个模型类将扩展 **AbstractModel** 类 **Magento\Framework\Model\AbstractModel** ，并实现 **PostInterface** 和 **IdentityInterface**** \Magento\Framework\DataObject\IdentityInterface** 。 IdentityInterface将强制模型类定义  
**getIdentities()** 方法，该方法将返回模型的惟一id。如果您的模型在数据库操作后需要缓存刷新并将信息呈现给前端页面，那么您必须使用此接口。

每当实例化一个模型时，都会调用 **\_construct()** 方法。每个CRUD模型都必须使用 **\_construct()** 调用 **\_init()** 方法的方法. **\_init()** 方法将定义资源模型，它将实际从数据库获取信息。如上所述，我们定义了资源模型 **Magentochina\Post\Model\ResourceModel\Post** 模型的最后一件事是您应该在模型中使用的变量:

- $\_eventPrefix -触发事件的前缀
- $\_eventObject -事件中访问的对象名称
- $\_cacheTag - 缓存中使用的惟一标识符

####Step 3:资源模型

如您所知，模型文件包含整个数据库逻辑，它不执行sql查询。资源模型会这么做。现在我们将创建这个表的资源模型: **Magentochina\HelloWorld\Model\ResourceModel\Post**  
内容:

```
<?php
namespace Magentochina\HelloWorld\Model\ResourceModel;
class Post extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
/**
 * 数据模型
 * 
 * @var \Magento\Framework\Stdlib\DateTime\DateTime
 */
protected $_date;

/**
 * 构造函数
 * 
 * @param \Magento\Framework\Stdlib\DateTime\DateTime $date
 * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
 */
public function __construct(
    \Magento\Framework\Stdlib\DateTime\DateTime $date,
    \Magento\Framework\Model\ResourceModel\Db\Context $context
)
{
    $this->_date = $date;
    parent::__construct($context);
}

/**
 * 初始化资源模型
 *
 * @return void
 */
protected function _construct()
{
    $this->_init('magentochina_helloworld_post', 'post_id');
}

/**
 * 通过传递id从DB搜索帖子标题。
 *
 * @param string $id
 * @return string|bool
 */
public function getPostNameById($id)
{
    $adapter = $this->getConnection();
    $select = $adapter->select()
        ->from($this->getMainTable(), 'name')
        ->where('post_id = :post_id');
    $binds = ['post_id' => (int)$id];
    return $adapter->fetchOne($select, $binds);
}
/**
 * 保存之前回调
 *
 * @param \Magento\Framework\Model\AbstractModel|\Mageplaza\HelloWorld\Model\Post $object
 * @return $this
 */
protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object)
{
    $object->setUpdatedAt($this->_date->date());
    if ($object->isObjectNew()) {
        $object->setCreatedAt($this->_date->date());
    }
    return parent::_beforeSave($object);
}
}

```

在Magento中，每个CRUD资源模型都必须扩展抽象类 **\Magento\Framework\Model\ResourceModel\Db\AbstractDb** ，其中包含从数据库获取信息的功能。

就像模型类一样，这个资源模型类将需要方法 **\_construct()** 。这个方法将调用 **\_init()** 函数来定义表的名称和主键。在这个示例中，我们有表’ **magnetochina\_post**'和主键 ’ **post\_id**'.

####Step 4:资源模型集合-获取模型集合

**集合模型** 被认为是一种资源模型，它允许我们过滤和获取收集表数据。收集模型将被放置在:

> Magentochina\HelloWorld\Model\ResourceModel\Post\Collection.php

内容:

```
<?php
namespace Magentochina\HelloWorld\Model\ResourceModel\Post;

class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
protected $_idFieldName = 'post_id';
protected $_eventPrefix = 'magentochina_helloworld_post_collection';
protected $_eventObject = 'post_collection';

/**
 * 定义资源模型
 *
 * @return void
 */
protected function _construct()
{
    $this->_init('Magentochina\HelloWorld\Model\Post', 'Magentochina\HelloWorld\Model\ResourceModel\Post');
}

/**
 * sql获取记录总数
 * 附带GROUP BY添加.
 *
 * @return \Magento\Framework\DB\Select
 */
public function getSelectCountSql()
{
    $countSelect = parent::getSelectCountSql();
    $countSelect->reset(\Zend_Db_Select::GROUP);
    return $countSelect;
}
/**
 * @param string $valueField
 * @param string $labelField
 * @param array $additional
 * @return array
 */
protected function _toOptionArray($valueField = 'post_id', $labelField = 'name', $additional = [])
{
    return parent::_toOptionArray($valueField, $labelField, $additional);
}
}

```

CRUD集合类必须从 **\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection** 扩展，并调用 \*\*\_init()\*\*方法在 **\_construct()** 函数中初始化模型、资源模型。

####Step 5:工厂对象

我们已经完成了创建数据库表、CRUD模型、资源模型和集合的工作。那么如何使用它们呢?  
在这一部分，我们将讨论模型的工厂对象。正如您在OOP中所知道的，工厂方法将用于实例化一个对象。在Magento，工厂对象也做同样的事情。

工厂类名是模型类的名称，并附加 **Factory** 字。对于我们的示例，我们将有 **PostFactory** 类。您不能创建该类。Magento将为你创建它。每当Magento的对象管理器遇到在 **Factory** 中结尾的类名时，如果类不存在，它将自动生成在 **var/generation** 文件夹中的工厂类。你将看到工厂类在:

> var/generation/\<vendor\_name\>/\<module\_name\>/Model/ClassFactory.php

在这个例子中会是:

> var/generation/Magentochina/HelloWorld/Model/PostFactory.php

要实例化一个模型对象，我们将使用自动构造函数依赖注入注入一个工厂对象，然后使用工厂对象实例化模型对象。

例如，我们将调用该模型以获取块(Block)中的数据。我们将创建一个Post块:

> Magentochina\HelloWorld\Block\Post.php

内容:

```
<?php
namespace Magentochina\HelloWorld\Block;
class Post extends \Magento\Framework\View\Element\Template
{
protected $_postFactory;
public function _construct(
	\Magento\Framework\View\Element\Template\Context $context,
	\Magentochina\HelloWorld\Model\PostFactory $postFactory
){
	$this->_postFactory = $postFactory;
	parent::_construct($context);
}

public function _prepareLayout()
{
	$post = $this->_postFactory->create();
	$collection = $post->getCollection();
	foreach($collection as $item){
		var_dump($item->getData());
	}
	exit;
}
}

```

正如您在这个块中看到的， **PostFactory** 对象将在 **\_construct()** 函数中创建。在 \*\*\_prepareLayout()\*\*函数中，我们使用 **$post = $this-\>\_postFactory-\>create();** 创建模型对象。

[本列的完整代码](https://github.com/mageplaza/magento-2-sample-module/tree/master/Model)

####翻译：西帅  
####转载说明:非常欢迎转载，但是必须保证文章的完整性并附上原文地址和翻译原文的地址，保证阅读者不会产生歧义.

---

<div class="post-metadata">

### 作者： ![zhou](https://forum.magentochina.org/letter_avatar_proxy/v4/letter/z/3e96dc/32.png) [@zhou](https://forum.magentochina.org/u/zhou)
#### 发布日期： [2017 年10 月 20 日 03:06 UTC](https://forum.magentochina.org/t/magento2-crud%E6%A8%A1%E5%9E%8B%E8%B5%84%E6%BA%90%E9%9B%86%E5%90%88%E5%B7%A5%E5%8E%82%E5%AF%B9%E8%B1%A1%E8%AF%A6%E8%A7%A3%E6%95%99%E7%A8%8B/43/2 "2017-10-20T03:06:31Z")

</div>

> [@Shuai](#):
>
> [‘name’,‘url\_key’,‘post\_content’,‘tags’,‘featured\_image’,‘sample\_upload\_file’]

这里没有’sample\_upload\_file’，需要去掉，不然无法创建数据库。

> [@Shuai](#):
>
> class Post extends \Magento\Framework\Model\AbstractModel implements  
> \Magento\Framework\DataObject\IdentityInterface,  
> \Magentochina\HelloWorld\Model\Api\Data\PostInterface

关于接口 PostInterface，Post类继承了这个接口，却没有实现里面的方法，导致页面报错，最后去掉它，页面就正常渲染出数据了。

然后CURD模型出来了，如果有能加上CURD的操作示例就更好了。

文章中的代码示例，看着像是后台的grid页面，和文章出入有点大

这个是我的测试代码git地址：  
[https://github.com/wucangzhou/Magento2-Tutorial/commits/master](https://github.com/wucangzhou/Magento2-Tutorial/commits/master)
