<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dev-Iadicola</title>
    <description>The latest articles on DEV Community by Dev-Iadicola (@dev_iadicola).</description>
    <link>https://dev.to/dev_iadicola</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2138925%2F6dd30c44-71a1-4568-a668-759ac9486600.png</url>
      <title>DEV Community: Dev-Iadicola</title>
      <link>https://dev.to/dev_iadicola</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dev_iadicola"/>
    <language>en</language>
    <item>
      <title>A Clean, Driver-Aware ORM Architecture in PHP</title>
      <dc:creator>Dev-Iadicola</dc:creator>
      <pubDate>Thu, 13 Nov 2025 23:10:58 +0000</pubDate>
      <link>https://dev.to/dev_iadicola/a-clean-driver-aware-orm-architecture-in-php-20h2</link>
      <guid>https://dev.to/dev_iadicola/a-clean-driver-aware-orm-architecture-in-php-20h2</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;I recently completed a major refactor of my custom ORM, evolving it from a hybrid Active Record structure into a clean, layered, driver-aware architecture. &lt;br&gt;
The new design removes hidden “magic”, improves type-safety, and gives each component a precise, single responsibility.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnw58fxkdlem6agqvhrkz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnw58fxkdlem6agqvhrkz.png" alt="Diagram of ORM" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Model Layer
&lt;/h2&gt;

&lt;p&gt;Models now define only structure and metadata.&lt;br&gt;&lt;br&gt;
They no longer execute queries or manage persistence.&lt;/p&gt;

&lt;p&gt;Instead, each Model delegates all operations to a dedicated ActiveQuery pipeline, ensuring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deterministic behavior
&lt;/li&gt;
&lt;li&gt;zero hidden state
&lt;/li&gt;
&lt;li&gt;strict separation of concerns
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In future updates, I will remove the "fillable" property entirely and introduce true encapsulated domain fields, powered by Value Objects.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. ORM Runtime
&lt;/h2&gt;

&lt;p&gt;The ORM runtime stores only two global resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the active PDO instance
&lt;/li&gt;
&lt;li&gt;the SQL driver currently in use
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No additional state is leaked across queries.&lt;br&gt;&lt;br&gt;
This makes the system predictable, stable, and fully driver-agnostic.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. ActiveQueryFactory
&lt;/h2&gt;

&lt;p&gt;This factory assembles the complete ORM pipeline for every Model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;loads model metadata
&lt;/li&gt;
&lt;li&gt;creates the correct QueryBuilder based on the driver
&lt;/li&gt;
&lt;li&gt;instantiates the QueryExecutor
&lt;/li&gt;
&lt;li&gt;instantiates the ModelHydrator
&lt;/li&gt;
&lt;li&gt;returns a clean, isolated ActiveQuery instance
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every query begins with a fresh builder and clean state.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Driver-Specific Query Builders
&lt;/h2&gt;

&lt;p&gt;The QueryBuilder is now split into two layers:&lt;/p&gt;

&lt;h3&gt;
  
  
  AbstractBuilder
&lt;/h3&gt;

&lt;p&gt;Contains all shared SQL logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SELECT, WHERE, JOIN
&lt;/li&gt;
&lt;li&gt;GROUP BY, HAVING, ORDER BY
&lt;/li&gt;
&lt;li&gt;LIMIT, OFFSET
&lt;/li&gt;
&lt;li&gt;binding system
&lt;/li&gt;
&lt;li&gt;SQL composition
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Per-Driver Builders (e.g., MySQL, PostgreSQL)
&lt;/h3&gt;

&lt;p&gt;Override only what is truly different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;syntax differences
&lt;/li&gt;
&lt;li&gt;identifier quoting
&lt;/li&gt;
&lt;li&gt;RETURNING clauses
&lt;/li&gt;
&lt;li&gt;last inserted ID behavior
&lt;/li&gt;
&lt;li&gt;Postgres vs MySQL parameterization nuances
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This dramatically simplifies maintenance and allows the ORM to support new databases easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. ActiveQuery
&lt;/h2&gt;

&lt;p&gt;ActiveQuery is the central point of the ORM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;receives the QueryBuilder
&lt;/li&gt;
&lt;li&gt;executes the SQL through QueryExecutor
&lt;/li&gt;
&lt;li&gt;hydrates results with ModelHydrator
&lt;/li&gt;
&lt;li&gt;returns model instances, collections, or primitives
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Its API mirrors the simplicity of modern ORMs but keeps everything explicit and controlled.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Hydration Layer
&lt;/h2&gt;

&lt;p&gt;ModelHydrator takes care of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;instantiating model objects
&lt;/li&gt;
&lt;li&gt;assigning attributes safely
&lt;/li&gt;
&lt;li&gt;handling single results vs collections
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the future, this layer will also handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;typed model properties
&lt;/li&gt;
&lt;li&gt;value objects
&lt;/li&gt;
&lt;li&gt;domain transformations
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This architecture brings my ORM very close to the clarity of Doctrine’s design while preserving the approachability of Active Record.&lt;br&gt;&lt;br&gt;
It is driver-aware, predictable, cleanly separated, and ready for future features like typed fields and full domain-driven modeling.&lt;/p&gt;

&lt;p&gt;This work represents an important evolution toward a modern, stable, framework-quality ORM written entirely in PHP.&lt;/p&gt;

&lt;p&gt;GitHub repo &lt;a href="https://github.com/dev-iadicola/soft-php-mvc" rel="noopener noreferrer"&gt;GitHub &lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>mvc</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Build a PHP QueryBuilder from scratch!</title>
      <dc:creator>Dev-Iadicola</dc:creator>
      <pubDate>Tue, 11 Nov 2025 16:52:56 +0000</pubDate>
      <link>https://dev.to/dev_iadicola/build-a-php-querybuilder-from-scratch-4891</link>
      <guid>https://dev.to/dev_iadicola/build-a-php-querybuilder-from-scratch-4891</guid>
      <description>&lt;p&gt;I recently completed one of the most challenging milestones of my framework: the ORM and QueryBuilder system.&lt;br&gt;&lt;br&gt;
It’s a complex component that required deep understanding of OOP, SQL abstraction, and design principles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3ywoct07v6afvyybhkm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3ywoct07v6afvyybhkm.jpg" alt=" " width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Goal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wanted to understand what truly happens behind the scenes when writing fluent database queries.&lt;br&gt;&lt;br&gt;
Instead of using Laravel or Symfony, I decided to build my own ORM from scratch — focusing on architecture, not shortcuts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The Soft ORM system is inspired by modern framework technologies,&lt;br&gt;&lt;br&gt;
but built entirely from scratch.&lt;br&gt;&lt;br&gt;
Every Model represents a table, and the QueryBuilder dynamically generates and executes SQL statements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Execution Flow&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;User::find(1)&lt;/code&gt; → Extends the base Model
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Model.php&lt;/code&gt; → Creates the instance and forwards static calls to the QueryBuilder
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;QueryBuilder.php&lt;/code&gt; → Builds the SQL query (SELECT * FROM ...)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Database.php&lt;/code&gt; → Singleton PDO that executes the query
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ResultSet&lt;/code&gt; → Handles model hydration&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Design Patterns
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Active Record&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each Model manages its table and CRUD operations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Builder&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The QueryBuilder constructs SQL queries fluently.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PDO is injected via container for separation of concerns.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Chain of Responsibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Methods like &lt;code&gt;where()&lt;/code&gt; and &lt;code&gt;join()&lt;/code&gt; return &lt;code&gt;self&lt;/code&gt; for chaining.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fail Fast Design&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Invalid Model structures throw &lt;code&gt;ModelStructureException&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Writing an ORM from scratch forces a focus on architecture, abstraction, and class responsibility.&lt;br&gt;&lt;br&gt;
It reveals the value of separation between logic, data, and infrastructure layers.&lt;/p&gt;

&lt;p&gt;The first unit tests for the QueryBuilder validate SQL generation and parameter binding.&lt;br&gt;&lt;br&gt;
They ensure the ORM behaves consistently before adding advanced features such as relationships and eager loading.&lt;/p&gt;

&lt;p&gt;Try It Out&lt;/p&gt;

&lt;p&gt;Repository:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/dev-iadicola/soft-php-mvc" rel="noopener noreferrer"&gt;GitHub – dev-iadicola/soft-php-mvc&lt;/a&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
git clone https://github.com/dev-iadicola/soft-php-mvc.git
cd soft-php-mvc
composer install
php soft serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>php</category>
      <category>mvc</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
