PHPackages                             ristocloud-group/php-activerecord - 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. ristocloud-group/php-activerecord

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

ristocloud-group/php-activerecord
=================================

php-activerecord is an open source ORM library based on the ActiveRecord pattern.

2.0.0(today)01↑2900%MITPHPPHP ^8.3CI passing

Since Feb 28Pushed todayCompare

[ Source](https://github.com/ristocloud-group/php-activerecord)[ Packagist](https://packagist.org/packages/ristocloud-group/php-activerecord)[ Docs](https://github.com/ristocloud-group/php-activerecord)[ RSS](/packages/ristocloud-group-php-activerecord/feed)WikiDiscussions master Synced today

READMEChangelog (1)Dependencies (6)Versions (22)Used By (0)

PHP ActiveRecord
================

[](#php-activerecord)

[![CI](https://github.com/ristocloud-group/php-activerecord/actions/workflows/ci.yml/badge.svg)](https://github.com/ristocloud-group/php-activerecord/actions/workflows/ci.yml)

> **This is a fork maintained by Ristocloud Group S.r.l.**
>
> It is based on [`zamzar/php-activerecord`](https://github.com/zamzar/php-activerecord) (itself a fork of the original [`jpfuentes2/php-activerecord`](https://github.com/jpfuentes2/php-activerecord)). We vendor it into our own applications and maintain it — fixing bugs and keeping it running on modern PHP and database versions. It is not affiliated with, nor endorsed by, the original authors.

Originally created by:

- [@kla](https://github.com/kla) - Kien La
- [@jpfuentes2](https://github.com/jpfuentes2) - Jacques Fuentes
- [and these contributors](https://github.com/kla/php-activerecord/contributors)

Upstream documentation:

Introduction
------------

[](#introduction)

A brief summarization of what ActiveRecord is:

> Active record is an approach to access data in a database. A database table or view is wrapped into a class, thus an object instance is tied to a single row in the table. After creation of an object, a new row is added to the table upon save. Any object loaded gets its information from the database; when an object is updated, the corresponding row in the table is also updated. The wrapper class implements accessor methods or properties for each column in the table or view.

More details can be found [here](http://en.wikipedia.org/wiki/Active_record_pattern).

This implementation is inspired and thus borrows heavily from Ruby on Rails' ActiveRecord. We have tried to maintain their conventions while deviating mainly because of convenience or necessity. Of course, there are some differences which will be obvious to the user if they are familiar with rails.

Minimum Requirements
--------------------

[](#minimum-requirements)

- PHP 8.3+ (tested on PHP 8.3, 8.4 and 8.5)
- PDO driver for your respective database

Supported Databases
-------------------

[](#supported-databases)

- **MySQL** — the primary production target
- **MariaDB**
- **PostgreSQL**
- **SQLite**

Continuous integration runs the full test suite across PHP 8.3, 8.4 and 8.5 against MySQL 9.7, MariaDB 11.4, PostgreSQL 18 and SQLite. The Oracle (`oci`) adapter was removed in v1.8.0.

Features
--------

[](#features)

- Finder methods
- Dynamic finder methods
- Writer methods
- Relationships
- Validations
- Callbacks
- Serializations (json/xml)
- Transactions
- Support for multiple adapters
- Miscellaneous options such as: aliased/protected/accessible attributes

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

[](#installation)

This fork is not published on Packagist. Install it with [Composer](https://getcomposer.org/) from its Git repository — add a VCS repository to your `composer.json` and require the package by its name (`ristocloud-group/php-activerecord`):

```
{
    "repositories": [
        { "type": "vcs", "url": "https://github.com/ristocloud-group/php-activerecord" }
    ],
    "require": {
        "ristocloud-group/php-activerecord": "dev-master"
    }
}
```

Setup is very easy and straight-forward. There are essentially only two configuration points you must concern yourself with:

1. Configuring your database connections.
2. Setting the database connection to use for your environment.

Example:

```
ActiveRecord\Config::initialize(function (ActiveRecord\Config $cfg) {
    $cfg->set_connections([
        'development' => 'mysql://username:password@localhost/development_database_name',
        'test' => 'mysql://username:password@localhost/test_database_name',
        'production' => 'mysql://username:password@localhost/production_database_name',
    ]);
});
```

Alternatively (without a closure):

```
$cfg = ActiveRecord\Config::instance();
$cfg->set_connections([
    'development' => 'mysql://username:password@localhost/development_database_name',
    'test' => 'mysql://username:password@localhost/test_database_name',
    'production' => 'mysql://username:password@localhost/production_database_name',
]);
```

MariaDB uses the same `mysql://` connection scheme (and the MySQL adapter) as MySQL.

PHP ActiveRecord will default to use your development database. For testing or production, you simply set the default connection according to your current environment ('test' or 'production'):

```
ActiveRecord\Config::initialize(function (ActiveRecord\Config $cfg) {
    $cfg->set_default_connection('production'); // 'development', 'test', or 'production'
});
```

Once you have configured these settings you are done. ActiveRecord takes care of the rest for you. It does not require that you map your table schema to yaml/xml files. It will query the database for this information and cache it so that it does not make multiple calls to the database for a single schema.

### Optional: caching the schema

[](#optional-caching-the-schema)

php-activerecord introspects each table's schema (columns, types, primary key) from the database. Within a single request this is kept in memory, but PHP's shared-nothing model means it is re-introspected on every request. To persist it across requests, configure an external cache. Three backends are bundled:

**Memcached** — requires the `memcached` PHP extension:

```
$cfg->set_cache('memcache://localhost:11211', ['expire' => 120, 'namespace' => 'my_app']);
```

**File** — a filesystem cache (added by this fork for hosts without memcached); no extension required:

```
$cfg->set_cache('file:///var/tmp/php-activerecord-cache');
```

The file backend stores one serialized file per cache key inside the directory you pass (creating the directory if it does not exist), and reads it back with `unserialize()`.

The **file** backend also honors the `expire` option: each entry stores an expiry timestamp and is treated as a miss once it lapses (deleted lazily on the next read). Writes are atomic (temp file + `rename`). **Behavior change:** because the default `expire` is 30 seconds, file entries that previously persisted forever now expire after 30s by default — pass `['expire' => 0]` to keep entries until you `flush()` them. Files written by older versions are treated as a miss and regenerated, so no manual purge is needed when upgrading.

**Redis** — requires the `predis/predis` Composer package (`composer require predis/predis`); no PHP extension needed:

```
$cfg->set_cache('redis://localhost:6379/0', ['expire' => 120, 'namespace' => 'my_app']);
```

Connection parameters are taken from the DSN, including its query string, so any Predis connection parameter is reachable — e.g. TLS and tuning:

```
$cfg->set_cache('redis://user:secret@redis.example.com:6379/0?read_write_timeout=2', [
    'namespace' => 'my_app',
]);
```

The same `redis://` DSN targets **Redis 6/7/8 and Valkey 7/8/9** interchangeably; the adapter is exercised against all six in CI. Values are serialized on write and unserialized on read. `ActiveRecord\Cache::flush()` deletes only the keys under the configured `namespace`(via `SCAN`/`DEL`); with no namespace it falls back to `FLUSHDB`, which clears the whole selected Redis database — set a `namespace` when the Redis instance is shared. **Do not** pass a Predis `prefix` client option for key isolation: Predis does not apply `prefix` to the plain `SCAN` command that namespace-scoped `flush()` relies on, so keys end up stored under `prefix + key` while `flush()` only matches `namespace::*`, silently deleting nothing — use the `namespace` option instead, which `flush()` already understands.

`ActiveRecord\Cache::flush()` invalidates the cache for any backend (for the file cache it deletes the cached files) — for example after running a schema migration. All backends accept a `namespace` option that prefixes every cache key, useful when several applications share one cache store.

The cache is lock-free: at each expiry, concurrent requests all recompute the cached value once (a brief stampede). For very hot deployments raise `expire` (or set it to `0`). Prefer a local filesystem for the file backend — TTLs rely on the host clock, so shared storage (NFS) across clock-skewed hosts can expire entries early or late.

BackendRequirementTTL (`expire`)PersistenceNamespace / flushConcurrencyBest for**Memcached**`memcached` PHP extensionYes (server-side)In-memory, evictable`namespace` prefix; `flush()` clears the whole serverAtomic server-side TTLExisting memcached infra**File**noneYes (since this fork)On disk until expiry/flush`namespace` prefix; `flush()` deletes filesLock-free; atomic writes, lazy GC, local-FS assumptionSingle host, no extra services**Redis / Valkey**`predis/predis` packageYes (server-side)In-memory (optionally persisted by the server)`namespace`-scoped `SCAN`/`DEL`, else `FLUSHDB`Atomic server-side TTLShared/networked cache, HABasic CRUD
----------

[](#basic-crud)

### Retrieve

[](#retrieve)

These are your basic methods to find and retrieve records from your database. See the *Finders* section for more details.

```
$post = Post::find(1);
echo $post->title; # 'My first blog post!!'
echo $post->author_id; # 5

# also the same since it is the first record in the db
$post = Post::first();

# finding using dynamic finders
$post = Post::find_by_name('The Decider');
$post = Post::find_by_name_and_id('The Bridge Builder',100);
$post = Post::find_by_name_or_id('The Bridge Builder',100);

# finding using a conditions array
$posts = Post::find('all', ['conditions' => ['name=? or id > ?', 'The Bridge Builder', 100]]);
```

### Create

[](#create)

Here we create a new post by instantiating a new object and then invoking the save() method.

```
$post = new Post();
$post->title = 'My first blog post!!';
$post->author_id = 5;
$post->save();
# INSERT INTO `posts` (title,author_id) VALUES('My first blog post!!', 5)
```

### Update

[](#update)

To update you would just need to find a record first and then change one of its attributes. It keeps an array of attributes that are "dirty" (that have been modified) and so our sql will only update the fields modified.

```
$post = Post::find(1);
echo $post->title; # 'My first blog post!!'
$post->title = 'Some real title';
$post->save();
# UPDATE `posts` SET title='Some real title' WHERE id=1

$post->title = 'New real title';
$post->author_id = 1;
$post->save();
# UPDATE `posts` SET title='New real title', author_id=1 WHERE id=1
```

### Delete

[](#delete)

Deleting a record will not *destroy* the object. This means that it will call sql to delete the record in your database but you can still use the object if you need to.

```
$post = Post::find(1);
$post->delete();
# DELETE FROM `posts` WHERE id=1
echo $post->title; # 'New real title'
```

Contributing
------------

[](#contributing)

Please refer to [CONTRIBUTING.md](CONTRIBUTING.md) for information on how to contribute to this fork.

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

54

—

FairBetter than 97% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity87

Battle-tested with a long release history

 Bus Factor2

2 contributors hold 50%+ of commits

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 ~245 days

Recently: every ~710 days

Total

21

Last Release

0d ago

Major Versions

v1.7.1 → 2.0.02026-07-31

PHP version history (5 changes)v1.1.1PHP &gt;=5.3.0

v1.5.0PHP &gt;=7.0

v1.6.0PHP &gt;=7.3

v1.7.0PHP ^7.4|^8.0

2.0.0PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/33d2570d0bdadc9606dd20e2783acc3d626269b1c5997b4a2e11f4997123c5c9?d=identicon)[nicolas-ristocloud](/maintainers/nicolas-ristocloud)

---

Top Contributors

[![kla](https://avatars.githubusercontent.com/u/27941?v=4)](https://github.com/kla "kla (233 commits)")[![jpfuentes2](https://avatars.githubusercontent.com/u/87429?v=4)](https://github.com/jpfuentes2 "jpfuentes2 (91 commits)")[![louismrose](https://avatars.githubusercontent.com/u/77096?v=4)](https://github.com/louismrose "louismrose (89 commits)")[![greut](https://avatars.githubusercontent.com/u/1388?v=4)](https://github.com/greut "greut (37 commits)")[![al-the-x](https://avatars.githubusercontent.com/u/96015?v=4)](https://github.com/al-the-x "al-the-x (14 commits)")[![koenpunt](https://avatars.githubusercontent.com/u/351038?v=4)](https://github.com/koenpunt "koenpunt (12 commits)")[![nicolas-ristocloud](https://avatars.githubusercontent.com/u/173287040?v=4)](https://github.com/nicolas-ristocloud "nicolas-ristocloud (8 commits)")[![faulkner](https://avatars.githubusercontent.com/u/22268?v=4)](https://github.com/faulkner "faulkner (7 commits)")[![Rican7](https://avatars.githubusercontent.com/u/742384?v=4)](https://github.com/Rican7 "Rican7 (6 commits)")[![teague2](https://avatars.githubusercontent.com/u/206609?v=4)](https://github.com/teague2 "teague2 (5 commits)")[![anther](https://avatars.githubusercontent.com/u/1376446?v=4)](https://github.com/anther "anther (5 commits)")[![jcs](https://avatars.githubusercontent.com/u/9888?v=4)](https://github.com/jcs "jcs (4 commits)")[![NanneHuiges](https://avatars.githubusercontent.com/u/1526794?v=4)](https://github.com/NanneHuiges "NanneHuiges (3 commits)")[![tomzx](https://avatars.githubusercontent.com/u/188960?v=4)](https://github.com/tomzx "tomzx (3 commits)")[![cvanschalkwijk](https://avatars.githubusercontent.com/u/42311?v=4)](https://github.com/cvanschalkwijk "cvanschalkwijk (3 commits)")[![brianmuse](https://avatars.githubusercontent.com/u/548885?v=4)](https://github.com/brianmuse "brianmuse (3 commits)")[![suxxes](https://avatars.githubusercontent.com/u/141334?v=4)](https://github.com/suxxes "suxxes (2 commits)")[![VendanAndrews](https://avatars.githubusercontent.com/u/1785814?v=4)](https://github.com/VendanAndrews "VendanAndrews (1 commits)")[![felixmiddendorf](https://avatars.githubusercontent.com/u/115792?v=4)](https://github.com/felixmiddendorf "felixmiddendorf (1 commits)")[![gsuess](https://avatars.githubusercontent.com/u/1558575?v=4)](https://github.com/gsuess "gsuess (1 commits)")

---

Tags

ormactiverecord

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ristocloud-group-php-activerecord/health.svg)

```
[![Health](https://phpackages.com/badges/ristocloud-group-php-activerecord/health.svg)](https://phpackages.com/packages/ristocloud-group-php-activerecord)
```

###  Alternatives

[matomo/matomo

Matomo is the leading Free/Libre open analytics platform

21.7k38.9k](/packages/matomo-matomo)[directorytree/ldaprecord

A fully-featured LDAP ORM.

5803.6M18](/packages/directorytree-ldaprecord)[cycle/database

DBAL, schema introspection, migration and pagination

71811.3k64](/packages/cycle-database)[tempest/framework

The PHP framework that gets out of your way.

2.3k37.6k18](/packages/tempest-framework)[api-platform/metadata

API Resource-oriented metadata attributes and factories

275.5M248](/packages/api-platform-metadata)[perplorm/perpl

Perpl is an improved and still maintained fork of Propel2, an open-source Object-Relational Mapping (ORM) for PHP.

2414.4k](/packages/perplorm-perpl)

PHPackages © 2026

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