Impossible to get datas from Json Flutter - json

In my Flutter project i would like to get datas from an API.
I used QuickType to generate my classes to parse my Json.
This error is always occured:
_TypeError (type 'List' is not a subtype of type 'String')
This my API response:
[
{
"id": 1,
"label": "Animals",
"created_at": "2022-05-25T13:36:42.517Z",
"updated_at": "2022-05-25T13:36:42.517Z",
"sub_categories": [
{
"id": 2,
"label": "Ant",
"category": 1,
"published_at": "2022-05-25T13:40:48.172Z",
"created_at": "2022-05-25T13:40:48.174Z",
"updated_at": "2022-05-25T16:29:58.047Z"
},
{
"id": 3,
"label": "Cats",
"category": 1,
"published_at": "2022-05-25T13:42:07.022Z",
"created_at": "2022-05-25T13:42:07.023Z",
"updated_at": "2022-05-25T16:30:08.576Z"
},
{
"id": 4,
"label": "Dogs",
"category": 1,
"published_at": "2022-05-25T13:42:12.270Z",
"created_at": "2022-05-25T13:42:12.271Z",
"updated_at": "2022-05-25T16:30:14.262Z"
}
]
},
{
"id": 2,
"label": "Art",
"created_at": "2022-05-25T13:36:45.994Z",
"updated_at": "2022-05-25T13:36:45.994Z",
"sub_categories": [
{
"id": 7,
"label": "Paint",
"category": 2,
"published_at": "2022-05-28T08:01:46.941Z",
"created_at": "2022-05-28T08:01:46.943Z",
"updated_at": "2022-05-28T08:01:46.948Z"
}
]
},
{
"id": 3,
"label": "Astrology",
"created_at": "2022-05-25T13:36:49.498Z",
"updated_at": "2022-05-25T13:36:49.498Z",
"sub_categories": [
{
"id": 8,
"label": "Taurus",
"category": 3,
"published_at": "2022-05-28T08:02:07.520Z",
"created_at": "2022-05-28T08:02:07.521Z",
"updated_at": "2022-05-28T08:02:07.526Z"
}
]
},
{
"id": 4,
"label": "Astronomy",
"created_at": "2022-05-25T13:36:54.035Z",
"updated_at": "2022-05-25T13:36:54.035Z",
"sub_categories": [
{
"id": 9,
"label": "Hubble",
"category": 4,
"published_at": "2022-05-28T08:02:14.747Z",
"created_at": "2022-05-28T08:02:14.748Z",
"updated_at": "2022-05-28T08:02:14.753Z"
}
]
},
{
"id": 5,
"label": "Cook",
"created_at": "2022-05-25T13:37:34.930Z",
"updated_at": "2022-05-25T13:37:34.930Z",
"sub_categories": [
{
"id": 5,
"label": "Plancha",
"category": 5,
"published_at": "2022-05-25T13:42:26.369Z",
"created_at": "2022-05-25T13:42:26.370Z",
"updated_at": "2022-05-25T16:30:23.512Z"
},
{
"id": 6,
"label": "Bakery",
"category": 5,
"published_at": "2022-05-25T16:31:49.887Z",
"created_at": "2022-05-25T16:31:49.890Z",
"updated_at": "2022-05-25T16:31:49.900Z"
}
]
},
{
"id": 6,
"label": "Crypto Currencies",
"created_at": "2022-05-25T13:37:43.113Z",
"updated_at": "2022-05-25T13:37:43.113Z",
"sub_categories": [
{
"id": 10,
"label": "Bitcoin",
"category": 6,
"published_at": "2022-05-28T08:02:52.814Z",
"created_at": "2022-05-28T08:02:52.815Z",
"updated_at": "2022-05-28T08:02:52.821Z"
}
]
},
{
"id": 7,
"label": "DIY",
"created_at": "2022-05-25T13:37:48.652Z",
"updated_at": "2022-05-25T13:37:48.652Z",
"sub_categories": [
{
"id": 11,
"label": "Tools",
"category": 7,
"published_at": "2022-05-28T08:03:05.030Z",
"created_at": "2022-05-28T08:03:05.031Z",
"updated_at": "2022-05-28T08:03:05.038Z"
}
]
},
{
"id": 8,
"label": "Ecology",
"created_at": "2022-05-25T13:37:53.776Z",
"updated_at": "2022-05-25T13:37:53.776Z",
"sub_categories": [
{
"id": 12,
"label": "Planet",
"category": 8,
"published_at": "2022-05-28T08:03:19.915Z",
"created_at": "2022-05-28T08:03:19.916Z",
"updated_at": "2022-05-28T08:03:19.922Z"
}
]
},
{
"id": 9,
"label": "Economy",
"created_at": "2022-05-25T13:37:59.384Z",
"updated_at": "2022-05-25T13:37:59.384Z",
"sub_categories": [
{
"id": 13,
"label": "Wall Street",
"category": 9,
"published_at": "2022-05-28T08:03:41.942Z",
"created_at": "2022-05-28T08:03:41.943Z",
"updated_at": "2022-05-28T08:03:41.948Z"
}
]
},
{
"id": 10,
"label": "Literature",
"created_at": "2022-05-25T13:38:07.354Z",
"updated_at": "2022-05-25T13:38:07.354Z",
"sub_categories": [
{
"id": 14,
"label": "Zola",
"category": 10,
"published_at": "2022-05-28T08:03:59.314Z",
"created_at": "2022-05-28T08:03:59.316Z",
"updated_at": "2022-05-28T08:03:59.322Z"
}
]
},
{
"id": 11,
"label": "Mode",
"created_at": "2022-05-25T13:38:32.915Z",
"updated_at": "2022-05-25T13:38:32.915Z",
"sub_categories": [
{
"id": 15,
"label": "Squirt",
"category": 11,
"published_at": "2022-05-28T08:04:10.245Z",
"created_at": "2022-05-28T08:04:10.246Z",
"updated_at": "2022-05-28T08:08:02.312Z"
}
]
},
{
"id": 12,
"label": "Movies",
"created_at": "2022-05-25T13:38:36.502Z",
"updated_at": "2022-05-25T13:38:36.502Z",
"sub_categories": [
{
"id": 16,
"label": "Tarantino",
"category": 12,
"published_at": "2022-05-28T08:04:48.519Z",
"created_at": "2022-05-28T08:04:48.520Z",
"updated_at": "2022-05-28T08:04:48.526Z"
}
]
},
{
"id": 13,
"label": "Music",
"created_at": "2022-05-25T13:38:41.227Z",
"updated_at": "2022-05-25T13:38:41.227Z",
"sub_categories": [
{
"id": 17,
"label": "Rock",
"category": 13,
"published_at": "2022-05-28T08:05:20.575Z",
"created_at": "2022-05-28T08:05:20.576Z",
"updated_at": "2022-05-28T08:05:20.580Z"
}
]
},
{
"id": 14,
"label": "Nature",
"created_at": "2022-05-25T13:38:46.772Z",
"updated_at": "2022-05-25T13:38:46.772Z",
"sub_categories": [
{
"id": 18,
"label": "Forest",
"category": 14,
"published_at": "2022-05-28T08:05:32.566Z",
"created_at": "2022-05-28T08:05:32.567Z",
"updated_at": "2022-05-28T08:05:32.572Z"
}
]
},
{
"id": 15,
"label": "Politic",
"created_at": "2022-05-25T13:38:52.016Z",
"updated_at": "2022-05-25T13:38:52.016Z",
"sub_categories": [
{
"id": 19,
"label": "Vote",
"category": 15,
"published_at": "2022-05-28T08:05:38.045Z",
"created_at": "2022-05-28T08:05:38.046Z",
"updated_at": "2022-05-28T08:05:38.052Z"
}
]
},
{
"id": 16,
"label": "Sciences",
"created_at": "2022-05-25T13:38:58.047Z",
"updated_at": "2022-05-25T13:38:58.047Z",
"sub_categories": [
{
"id": 20,
"label": "Chemistry",
"category": 16,
"published_at": "2022-05-28T08:05:51.520Z",
"created_at": "2022-05-28T08:05:51.521Z",
"updated_at": "2022-05-28T08:05:51.528Z"
},
{
"id": 21,
"label": "Chemistery",
"category": 16,
"published_at": "2022-05-28T08:05:54.625Z",
"created_at": "2022-05-28T08:05:54.627Z",
"updated_at": "2022-05-28T08:05:54.631Z"
}
]
},
{
"id": 17,
"label": "Series",
"created_at": "2022-05-25T13:39:02.864Z",
"updated_at": "2022-05-25T13:39:02.864Z",
"sub_categories": [
{
"id": 22,
"label": "GOT",
"category": 17,
"published_at": "2022-05-28T08:06:06.350Z",
"created_at": "2022-05-28T08:06:06.351Z",
"updated_at": "2022-05-28T08:06:06.355Z"
}
]
},
{
"id": 18,
"label": "Sexuality",
"created_at": "2022-05-25T13:39:10.901Z",
"updated_at": "2022-05-25T13:39:10.901Z",
"sub_categories": [
{
"id": 23,
"label": "Couple",
"category": 18,
"published_at": "2022-05-28T08:06:33.963Z",
"created_at": "2022-05-28T08:06:33.964Z",
"updated_at": "2022-05-28T08:06:33.968Z"
}
]
},
{
"id": 19,
"label": "Social Life",
"created_at": "2022-05-25T13:39:16.760Z",
"updated_at": "2022-05-25T13:39:16.760Z",
"sub_categories": [
{
"id": 24,
"label": "Interractions",
"category": 19,
"published_at": "2022-05-28T08:06:55.099Z",
"created_at": "2022-05-28T08:06:55.099Z",
"updated_at": "2022-05-28T08:06:55.104Z"
}
]
},
{
"id": 20,
"label": "Sport",
"created_at": "2022-05-25T13:39:21.296Z",
"updated_at": "2022-05-25T13:39:21.296Z",
"sub_categories": [
{
"id": 25,
"label": "Football",
"category": 20,
"published_at": "2022-05-28T08:07:13.972Z",
"created_at": "2022-05-28T08:07:13.973Z",
"updated_at": "2022-05-28T08:07:13.979Z"
}
]
},
{
"id": 21,
"label": "Theater",
"created_at": "2022-05-25T13:39:26.063Z",
"updated_at": "2022-05-25T13:39:26.063Z",
"sub_categories": [
{
"id": 26,
"label": "Molière",
"category": 21,
"published_at": "2022-05-28T08:07:22.217Z",
"created_at": "2022-05-28T08:07:22.219Z",
"updated_at": "2022-05-28T08:07:22.224Z"
}
]
},
{
"id": 22,
"label": "Vehicules",
"created_at": "2022-05-25T13:39:30.859Z",
"updated_at": "2022-05-25T13:39:30.859Z",
"sub_categories": [
{
"id": 27,
"label": "Audi",
"category": 22,
"published_at": "2022-05-28T08:07:30.514Z",
"created_at": "2022-05-28T08:07:30.515Z",
"updated_at": "2022-05-28T08:07:30.522Z"
}
]
},
{
"id": 23,
"label": "Video Games",
"created_at": "2022-05-25T13:39:36.926Z",
"updated_at": "2022-05-25T13:39:36.926Z",
"sub_categories": [
{
"id": 28,
"label": "Elden Ring",
"category": 23,
"published_at": "2022-05-28T08:07:38.335Z",
"created_at": "2022-05-28T08:07:38.339Z",
"updated_at": "2022-05-28T08:07:38.343Z"
}
]
}
]
My classes generated by QuickType:
class Category {
Category({
required this.id,
required this.label,
required this.createdAt,
required this.updatedAt,
required this.subCategories,
});
int id;
String label;
DateTime createdAt;
DateTime updatedAt;
List<SubCategory> subCategories;
factory Category.fromRawJson(String str) =>
Category.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Category.fromJson(Map<String, dynamic> json) => Category(
id: json["id"],
label: json["label"],
createdAt: DateTime.parse(json["created_at"]),
updatedAt: DateTime.parse(json["updated_at"]),
subCategories: List<SubCategory>.from(
json["sub_categories"].map((x) => SubCategory.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"id": id,
"label": label,
"created_at": createdAt.toIso8601String(),
"updated_at": updatedAt.toIso8601String(),
"sub_categories":
List<dynamic>.from(subCategories.map((x) => x.toJson())),
};
}
class SubCategory {
SubCategory({
required this.id,
required this.label,
required this.category,
required this.publishedAt,
required this.createdAt,
required this.updatedAt,
});
int id;
String label;
int category;
DateTime publishedAt;
DateTime createdAt;
DateTime updatedAt;
factory SubCategory.fromRawJson(String str) =>
SubCategory.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory SubCategory.fromJson(Map<String, dynamic> json) => SubCategory(
id: json["id"],
label: json["label"],
category: json["category"],
publishedAt: DateTime.parse(json["published_at"]),
createdAt: DateTime.parse(json["created_at"]),
updatedAt: DateTime.parse(json["updated_at"]),
);
Map<String, dynamic> toJson() => {
"id": id,
"label": label,
"category": category,
"published_at": publishedAt.toIso8601String(),
"created_at": createdAt.toIso8601String(),
"updated_at": updatedAt.toIso8601String(),
};
}
And this is my code:
List<Category> categoryFromJson(String str) =>
List<Category>.from(json.decode(str).map((x) => Category.fromJson(x)));
String categoryToJson(List<Category> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
Future<List<Category>> getCategories() async {
var response =
await http.get(Uri.parse("http://192.168.0.10:1337/categories/"));
if (response.statusCode == 200) {}
var decodedData = jsonDecode(response.body);
final category = categoryFromJson(decodedData);
print(category);
return category;
}
Thank you for your awnsers :)

API providing list of data, try using
Future<List<Category>?> loadData() async {
//use http insted of `rootBundle`
final response = await rootBundle.loadString("assets/j.json");
var decodedData = jsonDecode(response) as List;
final items = decodedData.map((e) => Category.fromJson(e)).toList();
return items;
}

Related

Order list sorting according to the annual income only when both cgpa were same

I got a collection but I want to sort that collection. If the two application's cgpa is equal then sort the list collection according to the annual_salary where lower will up at list. Default the collection sort with the cgpa. below is my server response.
Thanks in advance
"applications": [
{
"id": 1,
"name": "Dr. W Khan",
"annual_salary": 5000,
"created_at": "2022-11-07T19:16:01.000000Z",
"updated_at": "2022-11-07T19:16:01.000000Z",
"education": [
{
"id": 1,
"application_id": 1,
"name": "HSC",
"cgpa": 400,
"created_at": "2022-11-07T19:16:01.000000Z",
"updated_at": "2022-11-07T19:16:01.000000Z"
}
]
},
{
"id": 2,
"name": "Dr. M Khan",
"annual_salary": 7000,
"created_at": "2022-11-07T19:16:14.000000Z",
"updated_at": "2022-11-07T19:16:14.000000Z",
"education": [
{
"id": 2,
"application_id": 2,
"name": "HSC",
"cgpa": 350,
"created_at": "2022-11-07T19:16:14.000000Z",
"updated_at": "2022-11-07T19:16:14.000000Z"
}
]
},
{
"id": 3,
"name": "Dr.",
"annual_salary": 5000,
"created_at": "2022-11-07T19:16:28.000000Z",
"updated_at": "2022-11-07T19:16:28.000000Z",
"education": [
{
"id": 3,
"application_id": 3,
"name": "HSC",
"cgpa": 350,
"created_at": "2022-11-07T19:16:28.000000Z",
"updated_at": "2022-11-07T19:16:28.000000Z"
}
]
}
]
This is the query for me.
$application = Application::with(
[
'education' => function ($query) {
$query->orderBy('cgpa', 'desc');
},
]
)
->orderBy('annual_salary', 'asc')
->get();
But ordering the list followed by a second orderBy.

Flutter dynamic data from api to make Widgets

In my case the json will be different every time and i have to make widget like "inputtype": "dropdown", "inputtype": "radiobutton" but if i craete model it don't go as i wants it to.
{
"fields": [
{
"id": 31,
"name": "make",
"isrequired": "required",
"valuetype": "text",
"priority": 1,
"inputtype": "dropdown",
"max_min": [],
"rangeable": "false",
"choices": [
{
"id": 46,
"name": "Samsung",
"categoryextrafield_id": 31,
"created_at": "2021-12-29T01:30:47.000000Z",
"updated_at": "2021-12-29T01:30:47.000000Z",
"priority": 10
},
{
"id": 47,
"name": "Dell",
"categoryextrafield_id": 31,
"created_at": "2021-12-29T01:30:52.000000Z",
"updated_at": "2021-12-29T01:30:52.000000Z",
"priority": 20
},
{
"id": 48,
"name": "IBM",
"categoryextrafield_id": 31,
"created_at": "2021-12-29T01:31:09.000000Z",
"updated_at": "2021-12-29T01:31:09.000000Z",
"priority": 30
},
{
"id": 49,
"name": "Acer",
"categoryextrafield_id": 31,
"created_at": "2021-12-29T01:31:24.000000Z",
"updated_at": "2021-12-29T01:31:24.000000Z",
"priority": 40
}
],
"available": []
},
{
"id": 32,
"name": "model",
"isrequired": "required",
"valuetype": "text",
"priority": 2,
"inputtype": "textfield",
"max_min": [],
"rangeable": "false",
"choices": [],
"available": [
{
"model": "a51"
},
{
"model": "y9s"
},
{
"model": "a31"
},
{
"model": "yS10"
},
{
"model": "Y10S"
},
{
"model": "A551"
},
{
"model": "node8"
},
{
"model": "s9"
},
{
"model": null
},
{
"model": "2021"
},
{
"model": "2020"
},
{
"model": "2010"
},
{
"model": "Civic"
},
{
"model": "2019"
},
{
"model": "Daewooy9"
},
{
"model": "corei5"
},
{
"model": "corei9"
},
{
"model": "corei3"
},
{
"model": "corei11"
}
]
},
{
"id": 29,
"name": "features",
"isrequired": "required",
"valuetype": "text",
"priority": 3,
"inputtype": "checkbox",
"max_min": [],
"rangeable": "false",
"choices": [
{
"id": 41,
"name": "Bluetooth",
"categoryextrafield_id": 29,
"created_at": "2021-12-29T01:19:00.000000Z",
"updated_at": "2021-12-29T01:19:00.000000Z",
"priority": 1
},
{
"id": 42,
"name": "Fingerprint",
"categoryextrafield_id": 29,
"created_at": "2021-12-29T01:19:10.000000Z",
"updated_at": "2021-12-29T01:19:10.000000Z",
"priority": 10
},
{
"id": 43,
"name": "LedDisplay",
"categoryextrafield_id": 29,
"created_at": "2021-12-29T01:19:35.000000Z",
"updated_at": "2021-12-29T01:19:35.000000Z",
"priority": 15
}
],
"available": []
},
{
"id": 30,
"name": "condition",
"isrequired": "required",
"valuetype": "text",
"priority": 4,
"inputtype": "radiobutton",
"max_min": [],
"rangeable": "false",
"choices": [
{
"id": 44,
"name": "Used",
"categoryextrafield_id": 30,
"created_at": "2021-12-29T01:20:31.000000Z",
"updated_at": "2021-12-29T01:20:31.000000Z",
"priority": 10
},
{
"id": 45,
"name": "New",
"categoryextrafield_id": 30,
"created_at": "2021-12-29T01:20:38.000000Z",
"updated_at": "2021-12-29T01:20:38.000000Z",
"priority": 20
}
],
"available": []
}
]
}

How to parse THIS json reply in react native

I'am a noob in react native and i'am writing my first app to read a response from an API. Till now i know how to make the call and get the response and parse the regular replies in an array in the state, but i'am not finding the right way to parse the json reply of this specific form. I tried saving it in an array but its not working.
{
"activities": [
{
"id": 19,
"files": [
{
"id": 63,
"created_at": "15/05/2020 15:09:34",
"updated_at": "15/05/2020 15:09:34",
"file": "/media/activities/newFile133.11531268506258.jpg",
"is_default": false,
"activity": 19
},
{
"id": 65,
"created_at": "15/05/2020 15:10:15",
"updated_at": "15/05/2020 15:10:15",
"file": "/media/activities/newFile203.0997630588359.jpg",
"is_default": false,
"activity": 19
},
{
"id": 64,
"created_at": "15/05/2020 15:09:53",
"updated_at": "15/05/2020 15:10:27",
"file": "/media/activities/newFile193.8056151444231.jpg",
"is_default": true,
"activity": 19
},
{
"id": 62,
"created_at": "15/05/2020 15:08:56",
"updated_at": "15/05/2020 15:10:27",
"file": "/media/activities/newFile39.839136995440484.jpg",
"is_default": false,
"activity": 19
}
],
"created_at": "15/05/2020 14:52:54",
"updated_at": "15/05/2020 14:58:01",
"title": "Camping",
"description": "Camp Al Wilayah",
"category": 10
},
{
"id": 21,
"files": [
{
"id": 66,
"created_at": "15/05/2020 15:16:02",
"updated_at": "15/05/2020 15:16:02",
"file": "/media/activities/newFile46.35084778104737.jpg",
"is_default": true,
"activity": 21
},
{
"id": 67,
"created_at": "15/05/2020 15:16:33",
"updated_at": "15/05/2020 15:16:33",
"file": "/media/activities/newFile49.28140513312763.jpg",
"is_default": false,
"activity": 21
}
],
"created_at": "15/05/2020 15:11:22",
"updated_at": "15/05/2020 15:11:22",
"title": "Aashoura",
"description": "Majlis Aazaa",
"category": 12
},
{
"id": 18,
"files": [
{
"id": 68,
"created_at": "15/05/2020 15:21:18",
"updated_at": "15/05/2020 15:21:18",
"file": "/media/activities/newFile71.35101359514769.jpg",
"is_default": true,
"activity": 18
},
{
"id": 69,
"created_at": "15/05/2020 15:22:42",
"updated_at": "15/05/2020 15:22:42",
"file": "/media/activities/newFile107.17719628795922.jpg",
"is_default": false,
"activity": 18
}
],
"created_at": "15/05/2020 14:52:11",
"updated_at": "15/05/2020 15:17:34",
"title": "Takliff",
"description": "Taklif ceremony",
"category": 7
},
{
"id": 22,
"files": [
{
"id": 70,
"created_at": "15/05/2020 15:42:18",
"updated_at": "15/05/2020 15:43:03",
"file": "/media/activities/97026390_682458148963323_2038839267676913664_n.mp4",
"is_default": true,
"activity": 22
}
],
"created_at": "15/05/2020 15:24:46",
"updated_at": "15/05/2020 15:24:46",
"title": "Laylat Qader",
"description": "Event of Laylat al Qader",
"category": 6
},
{
"id": 23,
"files": [
{
"id": 73,
"created_at": "15/05/2020 17:42:43",
"updated_at": "15/05/2020 17:42:43",
"file": "/media/activities/newFile47.470692661344614.jpg",
"is_default": false,
"activity": 23
},
{
"id": 74,
"created_at": "15/05/2020 18:30:05",
"updated_at": "26/05/2020 21:34:09",
"file": "/media/activities/newFile28.504968134645907.jpg",
"is_default": false,
"activity": 23
},
{
"id": 72,
"created_at": "15/05/2020 15:51:29",
"updated_at": "15/07/2020 07:49:27",
"file": "/media/activities/newFile82.57124842095388.jpg",
"is_default": false,
"activity": 23
},
{
"id": 71,
"created_at": "15/05/2020 15:50:59",
"updated_at": "15/07/2020 07:50:09",
"file": "/media/activities/newFile3.0461490605067763.jpg",
"is_default": true,
"activity": 23
}
],
"created_at": "15/05/2020 15:50:36",
"updated_at": "15/07/2020 07:46:38",
"title": "Zayyana Al Dar",
"description": "Young generation posts",
"category": 11
}
],
"total": 5
}
Firstly, entire response is inside object bracket {}.
So, response.activities will give array of a activities.
Then use it like var x = response.activities
var first = x[0]
var second = x[1]
and so on .
It was my bad for checking the "activities" array in the state by "console.log(this.state.activities)" in the ".then" that is right after the setState function. i thought the code won't move to the second ".then" unless done with the first, so it was getting parsed in the way rock stated but printed in the console before that.

unable to get desired json format while creating laravel 5 api

i have the following Template model and TestCategory hasMany Test:
class Template extends Model
{
protected $table = 'test_templates';
protected $fillable = [
'customers_id',
'tests_categories_id',
'tests_id'
];
public function customers(){
return $this->belongsTo(Customer::class, 'customers_id');
}
public function testCategories(){
return $this->belongsTo(TestCategory::class, 'tests_categories_id');
}
public function tests(){
return $this->belongsTo(Test::class, 'tests_id');
}
}
And I have the following index method defined in TemplateController
public function index($customerId)
{
$templates = Template::with( 'customers','testCategories','tests')
->where('customers_id', $customerId)->get();
$templates = $templates->groupBy('tests_categories_id');
$templates = json_decode($templates, true);
$templates = array_values($templates);
return response()->json(['Status' => True, 'template' => $templates]);
}
I got the following json output:
{
"Status": true,
"template": [
[
{
"id": 1,
"customers_id": 1,
"tests_categories_id": 1,
"tests_id": 1,
"created_at": "2018-09-10 11:03:44",
"updated_at": "2018-09-10 11:03:44",
"customers": {
"id": 1,
"name": "Queen PVT.Ltd",
"address": "gotham",
"contact_number": "9842********",
"created_at": "2018-09-10 11:03:43",
"updated_at": "2018-09-10 11:03:43"
},
"test_categories": {
"id": 1,
"name": "General Tests",
"created_at": null,
"updated_at": null
},
"tests": {
"id": 1,
"tests_category_id": 1,
"name": "Lamination",
"created_at": null,
"updated_at": null
}
},
{
"id": 2,
"customers_id": 1,
"tests_categories_id": 1,
"tests_id": 2,
"created_at": "2018-09-10 11:03:44",
"updated_at": "2018-09-10 11:03:44",
"customers": {
"id": 1,
"name": "Queen PVT.Ltd",
"address": "gotham",
"contact_number": "984*******",
"created_at": "2018-09-10 11:03:43",
"updated_at": "2018-09-10 11:03:43"
},
"test_categories": {
"id": 1,
"name": "General Tests",
"created_at": null,
"updated_at": null
},
"tests": {
"id": 2,
"tests_category_id": 1,
"name": "Paper Type",
"created_at": null,
"updated_at": null
}
}
],
[
{
"id": 7,
"customers_id": 1,
"tests_categories_id": 2,
"tests_id": 8,
"created_at": "2018-09-10 11:03:44",
"updated_at": "2018-09-10 11:03:44",
"customers": {
"id": 1,
"name": "Queen PVT.Ltd",
"address": "gotham",
"contact_number": "984******",
"created_at": "2018-09-10 11:03:43",
"updated_at": "2018-09-10 11:03:43"
},
"test_categories": {
"id": 2,
"name": "Scratch Test",
"created_at": null,
"updated_at": null
},
"tests": {
"id": 8,
"tests_category_id": 2,
"name": "HRN Visibility",
"created_at": null,
"updated_at": null
}
}
]
]
}
I want to collect all the tests as per the test_categories without reprinting customers and test_categories as following:
{
"Status": true,
"template": [
[
{
"id": 1,
"customers_id": 1,
"tests_categories_id": 1,
"tests_id": 1,
"created_at": "2018-09-10 11:03:44",
"updated_at": "2018-09-10 11:03:44",
"customers": {
"id": 1,
"name": "Queen PVT.Ltd",
"address": "gotham",
"contact_number": "984*****",
"created_at": "2018-09-10 11:03:43",
"updated_at": "2018-09-10 11:03:43"
},
"test_categories": {
"id": 1,
"name": "General Tests",
"created_at": null,
"updated_at": null
},
"tests": {
{
"id": 1,
"tests_category_id": 1,
"name": "Lamination",
"created_at": null,
"updated_at": null
},
{
"id": 2,
"tests_category_id": 1,
"name": "Paper Type",
"created_at": null,
"updated_at": null
}
}
}
],
[
{
"id": 7,
"customers_id": 1,
"tests_categories_id": 2,
"tests_id": 8,
"created_at": "2018-09-10 11:03:44",
"updated_at": "2018-09-10 11:03:44",
"customers": {
"id": 1,
"name": "Queen PVT.Ltd",
"address": "gotham",
"contact_number": "984265*****",
"created_at": "2018-09-10 11:03:43",
"updated_at": "2018-09-10 11:03:43"
},
"test_categories": {
"id": 2,
"name": "Scratch Test",
"created_at": null,
"updated_at": null
},
"tests": {
{
"id": 8,
"tests_category_id": 2,
"name": "HRN Visibility",
"created_at": null,
"updated_at": null
}
}
}
]
]
}
If I understand the problem correctly the tests are being separated into different templates when you are expecting some of them to be related to the same template.
This is because your Template class is storing the test_id that it belongs to and it can only store 1 test per Template. So when you are calling Template::with('tests') it can only return one Test::class.
Because you expect to see that many Tests belongTo one Template, and when you look at the relationship from the Template to Test, you expect that a Template has many Tests. Therefore, your Template class should implement the hasMany relationship like so:
class Template extends Model
{
protected $fillable = [
'customers_id',
'tests_categories_id' // notice the removal of the test_id which should also be removed from the test_templates migration
];
...
public function tests(){
return $this->hasMany(Test::class); //change belongsTo to hasMany
}
...
}
Instead of storing the test_id on the Template class you should be storing the template_id on your Test class and look something like so:
class Test extends Model
{
protected $fillable = [
...
'test_template_id' // This should be added to $fillable and the tests migration
...
];
...
public function template(){
return $this->belongsTo(Template::class, test_template_id);
}
...
}
Now when you call Template::with('tests') it can return many Test::class and should match your expected output.

lumen order by with two level relation

this is a task manager web app
i have 3 tables rows, tasks and inputs
the inputs table has order column to choice which column to be shown first
row has many tasks
input has many tasks
so every task has an input and a row
i want to sort the tasks by row_id and then by input.order
first try
App\Row::with(['user', 'tasks', 'tasks.option', 'tasks.input'])
->orderBy('rows.id', 'ASC', 'order', 'ASC')
->get();
it ignores the order
result
[
{
"id": 1,
"user_id": 1,
"created_at": "2018-06-07 18:40:23",
"updated_at": "2018-06-07 18:40:23",
"tasks": [
{
"id": 1,
"row_id": 1,
"input_id": 1,
"option_id": 1,
"value": null,
"input": {
"id": 1,
"name": "assigned to",
"type": 1,
"value": "mario",
"required": 1,
"order": 2,
"user_id": 1,
"created_at": "2018-06-07 18:40:16",
"updated_at": "2018-06-08 10:08:19"
}
},
{
"id": 2,
"row_id": 1,
"input_id": 2,
"option_id": null,
"value": "test new option",
"input": {
"id": 2,
"name": "test new option",
"type": 0,
"value": "test new option",
"required": 0,
"order": 3,
"user_id": 1,
"created_at": "2018-06-07 18:40:44",
"updated_at": "2018-06-08 10:08:19"
}
},
{
"id": 3,
"row_id": 1,
"input_id": 3,
"option_id": null,
"value": "2018-07-01",
"input": {
"id": 3,
"name": "deadline",
"type": 3,
"value": "2018-07-01",
"required": 0,
"order": 4,
"user_id": 1,
"created_at": "2018-06-08 10:07:37",
"updated_at": "2018-06-08 10:08:19"
}
},
{
"id": 4,
"row_id": 1,
"input_id": 4,
"option_id": null,
"value": "",
"input": {
"id": 4,
"name": "priority",
"type": 6,
"value": "",
"required": 0,
"order": 1,
"user_id": 1,
"created_at": "2018-06-08 10:08:19",
"updated_at": "2018-06-08 10:08:19"
}
}
]
}
]
second try
App\Row::with(['user', 'tasks', 'tasks.option', 'tasks.input'])
->orderBy('rows.id', 'ASC')
->orderBy('order', 'ASC')
->get();
but it generated an error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'order' in 'order clause' (SQL: select * from `rows` order by `rows`.`id` asc, `order` asc)
third try
App\Row::with([
'user',
'tasks',
'tasks.option',
'tasks.input' =>
function($query) {
$query->orderBy('order', 'ASC');
}
])
->orderBy('rows.id', 'ASC')
->get();
it ignores the order result same as first
forth try
return App\Row::with(['user', 'tasks', 'tasks.option', 'tasks.input'])
->join('tasks', 'rows.id', '=', 'tasks.row_id')
->join('inputs', 'inputs.id', '=', 'tasks.input_id')
->orderBy('rows.id', 'ASC')
->orderBy( 'inputs.order', 'ASC')
->get();
it generate row for every column and not row as it suppose to
result
[
{
"id": 4,
"user_id": 1,
"created_at": "2018-06-08 10:08:19",
"updated_at": "2018-06-08 10:08:19",
"row_id": 1,
"input_id": 4,
"option_id": null,
"value": "",
"name": "priority",
"type": 6,
"required": 0,
"order": 1,
"tasks": []
},
{
"id": 1,
"user_id": 1,
"created_at": "2018-06-07 18:40:16",
"updated_at": "2018-06-08 10:08:19",
"row_id": 1,
"input_id": 1,
"option_id": 1,
"value": "mario",
"name": "assigned to",
"type": 1,
"required": 1,
"order": 2,
"tasks": [
{
"id": 1,
"row_id": 1,
"input_id": 1,
"option_id": 1,
"value": null,
"input": {
"id": 1,
"name": "assigned to",
"type": 1,
"value": "mario",
"required": 1,
"order": 2,
"user_id": 1,
"created_at": "2018-06-07 18:40:16",
"updated_at": "2018-06-08 10:08:19"
}
},
{
"id": 2,
"row_id": 1,
"input_id": 2,
"option_id": null,
"value": "test new option",
"input": {
"id": 2,
"name": "test new option",
"type": 0,
"value": "test new option",
"required": 0,
"order": 3,
"user_id": 1,
"created_at": "2018-06-07 18:40:44",
"updated_at": "2018-06-08 10:08:19"
}
},
{
"id": 3,
"row_id": 1,
"input_id": 3,
"option_id": null,
"value": "2018-07-01",
"input": {
"id": 3,
"name": "deadline",
"type": 3,
"value": "2018-07-01",
"required": 0,
"order": 4,
"user_id": 1,
"created_at": "2018-06-08 10:07:37",
"updated_at": "2018-06-08 10:08:19"
}
},
{
"id": 4,
"row_id": 1,
"input_id": 4,
"option_id": null,
"value": "",
"input": {
"id": 4,
"name": "priority",
"type": 6,
"value": "",
"required": 0,
"order": 1,
"user_id": 1,
"created_at": "2018-06-08 10:08:19",
"updated_at": "2018-06-08 10:08:19"
}
}
]
},
{
"id": 2,
"user_id": 1,
"created_at": "2018-06-07 18:40:44",
"updated_at": "2018-06-08 10:08:19",
"row_id": 1,
"input_id": 2,
"option_id": null,
"value": "test new option",
"name": "test new option",
"type": 0,
"required": 0,
"order": 3,
"tasks": []
},
{
"id": 3,
"user_id": 1,
"created_at": "2018-06-08 10:07:37",
"updated_at": "2018-06-08 10:08:19",
"row_id": 1,
"input_id": 3,
"option_id": null,
"value": "2018-07-01",
"name": "deadline",
"type": 3,
"required": 0,
"order": 4,
"tasks": []
}
]
this file is here
the full code is here
Because it searches columns in table rows not in input.
App\Row::with(['user', 'tasks', 'tasks.option', 'tasks.input'=> function ($q){
$q->orderBy('order', 'ASC'); // order from "rows" table
}])
->orderBy('id', 'ASC') // id from "rows" table
->get();