PHPackages                             preflow/data - 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. preflow/data

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

preflow/data
============

Preflow data — storage drivers, models, migrations

v0.13.1(1mo ago)0334MITPHPPHP &gt;=8.4

Since Apr 10Pushed 1mo agoCompare

[ Source](https://github.com/getpreflow/data)[ Packagist](https://packagist.org/packages/preflow/data)[ RSS](/packages/preflow-data/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (3)Versions (32)Used By (4)

preflow/data
============

[](#preflowdata)

Multi-storage data layer for Preflow. Supports JSON files and SQLite out of the box; different models can use different backends simultaneously.

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

[](#installation)

```
composer require preflow/data
```

Requires PHP 8.4+, `ext-pdo`, `ext-pdo_sqlite`.

What it does
------------

[](#what-it-does)

Models are annotated with PHP attributes. `DataManager` is the single entry point — it reads model metadata via reflection, selects the right storage driver, and returns typed results. `QueryBuilder` provides a fluent API for filtering, sorting, searching, and pagination. Migrations are handled by `Schema` + `Table` + `Migrator`.

API
---

[](#api)

### Attributes

[](#attributes)

AttributeTargetDescription`#[Entity(table: 'posts', storage: 'sqlite')]`ClassMaps model to a table/collection and a named driver. `storage` defaults to `'default'`.`#[Id]`PropertyMarks the primary key field. Works on any property — use `string $uuid` for UUIDs or `int $id` for auto-increment.`#[Field(searchable: true)]`PropertyMarks a field; `searchable: true` includes it in full-text search.`#[Timestamps]`ClassAdds `created_at` / `updated_at` handling.### `Model`

[](#model)

```
$model->fill(array $data): void   // hydrate from associative array
$model->toArray(): array          // export public properties
```

### `DataManager`

[](#datamanager)

```
$dm->find(Post::class, $id): ?Post
$dm->query(Post::class): QueryBuilder
$dm->save(Model $model): void           // INSERT when ID is empty, UPDATE otherwise
$dm->insert(Model $model): void         // explicit INSERT; reads back lastInsertId()
$dm->update(Model $model): void         // explicit UPDATE
$dm->delete(Post::class, $id): void     // delete by class + ID
$dm->delete($model): void               // delete by model instance
$dm->raw(string $sql, array $bindings, string $storage): array  // raw SQL query
```

`save()` detects an empty ID field and automatically issues an INSERT, then reads back `lastInsertId()` into the model — no need to pre-generate IDs for auto-increment tables.

### `QueryBuilder`

[](#querybuilder)

All methods return `$this` for chaining except the terminal methods.

```
->where('status', 'published')
->where('views', '>', 100)
->orWhere('featured', true)
->orderBy('created_at', SortDirection::Desc)
->limit(10)->offset(20)
->search('php')          // searches all #[Field(searchable: true)] fields
->get(): ResultSet       // all results
->first(): ?Model
->paginate(perPage: 15, currentPage: 2): PaginatedResult
```

### Storage drivers

[](#storage-drivers)

ClassBackend`JsonFileDriver`One `.json` file per record at `{basePath}/{table}/{id}.json``SqliteDriver`PDO-based SQLite via `PdoDriver` + `QueryCompiler`### Migrations

[](#migrations)

```
abstract class Migration
{
    abstract public function up(Schema $schema): void;
    public function down(Schema $schema): void {}
}
```

`Schema` methods: `create(string $table, callable $callback)`, `drop(string $table)`.

`Table` builder: `uuid`, `string`, `text`, `integer`, `boolean`, `json`, `timestamps`, `nullable()`, `primary()`, `index()`.

### Auto-increment IDs

[](#auto-increment-ids)

Place `#[Id]` on an `int` property and leave it at `0` (or unset). `save()` / `insert()` will issue an INSERT and read the generated ID back via `lastInsertId()`.

```
#[Entity(table: 'comments', storage: 'sqlite')]
final class Comment extends Model
{
    #[Id]
    public int $id = 0;

    #[Field]
    public string $body = '';
}

$comment = new Comment();
$comment->body = 'Great post!';
$dm->save($comment);
echo $comment->id; // e.g. 42 — populated after insert
```

### Raw SQL

[](#raw-sql)

Use `raw()` to run a query that the fluent builder cannot express. Results are returned as plain arrays.

```
$rows = $dm->raw(
    'SELECT p.*, COUNT(c.id) AS comment_count FROM posts p LEFT JOIN comments c ON c.post_id = p.id GROUP BY p.id',
    [],
    'sqlite',
);
```

Usage
-----

[](#usage)

**Model:**

```
use Preflow\Data\{Model, ModelMetadata};
use Preflow\Data\Attributes\{Entity, Id, Field, Timestamps};

#[Entity(table: 'posts', storage: 'sqlite')]
#[Timestamps]
final class Post extends Model
{
    #[Id]
    public string $id;

    #[Field(searchable: true)]
    public string $title;

    #[Field(searchable: true)]
    public string $body;

    public string $status = 'draft';
}
```

**Querying:**

```
// Find by ID
$post = $dm->find(Post::class, 'abc-123');

// Filtered query
$posts = $dm->query(Post::class)
    ->where('status', 'published')
    ->orderBy('created_at', SortDirection::Desc)
    ->paginate(perPage: 10, currentPage: 1);

// Full-text search across searchable fields
$results = $dm->query(Post::class)->search('preflow')->get();
```

**Saving:**

```
$post = new Post();
$post->fill(['id' => 'abc-123', 'title' => 'Hello', 'body' => '...', 'status' => 'published']);
$dm->save($post);
```

**Migration:**

```
use Preflow\Data\Migration\{Migration, Schema};

final class CreatePostsTable extends Migration
{
    public function up(Schema $schema): void
    {
        $schema->create('posts', function (Table $t) {
            $t->uuid('id')->primary();
            $t->string('title')->index();
            $t->text('body');
            $t->string('status');
            $t->timestamps();
        });
    }

    public function down(Schema $schema): void
    {
        $schema->drop('posts');
    }
}
```

**Multi-storage setup:**

```
$dm = new DataManager([
    'default' => new JsonFileDriver(basePath: '/storage/data'),
    'sqlite'  => new SqliteDriver(new \PDO('sqlite:/storage/db.sqlite')),
]);
```

Models with `#[Entity(storage: 'sqlite')]` use SQLite; all others fall back to `'default'`.

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance89

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity52

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

Total

31

Last Release

53d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/6d8b54efbc79683c32645e3fa8d3590037fa003963a16e0ed989104c6f4a2723?d=identicon)[smyr](/maintainers/smyr)

---

Top Contributors

[![smeyerme](https://avatars.githubusercontent.com/u/1925560?v=4)](https://github.com/smeyerme "smeyerme (29 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/preflow-data/health.svg)

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

###  Alternatives

[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k116.5M113](/packages/jdorn-sql-formatter)[propel/propel1

Propel is an open-source Object-Relational Mapping (ORM) for PHP5.

8361.6M87](/packages/propel-propel1)[mpociot/laravel-composite-key

Support composite keys in your laravel app.

3544.8k1](/packages/mpociot-laravel-composite-key)

PHPackages © 2026

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