ForgeExplicitOrm Module
Overview
ForgeExplicitOrm is a data access layer that provides a more explicit and structured approach to database interactions compared to traditional ORMs. It emphasizes type safety, explicit relationships, and clear data flow through the use of repositories and Data Transfer Objects (DTOs).
Repositories
Repositories provide a clean abstraction for database operations. Each repository is responsible for a specific entity and implements explicit methods for data access:
class CategoryRepository extends BaseRepository
{
protected string $dtoClass = CategoryDTO::class;
protected string $table = 'categories';
public function __construct(DatabaseInterface $database)
{
parent::__construct($database);
}
public function findBySlug(string $slug): ?CategoryDTO
{
$categories = $this->whereCriteria(['slug' => $slug]);
return $categories[0] ?? null;
}
}
Data Transfer Objects
DTOs provide a structured way to transfer data between layers of your application. They ensure type safety and make data requirements explicit:
class CategoryDTO extends BaseDTO
{
public int $id;
public string $name;
public string $slug;
public function __construct(array $data)
{
$this->id = (int)$data['id'];
$this->name = (string)$data['name'];
$this->slug = (string)$data['slug'];
}
}
Relationships
ForgeExplicitOrm handles relationships through explicit method definitions in repositories:
class SectionRepository extends BaseRepository
{
protected function belongsToCategory(string $relationName = 'category'): Relation
{
return new Relation(
CategoryRepository::class,
'category_id',
'id',
'id',
$relationName,
$this
);
}
public function __call(string $method, array $arguments)
{
if (str_starts_with($method, 'belongsTo')) {
$relationName = lcfirst(substr($method, 9));
if (method_exists($this, 'belongsTo' . ucfirst($relationName))) {
return $this->{'belongsTo' . ucfirst($relationName)}($relationName);
}
}
throw new \BadMethodCallException();
}
}
Usage Example
Here's a practical example of using ForgeExplicitOrm in a controller:
class DashboardController
{
/**
* @inject
*/
private CategoryRepository $categoryRepository;
public function index(Request $request): Response
{
$categoryId = 1;
/** @var CategoryDTO $categoryDto */
$categoryDto = $this->categoryRepository->find($categoryId);
$sections = null;
if ($categoryDto) {
$sectionsRelation = $this->categoryRepository->sections();
$sections = $sectionsRelation->for($categoryDto);
}
return $this->view->render('storage.dashboard', [
'category' => $categoryDto,
'sections' => $sections
]);
}
}