Steeven Delucis
Steeven Delucis

Reputation: 352

FIREBASE : get data details from another child

I come to solicit your help on a problem that I have with my application. The problem is this: there are posts on the newsfeed that are in the database under this Posts -> [postid] location as on this capture post reference , and when a user save a post, in the database it is stored like this: Users -> [userid] -> saves -> [postid], as you can see in this screenshot save reference.
And I want that based on the id of the post that will be in save of the user, that he can retrieve the information details (like post description) on these posts and displayed them in my fragment "Saves" of my application. Here is what I tried to do

THE SAVE FRAGMENT ADAPTER

package com.ackerman.ubi.Adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.ackerman.ubi.Model.PostModel;
import com.ackerman.ubi.R;
import com.bumptech.glide.Glide;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SaveAdapter extends RecyclerView.Adapter<SaveAdapter.ViewHolder> {

    private Context mContext;
    private List<PostModel> mPostModel;

    public SaveAdapter(Context mContext, List<PostModel> mPostModel) {
        this.mContext = mContext;
        this.mPostModel = mPostModel;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.item_save, parent, false);
        return new SaveAdapter.ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {

        PostModel post = mPostModel.get(position);
        adaptSave(post, holder.post_image, holder.description, holder.price, holder.location, holder.product_state, holder.title);


    }


    @Override
    public int getItemCount() {
        return mPostModel.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        ImageView post_image;
        TextView description, price, location, product_state, title;

        ViewHolder(@NonNull View itemView) {
            super(itemView);

            post_image = itemView.findViewById(R.id.post_image);
            description = itemView.findViewById(R.id.description);
            price = itemView.findViewById(R.id.price);
            location = itemView.findViewById(R.id.location);
            product_state = itemView.findViewById(R.id.product_state);
            title = itemView.findViewById(R.id.title);

        }
    }

    //END OF ADAPTER SECTION

    //METHOD SECTION

    /**
     * Methode permettat d'adapter les sauvegarde
     *
     * @param post
     * @param postImage
     * @param description
     * @param price
     * @param location
     * @param productState
     * @param title
     */
    private void adaptSave(final PostModel post, final ImageView postImage, final TextView description, final TextView price, final TextView location, final TextView productState, final TextView title) {

        Glide.with(mContext).load(post.getPostimage()).into(postImage);

        //description.setText(post.get("description").toString());
//        price.setText(post.get("price").toString());
//        location.setText(post.get("location").toString());
//        productState.setText(post.get("product_state").toString());
//        title.setText(post.get("title").toString());
//        description.setText(post.getDescription());
//        title.setText(post.getTitle());
//
//        price.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_price_tag_post, 0, 0);
//        price.setText(post.getPrice());
//
//        location.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_placeholder_post, 0, 0);
//        location.setText(post.getLocation());
//
//        getProductState(post, productState);

    }

    /**
     * Methode permettant de verifier l'etat du produit
     *
     * @param post
     * @param productState
     */
    private void getProductState(PostModel post, TextView productState) {
        String product_state = post.getProduct_state();

        if (product_state.equals("Neuf")) {
            productState.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_box_new_post, 0, 0);
        } else {
            productState.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_box_used_post, 0, 0);

        }
    }

}

THE SAVE FRAGMENT

package com.ackerman.ubi.Fragment;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.ackerman.ubi.Adapter.SaveAdapter;
import com.ackerman.ubi.Model.PostModel;
import com.ackerman.ubi.R;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import es.dmoral.toasty.Toasty;

public class SaveFragment extends Fragment {

    private List<PostModel> saveList;
    private SaveAdapter saveAdapter;

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_save, container, false);


        RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
        linearLayoutManager.setReverseLayout(true);
        linearLayoutManager.setStackFromEnd(true);
        recyclerView.setLayoutManager(linearLayoutManager);
        saveList = new ArrayList<>();
        saveAdapter = new SaveAdapter(getContext(), saveList);
        recyclerView.setAdapter(saveAdapter);

        getSaves();
        return view;
    }

    /**
     * Methode permettant de recuperer toute les sauvegardes de l'utilisateur
     */
    private void getSaves() {

        String userid = FirebaseAuth.getInstance().getCurrentUser().getUid();       //Recuperation de l'ID de l'utilisateur

        //Reference de l'emplacement des posts, Users -> ID de l'utilisateur -> saves
        DatabaseReference saveReference = FirebaseDatabase.getInstance().getReference("Users").child(userid).child("saves");

        saveReference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                saveList.clear();
                for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                    PostModel save = snapshot.getValue(PostModel.class);
                    saveList.add(save);
                    saveAdapter.notifyDataSetChanged();

                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
    }


}

THE POST MODEL

package com.ackerman.ubi.Model;

public class PostModel {
    //Ensemble des attribus que peut avoir un post
    private String postid, postimage, description, publisher, location, price, product_state, title;

    //Constructeur vide (Obligatoire(pour une initialisation))
    public PostModel() {

    }

    /**
     * Constructeur contenant les attributs de nos post
     *
     * @param postid
     * @param postimage
     * @param description
     * @param publisher
     * @param location
     * @param price
     * @param product_state
     * @param title
     */
    public PostModel(String postid, String postimage, String description, String publisher, String location, String price, String product_state, String title) {
        this.postid = postid;
        this.postimage = postimage;
        this.description = description;
        this.publisher = publisher;
        this.location = location;
        this.price = price;
        this.product_state = product_state;
        this.title = title;
    }

    //Les getters et les setters
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public String getProduct_state() {
        return product_state;
    }

    public void setProduct_state(String product_state) {
        this.product_state = product_state;
    }

    public String getPostid() {
        return postid;
    }

    public void setPostid(String postid) {
        this.postid = postid;
    }

    public String getPostimage() {
        return postimage;
    }

    public void setPostimage(String postimage) {
        this.postimage = postimage;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getPublisher() {
        return publisher;
    }

    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }
}

but I get this error. THE ERROR WHAT I GET

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.ackerman.ubi, PID: 6313
    com.google.firebase.database.DatabaseException: Can't convert object of type java.lang.String to type com.ackerman.ubi.Model.PostModel
        at com.google.firebase.database.core.utilities.encoding.CustomClassMapper.convertBean(com.google.firebase:firebase-database@@19.1.0:418)
        at com.google.firebase.database.core.utilities.encoding.CustomClassMapper.deserializeToClass(com.google.firebase:firebase-database@@19.1.0:214)
        at com.google.firebase.database.core.utilities.encoding.CustomClassMapper.convertToCustomClass(com.google.firebase:firebase-database@@19.1.0:79)
        at com.google.firebase.database.DataSnapshot.getValue(com.google.firebase:firebase-database@@19.1.0:203)
        at com.ackerman.ubi.Fragment.SaveFragment$1.onDataChange(SaveFragment.java:73)
        at com.google.firebase.database.core.ValueEventRegistration.fireEvent(com.google.firebase:firebase-database@@19.1.0:75)
        at com.google.firebase.database.core.view.DataEvent.fire(com.google.firebase:firebase-database@@19.1.0:63)
        at com.google.firebase.database.core.view.EventRaiser$1.run(com.google.firebase:firebase-database@@19.1.0:55)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6688)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)

the error occurs at this line "Postmodel save = snapshot.getValue (PostModel.class);" in the SaveFragment

I hope I was quite understandable if not hesitate to ask me questions. Thank you

Upvotes: 0

Views: 219

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 600091

It looks like the error comes from this line:

PostModel save = snapshot.getValue(PostModel.class);

This line loads data from FirebaseDatabase.getInstance().getReference("Users").child(userid).child("saves"), which is the path /Users/$uid/saves. Under that path in your screenshots, I see a structure like this:

enter image description here

So by the time you loop over dataSnapshot.getChildren(), each individual value is just a string. Your PostModel class has way more properties than exist in this location, which is why Firebase tells you that it Can't convert object of type java.lang.String to type com.ackerman.ubi.Model.PostModel.

If you want to load the post that is referenced by this ID, you need to do an extra call to the database:

String modelKey = snapshot.getValue(String.class); 
DatabaseReference postRef = FirebaseDatabase.getInstance().getReference("Posts").child(modelKey);
postRef.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot postSnapshot) {
        PostModel save = postSnapshot.getValue(PostModel.class);
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        throw databaseError.toException();
    }
}

Upvotes: 3

Related Questions