Model Architecture
Magento models follow a three-layer architecture: Model, Resource Model, Collection.
Architecture:
Model (business logic)
├── Resource Model (database operations)
└── Collection (multiple models query)
Basic model:
<?php
namespace Vendor\Blog\Model;
use Magento\Framework\Model\AbstractModel;
class Post extends AbstractModel
{
protected function _construct()
{
$this->_init(\Vendor\Blog\Model\ResourceModel\Post::class);
}
// Getters and setters
public function getTitle(): string
{
return $this->getData('title');
}
public function setTitle(string $title): self
{
return $this->setData('title', $title);
}
}
Model with data interface:
<?php
namespace Vendor\Blog\Model;
use Magento\Framework\Model\AbstractModel;
use Vendor\Blog\Api\Data\PostInterface;
class Post extends AbstractModel implements PostInterface
{
protected function _construct()
{
$this->_init(\Vendor\Blog\Model\ResourceModel\Post::class);
}
public function getTitle(): string
{
return $this->getData(self::TITLE);
}
public function setTitle(string $title): PostInterface
{
return $this->setData(self::TITLE, $title);
}
}
Data Interfaces
Data interfaces define the API contract for models.
Data interface:
<?php
namespace Vendor\Blog\Api\Data;
interface PostInterface
{
const TITLE = 'title';
const CONTENT = 'content';
const STATUS = 'status';
const CREATED_AT = 'created_at';
public function getId(): ?int;
public function getTitle(): string;
public function setTitle(string $title): self;
public function getContent(): string;
public function setContent(string $content): self;
public function getStatus(): int;
public function setStatus(int $status): self;
}
Extension attributes:
<?php
namespace Vendor\Blog\Api\Data;
interface PostExtensionAttributesInterface
{
public function getAuthorName(): ?string;
public function setAuthorName(string $authorName): self;
public function getTags(): ?array;
public function setTags(array $tags): self;
}
Using data interfaces:
// Repository returns interface
$post = $this->postRepository->getById(1);
// Type-hint against interface
public function save(PostInterface $post): PostInterface
{
// Work with interface, not concrete class
$title = $post->getTitle();
$post->setTitle('New Title');
return $this->postRepository->save($post);
}
Model Loading and Save/Delete
CRUD operations through models.
Loading a model:
// Load by ID
$post = $this->postFactory->create()->load($id);
// Load by attribute
$post = $this->postFactory->create();
$post->load('my-url-key', 'url_key');
// Load by ID via resource model
$resource = $this->postFactory->create()->getResource();
$resource->load($post, $id);
Saving a model:
// Create new
$post = $this->postFactory->create();
$post->setTitle('My Post');
$post->setContent('Content');
$post->save();
// Update existing
$post = $this->postFactory->create()->load(1);
$post->setTitle('Updated Title');
$post->save();
// Save with data
$post = $this->postFactory->create();
$resource = $post->getResource();
$resource->save($post);
Deleting a model:
// Delete by ID
$post = $this->postFactory->create()->load(1);
$post->delete();
// Delete via resource
$resource = $this->postFactory->create()->getResource();
$resource->delete($post);
Bulk operations:
// Bulk save
foreach ($posts as $post) {
$this->postRepository->save($post);
}
// Bulk delete
foreach ($postIds as $id) {
$this->postRepository->deleteById($id);
}
Model Best Practices
Best practices for Magento model implementation.
1. Use factories, not ObjectManager:
// BAD
$post = $objectManager->create(Post::class);
// GOOD
$post = $this->postFactory->create();
2. Type-hint interfaces:
// BAD
public function save(Post $post)
// GOOD
public function save(PostInterface $post)
3. Keep models thin:
// BAD: Business logic in model
class Post extends AbstractModel
{
public function processOrder() { /* 100 lines */ }
}
// GOOD: Delegate to service
class Post extends AbstractModel
{
// Only data and basic operations
}
// Service class handles business logic
-class PostService
{
public function processPost(PostInterface $post) { /* ... */ }
}
4. Use data patches for data:
// Instead of install/upgrade scripts
public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
// Create sample data
$post = $this->postFactory->create();
$post->setTitle('Sample Post');
$post->save();
$setup->endSetup();
}
5. Check existence before load:
$post = $this->postFactory->create()->load($id);
if (!$post->getId()) {
throw new \Magento\Framework\Exception\NoSuchEntityException(
__('Post with id %1 does not exist', $id)
);
}
Quiz
1. What three components make up the Magento model architecture?
2. What does _construct() do in a model?
3. How should you type-hint model dependencies?
4. What should models NOT contain?
Flashcards
Question
What are the three model components?
Click to reveal answer
Answer
Model, Resource Model, Collection
Question
What does _construct() initialize?
Click to reveal answer
Answer
The resource model via $this->_init()
Question
How do you load a model?
Click to reveal answer
Answer
$model->load($id) or $resource->load($model, $id)
Question
What should type-hint model dependencies?
Click to reveal answer
Answer
Data interfaces (PostInterface, not Post)
Question
Where should business logic reside?
Click to reveal answer
Answer
Service classes, not models
Revision Notes
Key Takeaways
- 1. Magento models have three layers: Model, Resource Model, Collection
- 2. _construct() initializes the resource model
- 3. Data interfaces define API contracts for models
- 4. Use factories for object creation, not ObjectManager
- 5. Type-hint interfaces, not concrete classes
- 6. Keep models thin — delegate to service classes
Interview Tips
- • Explain the three-layer model architecture
- • Describe how to create a model with data interface
- • Discuss why interfaces are preferred over concrete classes
- • Know how to load, save, and delete models
Cheat Sheet
Models Cheat Sheet
Architecture:
- Model: Business logic + data
- Resource Model: Database operations
- Collection: Multiple model queries
Basic model:
class Post extends AbstractModel
{
protected function _construct()
{
$this->_init(ResourceModel\Post::class);
}
}
Load/Save/Delete:
$post->load($id);
$post->save();
$post->delete();
Type-hint: PostInterface, not Post
Create: Use factories, not ObjectManager