Repository navigation
Query Builder Getting Rows
A query runs when you ask for its rows: get() for every
row, first() and find() for one, value() and pluck() for a value or a column, count() and exists() for a
number or a yes, chunk() for many rows in parts, paginate() for a page. What you get is arrays from
Database::table() and models from a model class. Since 2.19.0.
use Caligrafy\Database;
$rows = Database::table('books')->where('author_id', 2)->get(); // arrays
$book = Book::find(3); // a Book, or null
$count = Book::where('author_id', 2)->count(); // 2/caligrafy-quill:new-capability get_book books:read read-only: one book by its id, not found when there is none
Claude reads the row with Book::find($input['id']) (or with(...)->find(...)) and throws
ServiceError::notFound(...) when it is null.
Check: php caligrafer.php call get_book id=999 answers not_found, not an empty data.
-
Arrays or models.
Database::table('books')gives arrays, one per row.Book::query(), or a call of the query builder on the model class (Book::where(...),Book::find(3),Book::with(...),Book::first(),Book::paginate(20)...), givesBookmodels, on the model's table: the class name in lower case pluss(Book->books), unless the class declaresprotected static $tableName = 'book_reviews';. -
Switch between them.
arrays()keeps a model query's rows as arrays;models(Book::class)turns aDatabase::table()query's rows into models:Book::query()->select('id', 'title')->arrays()->get(); // [['id' => 1, 'title' => 'A Wizard of Earthsea'], ...] Database::table('books')->models(Book::class)->get(); // Book models
-
One row or nothing. In a capability, a missing row is a refusal:
$book = Book::find($input['id']); if (!$book) { throw ServiceError::notFound('There is no book #' . $input['id']); } return $book;
| To get | Call | Returns |
|---|---|---|
| every row | get() |
a list (empty when none) |
| the first row | first() |
one row, or null
|
| one row by id | find(3), find($value, 'isbn') |
one row, or null
|
| one value | value('title') |
'Kindred', or null
|
| one column | pluck('title'), pluck('title', 'id') |
['A Wizard of Earthsea', ...], [1 => 'A Wizard of Earthsea', ...]
|
| how many | count() |
a number (order and limit ignored) |
| whether any | exists() |
true or false
|
| a page | paginate(20, $page) |
['data', 'total', 'page', 'per_page', 'pages'] |
| many rows, in parts | chunk(500, function ($rows, $part) { ... }) |
true, or false when the function returned false
|
-
pluck()also takes a count ofwithCount()or a relation ofwith():Author::withCount('books')->pluck('books_count', 'name')gives['Ursula K. Le Guin' => 2, ...]. -
chunk()reads inidorder unless the query has an order; the function gets the rows and the number of the part (from 1), and returningfalsestops. -
count(),exists(),value()andpluck()of a column run one small query, without the relations and without models.
| Query | Rows |
|---|---|
Database::table('books') |
arrays |
Book::query(), Book::where(...), Book::find(3)
|
Book models, related rows (with()) as arrays |
->arrays() |
arrays (undoes the models) |
->models(Book::class) |
instances of a class that extends Caligrafy\Model (another class is refused) |
A model has every property its class declares: with select(), the other columns are null, and a capability returns
them, nulls included. For only the columns you selected, add arrays().
-
Book::find()with afind()of your own in the model (G122):Book::find(1)then fails with "Non-static method cannot be called statically". Leave it out, or writeBook::query()->find(1). -
A table the class name does not give (G123):
BookReviewreadsbookreviews; declareprotected static $tableName = 'book_reviews';. -
Selected columns with models (G138): add
arrays(). -
A query inside a
foreachover rows: load the related rows withwith()instead.
Caligrafy: Describe it. Approve it. Ship it. Your idea turned into a complete app: every feature built once, for web pages, Vue apps, a REST API and AI agents, and online.
Why Caligrafy: the benefits, the measurements, and what it is not yet
-
Quickstart with Claude: describe it, approve it, ship it
or the Quickstart: the same three steps by hand, in 5 minutes
- How Caligrafy works: the ideas in 5 minutes
- Your first capability: build a feature, page included
-
Capabilities in depth
-
#[Capability]: every option - Scheduled jobs: capabilities on a timer
- Notifications: web push and email
- Background jobs: capabilities run later
- Migrations: database changes, in order
- The cache
-
Testing your app
- Dependency injection
- Validation rules
-
Web pages
- Views and page components
- Design styles: another look, one setting
- Declarative routes: every request, in one line
-
Route::page·Route::form·Route::auth·Route::webhook·Route::download·Route::redirect·Route::action·Route::snap· capability routes - How to export a list as CSV
- Controllers: moving one to a declared route · how to, step by step
- Forms and helpers
-
Vue apps
-
Mobile and tablet
-
REST API
-
AI agents
-
Snap Cards and the assistant
- Payments: Stripe and crypto
- Face detection and face login
- OpenAI: chat and images · Assistants
- Open models: Llama, Mistral...
- Chatbots with Watson
- Machine learning in the browser
- Metadata and social cards · search and analytics
-
Putting it online: the production image, the database at every start
-
API reference: every class and method
-
Routing ·
Route·AuthRoutes· pages ·Controller· capabilities -
Data ·
Model·Query· sign-in · jobs ·Notify· payments · AI -
Interfaces · Snap ·
Snap\Cards·Snap\Conversation· testing · tooling · helper functions
-
Routing ·
-
Caligrafer: every command
- Project · build · data · agents · jobs and tests · maintenance · deploy
- Installation, configuration and Caligrafer
- Routing · Request · Controllers · Views
- Vue: flow of information · routes · validations
- Security and upgrading
- Release notes: 2.26 · 2.25 · 2.24 · 2.23 · 2.22 · 2.21 · 2.20 · 2.19 · 2.18 · 2.17 · 2.16 · 2.15 · 2.14 · 2.13 · 2.12 · 2.11 · 2.10 · 2.9 · 2.8 · 2.7 · 2.6 · 2.5 · 2.4 · 2.3 · 2.1 · October 2026 · changelog
- Testing Caligrafy (contributors)