PHPackages                             mattdwyercool/porm - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. mattdwyercool/porm

ActiveLibrary[Database &amp; ORM](/categories/database)

mattdwyercool/porm
==================

Bare bones vanilla ORM for PHP

1.2.3(7mo ago)132MITPHPPHP &gt;=8.1

Since Jun 17Pushed 7mo agoCompare

[ Source](https://github.com/mattdinthehouse/porm)[ Packagist](https://packagist.org/packages/mattdwyercool/porm)[ RSS](/packages/mattdwyercool-porm/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependenciesVersions (7)Used By (0)

PORM: a vanilla ORM for PHP
===========================

[](#porm-a-vanilla-orm-for-php)

I think that Eloquent is too abstracted and feels bad once you go beyond single-table CRUD objects, Doctrine is too verbose, and neither of them are feasible to put into legacy projects.

PHP has [`PDOStatement::fetchObject()`](https://www.php.net/manual/en/pdostatement.fetchobject.php) and I like to write my SQL manually, so I ended up developing a design pattern of sorts for reading data out of the database and now I'm formalising that into PORM ☺️

PORM works more like a "framework" by providing the mechanisms for object mapping and loading associated records, but let's you write your own SQL and whatever logic for loading those associations and casting data types.

Installation
------------

[](#installation)

`composer require mattdwyercool/porm`

Features
--------

[](#features)

- You get clean, vanilla PHP objects that you can do whatever you want with.
- The class properties don't have to match a DB table - you can do JOINs and fancy SELECTs.
- Protection from N+1 queries by default.
- You control the type casting and property visibility.

Database support
----------------

[](#database-support)

PORM supports PDO and MySQLi but leaves it up to you to manage them - you can use a Service Container, a global variable, or even the procedural `mysqli_xxx()` functions.

PORM includes a pair of *optional* helper facades that you can initialise during your app's bootstrapping with:

- PDO: `new \PORM\Helpers\PDO\DB( new PDO( ... ) )`
- MySQLi: `new \PORM\Helpers\MySQLi\DB( new mysqli( ... ) )`

How it works
------------

[](#how-it-works)

I haven't written proper docs yet so have a look at the `examples/` folder for a demo or read below for a crash course.

But in a nutshell: define a class with properties like you normally would, then make it `use \PORM\Model` and define some static getter methods. Those getter methods should build a `PDOStatement` object whose columns match up with your class properties, and then call `static::one()` for a single-record getter or `static::many()` for an array.

`one()` and `many()` will call `PDOStatement::fetchObject( static::class )` on the statement you give it, then scan the class for any relationships you've defined (see below). PORM `unset()`'s those relationship properties so that you keep IDE autocomplete, and relies on `__get()` to lazy-load them when called upon. When that happens, PORM will also load the related records for the "siblings" in your original `static::many()` call to avoid N+1 queries.

Object Mapping
--------------

[](#object-mapping)

Create a plain PHP class with your properties like:

```
use PORM\Model;

final class Person
{
	use Model;

	public int $id;
	public string $name;
	public string $email;
}
```

That's it, but keep the data types as simple scalars (see below for non-scalar Type Casting).

### Getter functions

[](#getter-functions)

You can define whatever model getters you need by adding static methods to your class that creates a `PDOStatement` or `mysqli_result` object and passes it to `static::one()` for single-record getters or `static::many()` for an array.

`static::one()` returns an instance of your class or `null` if the SQL query returned empty:

```
final class Person
{
	public static function get( int $id ): ?static
	{
		$stmt = DB::select( parent_id === $parent->id`.

`HasMany` makes the same guesses as `HasOne` and you can manually specify the same properties, but because PHP doesn't have typing for arrays yet you have to specify the remote class manually and a PHPDoc to get IDE autocomplete working.

Writing to the DB
=================

[](#writing-to-the-db)

Right now the scope of PORM is just for reading from the DB but it's your code so you can do whatever you want.

I plan to add insert and update helpers to the `DB` class as well as some other utilities so check back later!

Contributing
============

[](#contributing)

Yeah go for it mate, no worries 😊

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance62

Regular maintenance activity

Popularity9

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~20 days

Total

6

Last Release

232d ago

PHP version history (2 changes)1.0.0PHP &gt;=8.0

1.2.1PHP &gt;=8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/f9fc69a89b71498aff85fbd6074fdb33efdda54421d8477b72f426df892c8b17?d=identicon)[mattdwyercool](/maintainers/mattdwyercool)

---

Top Contributors

[![mattdinthehouse](https://avatars.githubusercontent.com/u/6799224?v=4)](https://github.com/mattdinthehouse "mattdinthehouse (20 commits)")

### Embed Badge

![Health badge](/badges/mattdwyercool-porm/health.svg)

```
[![Health](https://phpackages.com/badges/mattdwyercool-porm/health.svg)](https://phpackages.com/packages/mattdwyercool-porm)
```

###  Alternatives

[doctrine/orm

Object-Relational-Mapper for PHP

10.2k285.3M6.2k](/packages/doctrine-orm)[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k115.1M102](/packages/jdorn-sql-formatter)[illuminate/database

The Illuminate Database package.

2.8k52.4M9.4k](/packages/illuminate-database)[ramsey/uuid-doctrine

Use ramsey/uuid as a Doctrine field type.

90440.3M211](/packages/ramsey-uuid-doctrine)[reliese/laravel

Reliese Components for Laravel Framework code generation.

1.7k3.4M16](/packages/reliese-laravel)[wildside/userstamps

Laravel Userstamps provides an Eloquent trait which automatically maintains `created\_by` and `updated\_by` columns on your model, populated by the currently authenticated user in your application.

7511.7M13](/packages/wildside-userstamps)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
