Salvando dados com Firebase Realtime Database para C++

Iniciar

Consulte primeiro o guia Get Started se ainda não tiver configurado seu aplicativo e acesso ao banco de dados.

Obtenha uma referência de banco de dados

Para gravar dados no banco de dados, você precisa de uma instância de DatabaseReference :

    // Get the root reference location of the database.
    firebase::database::DatabaseReference dbref = database->GetReference();

Salvando dados

Existem quatro métodos para gravar dados no Firebase Realtime Database:

Método Usos comuns
SetValue() Grave ou substitua dados em um caminho definido, como users/<user-id>/<username> .
PushChild() Adicione a uma lista de dados. Cada vez que você chama Push() , o Firebase gera uma chave exclusiva que também pode ser usada como um identificador exclusivo, como user-scores/<user-id>/<unique-score-id> .
UpdateChildren() Atualize algumas das chaves de um caminho definido sem substituir todos os dados.
RunTransaction() Atualize dados complexos que podem ser corrompidos por atualizações simultâneas.

Gravar, atualizar ou excluir dados em uma referência

Operações básicas de gravação

Para operações básicas de gravação, você pode usar SetValue() para salvar dados em uma referência especificada, substituindo quaisquer dados existentes nesse caminho. Você pode usar este método para passar tipos aceitos pelo JSON por meio de um tipo Variant que suporta:

  • Nulo (isso exclui os dados)
  • Inteiros (64 bits)
  • Números de ponto flutuante de precisão dupla
  • Booleanos
  • Cordas
  • Vetores de variantes
  • Mapas de strings para variantes

Usar SetValue() dessa forma substitui os dados no local especificado, incluindo quaisquer nós filhos. No entanto, você ainda pode atualizar um filho sem reescrever o objeto inteiro. Se quiser permitir que os usuários atualizem seus perfis, você pode atualizar o nome de usuário da seguinte maneira:

dbref.Child("users").Child(userId).Child("username").SetValue(name);

Anexar a uma lista de dados

Use o método PushChild() para anexar dados a uma lista em aplicativos multiusuário. O método PushChild() gera uma chave exclusiva sempre que um novo filho é adicionado à referência especificada do Firebase. Ao usar essas chaves geradas automaticamente para cada novo elemento na lista, vários clientes podem adicionar filhos ao mesmo local ao mesmo tempo, sem conflitos de gravação. A chave exclusiva gerada por PushChild() é baseada em um carimbo de data/hora, portanto os itens da lista são automaticamente ordenados cronologicamente.

Você pode usar a referência aos novos dados retornados pelo método PushChild() para obter o valor da chave gerada automaticamente do filho ou definir dados para o filho. Chamar GetKey() em uma referência PushChild() retorna o valor da chave gerada automaticamente.

Atualizar campos específicos

Para gravar simultaneamente em filhos específicos de um nó sem substituir outros nós filhos, use o método UpdateChildren() .

Ao chamar UpdateChildren() , você pode atualizar valores filho de nível inferior especificando um caminho para a chave. Se os dados forem armazenados em vários locais para melhor escalar, você poderá atualizar todas as instâncias desses dados usando o data fan-out . Por exemplo, um jogo pode ter uma classe LeaderboardEntry como esta:

class LeaderboardEntry {
  std::string uid;
  int score = 0;

 public:
  LeaderboardEntry() {
  }

  LeaderboardEntry(std::string uid, int score) {
    this->uid = uid;
    this->score = score;
  }

  std::map<std::string, Object> ToMap() {
    std::map<string, Variant> result = new std::map<string, Variant>();
    result["uid"] = Variant(uid);
    result["score"] = Variant(score);

    return result;
  }
}

Para criar um LeaderboardEntry e atualizá-lo simultaneamente para o feed de pontuação recente e a lista de pontuação do próprio usuário, o jogo usa o seguinte código:

void WriteNewScore(std::string userId, int score) {
  // Create new entry at /user-scores/$userid/$scoreid and at
  // /leaderboard/$scoreid simultaneously
  std::string key = dbref.Child("scores").PushChild().GetKey();
  LeaderBoardEntry entry = new LeaderBoardEntry(userId, score);
  std::map<std::string, Variant> entryValues = entry.ToMap();

  std::map<string, Variant> childUpdates = new std::map<string, Variant>();
  childUpdates["/scores/" + key] = entryValues;
  childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

  dbref.UpdateChildren(childUpdates);
}

Este exemplo usa PushChild() para criar uma entrada no nó contendo entradas para todos os usuários em /scores/$key e recuperar simultaneamente a chave com key() . A chave pode então ser usada para criar uma segunda entrada nas pontuações do usuário em /user-scores/$userid/$key .

Usando esses caminhos, você pode executar atualizações simultâneas em vários locais na árvore JSON com uma única chamada para UpdateChildren() , como este exemplo cria a nova entrada em ambos os locais. As atualizações simultâneas feitas dessa maneira são atômicas: ou todas as atualizações são bem-sucedidas ou todas as atualizações falham.

Excluir dados

A maneira mais simples de excluir dados é chamar RemoveValue() em uma referência ao local desses dados.

Você também pode excluir especificando um Variant null como o valor para outra operação de gravação, como SetValue() ou UpdateChildren() . Você pode usar esta técnica com UpdateChildren() para excluir vários filhos em uma única chamada de API.

Saiba quando seus dados são confirmados.

Para saber quando seus dados são confirmados no servidor Firebase Realtime Database, verifique o resultado futuro para ver se há sucesso.

Salvar dados como transações

Ao trabalhar com dados que podem ser corrompidos por modificações simultâneas, como contadores incrementais, você pode usar uma operação de transação . Você atribui a esta operação uma função DoTransaction . Esta função de atualização toma o estado atual dos dados como argumento e retorna o novo estado desejado que você gostaria de escrever. Se outro cliente gravar no local antes que seu novo valor seja gravado com êxito, sua função de atualização será chamada novamente com o novo valor atual e a gravação será repetida.

Por exemplo, em um jogo você pode permitir que os usuários atualizem uma tabela de classificação com as cinco pontuações mais altas:

void AddScoreToLeaders(std::string email,
                       long score,
                       DatabaseReference leaderBoardRef) {
  leaderBoardRef.RunTransaction([](firebase::database::MutableData* mutableData) {
    if (mutableData.children_count() >= MaxScores) {
      long minScore = LONG_MAX;
      MutableData *minVal = null;
      std::vector<MutableData> children = mutableData.children();
      std::vector<MutableData>::iterator it;
      for (it = children.begin(); it != children.end(); ++it) {
        if (!it->value().is_map())
          continue;
        long childScore = (long)it->Child("score").value().int64_value();
        if (childScore < minScore) {
          minScore = childScore;
          minVal = &*it;
        }
      }
      if (minScore > score) {
        // The new score is lower than the existing 5 scores, abort.
        return kTransactionResultAbort;
      }

      // Remove the lowest score.
      children.Remove(minVal);
    }

    // Add the new high score.
    std::map<std::string, Variant> newScoreMap =
      new std::map<std::string, Variant>();
    newScoreMap["score"] = score;
    newScoreMap["email"] = email;
    children.Add(newScoreMap);
    mutableData->set_value(children);
    return kTransactionResultSuccess;
  });
}

O uso de uma transação evita que o placar fique incorreto se vários usuários registrarem pontuações ao mesmo tempo ou se o cliente tiver dados desatualizados. Se a transação for rejeitada, o servidor retorna o valor atual ao cliente, que executa a transação novamente com o valor atualizado. Isso se repete até que a transação seja aceita ou muitas tentativas tenham sido feitas.

Gravar dados off-line

Se um cliente perder a conexão de rede, seu aplicativo continuará funcionando corretamente.

Cada cliente conectado a um banco de dados Firebase mantém sua própria versão interna de quaisquer dados ativos. Quando os dados são gravados, eles são gravados primeiro nesta versão local. O cliente Firebase então sincroniza esses dados com os servidores de banco de dados remotos e com outros clientes com base no "melhor esforço".

Como resultado, todas as gravações no banco de dados acionam eventos locais imediatamente, antes que qualquer dado seja gravado no servidor. Isso significa que seu aplicativo permanece responsivo independentemente da latência ou conectividade da rede.

Depois que a conectividade for restabelecida, seu aplicativo receberá o conjunto apropriado de eventos para que o cliente sincronize com o estado atual do servidor, sem precisar escrever nenhum código personalizado.

Próximos passos