Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Orm Laravel Package

mindy/orm

Mindy ORM is a Laravel/PHP ORM package for modeling database data and relationships in PHP applications. It offers an alternative ORM layer for common model and query workflows, but has minimal adoption and limited community feedback.

View on GitHub
Deep Wiki
Context7

Getting Started

mindy/orm is the model layer of the MindyPHP micro-framework, not an Eloquent plugin. It is a standalone ORM that talks to mindy/db and mindy/query-builder. Treat adopting it in a Laravel app as "adding a second ORM for one bounded context", not "swapping Eloquent".

4.0.4 (latest) changed the version story: the line is now 4.x, not the old 1.0 tag, and it declares mindy/query-builder:>=4.0.4. Pin both together — a stale query-builder is the most likely cause of "class not found" or missing-operator errors after upgrade.

  1. Install and align the pair.
    composer require mindy/orm:^4.0 mindy/query-builder:^4.0.4
    
    Let Composer resolve, then check composer show mindy/query-builder actually landed ≥4.0.4. The ORM's new JSON field support depends on the newer builder; mismatched minors can install cleanly and fail at runtime.
  2. Read in this order: src/Model.php → src/BaseManager.php / src/QuerySet.php → src/Fields/. The Fields/* classes are the actual API surface (type mapping, casting, validation, and often DDL); Model is mostly glue and magic-property resolution. For 4.0.4, read the JSON field class and its QuerySet/builder counterpart first — that is the only new behaviour.
  3. Wire the connection yourself. There is no service provider and no facade. Create one in app/Providers/MindyOrmServiceProvider.php, build the Mindy\Query\Connection, and register it where Model/BaseManager expects it (check the exact static/getConnection() in your checkout before assuming).
  4. First use case: a small read-heavy slice over an existing schema — a reporting table, a legacy MySQL database, or a data set where you want getFields() to double as a validation contract. With 4.0.4, a settings/attributes JSON column is now a viable first slice too.
use Mindy\Orm\Model;
use Mindy\Orm\Fields\IntField;
use Mindy\Orm\Fields\CharField;
use Mindy\Orm\Fields\JsonField;
use Mindy\Orm\Fields\ForeignField;

class Article extends Model
{
    public static function tableName()
    {
        return 'articles';
    }

    public static function getFields()
    {
        return [
            'id'     => ['class' => IntField::class, 'primary' => true, 'autoComplete' => true],
            'title'  => ['class' => CharField::class, 'required' => true],
            'meta'   => ['class' => JsonField::class],   // new in 4.0.4 — confirm class name in src/Fields/
            'author' => ['class' => ForeignField::class, 'modelClass' => Author::class],
        ];
    }
}

$articles = Article::objects()
    ->filter(['title__like' => '%orm%'])
    ->order(['-id'])
    ->limit(20)
    ->all();

Implementation Patterns

  • Model-as-schema-contract. Fields are declared in getFields() as a config array with a class key. This is the single source of truth for both persistence and input validation (required, validator, etc.) — Mindy's form layer consumes the same config.
  • JSON attributes (4.0.4). Declare a JsonField and let the field class do the encode/decode; do not hand-json_encode() into a TextField anymore, or you will have two representations of the same column in one codebase. Keep JSON blobs to genuinely schemaless data — flags, per-tenant settings, third-party payloads — and keep anything you filter or join on as a real column.
  • QuerySet chaining. Model::objects() returns a fresh QuerySet. Compose filter(), exclude(), order(), limit(), offset(), then a terminal all(), count(), get(). Operators are suffix-style (title__like, id__in); verify accepted suffixes in QuerySet before you rely on them.
  • Relations via accessor methods. Declare getAuthor(), getComments() returning $this->belongsTo(...)/$this->hasMany(...). The base model resolves these as virtual properties. Map mentally to Eloquent's belongsTo/hasMany, but do not assume with() exists — check QuerySet first.
  • Repository seam. Wrap every model in a small repository class (ArticleRepository::recent(int $n)) rather than scattering objects()->filter() through controllers. This is also your escape hatch if you later migrate that context to Eloquent.
  • No eager-load contract? Batch by IDs. If eager loading isn't available, do Author::objects()->filter(['id__in' => $ids])->all() and hydrate in PHP. Same N+1 discipline as Eloquent, just manual.
  • Transactions. Pull the connection (from the model or your container binding) and use beginTransaction() / commit() / rollBack() explicitly around multi-model writes. There is no DB::transaction() helper.
  • Manager subclasses for scopes. Subclass Mindy\Orm\Manager and point the model at it to get named scopes (Article::objects()->published()), mirroring Eloquent local scopes.

Gotchas and Tips

  • Upgrade to 4.0.4 as a pair, and read the diff. The changelog is two lines (JSON field support, query-builder >=4.0.4), so the JSON path is the thing to review in source: the field class, its cast on __get, and how QuerySet builds a JSON where-clause. Nothing else is documented as changed, which is not the same as nothing having changed in a major bump.
  • JSON querying is where the release is thinnest. Having a JSON field type does not guarantee a JSON path operator (meta->key, meta__key, JSON_EXTRACT) in QuerySet. Check the builder before designing screens around filtering inside JSON; if it's absent, filter in PHP over a bounded set, or promote hot keys to columns.
  • Storage type matters. Confirm whether the field writes to a native JSON column or a TEXT/LONGTEXT. Native JSON gives you MySQL-side validation and indexable generated columns; text does not. Own the column type in your Laravel migration either way.
  • Round-trip and empty-state behaviour. Assert what comes back for null, {}, [], and a scalar. Encoders that turn [] into {} (or vice versa) will silently break consumers, and ->filter(['meta__in' => ...]) against a JSON column is a different query than against text.
  • Bus factor is still the headline. Still a micro-framework component with a tiny dependent base. A 4.0.4 tag shows the package isn't frozen, but a two-line changelog is not a maintenance guarantee. If it becomes load-bearing, vendor the source and pin an exact commit; assume no upstream security patching.
  • PHP 8.x deprecations. The codebase has 2018-era ancestry; the 4.x line may have modernised it, but verify in your checkout. Watch for dynamic-property notices from magic __get/__set — declare explicit properties on your subclasses if so.
  • No migrations. Field classes may expose a DDL hook (getSql() / getDbType()); inspect before writing a schema syncer. With JSON columns this matters more, since Laravel's json() column type and the field's assumed DDL can disagree. Pragmatic path: own the schema in Laravel migrations and let Mindy read it.
  • Connection wiring is manual and easy to get wrong. If each model resolves its own connection you will exhaust DB connections under Octane/queues. Bind one shared Connection as a singleton and inject it once.
  • Naming conventions are opinionated. Table names, foreign-key columns, and pivot table names are derived. Confirm in QuerySet/Manager before designing the schema — retrofitting column names onto a convention-driven ORM is miserable.
  • Debugging SQL. Find the getSql()/toSql() equivalent on QuerySet (or attach a logger to the connection) and dump it. This is now your primary tool for verifying the new JSON clauses — composite __ suffixes are opaque when they silently return empty sets.
  • Composite keys. Verify support explicitly. If primary is assumed single-column, don't model join-ish tables on it — use a surrogate id.
  • Interop is nil. Mindy models don't implement Illuminate\Contracts anything: no Model::find(), no policies, no API resources, no factories, no route() model binding. JSON columns don't change this — expose them through DTOs, not by handing Mindy models to JsonResource.
  • Extension points worth knowing. Custom Field subclasses (type cast + validation + DDL in one place), custom Manager subclasses (scopes), and TreeModel for adjacency-list data (categories, menus). The new JsonField is a working example of the first — copy its shape for any bespoke cast you need.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect