Entity
About 3 min
Overview
The Entity class is the core component of the DataForge design, encapsulating the business logic of a module. Developers extend the DataForge\Entity
base class to define module-specific logic. This design promotes reusability, modularity, and efficiency by encapsulating module-specific queries and relationships within a single entity class.
All entity class files must be created in the following directory:
app/DataForge/Entity
Class Structure
namespace App\DataForge\Entity;
use DataForge\Entity;
class Product extends Entity
{
}
Methods
1. init($args)
(Abstract Method)
- Purpose: This method is mandatory in all extended entity classes.
- Description: It loads the entity base properties with the provided arguments.
- Functionality:
- Called during entity instantiation.
- Accepts parameters (e.g., integer, string, array) passed during instantiation.
- Developers use this method to query data (using SQL classes) based on the input arguments and bind the returned data as base properties of the entity.
- If no data is returned, the entity is not instantiated, and an error is raised.
- Example:
function init($id)
{
return \Sql('Product:list', ['id' => $id, 'select' => 'entity'])->fetchRow();
}
- Key Points:
- SQL class and method mapping is handled directly via the
\Sql
helper function. - This approach eliminates the need for configuration files or dependency injection, focusing on performance and simplicity.
- SQL class and method mapping is handled directly via the
2. access()
(Optional Method)
- Purpose: Defines access validation logic.
- Description: Developers write custom access checks inside this function, which returns true or false.
- Functionality:
- Automatically called after
init()
. - Implements custom business rules for access validation based on the logged-in user.
- Returns a boolean value (true or false).
- If false is returned, the entity instantiation is halted, and an error is raised.
- Automatically called after
- Example:
function access()
{
return \Sql('Product:access', ['id' => $this->id, 'user_id' => Auth::userId()])->fetchColumn();
}
3. Attrib/Property Methods
- Purpose: Defines entity attributes as methods, allowing dynamic, lazy-loaded properties.
- Description:
- Entity attributes are defined as methods prefixed with "get".
- Supports lazy-loading by loading properties only when accessed.
- Methods can assign data directly, load from SQL methods, or build relationships with other entities.
- Example:
function getPrice()
{
return $this->price;
}
function getCategory()
{
return DataForge::getCategory($this->category_id);
}
function getFormattedPrice()
{
return '$' . number_format($this->price, 2);
}
Usage:
$product = DataForge::getProduct(12345);
print $product->Price; // Calls `getPrice` and prints the price.
print $product->FormattedPrice; // Calls `getFormattedPrice` and prints the formatted price.
print_r($product->Category); // Calls `getCategory` and prints the category entity.
4. attribGroups()
(Optional Method)
- Purpose: Defines collections of related attributes for an entity.
- Description:
- Groups related attributes into named collections.
- Simplifies bulk retrieval of multiple entity properties with a single call.
- Example:
function attribGroups()
{
return [
'Summary' => 'Name, Price, StockStatus',
'Details' => 'FormattedPrice, Category, ImageUrl'
];
}
Usage:
$productSummary = $product->toArray('Summary');
// Returns ['Name', 'Price', 'StockStatus']
Key Features
1. Lazy Loading
- Entity properties are only loaded when accessed, ensuring optimal performance.
- Once loaded, subsequent accesses return cached data from memory, avoiding redundant queries.
$price = $product->Price; // Calls `getPrice()` only on the first access.
2. Inter-Entity Connectivity
- Entities can be linked seamlessly, facilitating inter-module relationships without duplicating queries or code.
$category = $product->Category; // Access category entity properties directly.
3. Customizable Access Control
- Implement custom access checks within entity methods to secure sensitive data.
4. Attribute Collections
- Group and retrieve multiple entity attributes with minimal effort using
attribGroups()
andtoArray()
methods.
5. Single Inheritance
- Entities can inherit functionality from base classes, supporting shared logic and specialized extensions.
class DiscountedProduct extends Product
{
function getDiscountedPrice($discount)
{
return $this->price * (1 - $discount);
}
}
Advanced Example
Core Example: Product Entity
namespace App\DataForge\Entity;
use DataForge\Entity;
class Product extends Entity
{
/**
* Initialize the product entity.
*
* @param int|array $args Product ID or filter criteria.
* @return array|null
*/
public function init($id = null)
{
$args = $id;
if (!is_array($id)) {
$args = ['id' => $id];
}
$args['select_type'] = 'entity';
return \Sql('Product:list', $args)->fetchRow();
}
/**
* Access control for the product entity.
*
* @return bool
*/
public function access()
{
return true; // Customize access control logic if needed.
}
/**
* Get the product's category entity.
*
* @return Category|null
*/
public function getCategory()
{
return \DataForge::getCategory($this->category_id);
}
/**
* Get the product's formatted price.
*
* @return string
*/
public function getFormattedPrice()
{
return '$' . number_format($this->price, 2);
}
/**
* Get the product's stock status.
*
* @return string
*/
public function getStockStatus()
{
return $this->stock > 0 ? 'In Stock' : 'Out of Stock';
}
/**
* Get the product's image URL.
*
* @return string
*/
public function getImageUrl()
{
return $this->image_url ?: '/images/default-product.png';
}
/**
* Get related products in the same category.
*
* @return array
*/
public function getRelatedProducts()
{
return \Sql('Product:list', [
'category_id' => $this->category_id,
'id' => ['!=' => $this->id],
'select' => 'list',
])->fetchRowList();
}
/**
* Attribute groups for the product entity.
*
* @return array
*/
public function attribGroups()
{
return [
'Summary' => 'name, description, price, stock, Category',
'Details' => 'FormattedPrice, StockStatus, ImageUrl, RelatedProducts',
];
}
}
Extended Example: Discounted Product Entity
class DiscountedProduct extends Product
{
function init($args = null)
{
$product = parent::init($args);
if (!$product) return false;
$discount = \Sql('Product:discountDetails', ['id' => $product['id']])->fetchRow();
if (!$discount) return false;
return array_merge($product, $discount);
}
function getDiscountedPrice()
{
return $this->price - ($this->price * $this->discount_percentage / 100);
}
}
Usage:
$product = DataForge::getDiscountedProduct(123); // Instantiate discounted product entity by ID
print $product->DiscountedPrice(0.2); // Prints the discounted price
Ideal Use Cases
- Small Applications: Quickly bootstrap functionality with compact, ready-to-use entities.
- Large Applications: Integrate seamlessly as a stable foundation for scalable systems.