Magento 2 EAV Model 介紹 (3) – 在 Magento 2 使用程式新增 entity type

之前的文章內,我們介紹了 Eav Model 的關係,以及如何使用程式新增 Attribute,但是在 Magento 裡面使用了許多的 Entity Type,今天要透過程式的方式來教大家如何新增 Entity Type 及其相關的資料表,讓自己的定義的 Entity Type 也可以存取 Attribute 屬性唷!
※Magento 版本:2.1 以上
1.新增資料表
要建立 Entity type,必須依照 Magento 的規則建立相對應的資料表,我們以建立 Post 的 Entity type 為例,共需要產生以下 6 張資料表
* post_entity
* post_entity_datetime
* post_entity_decimal
* post_entity_int
* post_entity_text
* post_entity_varchar
要建立資料表,我們必須先在 module 的 Setup 資料夾內建立 InstallSchema.php,裡面程式碼如下:
<?php
namespace Grayson\Post\Setup;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
/**
* Class InstallSchema
* @package Grayson\Post\Setup
*/
class InstallSchema implements InstallSchemaInterface
{
const POST_ENTITY = 'post_entity';
/**
* @param SchemaSetupInterface $setup
* @param ModuleContextInterface $context
* @throws \Zend_Db_Exception
*/
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$installer = $setup;
$installer->startSetup();
}
}
(1) post_entity 資料表
這張是實體 ( Entity ) 主表,所有的紀錄都會在這邊產生,程式如下:
/**
* @param SchemaSetupInterface $setup
* @param string $entity
* @return InstallSchema
* @throws \Zend_Db_Exception
*/
private function addEntityTable(SchemaSetupInterface $setup, string $entity): InstallSchemaInterface
{
$table = $setup->getConnection()
->newTable($setup->getTable($entity))
->addColumn(
'entity_id',
Table::TYPE_INTEGER,
null,
[
'identity' => true,
'unsigned' => true,
'nullable' => false,
'primary' => true
],
'Entity ID'
)->addColumn(
'created_at',
Table::TYPE_TIMESTAMP,
null,
[
'nullable' => false,
'default' => Table::TIMESTAMP_INIT
],
'Creation Time'
)->addColumn(
'updated_at',
Table::TYPE_TIMESTAMP,
null,
[
'nullable' => false,
'default' => Table::TIMESTAMP_INIT_UPDATE
],
'Update Time'
);
$setup->getConnection()->createTable($table);
return $this;
}
(2) post_entity_datetime 資料表
/**
* @param SchemaSetupInterface $setup
* @param string $entity
* @return InstallSchema
* @throws \Zend_Db_Exception
*/
private function addDatetimeTable(SchemaSetupInterface $setup, string $entity): InstallSchemaInterface
{
$table = $setup->getConnection()
->newTable($setup->getTable($entity . '_datetime')
)->addColumn(
'value_id',
Table::TYPE_INTEGER,
null,
['identity' => true, 'nullable' => false, 'primary' => true],
'Value ID'
)->addColumn(
'attribute_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Attribute Id'
)->addColumn(
'store_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Store ID'
)->addColumn(
'entity_id',
Table::TYPE_INTEGER,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Entity Id'
)->addColumn(
'value',
Table::TYPE_DATETIME,
null,
[],
'value'
)->addIndex(
$setup->getIdxName($entity . '_decimal',
['entity_id', 'attribute_id', 'store_id'],
AdapterInterface::INDEX_TYPE_UNIQUE),
['entity_id', 'attribute_id', 'store_id'],
['type' => AdapterInterface::INDEX_TYPE_UNIQUE]
)->addIndex(
$setup->getIdxName($entity . '_datetime',
['store_id']),
['store_id']
)->addIndex(
$setup->getIdxName($entity . '_datetime',
['attribute_id']),
['attribute_id']
)->addForeignKey(
$setup->getFkName(
$entity . '_datetime',
'attribute_id',
'eav_attribute',
'attribute_id'
),
'attribute_id',
$setup->getTable('eav_attribute'),
'attribute_id',
Table::ACTION_CASCADE
)->addForeignKey(
$setup->getFkName(
$entity . '_datetime',
'entity_id',
$entity,
'entity_id'
),
'entity_id',
$setup->getTable($entity),
'entity_id',
Table::ACTION_CASCADE
)->addForeignKey(
$setup->getFkName(
$entity . '_datetime', 'store_id', 'store', 'store_id'
),
'store_id',
$setup->getTable('store'),
'store_id',
Table::ACTION_CASCADE
);
$setup->getConnection()->createTable($table);
return $this;
}
(3) post_entity_decimal 資料表
/**
* @param SchemaSetupInterface $setup
* @param string $entity
* @return InstallSchema
* @throws \Zend_Db_Exception
*/
private function addDecimalTable(SchemaSetupInterface $setup, string $entity): InstallSchemaInterface
{
$table = $setup->getConnection()
->newTable($setup->getTable($entity . '_decimal'))
->addColumn(
'value_id',
Table::TYPE_INTEGER,
null,
['identity' => true, 'nullable' => false, 'primary' => true],
'Value ID'
)->addColumn(
'attribute_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Attribute Id'
)->addColumn(
'store_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Store ID'
)->addColumn(
'entity_id',
Table::TYPE_INTEGER,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Entity Id'
)->addColumn(
'value',
Table::TYPE_DECIMAL,
'12,4',
[],
'value'
)->addIndex(
$setup->getIdxName($entity . '_decimal',
['entity_id', 'attribute_id', 'store_id'],
AdapterInterface::INDEX_TYPE_UNIQUE),
['entity_id', 'attribute_id', 'store_id'],
['type' => AdapterInterface::INDEX_TYPE_UNIQUE]
)->addIndex(
$setup->getIdxName($entity . '_decimal',
['store_id']),
['store_id']
)->addIndex(
$setup->getIdxName($entity . '_decimal',
['attribute_id']),
['attribute_id']
)->addForeignKey(
$setup->getFkName(
$entity . '_decimal',
'attribute_id',
'eav_attribute',
'attribute_id'
),
'attribute_id',
$setup->getTable('eav_attribute'),
'attribute_id',
Table::ACTION_CASCADE
)->addForeignKey(
$setup->getFkName(
$entity . '_decimal',
'entity_id',
$entity . '_entity',
'entity_id'
),
'entity_id',
$setup->getTable($entity),
'entity_id',
Table::ACTION_CASCADE
)->addForeignKey(
$setup->getFkName(
$entity . '_decimal', 'store_id', 'store', 'store_id'
),
'store_id',
$setup->getTable('store'),
'store_id',
Table::ACTION_CASCADE
);
$setup->getConnection()->createTable($table);
return $this;
}
(4) post_entity_int 資料表
<?php
/**
* @param SchemaSetupInterface $setup
* @param $entity
* @return InstallSchema
* @throws \Zend_Db_Exception
*/
private function addIntTable(SchemaSetupInterface $setup, $entity): InstallSchemaInterface
{
$table = $setup->getConnection()
->newTable($setup->getTable($entity . '_int'))
->addColumn(
'value_id',
Table::TYPE_INTEGER,
null,
['identity' => true, 'nullable' => false, 'primary' => true],
'Value ID'
)->addColumn(
'attribute_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Attribute Id'
)->addColumn(
'store_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Store ID'
)->addColumn(
'entity_id',
Table::TYPE_INTEGER,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Entity Id'
)->addColumn(
'value',
Table::TYPE_INTEGER,
null,
[],
'value'
)->addIndex(
$setup->getIdxName($entity . '_int',
['entity_id', 'attribute_id', 'store_id'],
AdapterInterface::INDEX_TYPE_UNIQUE),
['entity_id', 'attribute_id', 'store_id'],
['type' => AdapterInterface::INDEX_TYPE_UNIQUE]
)->addIndex(
$setup->getIdxName($entity . '_int',
['store_id']),
['store_id']
)->addIndex(
$setup->getIdxName($entity . '_int',
['attribute_id']),
['attribute_id']
)->addForeignKey(
$setup->getFkName(
$entity . '_int',
'attribute_id',
'eav_attribute',
'attribute_id'
),
'attribute_id',
$setup->getTable('eav_attribute'),
'attribute_id',
Table::ACTION_CASCADE
)->addForeignKey(
$setup->getFkName(
$entity . '_int',
'entity_id',
$entity,
'entity_id'
),
'entity_id',
$setup->getTable($entity),
'entity_id',
Table::ACTION_CASCADE
)->addForeignKey(
$setup->getFkName(
$entity . '_int', 'store_id', 'store', 'store_id'
),
'store_id',
$setup->getTable('store'),
'store_id',
Table::ACTION_CASCADE
);
$setup->getConnection()->createTable($table);
return $this;
}
(5) post_entity_text 資料表
<?php
/**
* @param SchemaSetupInterface $setup
* @param string $entity
* @return InstallSchema
* @throws \Zend_Db_Exception
*/
private function addTextTable(SchemaSetupInterface $setup, string $entity): InstallSchemaInterface
{
$table = $setup->getConnection()
->newTable($setup->getTable($entity . '_text'))
->addColumn(
'value_id',
Table::TYPE_INTEGER,
null,
['identity' => true, 'nullable' => false, 'primary' => true],
'Value ID'
)->addColumn(
'attribute_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Attribute Id'
)->addColumn(
'store_id',
Table::TYPE_SMALLINT,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Store ID'
)->addColumn(
'entity_id',
Table::TYPE_INTEGER,
null,
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Entity Id'
)->addColumn(
'value',
Table::TYPE_TEXT,
255,
[],
'value'
)->addIndex(
$setup->getIdxName($entity . '_text',
['entity_id', 'attribute_id', 'store_id'],
AdapterInterface::INDEX_TYPE_UNIQUE),
['entity_id', 'attribute_id', 'store_id'],
['type' => AdapterInterface::INDEX_TYPE_UNIQUE]
)->addIndex(
$setup->getIdxName($entity . '_text',
['store_id']),
['store_id']
)->addIndex(
$setup->getIdxName($entity . '_text',
['attribute_id']),
['attribute_id']
)->addForeignKey(
$setup->getFkName(
$entity . '_text',
'attribute_id',
'eav_attribute',
'attribute_id'
),
'attribute_id',
$setup->getTable('eav_attribute'),
'attribute_id',
Table::ACTION_CASCADE
)->addForeignKey(
$setup->getFkName(
$entity . '_text',
'entity_id',
$entity,
'entity_id'
),
'entity_id',
$setup->getTable($entity),
'entity_id',
Table::ACTION_CASCADE
)->addForeignKey(
$setup->getFkName(
$entity . '_text', 'store_id', 'store', 'store_id'
),
'store_id',
$setup->getTable('store'),
'store_id',
Table::ACTION_CASCADE
);
$setup->getConnection()->createTable($table);
return $this;
}
(6) post_entity_varchar 資料表
<?php
/**
* @param SchemaSetupInterface $setup
* @param $entity
* @return InstallSchema
* @throws \Zend_Db_Exception
*/
private function addVarcharTable(SchemaSetupInterface $setup, $entity): InstallSchemaInterface
{
$table = $setup->getConnection()
->newTable($setup->getTable($entity . '_varchar'))
->addColumn(
'value_id',
Table::TYPE_INTEGER,
null,
[
'identity' => true,
'nullable' => false,
'primary' => true
],
'Value ID'
)->addColumn(
'attribute_id',
Table::TYPE_SMALLINT,
null,
[
'unsigned' => true,
'nullable' => false,
'default' => '0'
],
'Attribute Id'
)->addColumn(
'store_id',
Table::TYPE_SMALLINT,
null,
[
'unsigned' => true,
'nullable' => false,
'default' => '0'
],
'Store ID'
)->addColumn(
'entity_id',
Table::TYPE_INTEGER,
null,
[
'unsigned' => true,
'nullable' => false,
'default' => '0'
],
'Entity Id'
)->addColumn(
'value',
Table::TYPE_TEXT,
256,
[],
'value'
)->addIndex(
$setup->getIdxName($entity . '_varchar',
['entity_id', 'attribute_id', 'store_id'],
AdapterInterface::INDEX_TYPE_UNIQUE),
['entity_id', 'attribute_id', 'store_id'],
['type' => AdapterInterface::INDEX_TYPE_UNIQUE]
)->addIndex(
$setup->getIdxName($entity . '_varchar',
['store_id']),
['store_id']
)->addIndex(
$setup->getIdxName($entity . '_varchar',
['attribute_id']),
['attribute_id']
)->addForeignKey(
$setup->getFkName(
$entity . '_varchar',
'attribute_id',
'eav_attribute',
'attribute_id'
),
'attribute_id',
$setup->getTable('eav_attribute'),
'attribute_id',
Table::ACTION_CASCADE
)->addForeignKey(
$setup->getFkName(
$entity . '_varchar',
'entity_id',
$entity,
'entity_id'
),
'entity_id',
$setup->getTable($entity),
'entity_id',
Table::ACTION_CASCADE
)->addForeignKey(
$setup->getFkName(
$entity . '_varchar', 'store_id', 'store', 'store_id'
),
'store_id',
$setup->getTable('store'),
'store_id',
Table::ACTION_CASCADE
);
$setup->getConnection()->createTable($table);
return $this;
}
(7) InstallSchema
接下來我們在 InsatallSchema 內使用剛剛建立的 function,並帶入 $installer 及 self::POST_ENTITY 變數,參考底下程式碼:
<?php
namespace Grayson\Post\Setup;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
/**
* Class InstallSchema
* @package Grayson\Post\Setup
*/
class InstallSchema implements InstallSchemaInterface
{
const POST_ENTITY = 'post_entity';
/**
* @param SchemaSetupInterface $setup
* @param ModuleContextInterface $context
* @throws \Zend_Db_Exception
*/
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$installer = $setup;
$installer->startSetup();
$this->addEntityTable($installer, self::POST_ENTITY);
$this->addDatetimeTable($installer, self::POST_ENTITY);
$this->addDecimalTable($installer, self::POST_ENTITY);
$this->addIntTable($installer, self::POST_ENTITY);
$this->addTextTable($installer, self::POST_ENTITY);
$this->addVarcharTable($installer, self::POST_ENTITY);
}
//... 略
2.新增 Model、Collection、ResouceModel
MVC 是一種現代化的設計模式,Model 則是 MVC 中的 M,它負責資料庫的操作,而 Magento 的 ORM 已經幫我們封裝好 Model 的 Class ,我們使用時僅需要繼承正確的抽象類別 ( Abstract Class ) 即可,而除了 Model 之外,也幫我們封裝了 Collection 及 ResourceModel 的兩種類別,讓我們在使用 ORM 的時候更加的方便,底下我們就要在這模組中新增這幾隻程式,來完成我們 Post 的 Entity Type。
(1) Model
請注意!這邊是非常大的地雷!需要特別注意!
一般的 Model 是繼承 \Magento\Framework\Model\AbstractModel,但是因為是需要操作 Entity Type 的關係,我們繼承的 Class 需更改為 Magento\Catalog\Model\AbstractModel,可以參考下面程式的第 6 行:
<?php
namespace Grayson\Post\Model;
use Magento\Catalog\Model\AbstractModel;
use Magento\Framework\DataObject\IdentityInterface;
/**
* Class Post
* @package Grayson\Post\Model
*/
class Post extends AbstractModel implements IdentityInterface
{
/**
* Entity code.
* Can be used as part of method name for entity processing
*/
const ENTITY = 'grayson_post';
const CACHE_TAG = 'grayson_post';
const STORE_ID = 'store_id';
/* @var string */
protected $_eventPrefix = 'grayson_post';
/* @var string */
protected $_eventObject = 'post';
/* @var string */
protected $_cacheTag = self::CACHE_TAG;
/**
* @return void
*/
protected function _construct()
{
$this->_init(ResourceModel\Post::class);
}
/**
* @return array
*/
public function getIdentities()
{
return [self::CACHE_TAG . '_' . $this->getId()];
}
}
(2) ResourceModel
也不同於原本繼承 Magento\Framework\Model\ResourceModel\Db\AbstractDb,
這邊要更換為Magento\Catalog\Model\ResourceModel\AbstractResource
<?php
namespace Grayson\Post\Model\ResourceModel;
use Magento\Catalog\Model\ResourceModel\AbstractResource;
/**
* Class Post
* @package Grayson\Post\Model\ResourceModel
*/
class Post extends AbstractResource
{
/**
*
* @return \Magento\Eav\Model\Entity\Type
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function getEntityType()
{
if (empty($this->_type)) {
$this->setType(\Grayson\Post\Model\Post::ENTITY);
}
return parent::getEntityType();
}
}
(3) Collection
Collection 的部分也需要由原本繼承的 Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection 改為 Magento\Catalog\Model\ResourceModel\Collection\AbstractCollection 才行:
<?php
namespace Grayson\Post\Model\ResourceModel\Post;
use Grayson\Post\Model\Post;
use Grayson\Post\Model\ResourceModel\Post as ResourceModelPost;
use Magento\Catalog\Model\ResourceModel\Collection\AbstractCollection;
/**
* Class Collection
* @package Grayson\Post\Model\ResourceModel\Post
*/
class Collection extends AbstractCollection
{
protected function _construct()
{
$this->_init(Post::class, ResourceModelPost::class);
}
}
3.執行 Upgrade 指令
在我們都完成上面的程式檔案後,並且做最後的確認,是否有 Module 沒有複製到正確的資料。如果一切都沒有問題,直接執行
$ bin/magento setup:upgrade
即可看到安裝完成的訊息,我們可以至 eav_entity_type 資料表內檢查,是否有多出 grayson_post 這個 Entity Type 呢?如果沒有,請回頭至第一個步驟再檢查一次有沒有程式碼遺漏掉,還是沒有的話,可以參考我下面的 Github 連結,裡面有完整的模組範例,包含上述的程式碼。
參考連結:Gitbub
想看更多Magento 2 教學導覽,別忘了訂閱我們的電子報,以及追蹤我們的Facebook粉絲專頁唷!
更多Magento相關文章請看: Magento教學導覽
我要留言