本篇我们将介绍通过 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.php
和 UpgradeData.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
zhao
无意水贴,先支持一下,文章很系统。值得学习。