🧩 Data Objects in MahxCheckout
MahxCheckout uses Data Objects (DOs) and Data Transfer Objects (DTOs) to structure and validate data across the entire checkout system — from controllers to view models to templates.
💡 Why Data Objects?
Data Objects give you a better way to represent and work with structured data.
Traditional Magento development leans on Magento\Framework\DataObject
, which relies on getData()
and setData()
magic methods. While flexible, it's hard to reason about and doesn’t clearly express the shape of the data.
What's Better About DOs?
- Explicit: You always know what properties are available.
- Typed: Properties use strict types, improving reliability.
- Immutable by default: Promotes safe, predictable data flow.
- IDE-friendly: Code completion and refactoring just work.
- Great for templates: Data passed to
.phtml
is clear and documented.
✅ Enabled by PHP 8+
Thanks to PHP 8+ features like:
- Constructor property promotion
- Union types
- Attributes (if used)
- Native type system
…it’s now easier and cleaner than ever to build robust data classes.
👤 Example: A Simple User Data Object
class User
{
public function __construct(
public string $firstname,
public string $lastname,
public string $email,
public ?string $middlename = '',
) {}
}
Here, the structure is immediately obvious. If you try to create a User
without firstname
or with a non-string email
, PHP will throw a type error. That’s built-in validation for free.
But for real-world use cases, you’ll need more than type safety. You’ll want validation rules like “required”, “email format”, or even “must be one of these values”.
That’s where magento-data
comes in.
✅ Advanced Validation with magento-data
MahxCheckout integrates with rajeev-k-tomy/magento-data
— a powerful package for defining validation rules directly inside your data objects.
Inspired by Spatie’s and Laravel’s DTOs, it brings modern data handling patterns into the Magento ecosystem.
📧 Example: GuestEmailData
use Rkt\MageData\Data;
class GuestEmailData extends Data
{
public function __construct(
public string $email,
) {}
public function rules(): array
{
return [
'email' => 'required|email',
];
}
}
🔍 What is rules()
?
The rules()
method defines validation logic for each property in your data object. It returns an array where:
- The key is the property name (e.g.,
'email'
) - The value is a string (or array) of validation rules to apply
In the above example:
'email' => 'required|email'
Means:
email
must be present (required
)- and it must be a valid email address (
email
)
✅ Supported Rules
Rules are interpreted using Rakit Validation, so you get access to common rules like:
required
email
numeric
min
,max
,between
in
,not_in
date
,url
,boolean
same
,different
,regex
, etc.
You can also define custom rules if needed.
⚙️ Controller Usage
MahxCheckout controllers consistently follow this pattern:
public function execute(): ResultInterface
{
$guestEmailData = GuestEmailData::from([
'email' => $this->getRequest()->getParam('email'),
]);
try {
$guestEmailData->validate();
// Passed to service layer
} catch (\Exception $e) {
// Handle validation errors
}
}
from()
is a static constructor to instantiate a data object.validate()
checks the rules defined in therules()
method- After validation, the object is passed to the service layer
This provides a standardized, declarative, and testable approach to request validation.
🛠️ Instantiation: Why new
is Perfectly Fine for Data Objects
In Magento, we typically avoid using new
directly. Instead, we use dependency injection (DI) and factories — especially for services, models, or any class with dependencies. This is a Magento best practice to ensure testability, extensibility, and proper object lifecycle handling.
However, Data Objects are a different story — and here’s why:
💡 Data Objects are Simple Value Containers
Data Objects:
- Represent structured data (e.g., form submissions, DTOs)
- Are stateless (they hold values, not behavior)
- Don’t require Magento DI or configuration
- Are created from dynamic input (like
$_POST
or request parameters)
Because of this, using new
or static constructors like from()
is not just acceptable — it's the right tool for the job.
$guestEmail = new GuestEmailData('test@example.com');
// or
$guestEmail = GuestEmailData::from(['email' => 'test@example.com']);
✅ Why This Is Safe and Common Practice
This pattern is widely accepted in the broader PHP ecosystem:
- In Laravel, you’ll find similar usage with
spatie/laravel-data
- In Symfony, DTOs are created manually from request data
- Even in PSR-oriented codebases, static factory methods are common for immutable value objects
It avoids unnecessary factories and boilerplate for simple, one-off objects whose only job is to hold and validate data.
📦 Not Just for MahxCheckout
Although MahxCheckout uses this pattern extensively, the magento-data
module is a great fit for any Magento project.
Use it to:
- Validate incoming request data
- Create clean, structured DTOs for services
- Improve the developer experience in templates
- Eliminate magic
getData()
calls
🧠 You'll find this approach especially helpful in form submissions, API handling, checkout customizations, and anywhere structured input needs to be validated and passed through the system.
🔚 Conclusion
MahxCheckout uses data objects not just for convenience — but to enforce a cleaner, safer, and more expressive architecture.
- ✅ Better structure
- ✅ Built-in validation
- ✅ Clean separation of concerns
- ✅ Stronger template support
- ✅ Reusable across Magento modules
We strongly recommend using magento-data
beyond MahxCheckout in your own Magento projects.
💬 You can find more advanced features (nested DTOs, rule groups, serialization) in the module's documentation.