Les migrations sont un outil de CakePHP permettant de gérer les modifications du schéma de base de données. Elles permettent de modifier la structure de votre base de données de manière contrôlée par version, ce qui facilite le suivi, le partage et le déploiement des modifications dans différents environnements ou installations de votre application. CakePHP dispose d'outils permettant de générer des instantanés de la structure de votre base de données et des migrations entre elles. Étant donné que les migrations peuvent également exécuter des instructions de langage de requête sur votre base de données, elles peuvent également être utilisées pour modifier les données. Willow CMS a évolué au fil de plusieurs versions depuis sa mise en ligne sur ce site, certaines de ces versions nécessitant des modifications du schéma et des données de la base de données. Dans cet article, je vais vous présenter quelques exemples pratiques de la manière dont j'ai géré cela.
Créer des migrations – un exemple de base
Dans CakePHP, les migrations sont créées à l'aide debake migration
commande. Cette commande génère une classe PHP qui définit les instructions SQL pour la migration. Par exemple, pour créer une migration pour ajouter un nouveauemail
colonne à lausers
table, vous pouvez exécuter :
bin/cake bake migration AddEmailToUsers email:string
Cette commande crée un fichier de migration nomméAddEmailToUsers
dans tonconfig/Migrations
répertoire avec une méthode appeléechange()
qui définit le changement nécessaire. Vous pouvez utiliser les méthodes de CakePHPTable
classe pour définir les modifications du schéma. Voici un exemple de méthode change() pour ajouter laemail
colonne:
use Migrations\AbstractMigration;
class AddEmailToUsers extends AbstractMigration
{
/**
* Change Method.
*
* More information on this method is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
* @return void
*/
public function change(): void
{
$table = $this->table('users');
$table->addColumn('email', 'string', [
'default' => null,
'limit' => 255,
'null' => false,
]);
$table->update();
}
}
Les migrations peuvent également être annulées à l'aide de labin/cake migrations rollback
commande. Lorsque vous exécutez cette commande, le dernier fichier de migration enregistré comme exécuté sera annulé. Votre code pour effectuer les actions d'annulation doit être ajouté au fichier de migration via lepublic function down(): void {}
méthode.
Exécution des migrations
Une fois que vous avez défini les modifications du schéma, appliquez-les à l'aide de l'migrate
commande:
bin/cake migrations migrate
Cette commande exécute toutes les migrations en attente et met à jour votre schéma de base de données. L'avantage est que les migrations sont marquées comme terminées une fois exécutées via un enregistrement dans lephinxlog
table dans la base de données, ce qui signifie qu'il est sûr d'utiliser les migrations de manière automatisée, comme dans le script setup_dev_env.sh de l'environnement de développement Willow CMS ou dans le script de démarrage d'une installation de production .
Ici vous pouvez voir lephinxlog
tableau sur mon environnement de développement suite à une exécution du script d'installation et une exécution des 4 migrations depuis la V1.
Entrons dans chacune de ces migrations…
Migrations – Quelques exemples concrets
V1 – Créer ou supprimer les tables de base
Jetez un œil à la source de la v1, la première migration pour Willow qui, bien que longue, est en fait super basique.up
méthode a utilise letable
méthodesaddColumn
etcreate
pour créer chaque table de la base de données. La seule différence entre ce fichier et le premier exemple est que les méthodes sont chaînées.
public function up(): void
{
$this->table('aiprompts', ['id' => false, 'primary_key' => ['id']])
->addColumn('id', 'uuid', [
'default' => null,
'limit' => null,
'null' => false,
])
->addColumn('task_type', 'string', [
'default' => null,
'limit' => 50,
'null' => false,
])
// more addColumns......
->create();
// more calls to $this->table......
La méthode down annule ces instructions avec ledrop
etsave
méthodes de table.
public function down(): void
{
$this->table('aiprompts')->drop()->save();
// more calls to $this->table......
Il n'y a pas de données dans cette migration, elle crée/supprime simplement les tables requises par Willow CMS. Même pour l'application CakePHP la plus basique, vous aurez besoin d'une migration comme celle-ci si vous avez l'intention d'exécuter des tests PHPUnit. CakePHP exécutera vos migrations à chaque fois que vous exécuterez les tests afin que vos données de test (fixtures) puissent être chargées.
ChangeExpiresAtToDatetime – Modifier le type de données d'une colonne
Jetez un œil à la source de ChangeExpiresAtToDatetime qui est une autre méthode simple, cette fois-ci en modifiant le type de données d'une colonne dans leblocked_ips
tableau.
La méthode up utilise lechangeColumn
méthode pour spécifier le changement àdatetime
pour leexpires_at
colonne.
public function up(): void
{
$this->table('blocked_ips')
->changeColumn('expires_at', 'datetime', [
'default' => null,
'limit' => null,
'null' => true,
])
->update();
}
La méthode down le ramène à nouveau àdate
.
public function down(): void
{
$this->table('blocked_ips')
->changeColumn('expires_at', 'date', [
'default' => null,
'length' => null,
'null' => true,
])
->update();
}
InsertSettings – Charger les données par défaut dans la table des paramètres
Jetez un œil à la source de InsertSettings qui utilise uniquement leinsert
etsave
méthodes de table pour charger les paramètres par défaut de Willow CMS dans lesettings
tableau.
public function change(): void
{
$this->table('settings')
->insert([
'id' => Text::uuid(),
'ordering' => 7,
'category' => 'AI',
'key_name' => 'articleSummaries',
'value' => '0',
'value_type' => 'bool',
'value_obscure' => false,
'description' => 'Automatically generate concise and compelling summaries for your articles and pages. When enabled, the system will analyze the content and create a brief synopsis that captures the key points. These summaries will appear on the article index page and other areas where a short overview is preferable to displaying the full text.',
'data' => null,
'column_width' => 2,
])
// More insert statements below......
En fait, je n'ai pas pris la peine d'en faire undown
méthode pour gérer les retours en arrière avec cette migration, mais je devrais peut-être en ajouter une. Je vais mettre un ticket sur le tableau des tickets de Willow CMS .
AddRobotsTemplate – Charger plus de données par défaut
Jetez un œil à la source de AddRobotsTemplate.php qui ajoute un autre paramètre. Vous vous demandez peut-être pourquoi ne pas simplement l'insérer dans la migration précédente d'InsertSettings ? Eh bien, d'une part, je n'avais pas besoin de ce paramètre et d'autre part, j'avais déjà déployé la dernière version de Willow CMS en production - ce qui inclut l'exécution de la migration précédente sur la base de données de production dans le cadre du déploiement automatisé qui crée une nouvelle image Docker et exécute à son tour les commandes de migration CakePHP pour mettre à niveau la base de données .
Voici donc un bel exemple de la manière dont CakePHP aura marqué la migration précédente comme exécutée dans lephinxlog
table afin qu'elle ne soit plus jamais exécutée (à moins d'être annulée) et donc cette nouvelle migration est nécessaire. Il y a quelques bizarreries dans ce fichier en ce qui concerne les données par défaut pour le paramètre qui est un morceau de texte, le plus facilement stocké à l'aide de marqueurs EOT pour le stockage dans un$robotsTemplate
variable à laquelle il est ensuite fait référence dans les appels enchaînés pour charger le paramètre. Les migrations ne sont que du code PHP, vous pouvez donc faire tout type de code et de logique supplémentaires autour des appels aux méthodes de table.
public function change(): void
{
$robotsTemplate = <<table('settings')
->insert([
'id' => Text::uuid(),
'ordering' => 4,
'category' => 'SEO',
'key_name' => 'robots',
'value' => $robotsTemplate,
'value_type' => 'textarea',
'value_obscure' => false,
'description' => 'The template for robots.txt file. Use {LANG} as a placeholder for the language code. This template will be used to generate the robots.txt file content.',
'data' => null,
'column_width' => 4,
])
->save();
}
}
Newslugstable – Modification d'un schéma de table et migration des données
Jetez un œil à la source de Newslugstable où j'utilise le$this->execute
méthode pour exécuter des instructions SQL sur la base de données dans le cadre de la migration.slugs
La table de la base de données de production de ce site contiendra déjà des données qui doivent être migrées vers la nouvelle structure de table. Pour ce faire, la méthode up suit les étapes suivantes :
- Utiliser
addColumn
méthode table pour créer de nouvelles colonnes de table, avec des valeurs par défaut assouplies (autoriser les valeurs nulles) - Utiliser
execute
méthode pour migrer les données des anciennes colonnes vers les nouvelles colonnes à l'aide d'une instruction SQL - Utiliser
changeColumn
méthode de table pour modifier les nouvelles colonnes afin de ne plus autoriser les valeurs nulles - Utilisez le
addIndex
méthode de table pour créer des index pour la nouvelle structure de colonne - Utilisez le
removeColumn
méthode de table pour supprimer les anciennes colonnes qui ne sont plus nécessaires - utiliser le
execute
méthode et un peu de code pour créer de nouvelles données dans la table des slugs (puisque les balises utilisent également un nouveau SlugBehavior et doivent avoir des enregistrements dans cette table)
public function up(): void
{
// First, add the new 'model' column
$this->table('slugs')
->addColumn('model', 'string', [
'limit' => 20,
'null' => true, // Temporarily allow null
'after' => 'id',
])
->update();
// Add the new foreign_key column
$this->table('slugs')
->addColumn('foreign_key', 'uuid', [
'null' => true, // Temporarily allow null
'after' => 'model',
])
->update();
// Update existing records to set model and foreign_key
$this->execute("UPDATE slugs SET model = 'Articles', foreign_key = article_id");
// Now make the new columns required
$this->table('slugs')
->changeColumn('model', 'string', [
'limit' => 20,
'null' => false,
])
->changeColumn('foreign_key', 'uuid', [
'null' => false,
])
->update();
// Add indexes for the new structure
$this->table('slugs')
->addIndex(['model', 'slug'], [
'name' => 'idx_slugs_lookup',
])
->addIndex(['model', 'foreign_key'], [
'name' => 'idx_slugs_foreign',
])
->update();
// Remove the modified column as it's no longer needed
$this->table('slugs')
->removeColumn('modified')
->update();
// Finally, remove the old article_id column
$this->table('slugs')
->removeColumn('article_id')
->update();
// Migrate existing tag slugs to the slugs table
$connection = $this->getAdapter()->getConnection();
$tags = $connection->execute("
SELECT id, slug, DATE_FORMAT(created, '%Y-%m-%d %H:%i:%s') as created
FROM tags
WHERE slug IS NOT NULL
AND slug != ''
")->fetchAll('assoc');
foreach ($tags as $tag) {
$this->table('slugs')
->insert([
'id' => Text::uuid(),
'foreign_key' => $tag['id'],
'model' => 'Tags',
'slug' => $tag['slug'],
'created' => $tag['created'],
])
->save();
}
}
La méthode down annule ces modifications. Pour être complet, elle devrait probablement créer les index pour les colonnes que nous recréons, mais comme je n'ai pas l'intention d'annuler définitivement cette migration, je n'ai pas pris la peine de le faire.
public function down(): void
{
// Add back the article_id column
$this->table('slugs')
->addColumn('article_id', 'uuid', [
'null' => true,
'after' => 'id',
])
->update();
// Restore data from foreign_key to article_id where model is 'Articles'
$this->execute("UPDATE slugs SET article_id = foreign_key WHERE model = 'Articles'");
// Make article_id required again
$this->table('slugs')
->changeColumn('article_id', 'uuid', [
'null' => false,
])
->update();
// Add back the modified column
$this->table('slugs')
->addColumn('modified', 'datetime', [
'null' => true,
])
->update();
// Remove the new polymorphic columns and indexes
$this->table('slugs')
->removeIndex(['model', 'slug'])
->removeIndex(['model', 'foreign_key'])
->removeColumn('model')
->removeColumn('foreign_key')
->update();
}
Ici vous pouvez voir la structure de la table des slugs avant la migration…
… et après…
Pour conclure
Nous avons abordé les bases des migrations dans CakePHP, de la création et de l'exécution de migrations simples à des scénarios plus complexes comme la modification des types de colonnes et la migration de données existantes. Les principaux points à retenir sont les suivants :
- Structure et contrôle des versions : les migrations mettent de l'ordre dans les modifications de votre base de données, les rendant traçables et réversibles.
- Déploiement simplifié : utilisez les migrations pour déployer en toute confiance les mises à jour de schéma dans différents environnements, du développement à la production.
- Automatisation : intégrez les migrations dans vos scripts de déploiement pour des mises à jour de base de données transparentes et automatisées.
À la prochaine, bonne pâtisserie !