I can not recover Firebase data with orderByChild in Android Studio - Fixed

1

Problem

I hope that someone can help me since I have spent the whole day on this, I try to read data from Firebase, I have already done write operations against the database, but I can not access the same data that I registered, I have the following rules in the database:

Rules at the time of the problem

{
  "rules": {
    "perfiles":{
        ".read": "auth != null",
        ".write": "auth != null"      
    }
  }
}

Rules optimization

The optimization that occurred is to increase one more level of control to guarantee that the logged-in user has access only to his information node and not to other users (suggestion of @Franco in his comments).

{
  "rules": {
    "perfiles":{
      "$uid":{
        ".read": "auth.uid === $uid",
        ".write": "auth.uid === $uid"      
      }
    }
  }
}

With the problem

And the next code in a fragment

    baseDatos = FirebaseDatabase.getInstance();
    perfilesReferencia = baseDatos.getReference("perfiles");
    vista = inflater.inflate(R.layout.fragment_perfil, container, false);

    // se obtiene al usuario logueado
    usuarioLogueado = FirebaseAuth.getInstance().getCurrentUser();
    Query consulta = perfilesReferencia.orderByChild("correo").equalTo(usuarioLogueado.getEmail());
    consulta.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot ds) {

            Perfil perfilEncontrado = ds.getValue(Perfil.class);

            Toast.makeText(getContext(),perfilEncontrado.getCorreo(),Toast.LENGTH_LONG).show();

            cedulaET.setText(perfilEncontrado.getCedula());
            nombresET.setText(perfilEncontrado.getNombres());
            apellidosET.setText(perfilEncontrado.getApellidos());
            nacimientoET.setText(perfilEncontrado.getNacimiento());
            whatsappET.setText(perfilEncontrado.getWhatsapp());
            direccionET.setText(perfilEncontrado.getDireccion());

        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            Toast.makeText(getContext(),"No se ha encontrado el perfil",Toast.LENGTH_LONG).show();
        }
    });

Code Optimization

Basically what was improved in this code is not to ask in the query by the logged user's mail, but a modification when registering the profile and not using a random key, but the user's uid and in the query to work with this uid (another suggestion from @Franco).

public void verificaPerfil(View view) {

    Query consulta = perfilesReferencia.child(usuarioLogueado.getUid());

    consulta.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot ds) {

            if(ds.exists()){
                Perfil perfilEncontrado = ds.getValue(Perfil.class);

                Toast.makeText(getContext(),perfilEncontrado.getCorreo(),Toast.LENGTH_LONG).show();

                cedulaET.setText(perfilEncontrado.getCedula());
                nombresET.setText(perfilEncontrado.getNombres());
                apellidosET.setText(perfilEncontrado.getApellidos());
                nacimientoET.setText(perfilEncontrado.getNacimiento());
                whatsappET.setText(perfilEncontrado.getWhatsapp());
                direccionET.setText(perfilEncontrado.getDireccion());
            } else {
                Toast.makeText(getContext(),"Aún no ha ingresado los datos de su perfil",Toast.LENGTH_LONG).show();
            }

        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            Toast.makeText(getContext(),"Ocurrió un error en la ejecución",Toast.LENGTH_LONG).show();
        }
    });

    return;
}

Structure with the problem

The structure uses in this case a random key obtained through a database method:

{
  "perfiles" : {
    "-LReGzJ4jtu1pCfHqsGa" : {
      "apellidos" : "Jaramillo",
      "cedula" : "0103780835",
      "correo" : "[email protected]",
      "direccion" : "Salvador Allende",
      "nacimiento" : "5/3/1983",
      "nombres" : "Andres",
      "whatsapp" : "0984228708"
    }
  }
}

Optimization of structures

In this case the only change was to register the data with the user's login, (One more suggestion from @Franco).

{
  "perfiles" : {
    "-LReGzJ4jtu1pCfHqsGa" : {
      "apellidos" : "Jaramillo",
      "cedula" : "0103780835",
      "correo" : "[email protected]",
      "direccion" : "Salvador Allende",
      "nacimiento" : "5/3/1983",
      "nombres" : "Andres",
      "whatsapp" : "0984228708"
    }
  }
}

Up to here optimizations that came in favor of a better functioning of the application.

SOLUTION

The code even without the optimizations was functionally correct and it was not the reason why the information in the database could not be recovered, the problem was that the Profile class did not contemplate the constructor without arguments and only the constructor with arguments and this was what prevented the recovery of the data from the database.

    
asked by Andrés Jara Avila 19.11.2018 в 06:48
source

1 answer

0

To recover the data, make sure that the attributes of your Profile class are called the same as the nodes in Firebase. If you do not want to use the same name you have to do the following

public Class Perfil {

   public Perfil(){} //Firebase requiere que exista un constructor sin argumentos

   //Agregas tantos constructores como necesites

  @PropertyName("cedula") //donde el nombre es el nombre del nodo en firebase
  private String cedula; //aclarando el nombre en la linea de arriba no hace falta
                         //que tengan el mismo nombre que en firebase.

  public void setCedula(String cedula){
     this.cedula = cedula;
  }

  @PropertyName("cedula") //también es necesario aclararlo en el getter
  public String getCedula(){
     return this.cedula;
  }

  //haces lo mismo para cada atributo
}

Anyway, I recommend that you change the structure, when you add a user profile instead of creating an entry with a random key used as a key, the user's uid.

And your security rules are incorrect. Any user can write in the profiles node and not only in their node (you need one more level of depth in the tree to write).

Passing in clean:

Firebase structure

 {
  "perfiles" : {
    "zxwDkderfasdl2" : {  //corresponde al id del usuario y no una clave aleatoria
      "apellidos" : "Jaramillo",
      "cedula" : "0103780835",
      "correo" : "[email protected]",
      "direccion" : "Salvador Allende",
      "nacimiento" : "5/3/1983",
      "nombres" : "Andres",
      "whatsapp" : "0984228708"
    }
  }
}

firebase rules

{
 "rules": {
    "perfiles": {
      "$uid": {
        ".write": "$uid === auth.uid",
        ".read": "$uid === auth.uid"
      }
    }
  }
}

look at the rules that there is a level more "$ uid" to indicate that within the "profiles" node is like a list of entries. Now a user can only read and write within "profiles / their uid" and can not delete all the entries with a simple setValue at the level of "profiles /" since they will not have access.

Finally, to get the data from Android

baseDatos = FirebaseDatabase.getInstance();
perfilesReferencia = baseDatos.getReference("perfiles");
vista = inflater.inflate(R.layout.fragment_perfil, container, false);

// se obtiene al usuario logueado
usuarioLogueado = FirebaseAuth.getInstance().getCurrentUser();
String uid = usuarioLogueado.getUid() //no me acuerdo precisamente el nombre del método
Query consulta = perfilesReferencia.child(uid);
consulta.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot ds) {

        Perfil perfilEncontrado = ds.getValue(Perfil.class);

        Toast.makeText(getContext(),perfilEncontrado.getCorreo(),Toast.LENGTH_LONG).show();

        cedulaET.setText(perfilEncontrado.getCedula());
        nombresET.setText(perfilEncontrado.getNombres());
        apellidosET.setText(perfilEncontrado.getApellidos());
        nacimientoET.setText(perfilEncontrado.getNacimiento());
        whatsappET.setText(perfilEncontrado.getWhatsapp());
        direccionET.setText(perfilEncontrado.getDireccion());

    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {
        Toast.makeText(getContext(),"No se ha encontrado el perfil",Toast.LENGTH_LONG).show();
    }
});

Now it is not necessary to order by email or use "equalTo" you only need to obtain the profile that corresponds to the user's username. I hope it serves you!

    
answered by 20.11.2018 / 02:53
source