🧾 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!