Ir para o console

Como salvar dados com o Firebase Realtime Database para C++

Primeiros passos

Consulte o guia Get Started se você ainda não configurou seu app e o acesso ao banco de dados.

Receber uma referência do banco de dados

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

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

Salvar dados

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

Método Usos comuns
SetValue() Gravar ou substituir dados em um caminho definido, como users/<user-id>/<username>.
PushChild() Adicionar a uma lista de dados. Toda vez que você chama Push(), o Firebase gera uma chave exclusiva que também pode ser usada como um identificador exclusivo como, por exemplo, user-scores/<user-id>/<unique-score-id>.
UpdateChildren() Atualize algumas das chaves de um caminho definido sem substituir todos os dados.
RunTransaction() Atualizar 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

Em operações básicas de gravação, use SetValue() para salvar dados em uma referência e substitua os dados existentes no caminho. Você pode usar esse método para passar os tipos aceitos pela JSON por um tipo Variant que seja compatível com:

  • null (isso exclui os dados)
  • números inteiros (64 bits)
  • números de ponto flutuante de precisão dupla
  • booleanos
  • strings
  • vetores de variantes
  • mapas de strings para variantes

O SetValue() substitui os dados no local especificado, incluindo qualquer node filho. No entanto, ainda é possível atualizar um filho sem substituir o objeto inteiro. Para que os usuários atualizem os próprios perfis, atualize o nome deles desta forma:

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ários. O método PushChild() gera uma chave exclusiva sempre que um novo filho é adicionado a uma referência específica do Firebase. Ao usar essas chaves geradas automaticamente para cada novo elemento da lista, vários clientes podem adicionar filhos no mesmo local simultaneamente sem criar conflitos de gravação. A chave exclusiva gerada por PushChild() é baseada em um timestamp, portanto, os itens da lista são organizados automaticamente em ordem cronológica.

Use a referência aos novos dados retornados pelo método PushChild() para receber o valor da chave filho que foi gerada automaticamente ou para 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 node sem substituir outros nodes filhos, use o método UpdateChildren().

Ao chamar UpdateChildren(), você pode atualizar valores filhos de níveis inferiores. Basta especificar um caminho para a chave. Se os dados estiverem armazenados em vários locais para aprimorar a escalabilidade, atualize todas as instâncias usando a distribuição de dados. 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&ltstd::string, Object&gt ToMap() {
    std::map&ltstring, Variant&gt result = new std::map&ltstring, Variant&gt();
    result["uid"] = Variant(uid);
    result["score"] = Variant(score);

    return result;
  }
}

Para criar um LeaderboardEntry e atualizá-lo simultaneamente no feed de resultados recentes e na lista de pontuação do 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&ltstd::string, Variant&gt entryValues = entry.ToMap();

  std::map&ltstring, Variant&gt childUpdates = new std::map&ltstring, Variant&gt();
  childUpdates["/scores/" + key] = entryValues;
  childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

  dbref.UpdateChildren(childUpdates);
}

Esse exemplo usa PushChild() para adicionar uma entrada ao node que contém entradas de todos os usuários em /scores/$key e, simultaneamente, recuperar a chave com key(). A chave pode ser usada para criar uma segunda entrada na pontuação do usuário em /user-scores/$userid/$key.

Usando esses caminhos, você pode realizar atualizações simultâneas em vários locais da árvore JSON com uma só chamada para UpdateChildren(), por exemplo, criar uma nova entrada nos dois locais. Essas atualizações são atômicas: ou todas funcionam, ou todas falham.

Excluir dados

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

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

Saiba quando seus dados estão confirmados.

Para saber se os seus dados estão confirmados no servidor do Firebase Realtime Database, verifique se o resultado da classe Future teve sucesso.

Salvar dados como transações

Para tratar dados corrompidos por modificações simultâneas, como contadores incrementais, use uma operação de transação. Dê a esta operação uma função DoTransaction . A função de atualização usa o estado atual dos dados como argumento e retorna o novo estado de acordo com as preferências de gravação. Se outro cliente fizer uma gravação no local antes que seu novo valor seja gravado com sucesso, 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() &gt= MaxScores) {
      long minScore = LONG_MAX;
      MutableData *minVal = null;
      std::vector&ltMutableData&gt children = mutableData.children();
      std::vector&ltMutableData&gt::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 &lt minScore) {
          minScore = childScore;
          minVal = &amp*it;
        }
      }
      if (minScore &gt 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&ltstd::string, Variant&gt newScoreMap =
      new std::map&ltstd::string, Variant&gt();
    newScoreMap["score"] = score;
    newScoreMap["email"] = email;
    children.Add(newScoreMap);
    mutableData->set_value(children);
    return kTransactionResultSuccess;
  });
}

Usar uma transação evita que a tabela de classificação fique incorreta caso vários usuários gravem resultados ao mesmo tempo ou o cliente tenha dados desatualizados. Se a transação for rejeitada, o servidor retornará o valor atual ao cliente, que executará a transação novamente com o valor atualizado. Isso se repetirá até que a transação seja aceita ou até que muitas tentativas sejam realizadas.

Gravar dados off-line

Se um cliente perder a conexão de rede, o app continuará funcionando.

Todos os clientes conectados a um banco de dados do Firebase mantêm a própria versão interna de dados ativos. A gravação deles ocorre primeiro nessa versão local. Depois, o cliente do Firebase sincroniza esses dados com os servidores remotos e com outros de acordo com o modelo “melhor esforço".

Consequentemente, todas as gravações no banco de dados acionam eventos locais, antes de qualquer dado ser gravado no servidor, e o app continua responsivo, independentemente da conectividade ou da latência da rede.

Para que a conectividade seja restabelecida, seu app recebe o conjunto apropriado de eventos, e o cliente faz a sincronização com o estado atual do servidor, sem precisar de um código personalizado.

Próximas etapas