pdphilip/laravel-data-set

Eloquent style management of data sets in Laravel

Fund package maintenance!
PDPhilip

Installs: 9 502

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/pdphilip/laravel-data-set

v1.0.0 2026-02-20 22:12 UTC

This package is auto-updated.

Last update: 2026-02-21 11:20:40 UTC


README

Laravel Data Set

Latest Version on Packagist GitHub Tests Action Status GitHub phpstan Action Status Total Downloads

Eloquent-style querying for in-memory data sets. No database required.

Replace static arrays and config lookups with a queryable, model-like interface. Ideal for reference data (countries, currencies, timezones), test fixtures, and anywhere you need Eloquent ergonomics without a database.

// Define your data set
class CountrySet extends DataSet
{
    protected string $modelClass = CountryDataModel::class;

    protected function data(): array
    {
        return [
            ['id' => 'US', 'name' => 'United States', 'currency' => 'USD', 'dial_code' => '+1'],
            ['id' => 'GB', 'name' => 'United Kingdom', 'currency' => 'GBP', 'dial_code' => '+44'],
            ['id' => 'DE', 'name' => 'Germany', 'currency' => 'EUR', 'dial_code' => '+49'],
            // ...
        ];
    }
}

// Query it like Eloquent
CountrySet::find('US')->name;                              // 'United States'
CountrySet::where('currency', 'EUR')->get();               // Collection of European countries
CountrySet::search('united')->pluck('name');               // ['United States', 'United Kingdom', ...]
CountrySet::where('dial_code', '+1')->first()->currency;   // 'USD'

Requirements

  • PHP 8.2+
  • Laravel 10, 11, or 12

Installation

composer require pdphilip/laravel-data-set

Quick Start

Inline usage

use PDPhilip\DataSet\DataSet;

$set = new DataSet;

$set->add(['name' => 'Alpha', 'status' => 'active', 'score' => 85]);
$set->add(['name' => 'Beta', 'status' => 'inactive', 'score' => 42]);
$set->add(['name' => 'Charlie', 'status' => 'active', 'score' => 91]);

$set->where('status', 'active')->count();                  // 2
$set->where('score', '>', 80)->orderBy('name')->get();     // Alpha, Charlie
$set->search('beta')->first()->score;                      // 42

Extended data set (recommended)

Create a dedicated class with seeded data:

use PDPhilip\DataSet\DataSet;

class TimezoneSet extends DataSet
{
    protected function data(): array
    {
        return [
            ['id' => 'UTC', 'name' => 'Coordinated Universal Time', 'offset' => '+00:00'],
            ['id' => 'EST', 'name' => 'Eastern Standard Time', 'offset' => '-05:00'],
            ['id' => 'PST', 'name' => 'Pacific Standard Time', 'offset' => '-08:00'],
            // ...
        ];
    }
}

// Static facade - data loads once, cached per request
TimezoneSet::find('EST')->name;        // 'Eastern Standard Time'
TimezoneSet::count();                  // 3

Custom model class

Type your data with a custom model for IDE autocompletion:

use PDPhilip\DataSet\DataModel;

/**
 * @property string $id
 * @property string $name
 * @property string $currency
 * @property string $dial_code
 */
class CountryDataModel extends DataModel {}
use PDPhilip\DataSet\DataSet;

class CountrySet extends DataSet
{
    protected string $modelClass = CountryDataModel::class;

    protected function data(): array
    {
        return [...];
    }
}

Now CountrySet::find('US') returns a CountryDataModel with typed properties.

Relationship-like access

Use a DataSet as a pseudo-relationship on your Eloquent models:

class User extends Model
{
    public function country(): ?CountryDataModel
    {
        return CountrySet::find($this->country_code);
    }
}

$user->country()->name;       // 'United States'
$user->country()->dial_code;  // '+1'

API Reference

CRUD

Method Returns Description
create(array $attributes) DataModel Create an unsaved model instance
add(array $attributes) DataModel Create and save a model
insert(array $rows) static Bulk insert rows
$model->save() static Save or update a model
$model->delete() void Remove a model from the set
// Create without saving
$model = $set->create(['name' => 'Draft']);
$model->status = 'pending';
$model->save();

// Create and save in one step
$model = $set->add(['name' => 'Ready', 'status' => 'active']);

// Bulk insert
$set->insert([
    ['name' => 'Alpha', 'status' => 'active'],
    ['name' => 'Beta', 'status' => 'inactive'],
]);

// Modify and re-save
$model = $set->find('us');
$model->name = 'Updated';
$model->save();

// Delete
$model->delete();

Records without an id get a UUID assigned automatically. Auto-generated IDs are hidden from toArray() output, so data round-trips cleanly (load from source, query, modify, export back). The ID is still accessible on the model for find(), save(), and delete() operations.

To use a custom primary key:

class MySet extends DataSet
{
    protected string $primaryKey = 'code';
}

Query Methods

All query methods return a new instance, leaving the original untouched.

Method Description
where(string $key, mixed $operator, mixed $value) Filter by field. Supports =, !=, <>, <, >, <=, >=, like, ===, !==
where(string $key, mixed $value) Shorthand for where($key, '=', $value)
whereNot(string $key, mixed $value) Shorthand for where($key, '!=', $value)
whereStrict(string $key, mixed $value) Strict === comparison
whereIn(string $key, array $values) Filter where field value is in array
whereNotIn(string $key, array $values) Filter where field value is not in array
whereBetween(string $key, array $range) Filter where field is between [$min, $max]
whereNotBetween(string $key, array $range) Filter where field is outside [$min, $max]
whereNull(string $key) Filter where field is null or missing
whereNotNull(string $key) Filter where field is not null
search(string $term) Case-insensitive search across all string fields
orderBy(string $key, string $direction) Sort results (asc or desc)
orderByDesc(string $key) Sort descending
groupBy(string $key) Group results by field (applied on get())
limit(int $count) Limit result count
offset(int $count) Skip first N results
// Chaining
$set->where('status', 'active')
    ->where('score', '>', 50)
    ->orderBy('name')
    ->limit(10)
    ->get();

// Dot notation for nested data
$set->where('address.city', 'Sydney')->get();

// Array field membership
$set->insert([
    ['id' => '1', 'tags' => ['php', 'laravel']],
    ['id' => '2', 'tags' => ['js', 'react']],
]);
$set->where('tags', 'php')->get(); // Row 1

// Group by
$set->groupBy('browser')->get();
// => ['Chrome' => Collection, 'Firefox' => Collection, ...]

// Clone isolation - queries never pollute the base set
$active = $set->where('status', 'active');
$activeHigh = $active->where('score', '>', 80);
$activeLow = $active->where('score', '<', 30);
// $active, $activeHigh, $activeLow are all independent

Terminal Methods

Method Returns Description
get() Collection Execute query, return Collection of models
all() Collection All records (ignores filters)
first() DataModel|null First matching record
find(mixed $id) DataModel|null Find by primary key
fetch(string $key, mixed $value) DataModel|null Find first where key equals value
firstOrCreate(array $attributes, array $values) DataModel Find matching or create with merged attributes
count() int Count matching records
exists() bool Any matches?
pluck(string $value, ?string $key) Collection Pluck field values
toArray() array Raw array output (auto-IDs stripped)
paginate(int $perPage) LengthAwarePaginator Paginated results
update(array $attributes) int Bulk update matching rows, returns count
delete() int Bulk delete matching rows, returns count
// Fetch - shorthand for where()->first()
$set->fetch('email', 'john@example.com');

// First or create
$set->firstOrCreate(
    ['email' => 'john@example.com'],           // search by
    ['name' => 'John', 'status' => 'active']   // merge if creating
);

// Bulk update
$set->where('status', 'draft')->update(['status' => 'published']); // => 3

// Bulk delete
$set->where('status', 'inactive')->delete(); // => 2

Static Facade

Extended DataSet classes support static method calls. The instance is cached per class for the duration of the request.

CountrySet::where('currency', 'EUR')->get();
CountrySet::find('US');
CountrySet::count();
CountrySet::search('island')->pluck('name');

Use flush() to clear the cached instance (useful in tests or Laravel Octane):

CountrySet::flush();

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Credits

License

The MIT License (MIT). Please see License File for more information.