🧾 Introduction: Why Custom Tables?
Magento already has many built-in tables, but when you need to store extra data—like logs, feedback, or external API data—you’ll want your own. In Magento 2.3+, Declarative Schema makes this super easy.
In this guide, we’ll create a simple Novatra_CustomTable
module with its own database table.
🛠️ Step 1: Create the Module Folder Structure
app/code/Novatra/CustomTable/ ├── etc/ │ └── module.xml ├── registration.php ├── etc/db_schema.xml ├── Model/ │ └── Topic.php ├── Model/ResourceModel/ │ └── Topic.php │ └── Topic/Collection.php ├── Api/ │ └── TopicRepositoryInterface.php ├── Repository/ │ └── TopicRepository.php
📄 Step 2: Module Declaration Files
🔧 registration.php
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Novatra_CustomTable', __DIR__ );
🔧 etc/module.xml
🧱 Step 3: Create Table Using Declarative Schema
🔧 etc/db_schema.xml
💡 Step 4: Create Model and ResourceModel
🔧 Model/Topic.php
<?php namespace Novatra\CustomTable\Model; use Magento\Framework\Model\AbstractModel; class Topic extends AbstractModel { protected function _construct() { $this->_init(\Novatra\CustomTable\Model\ResourceModel\Topic::class); } }
🔧 ResourceModel/Topic.php
<?php namespace Novatra\CustomTable\Model\ResourceModel; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; class Topic extends AbstractDb { protected function _construct() { $this->_init('novatra_topic', 'topic_id'); } }
🔧 ResourceModel/Topic/Collection.php
<?php namespace Novatra\CustomTable\Model\ResourceModel\Topic; use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; class Collection extends AbstractCollection { protected function _construct() { $this->_init( \Novatra\CustomTable\Model\Topic::class, \Novatra\CustomTable\Model\ResourceModel\Topic::class ); } }
📦 Step 5: Add Repository (Recommended)
🔧 Api/TopicRepositoryInterface.php
<?php namespace Novatra\CustomTable\Api; use Novatra\CustomTable\Model\Topic; interface TopicRepositoryInterface { public function save(Topic $topic); public function getById($id); public function delete(Topic $topic); }
🔧 Repository/TopicRepository.php
<?php namespace Novatra\CustomTable\Repository; use Novatra\CustomTable\Model\Topic; use Novatra\CustomTable\Model\ResourceModel\Topic as TopicResource; use Novatra\CustomTable\Api\TopicRepositoryInterface; use Magento\Framework\Exception\NoSuchEntityException; class TopicRepository implements TopicRepositoryInterface { protected $resource; protected $topicFactory; public function __construct( TopicResource $resource, \Novatra\CustomTable\Model\TopicFactory $topicFactory ) { $this->resource = $resource; $this->topicFactory = $topicFactory; } public function save(Topic $topic) { $this->resource->save($topic); return $topic; } public function getById($id) { $topic = $this->topicFactory->create(); $this->resource->load($topic, $id); if (!$topic->getId()) { throw new NoSuchEntityException(__('Topic with ID "%1" does not exist.', $id)); } return $topic; } public function delete(Topic $topic) { $this->resource->delete($topic); return true; } }
🧪 Step 6: Run Setup & Test
bin/magento module:enable Novatra_CustomTable bin/magento setup:upgrade
✅ Summary
You’ve successfully:
- Created a Magento 2 custom module
- Built a custom database table with declarative schema
- Added a model, resource model, and repository
- Ran setup and verified table creation
Now you’re ready to use custom tables in real projects!