Resource Model Structure
Resource models handle all database operations for models.
Basic resource model:
<?php
namespace Vendor\Blog\Model\ResourceModel;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
class Post extends AbstractDb
{
protected function _construct()
{
$this->_init('vendor_blog_post', 'entity_id');
}
}
_init() parameters:
- First: table name (
vendor_blog_post) - Second: primary key column (
entity_id)
Custom table name:
protected function _construct()
{
$this->_init(
'vendor_blog_post', // Table name
'entity_id' // Primary key
);
}
Table with prefix:
// Table name automatically gets store prefix
$this->_init('vendor_blog_post', 'entity_id');
// Becomes: {prefix}_vendor_blog_post
CRUD Operations
Resource models provide methods for database CRUD.
Insert (Create):
$resource = $this->postFactory->create()->getResource();
$post = $this->postFactory->create();
$post->setTitle('My Post');
$post->setContent('Content');
$resource->save($post);
// INSERT INTO vendor_blog_post (title, content) VALUES ('My Post', 'Content')
Update:
$post = $this->postFactory->create()->load(1);
$post->setTitle('Updated Title');
$resource->save($post);
// UPDATE vendor_blog_post SET title = 'Updated Title' WHERE entity_id = 1
Delete:
$post = $this->postFactory->create()->load(1);
$resource->delete($post);
// DELETE FROM vendor_blog_post WHERE entity_id = 1
Load:
$resource = $this->postFactory->create()->getResource();
$post = $this->postFactory->create();
$resource->load($post, 1);
// SELECT * FROM vendor_blog_post WHERE entity_id = 1
// Load by attribute
$resource->load($post, 'my-url-key', 'url_key');
// SELECT * FROM vendor_blog_post WHERE url_key = 'my-url-key'
Check existence:
$exists = $resource->checkExist($post, 'my-url-key', 'url_key');
Lifecycle Hooks
Resource models provide hooks for save/load/delete operations.
Available hooks:
_beforeSave() — Before inserting/updating
_afterSave() — After inserting/updating
_beforeLoad() — Before loading
_afterLoad() — After loading
_beforeDelete() — Before deleting
_afterDelete() — After deleting
_afterSave example:
protected function _afterSave(\Magento\Framework\Model\AbstractModel $object)
{
// Process after save
$this->processTags($object);
$this->updateSearchIndex($object);
return parent::_afterSave($object);
}
private function processTags(\Magento\Framework\Model\AbstractModel $object): void
{
$tags = explode(',', $object->getTags());
foreach ($tags as $tag) {
$this->tagResource->addTagToPost($object->getId(), trim($tag));
}
}
_afterLoad example:
protected function _afterLoad(\Magento\Framework\Model\AbstractModel $object)
{
// Load related data
$tags = $this->tagResource->getTagsByPost($object->getId());
$object->setData('tags', $tags);
// Format dates
if ($object->getCreatedAt()) {
$object->setCreatedAtFormatted(
$this->dateFormatter->format($object->getCreatedAt())
);
}
return parent::_afterLoad($object);
}
_beforeSave example:
protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object)
{
// Validate data
if (empty($object->getTitle())) {
throw new \Magento\Framework\Exception\LocalizedException(
__('Title cannot be empty')
);
}
// Set timestamps
if ($object->isObjectNew()) {
$object->setCreatedAt($this->dateFormatter->formatDate());
}
$object->setUpdatedAt($this->dateFormatter->formatDate());
return parent::_beforeSave($object);
}
Resource Model Best Practices
Best practices for resource model implementation.
1. Keep resource models focused:
// BAD: Business logic in resource model
class Post extends AbstractDb
{
public function processOrder() { /* ... */ }
}
// GOOD: Resource model only handles DB
class Post extends AbstractDb
{
protected function _construct()
{
$this->_init('vendor_blog_post', 'entity_id');
}
}
2. Use transactions for multi-table operations:
public function saveWithRelations(\Magento\Framework\Model\AbstractModel $object): void
{
$connection = $this->getConnection();
$connection->beginTransaction();
try {
$this->save($object);
$this->saveRelations($object);
$connection->commit();
} catch (\Exception $e) {
$connection->rollBack();
throw $e;
}
}
3. Use prepared statements:
// BAD: SQL injection risk
$connection->query("SELECT * FROM table WHERE id = $id");
// GOOD: Prepared statement
$connection->query(
'SELECT * FROM table WHERE id = ?',
[$id]
);
4. Handle NULL values:
protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object)
{
// Convert empty strings to NULL
foreach ($object->getData() as $key => $value) {
if ($value === '') {
$object->setData($key, null);
}
}
return parent::_beforeSave($object);
}
Quiz
1. What does _init() do in a resource model?
2. What hook fires after a model is saved?
3. How do you load a model via resource model?
4. What should resource models NOT contain?
Flashcards
Question
What does _init() set?
Click to reveal answer
Answer
Table name and primary key column
Question
What hooks are available?
Click to reveal answer
Answer
_beforeSave, _afterSave, _beforeLoad, _afterLoad, _beforeDelete, _afterDelete
Question
How do you save via resource model?
Click to reveal answer
Answer
$resource->save($model)
Question
How do you delete via resource model?
Click to reveal answer
Answer
$resource->delete($model)
Question
What is the parent class for resource models?
Click to reveal answer
Answer
Magento\Framework\Model\ResourceModel\Db\AbstractDb
Revision Notes
Key Takeaways
- 1. Resource models extend AbstractDb and handle database operations
- 2. _init() sets table name and primary key
- 3. Lifecycle hooks: _beforeSave, _afterSave, _beforeLoad, _afterLoad, _beforeDelete, _afterDelete
- 4. Use prepared statements to prevent SQL injection
- 5. Use transactions for multi-table operations
- 6. Keep resource models focused on DB operations only
Interview Tips
- • Explain resource model lifecycle hooks
- • Describe how to implement _afterSave for related data
- • Discuss transaction usage in resource models
- • Know the difference between Model and Resource Model
Cheat Sheet
Resource Models Cheat Sheet
Structure:
class Post extends AbstractDb
{
protected function _construct()
{
$this->_init('table_name', 'entity_id');
}
}
CRUD:
- $resource->save($model)
- $resource->load($model, $id)
- $resource->delete($model)
Hooks:
- _beforeSave() / _afterSave()
- _beforeLoad() / _afterLoad()
- _beforeDelete() / _afterDelete()
Best practices:
- Prepared statements
- Transactions
- No business logic