{ Hello Magento 2 }

解决 Magento 2 应用问题,更注重深度挖掘。(ง •̀_•́)ง

0%

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<?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

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

1
php bin/magento setup:upgrade

如果你的数据库中没有出现 thankit_helloworld_post 表,那么请在数据库中找到 setup_module 表,找到 ThankIT_HelloWorld 的记录,删掉这行记录。然后再运行上面的命令行。 InstallSchema.php 是用来创建数据库结构的,如果你想插入记录的话,使用 InstallData.php 参考下面的文件,看 InstallData.php 是怎么个用法:

1
2
3
- 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?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 中关键的代码是

1
2
3
4
5
6
7
8
9
/**
* 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?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 stringbool
*/
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() 方法来定义表名和主键。 该例中重要的代码是:

1
2
3
4
5
6
7
8
9
/**
* 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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 关键代码:

1
2
3
4
5
6
7
8
9
10
11
/**
* 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<?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