Model Usage
Create and save a record
use Modules\Blog\Models\Post;
$post = model(Post::class)
->create()
->fill([
'title' => 'Quantum 3.0',
'slug' => 'quantum-3',
'body' => 'Package notes',
'author_id' => 7,
]);
$post->save();
What happens here:
create()starts a new record context and clears current attributes.fill()applies only allowed fields.save()copies attributes into the ORM.- If the adapter generated a primary key, the model receives it back after save.
- If
HasTimestampsis used,updated_atis refreshed on every save, whilecreated_atis filled on the first save unless you already set it.
Query records with a builder
$posts = model(Post::class)
->criteria('status', '=', 'published')
->orderBy('created_at', 'desc')
->limit(10)
->get();
This returns a ModelCollection, not a plain array.
Useful collection methods:
all()count()first()last()isEmpty()
Use a fresh model instance for unrelated queries to avoid carrying accidental query state.
Prefer a fresh model for unrelated queries
DbModel query methods mutate the current model instance.
That means this pattern is safer:
$published = model(Post::class)
->criteria('status', '=', 'published')
->get();
$drafts = model(Post::class)
->criteria('status', '=', 'draft')
->get();
Instead of continuing to reuse one already-filtered model instance across unrelated queries.
Define and use joins through relations()
use Quantum\Database\Enums\Relation;
class Post extends DbModel
{
public function relations(): array
{
return [
User::class => [
'type' => Relation::BELONGS_TO,
'foreign_key' => 'author_id',
'local_key' => 'id',
],
];
}
}
Then query with:
$posts = model(Post::class)
->joinTo(model(User::class))
->select('posts.*')
->get();
Keep relation definitions explicit. Include type, foreign_key, and local_key for each joined model.
Paginate model queries
$paginator = model(Post::class)
->criteria('status', '=', 'published')
->paginate(15, 2);
$items = $paginator->data();
$total = $paginator->total();
The paginator clones the model and ORM for the total-count query so total calculation does not mutate the live query before page data is fetched.
Use soft deletes
$post = model(Post::class)->findOne(10);
$post?->delete();
$active = model(Post::class)->get();
$all = model(Post::class)->withTrashed()->get();
$trashed = model(Post::class)->onlyTrashed()->get();
Behavior to remember:
delete()becomes a soft delete when the trait is usedforceDelete()performs the real deleterestore()clears the deleted-at valuewithTrashed()changes later reads on that same model instance- Reusing one soft-delete builder across several reads keeps the accumulated scope on that builder, so a fresh model instance is the cleanest start for each query
Use plain models for non-database payloads
use Quantum\Model\Model;
class PublishPayload extends Model
{
protected array $fillable = ['title', 'slug'];
public array $hidden = ['slug'];
}
$payload = (new PublishPayload())->fill([
'title' => 'Hello',
'slug' => 'hello',
]);
$data = $payload->asArray();
Here asArray() returns only visible fields, while slug is still stored on the object.
Common caveats
new Post()does not attach an ORM instance by itself.fill()rejects unknown fields instead of ignoring them.- Direct assignment bypasses
fillablerules. create()clears existing attributes.- Soft-delete inclusion flags stay on the current model instance until you discard it.
- A fresh builder is the simplest way to avoid carrying earlier soft-delete scope into later reads.