How to work with nested Models in Sqlflite Flutter - sqlite

I am going to be working with sqlflite package in Flutter, and I have been reading a lot about it. However, everywhere I find the example being shown for the single model, that is
SingleModel{
String data1, data2;
....
}
I have followed
Store list of objects in sqlite
Sqlflite operation in Flutter
these as well, but that did not quite resolve it. My main requirements is to work with the data like this:
user: {
'id': unique_key,
'name': 'abc',
'courses': [
{ 'coursename': 'some_coursename'},
{ 'coursename': 'some_other_coursename'}
]
}
I have designed my model correctly,
UserModel Class
class UserModel{
int id;
String name;
List<Course> _courses;
UserModel.set(Map data){
this.id = data['id'];
this.name = data['name'];
if(data['courses'] != null)
this.courses = [data['courses']];
}
// append more courses
void setMorCourses(Map course){
// checking for duplicate errors
if(!this.courses.contains(course['course']))
this.courses.add(course['course'])
}
}
Now, I am confused on how to use the Nested Model with sqlflite. I believe I need to work in the DatabaseHelper class.
Course Class
class CourseModel{
String coursename;
CourseModel.set(Map data){
this.coursename = data['coursename'];
}
}

Related

How to make a Model Class Within Model Class (Flutter / Dart)

I am trying to make a multivendor app with Flutter.
The problem I am facing right now is how can I make a model class within a Model class:
Example:
class ProductModel {
String? name;
double? price;
String? imgUrl;
double? weight;
ProductModel();
ProductMode.fromSnapShot(DocumentSnapshot snapshot){
name = snapshot['name'];
price= snapshot['price'];
imgUrl= snapshot['imgUrl'];
weight= snapshot['weight'];
}
}
I have already retrieved all the products from Firestore:
class VendorModel{
String? name;
String? imgUrl;
DateTime? opensAt;
DateTime? closesAt;
VendorModel();
VendorModel.fromSnapshot(DocumentSnapshot snapshot){
name = snapshot['name'];
imgUrl= snapshot['imgUrl'];
opensAt= DateTime.tryParse(snapshot['opensAt'].toString());
closesAt= DateTime.tryParse(snapshot['closesAt'].toString());
}
}
I have also retrieved all the vendors from Firestore:
Here is What I am trying to do:
I would like to add the products I fetched from Firestore to their respective vendors using this model. But I don't know how to make this if my data is from Firestore.
class VendorWithProductsModel{
VendorModel vendor;
List<ProductModel> products;
VendorWithProductsModel();
(I AM STUCK HERE)
}
Here is a Snippet of my Firestore code in retrieving products and vendors:
VendorModel _vendor = VendorModel();
List<VendorModel> _allVendors = [];
Future<VendorModel> getCertainVendor(docId) async{
await vendor.doc(docId).get().then((value){
_vendor = VendorModel.fromSnapshot(value);
});
return _vendor;
}
Future<List<VendorModel>> getAllVendors() async{
await vendor.get().then((value){
_allVendors.add(VendorModel.fromSnapshot(value));
});
return _allVendors;
}
In your product model, you need something like vendorId, where you can know which product belongs to which vendor.
After fetching all your products and vendors. Use both list, and loop through them, this is pseudo code for example:
List<VendorWithProductsModel> doMagic (List<ProductModel> productList, List<VendorModel> vendorList) {
List<VendorWithProductsModel> vendorsWithProducts =[];
vendorList.forEach((vendor){
VendorWithProductsModel _vendorAndProduct = VendorWithProductsModel();
_vendorAndProduct.vendor = vendor;
_vendorAndProduct.products =[];
_vendorAndProduct.products = productList.where((e)=> e.vendorId == vendor.id).toList();
vendorsWithProducts.add(_vendorAndProduct);
});
return vendorsWithProducts;
}

How to get documents from different collections in firebase and add them to a single List to return as Stream?

I am trying to create an Attendance App, so I want to get the courses in which the students are registered. The Student's class looks something like this:
class Student {
final String name;
final String email;
final String number;
final String regNo;
List<CourseIDAndInstructorID> courses;
Student({this.name, this.email, this.number, this.regNo});
}
The List named courses contains the document-ID of the instructor whose course it is, and the document-ID of the course document.(As one student would obviously be taking classes from different instructors)
Now using these two fields, I want to get the documents of the courses, create an Object of the custom class Course and then add this object to a List, that would be returned so that it can be displayed on the GUI.
But I am getting this Exception, whereas I clearly have data in the object.
The image of the Exception Message can be seen here
final CollectionReference _instructorCollection =
Firestore.instance.collection('instructors');
final CollectionReference _studentCollection =
Firestore.instance.collection('students');
Stream<List<Course>> _getStudentCourses() async* {
List<Course> courseList;
DocumentSnapshot stdDoc = await _studentCollection.document(uid).get();
Student _student = new Student.fromSnapshot(stdDoc);
if (_student.courses.length == 0) {
return; //TODO add appropriate return type
} else {
int len = _student.courses.length;
while (len != 0) {
len--;
DocumentSnapshot snapshot = await _instructorCollection
.document(_student.courses[len].instructorUID)
.collection('courses')
.document(_student.courses[len].courseID)
.get();
print(snapshot.data);
Course course = new Course.fromSnapshot(snapshot);
courseList.add(course);
yield courseList;
}
}
}
Can someone tell me, what I'm doing wrong or how I can do this?
If something is missing from the context of the question, kindly tell me so that I can add that.
You may need to create the List object instead of just a pointer.
List<Course> courseList = new List();
source

Firebase Database multiple strings in one child

im working with firebase for a couple months and so far i had no issues until now. im using kotlin and my problem is simple but i cant find a way out. i storage strings in database: path"/user/friends" , but i cant change the variable name and the strings were overwriting itselves. My solution was use "push()" until the "setValue()", but with this i have the following firebase structure:
"users": {
"yvrYpjMwVSPBvMAvDGo26hPlWWQ2": {
"email": "vinibarros.sp#gmail.com",
"friends": {
"-LfD9z6ke7FXFjxUb4td": {
"email": "teste#gmail.com"
},
"-LfDA-NaAYAMoWiPhXy4": {
"email": "teste2#gmail.com"
}
},
"primeiroLogin": true,
"stars": 0,
"tutorialComplete": false,
"uid": "yvrYpjMwVSPBvMAvDGo26hPlWWQ2",
"username": "Vinicius Barros"
}
}
basically hashmaps inside a hashmap ps:lol mindblow
how do i get the pushcode.value into an arraylist?
i have a class user that have the friend variable, until now this variable was typed as hashMap. and the call to get the email was "user.friends.value"
this worked because was one friend....
com.google.firebase.database.DatabaseException: Failed to convert value of type java.util.HashMap to String
When I added more than one user I got this error that references my class user, where have a "hashMap". But the firebase gives me another hashmap instead of string. I was thinking about: hashMap<hashMap<String,String>, null>.
i 've tried this:
class friends:
#Parcelize
class Friend(val hashMap: HashMap < String, String > ? ): Parcelable {
constructor(): this(null)
}
The activity where i show the friends:
val ref2 = FirebaseDatabase.getInstance().getReference("/users/" + cUid + "/friends/")
ref2.addListenerForSingleValueEvent(object: ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach {
val friend = it.getValue(Friend::class.java)
Log.d("teste", it.toString())
if (friend != null) {
Log.d("teste", friend.hashMap ? .values.toString())
//friends.add(friend.hashMap?.values.toString())
}
}
}
})
this keeps returning null....
With your Friend class, the Firebase client looks for a JSON for each friend with this structure:
"-LfD9z6ke7FXFjxUb4td": {
"hashMap": {
...
}
},
This is because you're defining your Friend class with a property hashMap like this: class Friend(val hashMap: HashMap < String, String > ? ).
To be able to read your current structure, you'll need to define a data class like this:
data class Friend (
val email: String? = null
}
Now the email in the Friend class matched the email property in the JSON. And since email has a default value, the Friend class will have a default no-argument constructor, which Firebase relies on.
Solved!
After get into /friends
i did this:
var friends : ArrayList<String> = arrayListOf()
the onDataChanged where i get the string friend
override fun onDataChange(p1: DataSnapshot) {
p1.children.forEach { it1 ->
val friend = it1.value as String
friends.add(friend)
}
}

ASP.NET MVC Conditional ViewModel Abstraction

I am new to ASP.NET MVC and I am stuck on a point. I am working on a classified site. My situation is, I have a lot of categories in which a user can post their ads and each ad category have different View. I have created a Controller Action like
public ActionResult PostAd(string CategoryName, string SubCategoryName)
{
if(categoryName == "Vehicle" && SubCategoryName == "Cars")
{
var model = new CarAdViewModel();
// set CarAdViewModel properties...
return View("CarAdCreateView", model);
}
else if(categoryName == "Vehicle" && SubCategoryName == "Bikes")
{
var model = new BikeAdViewModel();
// set BikeAdViewModel properties...
return View("BikeAdViewModel", model);
}
else if(categoryName == "Property" && SubCategoryName == "RentHouse")
{
var model = new RentHouseAdViewModel();
// set RentHouseAdViewModel properties...
return View("RentHouseAdViewModel", model);
}
else................... so on and so on
}
My problem is I have huge number of Categories and Sub Categories almost 60+. And if I keep on coding like above for 60+ categories and subcategories, my PostAd method is going to blast and become unmanageable.
Please tell me some best practice or pattern which can bring me out of this problem.
Unfortunately, some of what you are doing cannot be avoided. There needs to be some form of model and view selection based on category.
Use a factory pattern. Create a base class:
public abstract class BaseCategory
{
public abstract string GetViewName();
public abstract Object CreateModelFromFormData();
}
For each category, create a sub-class derived from BaseCategory and implement the abstract functions.
In your action, do the following:
public ActionResult PostAd(string categoryName, string subCategoryName)
{
BaseFactory factory;
if (categoryName == "Vehicle")
{
if (subCategoryName == "Cars")
{
factory = new CarsFactory();
}
else ...
}
else ...
return View(factory.GetViewName(), factory.CreateModelFromFormData());
}
I have a couple reasons for this schema:
I am purposefully using if/else for the factory selection. Your controller is going to be created and re-created for every action call. So pre-populating a list will constantly and needlessly create objects for categories that will not be selected. A simple if/else will be more efficient. If you want to prevent the if/else, you can put your factories in a Dictionary and select based on the categories, but that would be a lot of needless constructor actions.
I made the CreateModelFromFormData a function because I assume you'll need to copy data from the posted form data. This may require passing in data, but I left the function parameterless.
I used base/derived classes because the copying of the form data will probably need to be custom from the model being created and the form data being posted. Also, saving to persistent storage (file or database) may be category-specific as well.
It would be one of some possible solutions
public class PostAdData
{
public string CategoryName;
public string SubCategoryName;
public string ViewName;
public Type Model;
}
public class PostController : Controller
{
private readonly List<PostAdData> _theData;
public HomeController()
{
_theData = InitializeData();
}
public ActionResult PostAd(string categoryName, string subCategoryName)
{
var data = _theData.FirstOrDefault(c => c.CategoryName == categoryName && c.SubCategoryName == subCategoryName);
if (data != null)
{
var model = Activator.CreateInstance(data.Model);
return View(data.ViewName, model);
}
return View("Error");
}
[NonAction]
public List<PostAdData> InitializeData()
{
var result = new List<PostAdData>
{
new PostAdData
{
CategoryName = "Vehicle",
SubCategoryName = "Cars",
ViewName = "CarAdCreateView",
Model = typeof (CarAdViewModel)
}
};
return result;
}
}
You should make this data driven. You create a lookup table that has a compound primary key of category and subcategory. Then it has a table with View in it. Then you simply ad rows for each category/subcategory/view combination.
If you absolutely don't want a database, then you can use a simple hashset or dictionary.
var views = new Dictionary<Tuple<string,string>,string>();
views.Add(new Tuple<string,string>("Vehicle", "Cars"), "CarAdCreateView");
Then in your PostAd you just lookup the correct view.
What a beautiful solution on www.asp.net to my question, here is the link : http://forums.asp.net/t/1923868.aspx/1?ASP+NET+MVC+Conditional+ViewModel+Abstraction
Edit:
My code is :
public class AdsController : Controller
{
private readonly IAdService _adService;
public AdsController(IAdService adService)
{
_adService = adService;
}
public ActionResult PostAd(string Category, string SubCategory)
{
//Here I will call
var strategy = GetStrategy(CategoryName, SubCategoryName);
strategy.FillModel(_adService );
return View(strategy.ViewName, strategy.Model);
}
}

Import two or multiple class models to a single controller on ASP.NET

I'm very new to ASP.NET, but I know a little programming in Java. I want to use a ZIP code to query a database which will return a string, then use that string to query another database. I wanted to do this on the same control model. I thought it would be easy, and it sounds pretty easy.
When I created the controller, I put the model class of the first database, and, so far, I've gotten as far as querying the first database, but now that I have the string I want to query a second database through the DBEntities.
This displays an error saying:
> The model item passed into the dictionary is of type
> 'System.Collections.Generic.List`1[FinalBallot.Models.AgainCandidate]',
> but this dictionary requires a model item of type
> 'System.Collections.Generic.IEnumerable`1[FinalBallot.Models.ZipTable]'.
Is there a way to solve this in an easy way?
public class Default1Controller : Controller
{
private CandidatesDBEntities db = new CandidatesDBEntities();
public string districString = "";
//
// GET: /Default1/
public ViewResult Index(string searchString)
{
var queryZip = from s in db.ZipTables select s;
var queryCandidates = from s1 in db.AgainCandidates select s1;
double sT = 0;
//method so it doesnt display the whole db
if (String.IsNullOrEmpty(searchString))
{
queryZip = queryZip.Where(s => s.ZipL.Equals(0));
}
if (!String.IsNullOrEmpty(searchString))
{
sT = double.Parse(searchString);
queryZip = queryZip.Where(s => s.ZipL.Equals(sT));
try
{
districString = queryZip.ToList().ElementAt(0).District;
}
catch
{
}
if (!String.IsNullOrEmpty(districString))
{
queryCandidates = queryCandidates.Where(s1 => s1.District.Equals(districString));
}
}
return View(queryCandidates.ToList());
}
In your view, did you specify the model to be IEnumerable<ZipTable>? The model that you're passing to your view is IEnumerable<AgainCandidate>, so you would get an error if you specified your model as something else. You'd need to change the model in your view to be IEnumerable<AgainCandidate>.
UPDATE:
Based on your revised explanation, you can do a couple things:
1) create a "ViewModel" that has two properties for each of your collections you want to display on the page like so:
public class MyViewModel
{
IEnumerable<ZipTable> Zips { get; set; }
IEnumerable<AgainCandidate> Candidates { get; set; }
}
Instantiate that in your action method and return that as your model. This would be my preferred approach.
2) Stash your two collections in the ViewData bag in your action method:
ViewData["Zips"] = queryZip.ToList();
ViewData["Candidates"] = queryCandidates.ToList();
return View(ViewData);
You can pull this data in your view like this:
#foreach (var zip in ViewData["Zips"] as IEnumerable<ZipTable>)
{
...
}

Resources