PHPackages                             grithin/record - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. grithin/record

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

grithin/record
==============

Managing inclusion of web assets

0.1(4y ago)02Apache-2.0PHPPHP &gt;=7

Since Jul 22Pushed 4y ago1 watchersCompare

[ Source](https://github.com/grithin/php-record)[ Packagist](https://packagist.org/packages/grithin/record)[ Docs](http://clrmo.com)[ RSS](/packages/grithin-record/feed)WikiDiscussions master Synced 6d ago

READMEChangelogDependencies (1)Versions (2)Used By (0)

PHP Record
==========

[](#php-record)

An arbitrarily deep array that tracks changes, emits events,

Intended to be an observable that matches a database record, allowing the handling of a record like an array, keeping track of changes, handling deep data structures, and allowing listeners to react to change events.

(Similar to SplSubject, but because SplSubject uses pointless SplObserver, SplSubject is not implemented)

```
composer require grithin/record
```

Use
===

[](#use)

Basic
-----

[](#basic)

Tracking arbitrarily deep structures

```
$data = [
	'id' => 1,
	'name' => 'bob',
	'children' => [
		['name' => 'sue']
	]
];

$record = new \Grithin\Record($data);
$record['name'] = 'bill';

$record->changes(); #> ['name'=>'bill']

$record->apply(); # or alias $record->save();

$record->changes(); #> []

$record['children'][] = ['name'=>'jan'];
$record->changes();
/*>
{"children": {
        "1": {
            "name": "jan"}}}
*/
unset($record['children'][0]);
$record->changes();
/*>
{"children": [
        {"_class": "Grithin\\MissingValue"},
        {"name": "jan"}]}
*/
```

Getter, Setter, And Events
--------------------------

[](#getter-setter-and-events)

```
# Lets make a mock database
$database_records = [
	1=>[
		'id'=>1,
		'name'=>'bob',
		'children' => '[{"name":"sue"}]'
]];

#+ make the getter and setter methods {
$get_from_db = function($Record) use ($database_records){
	return $database_records[$Record->id];
};
$set_to_db = function($Record, $changes) use ($database_records){
	$record_data = $database_records[$Record->id];
	$record_data= \Grithin\Dictionary::diff_apply($record_data, $changes);
	$database_records[$Record->id] = $record_data;
	return $record_data;
};
#+ }

# initialize the record, using the 'id' option
$record = new \Grithin\Record(false, $get_from_db, $set_to_db, ['id'=>1]);
$record->get();

# let's capitalize the name when we set it
$capitalize_name = function($Record, $diff){
	if(isset($diff['name'])){
		$diff['name'] = strtoupper($diff['name']);
	}
};
$record->before_change($capitalize_name);

$record['name'] = 'bill';
# the name will become capitalized because of the event listener
$record['name']; #> BILL

# the children key points to a JSON string
$record['children']; #> '[{"name":"sue"}]'

# We can make it so JSON is automatically encoded and decoded when moving between the database
$jsonify = function($Record, $diff){
	if(isset($diff['children'])){
		$diff['children'] = json_encode($diff['children']);
	}
};
$unjsonify = function($Record){
	if(isset($Record['children'])){
		$Record->record['children'] = json_decode($Record->record['children'], true);
	}
};

$record->after_get($unjsonify);
$record->before_update($jsonify);

$record->get();

# now can access the full structure
$record['children'][0]['name']; #> sue
$record['children'][0]['name'] = 'jan';
$record->save(); # will encode the json for the db, then decode it for regular access

$record['children'][0]['name']; #> 'jan'

var_export($database_records);
/* >
array (
  1 =>
  array (
    'id' => 1,
    'name' => 'bob',
    'children' => '[{"name":"sue"}]',
  ),
)
*/
```

Some additional highlights from the above.

On the `after_get` function `unjsonify`, `$Record->record['children']` is used to avoid triggering more events. If you don't mind `before_change` and `after_change` being run, `$Record['children']` could have been used.

When setting the record in the manner `$Record['name'] = 'bob'`, each assignment triggers two events. It may be preferable to do a bulk change, which allows multiple things to change and will only emit two events. This can be done with both the local copy and the source

```
# update local copy
$Record->local_update(['name'=>'bill', 'age'=>1]);

# update both
$Record->update(['name'=>'bill', 'age'=>1]);
```

Overview
========

[](#overview)

Events, Observers
=================

[](#events-observers)

Types:

- EVENT\_AFTER\_GET : immediately after getter function is called. This can be used to decode JSON
- EVENT\_CHANGE\_BEFORE
- EVENT\_CHANGE\_AFTER
- EVENT\_UPDATE\_BEFORE : before updating the source. This can be used to re-encode JSON
- EVENT\_UPDATE\_AFTER

Convenience functions `after_get`, `before_change`, `after_change`, `before_update`, `after_update` will call the parameter function on the corresponding event with parameters `($this, $details)`, where in `$details` is an array object of the change.

[![Events](about/events.png?raw=true "Events")](about/events.png?raw=true)

(EVENT\_UPDATE\_AFTER runs after EVENT\_AFTER\_GET since it may require the data to be reformatted by EVENT\_AFTER\_GET)

Update and Change events will fire only if there were changes.

The diff parameter presented to event observers is an ArrayObject, and that object is used when applying the diff to the record. Consequently, mutating the diff within a "before" event observer will affect the resulting record. If the diff count becomes 0, no change will be applied.

Potential uses of observers:

- mutate a particular column (timestamp to datetime format)
- check validity of record state. Throw an exception if a bad state, or clear the diff
- create a composite column. Ex: total\_cost = cost1 + cost2
- logging specific changes

###  Health Score

19

—

LowBetter than 10% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity2

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity41

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

Unknown

Total

1

Last Release

1761d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4543facab3c88d548b98d8472b532faf7bcd00555cc47c0dc808d935f8d3d73f?d=identicon)[grithin](/maintainers/grithin)

---

Top Contributors

[![grithin](https://avatars.githubusercontent.com/u/7241358?v=4)](https://github.com/grithin "grithin (7 commits)")

---

Tags

js css assets inclusion

### Embed Badge

![Health badge](/badges/grithin-record/health.svg)

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

PHPackages © 2026

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