CRUD Models in Magento 2

本篇我们将介绍通过 setup script 创建数据表,创建 Magento 2 model, resource model, collection ,以及如何通过它们对数据进行增删改查。

Magento 2 中的 CRUD Models 用于操纵数据库中的数据,不需要很多代码就可以创建一个 CRUD 。 CRUD 代表 Create, Read, Update and Delete (增删改查)。

我们将创建一个表 thankit_helloworld_post

Name Description
post_id ID 主键
name post 的名称
url_key Post URL Key
post_content post 的内容
tags post tags
status post 状态
featured_image Post Featured Image
sample_country_selection Post Sample Country Selection
sample_multiselect Post Sample Multiselect
created_at 创建时间
updated_at 更新时间

第一步:Setup Script

File:app/code/ThankIT/HelloWorld/Setup/InstallSchema.php

<?php
namespace ThankIT\HelloWorld\Setup;

class InstallSchema implements \Magento\Framework\Setup\InstallSchemaInterface
{
    /**
     * install tables
     *
     * @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('thankit_helloworld_post')) {
            $table = $installer->getConnection()->newTable(
                $installer->getTable('thankit_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(
                'sample_country_selection',
                \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                3,
                [],
                'Post Sample Country Selection'
            )
            ->addColumn(
                'sample_upload_file',
                \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                255,
                [],
                'Post Sample File'
            )
            ->addColumn(
                'sample_multiselect',
                \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                '64k',
                [],
                'Post Sample Multiselect'
            )
            ->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('thankit_helloworld_post'),
                $setup->getIdxName(
                    $installer->getTable('thankit_helloworld_post'),
                    ['name','url_key','post_content','tags','featured_image','sample_upload_file'],
                    \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_FULLTEXT
                ),
                ['name','url_key','post_content','tags','featured_image','sample_upload_file'],
                \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_FULLTEXT
            );
        }
        $installer->endSetup();
    }
}


了解 addColumn() addIndex() addForeignKey 的参数参照 vendor/magento/framework/DB/Ddl/Table

该文件只会在安装模块时执行一次。如果你以前安装过该模块了,这个文件就不会执行了。

执行命令行:

php bin/magento setup:upgrade

如果你的数据库中没有出现 thankit_helloworld_post 表,那么请在数据库中找到 setup_module 表,找到 ThankIT_HelloWorld 的记录,删掉这行记录。然后再运行上面的命令行。

InstallSchema.php 是用来创建数据库结构的,如果你想插入记录的话,使用 InstallData.php

参考下面的文件,看 InstallData.php 是怎么个用法:

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

这些安装文件是在初次安装时执行的,如果你想在升级时做点什么,请使用 UpgradeSchema.phpUpgradeData.php

第二步:创建 Model

Magento 2 中模型有很多作用,比如操作数据,安装或升级模块。在本教程中,我们只涉及数据的增删改查操作。我们要操作 thankit_helloworld_post 中的数据,需要创建 Model, Resource Model, Resource Model Conllection

File: app/code/ThankIT/HelloWorld/Model/Post.php

<?php
namespace ThankIT\HelloWorld\Model;

/**
 * @method Post setName($name)
 * @method Post setUrlKey($urlKey)
 * @method Post setPostContent($postContent)
 * @method Post setTags($tags)
 * @method Post setStatus($status)
 * @method Post setFeaturedImage($featuredImage)
 * @method Post setSampleCountrySelection($sampleCountrySelection)
 * @method Post setSampleUploadFile($sampleUploadFile)
 * @method Post setSampleMultiselect($sampleMultiselect)
 * @method mixed getName()
 * @method mixed getUrlKey()
 * @method mixed getPostContent()
 * @method mixed getTags()
 * @method mixed getStatus()
 * @method mixed getFeaturedImage()
 * @method mixed getSampleCountrySelection()
 * @method mixed getSampleUploadFile()
 * @method mixed getSampleMultiselect()
 * @method Post setCreatedAt(\string $createdAt)
 * @method string getCreatedAt()
 * @method Post setUpdatedAt(\string $updatedAt)
 * @method string getUpdatedAt()
 */
class Post extends \Magento\Framework\Model\AbstractModel
{

    /**
     * Initialize resource model
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('ThankIT\HelloWorld\Model\ResourceModel\Post');
    }
}

Model 必须继承 \Magento\Framework\Model\AbstractModel 而该类又继承自 \Magento\Framework\Object ,这样我们的 model 就拥有了额外的方法,比如 load, delete, save, toArray, toJson, toString, toXml

Model 中关键的代码是

/**
 * Initialize resource model
 *
 * @return void
 */
protected function _construct()
{
   $this->_init('ThankIT\HelloWorld\Model\ResourceModel\Post');
}

第三步:Resource Model

Model 中包含总体上的数据逻辑,但它不执行 sql 语句,由 Resource model 做。

File: ThankIT\HelloWorld\Model\ResourceModel\Post.php

<?php
namespace ThankIT\HelloWorld\Model\ResourceModel;

class Post extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
    /**
     * Date model
     *
     * @var \Magento\Framework\Stdlib\DateTime\DateTime
     */
    protected $_date;

    /**
     * constructor
     *
     * @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);
    }

    /**
     * Initialize resource model
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('thankit_helloworld_post', 'post_id');
    }

    /**
     * Retrieves Post Name from DB by passed id.
     * 本篇未使用到 保留 以后研究
     * @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);
    }
    /**
     * before save callback
     *
     * @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);
    }
}

所有 resource model 都必须继承 \Magento\Framework\Model\ResourceModel\Db\AbstractDb 该 class 包含着从数据库中取数据的方法。

和 model 类一样,resource model 也必须有 _construct() 方法,该方法调用 _init() 方法来定义表名和主键。

该例中重要的代码是:

/**
 * Initialize resource model
 *
 * @return void
 */
protected function _construct()
{
    $this->_init('thankit_helloworld_post', 'post_id');
}

第四步:Resource Model Collection

collection model 可以看作是一个 resource model,通过它我们可以获取数据的集合,可以对数据集合进行过滤。

File:ThankIT\HelloWorld\Model\ResourceModel\Post\Collection.php

<?php
namespace ThankIT\HelloWorld\Model\ResourceModel\Post;

class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
    /**
     * resource collection initialization
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('ThankIT\HelloWorld\Model\Post', 'ThankIT\HelloWorld\Model\ResourceModel\Post');
    }
}

collection 类继承自 \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection

关键代码:

/**
 * resource collection initialization
 *
 * @param string $model
 * @param string $resourceModel
 * @return $this
 */
protected function _construct()
{
   $this->_init('ThankIT\HelloWorld\Model\Post', 'ThankIT\HelloWorld\Model\ResourceModel\Post');
}

\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection 继承自 \Magento\Framework\Data\Collection\AbstractDb ,而 AbstractDb 继承自 \Magento\Framework\Data\Collection , Collection 最后实现以下接口 \Countable, Magento\Framework\Option\ArrayInterface, and
Magento\Framework\Data\CollectionDataSourceInterface 这么多层的继承,collection 类就获得了非常多的方法,比如 join, addFieldToFilter, count, getAllIds, getColumnValues, getFirstItem, getLastItem 等。

第五步:Factory Object

OOP 中,工厂方法是用来实例化对象的。在 Magento 2 中,Factory Object 也是一样。

Magento 的 object manager 遇到一个以 Factory 结尾的类时,如果该类不存在,那么就会在 var/generation 目录下自动生成一个。

下面以例子来说明:
File: ThankIT\HelloWorld\Block\Index\Index.php

<?php
namespace ThankIT\HelloWorld\Block\Index;

use Magento\Framework\View\Element\Template;

class Index extends Template
{
    protected $_postFactory;
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \ThankIT\HelloWorld\Model\PostFactory $postFactory
    ) {
        $this->_postFactory = $postFactory;
        parent::__construct($context);
    }

    public function _prepareLayout()
    {

        /* case 1
         * @return string 'ThankIT\HelloWorld\Model\PostFactory' (length=36)
         */
        // var_dump(
        //     get_class($this->_postFactory)
        // );
        // exit;

        /* case 2
         * 插入数据
         */
        // $post = $this->_postFactory->create();
        // $post->setData('name', 'fist post name')
        //     ->setUrlKey('fist')
        //     ->setPostContent('fist post content')
        //     ->save();
        // var_dump('Done');
        // exit;

        /* case 3
         * load ID 为 1 的 model
         */
        // $post = $this->_postFactory->create();
        // $post = $post->load(1);
        // var_dump($post->getData());
        // var_dump($post->getName());
        // var_dump($post->getData('url_key'));
        // var_dump($post->getNoExist()); //return null
        // exit;

        /* case 4
         * use collection
         */
        // $post = $this->_postFactory->create();
        // $post->setData('name', 'name')
        //     ->setUrlKey('url')
        //     ->setPostContent('post content')
        //     ->save();
        // $collection = $post->getCollection();
        // foreach ($collection as $item) {
        //     var_dump($item->getData());
        // }
        // exit;

        /* case 5
         * update
         */
        // $post = $this->_postFactory->create();
        // $post = $post->load(1);
        // var_dump($post->getData());
        // $post->setName('now update')->save();
        // var_dump($post->getData());
        // exit;

        /* case 6
         * delete
         */
        // $post = $this->_postFactory->create();
        // $collection = $post->getCollection();
        // foreach ($collection as $item) {
        //     var_dump($item->getData());
        // }
        // $post = $post->load(2)->delete();
        // var_dump('after delete');
        // foreach ($collection as $item) {
        //     var_dump($item->getData());
        // }
        // exit();

        /* case 7
         * collection count
         */
        // $post = $this->_postFactory->create();
        // $collection = $post->getCollection();
        // var_dump($collection->count());
        // var_dump($collection->getFirstItem()->getData());
        // exit;

        /* case 8
         * collection filter
         */
        // $post = $this->_postFactory->create();
        // $collection = $post->getCollection();
        // $post = $collection->addFieldToFilter('url_key', 'fist')->getFirstItem();
        // var_dump($post->getData());
        // exit;
    }
}

每个 case 分别去掉注释进行测试,查看效果。

参考文档

CRUD Models in Magento 2
Magento 2: CRUD Models for Database Access

代码下载

ThankIT_HelloWorld_3

发表评论

电子邮件地址不会被公开。 必填项已用*标注