Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

6.7KB

Database Skill

Purpose

Use this skill for PDO, repositories, SQL, migrations, transactions, database configuration, and persistence rules.


Database Stack

Preferred database access:

  • PDO
  • Repositories or data access classes
  • Optional SQLite, MySQL, or SQL Server through PDO
  • Prepared statements for all untrusted values

Database Class API

Core\Database wraps PDO and exposes these methods:

Method Returns Description
query(string $sql, array $params = []) array Runs a SELECT and returns all rows as associative arrays
first(string $sql, array $params = []) ?array Runs a SELECT and returns the first row, or null
execute(string $sql, array $params = []) bool Runs INSERT / UPDATE / DELETE
lastInsertId() string Returns the last auto-increment ID as a string — cast to int for integer PKs
transaction(callable $fn) mixed Runs $fn($db) inside a transaction; commits on success, rolls back and rethrows on failure
pdo() PDO Returns the raw PDO instance for advanced use

lastInsertId() is only meaningful immediately after an execute() INSERT on the same connection. Calling it at any other point returns "0".

Typical INSERT + ID retrieval in a repository:

public function create(Employee $employee): int
{
    $this->database->execute(
        'INSERT INTO employees (first_name, email) VALUES (:first_name, :email)',
        ['first_name' => $employee->firstName, 'email' => $employee->email]
    );

    return (int) $this->database->lastInsertId();
}

Database Access Rules

Use PDO or a well-maintained database abstraction layer/ORM.

Never concatenate untrusted input into SQL.

Bad:

$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];

Good:

$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_ASSOC);

Rules:

  • Use prepared statements and bound parameters.
  • Validate input before using it in writes.
  • Keep SQL out of templates.
  • Keep database access out of controllers where practical.
  • Use transactions when multiple writes must succeed or fail together.
  • Do not rely only on client-side validation.
  • Do not expose raw database errors to users.

Transactions

Use Database::transaction() when multiple writes must succeed or fail together. It begins a transaction, runs the callback, commits on success, and rolls back and rethrows on any Throwable.

$database->transaction(function (Database $db) use ($order): void {
    $orders->create($order);
    $auditLog->record('order.created', $order->id());
});

Do not call $database->pdo()->beginTransaction() directly for transaction management — use transaction() instead. Reserve pdo() for driver-specific features that have no Database API equivalent.


Repository Rules

  • Put persistence logic in repositories or data access classes.
  • Keep repositories focused around a table, aggregate, or use case.
  • Do not let repositories render HTML.
  • Do not let repositories read directly from $_GET, $_POST, or other superglobals.
  • Return domain objects, entities, DTOs, arrays, or ViewModels according to existing project convention.
  • Prefer explicit methods such as findById, findAllActive, and save over generic magic calls.

Migration System

The migration system is made up of three files:

  • core/Migration.php — abstract base class all migration files extend
  • core/MigrationManager.php — runs, rolls back, and tracks applied migrations
  • core/helpers.php — provides the migration_manager() helper that wires a MigrationManager to the app database and the database/migrations/ path

The CLI entry point is scripts/migrate.php. Run it from the project root:

Command Description
php scripts/migrate.php up Run all pending migrations
php scripts/migrate.php down [steps] Roll back the last N migrations (default: 1)
php scripts/migrate.php status Show which migrations have run and when
php scripts/migrate.php make <name> Scaffold a new timestamped migration file
php scripts/migrate.php fresh Roll back everything and re-run from scratch
php scripts/migrate.php fresh --seed Same as fresh, then run the employee seed

Migration files live in database/migrations/ and are named YYYYMMDD_HHMMSS_<slug>.php.

Each file must return a Migration instance:

<?php

declare(strict_types=1);

use Core\Database;
use Core\Migration;

return new class extends Migration
{
    public function up(Database $database): void
    {
        $database->execute('CREATE TABLE ...');
    }

    public function down(Database $database): void
    {
        $database->execute('DROP TABLE IF EXISTS ...');
    }
};

Migration Rules

  • Keep migrations small and reversible when practical.
  • Document destructive migrations clearly.
  • Do not mix schema changes with unrelated feature logic.
  • Use project migration conventions before inventing new ones.
  • Support SQLite/MySQL/SQL Server differences explicitly when the project targets multiple engines.
  • Do not use database-specific SQL in framework or migration code. INSERT OR IGNORE (SQLite) and INSERT IGNORE (MySQL) are not portable — use a check-then-insert pattern instead.
  • Migration records in the migrations table are permanent. A row means “this migration ran against this database.” Deleting a migration file does not remove the record and does not allow the migration to be re-run. To intentionally re-run a migration, delete its row from the migrations table manually — this makes the action explicit.
  • To reset completely, use php scripts/migrate.php fresh, which rolls back all migrations in reverse order and re-runs them from scratch.

SQL Safety Checklist

Before completing database work, verify:

  • SQL uses prepared statements or a safe query builder.
  • Untrusted values are never concatenated into SQL.
  • Writes validate input server-side.
  • Multi-step writes use transactions where needed.
  • Database errors are logged safely and not displayed raw to users.
  • Schema changes are documented.
  • Tests or verification steps cover the changed behavior.

Database Configuration

  • Keep database credentials out of source control.
  • Prefer environment variables or ignored local config files for secrets.
  • Provide safe examples such as .env.example.
  • Do not commit production DSNs, passwords, tokens, or private keys.

Example .env.example:

APP_ENV=local
APP_DEBUG=true
DATABASE_URL=mysql://user:password@localhost:3306/app

Powered by TurnKey Linux.