Migrations de base de données
Introduction
Les migrations sont un moyen de versionner votre base de données qui évolue souvent avec les modifications de l'application.
Les migrations sont sauvegardées dans le dossier migrations.
Ajouter une migration
Pour ajouter une migration, il faut passer par php bow avec la commande add:migration ensuite le nom de la migration (ex: create_todos_table). Bow créera un fichier du même nom précédé d'une date de création.
php bow add:migration create_todos_table
Les options --table et --create peuvent également être utilisées pour indiquer le nom de la table et indiquer si la migration créera une nouvelle table:
# Créer une nouvelle table
php bow add:migration create_todos_table --create=todos
# Modifier une table existante
php bow add:migration add_status_to_todos_table --table=todos
Structure de migration
Une classe de migration contient deux méthodes: up et rollback. La méthode up sert à ajouter de nouvelles tables, colonnes ou index à votre base de données, tandis que la méthode rollback doit inverser les opérations effectuées par la méthode up.
use Bow\Database\Migration\Migration;
use Bow\Database\Migration\Table;
class Version20190929153939CreateTodosTable extends Migration
{
/**
* Exécuter la migration
*/
public function up(): void
{
$this->create("todos", function (Table $table) {
$table->addIncrement('id');
$table->addString('title');
$table->addInteger('status', ['default' => 1]);
$table->addTimestamps();
});
}
/**
* Annuler la migration
*/
public function rollback(): void
{
$this->dropIfExists("todos");
}
}
Exécuter des migrations
Pour exécuter toutes vos migrations en attente:
php bow migration:migrate
# Raccourci
php bow migrate
Annuler les migrations
Pour annuler la dernière opération de migration:
php bow migration:rollback
Pour annuler toutes les migrations:
php bow migration:reset
Gestion des tables
Créer une table
$this->create("users", function (Table $table) {
$table->addIncrement('id');
$table->addString('name');
$table->addString('email', ['unique' => true]);
$table->addTimestamps();
});
Créer une table si elle n'existe pas
$this->createIfNotExists("users", function (Table $table) {
$table->addIncrement('id');
$table->addString('name');
$table->addTimestamps();
});
Modifier une table
$this->alter('users', function (Table $table) {
$table->addString('phone', ['nullable' => true]);
});
Renommer une table
$this->renameTable('old_table', 'new_table');
// Renommer si existe
$this->renameTableIfExists('old_table', 'new_table');
Supprimer une table
$this->drop('users');
// Supprimer si existe
$this->dropIfExists('users');
Connexion personnalisée
$this->connection('mysql_secondary')->create("logs", function (Table $table) {
$table->addIncrement('id');
$table->addText('message');
$table->addTimestamps();
});
Options de table
$this->create("users", function (Table $table) {
$table->withEngine('InnoDB');
$table->withCharset('utf8mb4');
$table->withCollation('utf8mb4_unicode_ci');
$table->addIncrement('id');
$table->addString('name');
});
SQL brut
// Exécuter du SQL brut dans la migration
$this->sql('ALTER TABLE users ADD INDEX idx_email (email)');
// Alternative
$this->addSql('CREATE INDEX idx_name ON users (name)');
Types de colonnes
Colonnes numériques
| Méthode | Description |
|---|---|
addIncrement('id') | INTEGER auto-incrémenté (clé primaire) |
addBigIncrement('id') | BIGINT auto-incrémenté (clé primaire) |
addMediumIncrement('id') | MEDIUMINT auto-incrémenté (clé primaire) |
addSmallIntegerIncrement('id') | SMALLINT auto-incrémenté (clé primaire) |
addInteger('column', $attr) | INTEGER |
addIntegerPrimary('column') | INTEGER (clé primaire) |
addBigInteger('column', $attr) | BIGINT |
addMediumInteger('column', $attr) | MEDIUMINT |
addSmallInteger('column', $attr) | SMALLINT |
addTinyInteger('column', $attr) | TINYINT |
addFloat('column', $attr) | FLOAT |
addFloatPrimary('column') | FLOAT (clé primaire) |
addDouble('column', $attr) | DOUBLE |
addDoublePrimary('column') | DOUBLE (clé primaire) |
addBoolean('column') | BOOLEAN |
Colonnes texte
| Méthode | Description |
|---|---|
addString('column', $attr) | VARCHAR (255 par défaut) |
addChar('column', $attr) | CHAR |
addText('column', $attr) | TEXT |
addLongtext('column', $attr) | LONGTEXT |
addJson('column', $attr) | JSON |
Colonnes date/heure
| Méthode | Description |
|---|---|
addDatetime('column', $attr) | DATETIME |
addDate('column', $attr) | DATE |
addTime('column', $attr) | TIME |
addTimestamp('column', $attr) | TIMESTAMP |
addYear('column', $attr) | YEAR |
addTimestamps() | Ajoute created_at et updated_at |
addSoftDelete() | Ajoute deleted_at pour soft delete |
Colonnes spéciales
| Méthode | Description |
|---|---|
addUuid('column', $attr) | UUID |
addUuidPrimary('column', $attr) | UUID (clé primaire) |
addBinary('column', $attr) | BINARY |
addBlob('column', $attr) | BLOB |
addTinyBlob('column', $attr) | TINYBLOB |
addMediumBlob('column', $attr) | MEDIUMBLOB |
addLongBlob('column', $attr) | LONGBLOB |
addIpAddress('column', $attr) | IP address |
addMacAddress('column', $attr) | MAC address |
addEnum('column', $attr) | ENUM |
addCheck('column', $attr) | CHECK constraint |
Attributs de colonne
Toutes les méthodes add* acceptent un tableau d'attributs:
$table->addString('email', [
'size' => 100, // Taille de la colonne
'nullable' => true, // Peut être NULL
'default' => 'N/A', // Valeur par défaut
'unique' => true, // Contrainte UNIQUE
'index' => true, // Ajouter un INDEX
'unsigned' => true, // Non signé (pour nombres)
'primary' => true, // Clé primaire
'increment' => true, // Auto-incrément
]);
Méthode addColumn
Toutes les méthodes helpers utilisent addColumn en interne:
$table->addColumn('price', 'decimal', [
'size' => '10,2',
'unsigned' => true,
'default' => 0.00
]);
SQL brut dans une colonne
$table->addRaw('FULLTEXT INDEX idx_content (title, body)');
Modification de colonnes
Renommer une colonne
$this->alter('users', function (Table $table) {
$table->renameColumn('name', 'full_name');
});
Modifier le type d'une colonne
Utilisez les méthodes change* correspondantes:
$this->alter('users', function (Table $table) {
$table->changeString('name', ['size' => 500]);
$table->changeInteger('status', ['default' => 0]);
});
Supprimer une colonne
$this->alter('users', function (Table $table) {
$table->dropColumn('temporary_field');
});
Contraintes
Clés étrangères
$this->create("posts", function (Table $table) {
$table->addIncrement('id');
$table->addInteger('user_id', ['unsigned' => true]);
$table->addString('title');
$table->addTimestamps();
// Définir la clé étrangère
$table->addForeign('user_id', [
'references' => 'id',
'on' => 'users',
'onDelete' => 'CASCADE',
'onUpdate' => 'CASCADE'
]);
});
Index
$this->alter('users', function (Table $table) {
$table->addIndex('email');
});
Contrainte UNIQUE
$this->alter('users', function (Table $table) {
$table->addUnique('email');
});
Exemple complet
use Bow\Database\Migration\Migration;
use Bow\Database\Migration\Table;
class Version20240101120000CreateBlogTables extends Migration
{
public function up(): void
{
// Table des catégories
$this->create("categories", function (Table $table) {
$table->addIncrement('id');
$table->addString('name');
$table->addString('slug', ['unique' => true]);
$table->addTimestamps();
});
// Table des articles
$this->create("posts", function (Table $table) {
$table->withEngine('InnoDB');
$table->withCharset('utf8mb4');
$table->addIncrement('id');
$table->addInteger('category_id', ['unsigned' => true]);
$table->addInteger('author_id', ['unsigned' => true]);
$table->addString('title');
$table->addString('slug', ['unique' => true]);
$table->addText('content');
$table->addEnum('status', ['values' => ['draft', 'published', 'archived']]);
$table->addBoolean('featured', ['default' => false]);
$table->addTimestamps();
$table->addSoftDelete();
$table->addForeign('category_id', [
'references' => 'id',
'on' => 'categories',
'onDelete' => 'CASCADE'
]);
$table->addForeign('author_id', [
'references' => 'id',
'on' => 'users',
'onDelete' => 'CASCADE'
]);
$table->addIndex('status');
});
}
public function rollback(): void
{
$this->dropIfExists("posts");
$this->dropIfExists("categories");
}
}
Il manque quelque chose ?
Si vous rencontrez des problèmes avec la documentation ou si vous avez des suggestions pour améliorer la documentation ou le projet en général, veuillez déposer une issue pour nous, ou envoyer un tweet mentionnant le compte Twitter @bowframework ou sur directement sur le github.