Skip to content
Greg Bowler edited this page Apr 2, 2026 · 17 revisions

WebEngine uses phpgt/database as its database layer, so the ideas here are the WebEngine way of working with queries and migrations.

If you want the lower-level component details, the full Database docs live at https://www.php.gt/docs/database/.

What WebEngine gives us

In a plain phpgt/database project we construct Settings, instantiate Database, and call the migrator ourselves.

In WebEngine, much of that wiring is already handled:

  • configuration is read from config.ini
  • query files live in the project query/ directory
  • the database service is available in page logic
  • migrations are exposed through gt migrate

That means we can focus mainly on query organisation and application logic.

Configuring the connection

Database settings belong in the [database] section of config.ini.

SQLite example:

[database]
driver=sqlite
schema=app.sqlite

MySQL example:

[database]
driver=mysql
host=localhost
schema=app_db
port=3306
username=app_user
password=app_pass

Useful keys:

  • driver
  • schema
  • host
  • port
  • username
  • password
  • query_path
  • migration_path
  • migration_table
  • dev_migration_path
  • dev_migration_table

For the full component-level explanation of connection settings, see https://www.php.gt/docs/database/configuration-and-connections/.

Writing queries

Query files normally live in query/.

For example, if we add:

query/user/getById.sql

we can call it from page logic like this:

public function go():void {
	$row = $this->database->fetch("user/getById", 42);
}

This keeps SQL out of page logic and makes the query easy to find on disk.

For more about collections, naming and PHP query classes, see https://www.php.gt/docs/database/query-collections/.

Binding values

The same two binding styles are available in WebEngine:

  • positional placeholders with ?
  • named placeholders with :name

Example:

$row = $this->database->fetch("user/getByEmail", [
	"email" => "dev@example.com",
	"isActive" => true,
]);

query/user/getByEmail.sql:

select
	id,
	email,
	name
	
from
	user
	
where
	email = :email
and
	isActive = :isActive
	
limit 1

For the full rules around special and dynamic bindings, see https://www.php.gt/docs/database/parameter-binding/.

Working with results

The database service returns the same Row and ResultSet objects as the standalone package.

That means we can use helpers such as:

  • fetch()
  • fetchAll()
  • fetchString()
  • fetchInt()
  • fetchDateTime()

For a full explanation, see:

Migrations

WebEngine exposes database migrations through gt migrate.

The default migration directory is:

query/_migration/

And branch-local dev migrations can live in:

query/_migration/dev/

Common commands:

gt migrate
gt migrate --force
gt migrate --reset
gt migrate --dev
gt migrate --dev-merge

This gives us a clean workflow:

  • use gt migrate for normal schema updates
  • use gt migrate --dev while a feature branch is still changing
  • use gt migrate --dev-merge when the branch-local migrations are ready to become canonical

For the full migration rules and integrity checks, see https://www.php.gt/docs/database/database-migrations/.

Tip

If you are new to the framework, keep the WebEngine view simple: write queries in query/, call them from page logic, and let gt migrate manage schema changes. The lower-level Database docs are there when you need more detail.


Next, move on to Page logic to see where this database service fits into the wider request flow.

Clone this wiki locally