StarDust is a high-performance library for CodeIgniter 4 that allows you to add dynamic data models to your application without the performance cost usually associated with EAV (Entity-Attribute-Value) or JSON storage.
Unlike other solutions that rely on slow software-side filtering or complex JOINs, StarDust uses a Runtime Indexer to automatically generate MySQL Virtual Columns and B-Tree Indexes for your JSON fields. Plus, every change is automatically versionedβgiving you a complete audit trail without extra work.
The result? You get the flexibility of NoSQL (add fields on the fly) with the query speed of a native SQL table, plus enterprise-grade change tracking built-in.
- Runtime Indexer: Automatically maintains virtual columns (
v_price_num,v_sku_str) and indexes for your dynamic fields, ensuring high-performanceWHEREandORDER BYoperations. - Dual Versioning: Complete change history for both models and entriesβevery update creates a new version while maintaining instant access to current data. Perfect for audit trails, compliance, and data recovery.
- Dynamic Modeling: Create new entities (Products, Pages, Tickets) and define their fields in JSON at runtime.
- Optimized Storage: Uses a "Flat Table" approach enhanced by JSON columns, avoiding the "EAV Join Hell".
- Syntax Processor: A recursive processor to parse nested data queries within JSON content (ideal for API-driven architectures).
- Unified Managers: Simple
ModelsManagerandEntriesManagerservices to handle CRUD.
StarDust revolves around three fundamental concepts:
-
Models (The Blueprint): Think of a Model as a dynamic table definition (e.g., "Products", "Tickets"). It defines the structure and settings for a collection of data.
-
Entries (The Record): An Entry is a single instance of a Model (e.g., a specific Product). Data is stored logically as JSON but physically optimized for SQL performance.
-
Fields (The Attribute): Fields are the data points defined within a Model (e.g.,
price,sku). StarDust maps these JSON fields to Virtual Columns, giving you the query performance of native SQL columns.
- PHP: 8.1 or later
- Framework: CodeIgniter 4.0+
- Database: Must support JSON and Generated Columns.
- MySQL 5.7+ (MySQL 8.0+ required for Async Indexing)
- MariaDB 10.2+ (MariaDB 10.6+ required for Async Indexing)
StarDust communicates with your users table to track who created or modified data.
Default Behavior: StarDust is pre-configured to work with CodeIgniter Shield out of the box.
Custom / Agnostic Setup:
You can use ANY authentication system or existing users table.
Option 1: ENV File (Easiest)
Add these to your .env file:
StarDust.usersTable = 'my_custom_users'
StarDust.usersIdColumn = 'user_uuid'
StarDust.usersUsernameColumn = 'display_name'Option 2: Config File (Recommended for teams)
Create a file at app/Config/StarDust.php:
<?php
namespace Config;
use StarDust\Config\StarDust as BaseStarDust;
class StarDust extends BaseStarDust
{
public $usersTable = 'my_custom_users';
public $usersIdColumn = 'user_uuid';
public $usersUsernameColumn = 'display_name';
}Automatic Polyfill:
For testing or fresh installations without an auth system, StarDust includes a Polyfill Migration. It will automatically create a minimal users table ONLY IF one does not already exist. This ensures you can get started immediately without setup friction.
-
Install via Composer:
composer require damarbob/stardust
-
Run Migrations:
php spark migrate -n StarDust
Define your data structure using the ModelsManager.
Key concept: When you create or update a model, the Runtime Indexer automatically generates virtual columns and indexes for fields in
model_fields.
use StarDust\Services\ModelsManager;
$manager = service('modelsManager');
$modelData = [
'name' => 'Products',
'slug' => 'products',
'description' => 'Main product catalog',
'model_fields' => json_encode([
// The 'type' determines the index suffix (_num, _str, _dt)
['id' => 'price_01', 'label' => 'Price', 'type' => 'number'], // Creates 'v_price_01_num'
['id' => 'sku_01', 'label' => 'SKU', 'type' => 'text'] // Creates 'v_sku_01_str'
])
];
$modelId = $manager->create($modelData, $currentUserId);Add records using EntriesManager. Data is stored as JSON but mirrored into the virtual columns for speed.
use StarDust\Services\EntriesManager;
$entriesManager = service('entriesManager');
$entryData = [
'model_id' => $modelId,
'fields' => json_encode([
'price_01' => '15000',
'sku_01' => 'PROD-001'
])
];
$entriesManager->create($entryData, $currentUserId);This is where StarDust shines. Instead of slow JSON searching, you query the auto-generated virtual columns directly.
Naming Convention: v_{field_id}_{suffix}
| Suffix | Field Types | SQL Type |
|---|---|---|
_num |
number, range |
DECIMAL(20,4) |
_dt |
date, datetime-local |
DATETIME |
_str |
text, select, etc. |
VARCHAR(191) |
Example:
$entriesModel = model('StarDust\Models\EntriesModel');
$results = $entriesModel->stardust()
// Native SQL speed! Validated by B-Tree Indexes.
->where('v_price_01_num >', 1000)
->where('v_sku_01_str', 'PROD-001')
->get()
->getResultArray();Use the stardust() method to query entries with pre-configured JOINs and virtual column support:
$entriesModel->stardust()
->where('v_price_01_num >', 1000)
->get();Note: See FAQ for why
stardust()is recommended over standard Model methods.
For fields not indexed (e.g., textarea types), use likeFields():
$entriesModel->stardust()->likeFields([
['field' => 'description_01', 'value' => 'fragile']
])->get();
β οΈ Performance: This performs a full table scan. See FAQ for details and alternatives.
Every update to a model or entry creates a new version. Access the complete history using the data models:
// Get all versions of a model's fields
$modelDataModel = model('StarDust\\Models\\ModelDataModel');
$modelVersions = $modelDataModel->where('model_id', $modelId)
->orderBy('created_at', 'DESC')
->findAll();
// Get all versions of an entry's data
$entryDataModel = model('StarDust\\Models\\EntryDataModel');
$entryVersions = $entryDataModel->where('entry_id', $entryId)
->orderBy('created_at', 'DESC')
->findAll();
// Each record includes:
// - fields (JSON): The data at that point in time
// - created_at: When this version was created
// - creator_id: Who made the changeπ‘ Tip: The
modelsandentriestables havecurrent_model_data_idandcurrent_entry_data_idpointers that always reference the latest version for instant access.
The SyntaxProcessor enables API-driven queries where logic is passed as JSON:
$processor = syntax_processor();
$result = $processor->process($jsonRequest);π‘ See FAQ - Syntax Processor for detailed usage examples.
For repair or regeneration scenarios (not needed for normal usage):
php spark stardust:generate-indexesNote: Virtual columns are automatically created when using
modelsManager->create()or->update(). This command is only needed for database repairs or migrations.
Additional commands:
stardust:cleanup-columns- Remove orphaned virtual columnsstardust:map-current- Update version pointers for latest entry datastardust:convert-fields- Migrate from v0.1.x data format
π See FAQ - CLI Commands for detailed usage and when to run each command.
By default, StarDust creates indexes immediately (synchronously). For high-traffic sites, this can cause table locks.
Enable Async Indexing:
composer require codeigniter4/queue- Run migrations to create the jobs table:
php spark migrate -n CodeIgniter\Queue - Set
$asyncIndexing = trueinapp/Config/StarDust.php
This moves the heavy ALTER TABLE operations to a background worker, making your application feel instant even during complex updates.
π‘ Free Hosting? You can use Async Indexing too! See the "Web Worker" guide in the FAQ.
β οΈ Requirement: If using the default Database Handler for queues, your database MUST supportSKIP LOCKED(MySQL 8.0.1+ or MariaDB 10.6+). For older databases, consider using Redis or Predis handlers.
A convenience wrapper to get a new instance of the SyntaxProcessor library.
// Instead of: $processor = new \StarDust\Libraries\SyntaxProcessor();
$processor = syntax_processor();
$result = $processor->process($jsonRequest);- FAQ - Common questions, advanced usage, and troubleshooting
- Migration Guide - Upgrading from v0.1.x to v0.2.0+
StarDust is GDPR-Ready and provides the necessary tools to help you reach compliance:
- Right to Erasure: Use
EntriesManager::purgeDeleted($entryId)to permanently remove data. (Default behavior is "Soft Delete", which is NOT sufficient for GDPR erasure requests). - Audit Trails: Every change is versioned with a timestamp and
creator_id, providing a complete history of data processing. - Data Minimization: The schemaless nature allows you to store only exactly what is needed.
β οΈ StarDust is NOT HIPAA-compliant out of the box.
If you are storing Protected Health Information (PHI):
- Encryption at Rest: You MUST enable Transparent Data Encryption (TDE) at the database level (e.g., MySQL Enterprise Encryption or MariaDB Data-at-Rest Encryption). StarDust stores data in standard JSON columns which are readable by anyone with database access.
- Search Index Risks: The "Virtual Columns" used for indexing (
v_diagnosis_str) contain plaintext copies of your data. If your database is not encrypted, these indexes are also exposed. - Access Control: You must implement strict access controls in your application layer (e.g., using CodeIgniter Shield). StarDust does not enforce row-level security.
MIT License.