Migrações
Migrações são mutações documentadas de banco de dados, criadas ao longo do ciclo de vida de desenvolvimento do seu aplicativo, que você pode reverter ou executar novamente a qualquer momento.
As migrações facilitam o trabalho em equipe, permitindo que as alterações no esquema do banco de dados de um desenvolvedor sejam facilmente rastreadas e aplicadas por outros desenvolvedores em sua organização.
Criando migrações
OBSERVAÇÃO
Para usar migrações, o Provedor de migrações deve primeiro ser registrado dentro do array aceProviders
do arquivo start/app.js
.
Vamos criar uma tabela users com a ajuda de migrações.
Primeiro, chame o comando adonis make:migration
para criar um arquivo de esquema:
adonis make:migration users
Quando solicitado, escolha a opção Create table
e pressione kbd:[Enter]:
# .Output
✔ create database/migrations/1502691651527_users_schema.js
Seu novo arquivo de esquema (prefixado com o timestamp atual) é criado no diretório database/migrations
, pronto para modificar conforme necessário:
// .database/migrations/...users_schema.js
'use strict'
const Schema = use('Schema')
class UsersSchema extends Schema {
up () {
this.create('users', (table) => {
table.increments()
table.timestamps()
})
}
down () {
this.drop('users')
}
}
module.exports = UsersSchema
Arquivos de esquema
Um arquivo de esquema requer dois métodos: up
e down
.
up()
O método up
é usado para executar ações em uma tabela. Ele pode ser usado para criar uma nova tabela ou alterar uma tabela existente.
down()
O método down
é usado para reverter as alterações aplicadas no método up
. Quando up
é usado para criar uma tabela, down
seria usado para remover essa tabela.
Atualize o arquivo de esquema que você acabou de criar com o seguinte código:
// .database/migrations/...users_schema.js
'use strict'
const Schema = use('Schema')
class UsersSchema extends Schema {
up () {
this.create('users', (table) => {
table.increments()
table.string('username', 80).notNullable().unique()
table.string('email', 254).notNullable().unique()
table.string('password', 60).notNullable()
table.timestamps()
})
}
down () {
this.drop('users')
}
}
module.exports = UsersSchema
O exemplo acima demonstra como criar/alterar uma tabela de banco de dados usando arquivos de esquema, encadeando diferentes métodos de tipo/modificador de coluna para definir as características de atributos de campo individuais no método up
.
Tipos de coluna/modificadores
OBSERVAÇÃO
Para obter a lista completa de métodos de tipo e modificador de coluna de esquema, consulte a documentação da API do Knex.
Tipos de coluna
Método | Descrição |
---|---|
table.bigInteger(name) | Adiciona uma coluna bigint. |
table.binary(name, [length]) | Adiciona uma coluna binary. |
table.boolean(name) | Adiciona uma coluna boolean. |
table.date(name) | Adiciona uma coluna date. |
table.datetime(name, [precision]) | Adiciona uma coluna datetime. |
table.decimal(name, [precision], [scale]) | Adiciona uma coluna decimal. |
table.enu(col, values, [options]) | Adiciona uma coluna enum. |
table.float(name, [precision], [scale]) | Adiciona uma coluna float. |
table.increments(name) | Adiciona uma coluna auto incrementing. |
table.integer(name) | Adiciona uma coluna integer. |
table.json(name) | Adiciona uma coluna json. |
table.string(name, [length=255]) | Adiciona uma coluna string. |
table.text(name, [textType]) | Adiciona uma coluna text. |
table.time(name, [precision]) | Adiciona uma coluna time. |
table.timestamp(name, [useTz], [precision]) | Adiciona uma coluna timestamp. |
table.timestamps([useTimestamps], [defaultToNow]) | Adiciona colunas created/updated. |
table.uuid(name) | Adiciona uma coluna uuid. |
Modificadores de coluna
Método | Descrição |
---|---|
.after(field) | Define a coluna a ser inserida após field . |
.alter() | Marca a coluna como uma alter/modify. |
.collate(collation) | Define a coluna collation (por exemplo, utf8_unicode_ci ). |
.comment(value) | Define a coluna comment. |
.defaultTo(value) | Define a coluna default value. |
.first() | Define a coluna a ser inserida na primeira posição. |
.index([indexName], [indexType]) | Especifica a coluna como um index. |
.inTable(table) | Defina tabela de chave estrangeira (cadeia após .references ). |
.notNullable() | Defina a coluna como não nula. |
.nullable() | Defina a coluna como anulável. |
.primary([constraintName]) | Defina a coluna como a chave primária para uma tabela. |
.references(column) | Defina coluna de chave estrangeira. |
.unique() | Defina a coluna como única. |
.unsigned() | Defina a coluna como sem sinal (se for inteiro). |
Conexões Múltiplas
Arquivos de esquema podem usar uma conexão diferente definindo um getter connection
(garanta que sua conexão diferente exista dentro do arquivo config/database.js
):
// .database/migrations/...users_schema.js
const Schema = use('Schema')
class UsersSchema extends Schema {
static get connection () {
return 'mysql'
}
// ...
}
module.exports = UsersSchema
NOTA
A tabela de banco de dados adonis_schema
é sempre criada dentro do banco de dados de conexão padrão para gerenciar o ciclo de vida das migrações (não há opção para substituí-la).
Executar migrações
Precisamos chamar o comando migration:run
para executar migrações (que executa o método up
em todos os arquivos de migração pendentes):
adonis migration:run
# .Output
migrate: 1502691651527_users_schema.js
Database migrated successfully in 117 ms
Status da migração
Você pode verificar o status de todas as migrações executando o seguinte comando:
adonis migration:status
DICA
O valor batch existe como uma referência que você pode usar para limitar reversões posteriormente.
É assim que as migrações funcionam nos bastidores:
- Chamar
adonis migration:run
executa todos os arquivos de esquema pendentes e os atribui a um novo lote. - Uma vez que um lote de arquivos de migração é executado, eles não são executados novamente.
- Chamar
adonis migration:rollback
reverte o último lote de migrações na ordem inversa.
DICA
Não crie várias tabelas em um único arquivo de esquema. Em vez disso, crie um novo arquivo para cada alteração no banco de dados. Dessa forma, você mantém seu banco de dados atômico e pode reverter para qualquer versão.
Comandos de migração
Abaixo está a lista de comandos de migração disponíveis.
Lista de comandos
Comando | Descrição |
---|---|
make:migration | Crie um novo arquivo de migração. |
migration:run | Execute todas as migrações pendentes. |
migration:rollback | Reverta o último conjunto de migrações. |
migration:refresh | Reverta todas as migrações para o lote 0 e execute-as novamente do início. |
migration:reset | Reverta todas as migrações para o lote 0 . |
migration:status | Obtenha o status de todas as migrações. |
Ajuda de comando
Para opções de comando detalhadas, anexe --help
a cada comando de migração:
adonis migration:run --help
# Output
Usage:
migration:run [options]
Options:
-f, --force Forcefully run migrations in production
-s, --silent Silent the migrations output
--seed Seed the database after migration finished
--log Log SQL queries instead of executing them
About:
Run all pending migrations
API de tabela de esquema
Abaixo está a lista de métodos de esquema disponíveis para interagir com tabelas de banco de dados.
create
Cria uma nova tabela de banco de dados:
up () {
this.create('users', (table) => {
})
}
createIfNotExists
Cria uma nova tabela de banco de dados (somente se ela não existir):
up () {
this.createIfNotExists('users', (table) => {
})
}
rename(from, to)
Renomeia uma tabela de banco de dados existente:
up () {
this.rename('users', 'my_users')
}
drop
Remove uma tabela de banco de dados:
down () {
this.drop('users')
}
dropIfExists
Remove uma tabela de banco de dados (somente se ela existir):
down () {
this.dropIfExists('users')
}
alter
Seleciona uma tabela de banco de dados para alteração:
up () {
this.alter('users', (table) => {
// add new columns or remove existing
})
}
raw
Executa um SQL arbitrário consulta:
up () {
this
.raw("SET sql_mode='TRADITIONAL'")
.table('users', (table) => {
table.dropColumn('name')
table.string('first_name')
table.string('last_name')
})
}
hasTable
Retorna se uma tabela existe ou não (este é um método async
):
async up () {
const exists = await this.hasTable('users')
if (!exists) {
this.create('up', (table) => {
})
}
}
Extensões
Abaixo está a lista de métodos de extensão que você pode executar ao executar migrações.
OBSERVAÇÃO
Extensões funcionam apenas com um banco de dados PostgreSQL.
createExtension(extensionName)
Criar uma extensão de banco de dados:
class UserSchema {
up () {
this.createExtension('postgis')
}
}
createExtensionIfNotExists(extensionName)
Criar uma extensão de banco de dados (somente se não existir):
class UserSchema {
up () {
this.createExtensionIfNotExists('postgis')
}
}
dropExtension(extensioName)
Remover uma extensão de banco de dados:
class UserSchema {
down () {
this.dropExtension('postgis')
}
}
dropExtensionIfExists(extensionName)
Remover uma extensão de banco de dados (somente se existir):
class UserSchema {
down () {
this.dropExtensionIfExists('postgis')
}
}
Executando código arbitrário
Os comandos escritos dentro dos métodos up
e down
são agendados para serem executados posteriormente dentro de uma migração.
Se você precisar executar comandos de banco de dados arbitrários, envolva-os dentro da função schedule
:
class UserSchema {
up () {
// create new table
this.create('new_users', (table) => {
})
// copy data
this.schedule(async (trx) => {
const users = await Database.table('users').transacting(trx)
await Database.table('new_users').transacting(trx).insert(users)
})
// drop old table
this.drop('users')
}
}
NOTA
O método schedule
recebe um objeto de transação. É importante executar todos os comandos de banco de dados dentro da mesma transação, caso contrário, suas consultas ficarão travadas para sempre.
Schema Builder API
A API do construtor de esquema usa a API Knex, então certifique-se de ler a documentação para mais informações.
fn.now()
O Knex tem um método chamado knex.fn.now(), que é usado para definir o carimbo de data/hora atual no campo do banco de dados.
No AdonisJs, você referencia esse método como this.fn.now()
:
up () {
this.table('users', (table) => {
table.timestamp('created_at').defaultTo(this.fn.now())
})
}