In this lesson, we will apply all the things we have learned in the previous lesson and build a Transactions CRUD screen:



Creating a Model
Let's start by creating our new Transaction Model to match our Laravel Model and Database fields:
lib/models/transaction.dart
class Transaction { int id; int categoryId; String categoryName; String description; String amount; String transactionDate; String createdAt; Transaction( {required this.id, required this.categoryId, required this.categoryName, required this.description, required this.amount, required this.transactionDate, required this.createdAt}); factory Transaction.fromJson(Map<String, dynamic> json) { return Transaction( id: json['id'], categoryId: json['category_id'], categoryName: json['category_name'], description: json['description'], amount: json['amount'], transactionDate: json['transaction_date'], createdAt: json['created_at'], ); }}Adding API Functions
Next, we need to add...
I notice that ApiService::addTransaction() is sending 'category_id' as a String. Whereas the corresponding ApiService::updateTransaction() method is sending it as int.
The server side doesn't mind, with the 'required', 'integer' validation accepting both forms and storing the new transaction. The trouble begins when it in turn sends the response back using the same format it received.
Back on the Flutter side, in the addTransaction() case, the returned String finds its way to Transaction.fromJson() which attempts to put it into the int categoryId. Exception: type 'String' is not a subtype of type 'int'.
I'm not sure why you got that exception, since the Model (Transaction.dart) should transform it to int:
But worst case, this can be updated to have a check for
json['id']and adjust if needed (for example, cast to int).ps. I had the same doubt that it will fail, but doing testing on the course - I have not seen this happen myself
BTW: Thanks for replies. :)
I'm not sure I understand how json['category_id'] would be transformed without using int.parse() from occasional String to int to work for that constructor? This is where the exception I saw originates. (Though caught within transaction_provider.dart and output with a subtle print call.)
I'm trying to go solely on the basis of the code shown above in case my own code has some differences. I cannot explain how you didn't see the same.
But even if ignoring the Flutter code and poking requests direct with Postman, it doesn't seem right to me that different 201 responses should vary in format depending on what is sent. Would it be a good idea if the server (ultimate arbiter of the API) explicitly cast to int for category_id (and id) within TransactionResource::toArray()? Nice if more strict about what it receives too, probably.
Part of what we do is discuss things and potentially learn more! :)
As for your question - that's kinda the problem with php itself. And by that I mean that - there's no way to strictly cast something into something. For example, typically in JSON you have arrays
[]and objects{}. But here's a thing - as soon as you return some Laravel relation - it will become an object in JSON. Why? Because it has keys associated by default...So the same thing can happen here - it can be
intmost of the time and then for whatever reason trigger JSON to think that hey - this is astring. I've had that happen in other projects and they were a pain to manage...And while it is not a great solution - the only way to solve this - adding a ton of checks for the data. Or even mutations.
Surely that's a feature, not a problem? :)
The change to TransactionResource::toArray gives consistent results, at least in this case. And I think the explicit cast insertions help document what to expect from the API response.
It depends - in some cases that can be a problem haha.
Will check the issue you described and see if I can reproduce it consistently. Will update the course if thats the case!
in order for it to work for me, i had to parse the category when sending the json to add the transaction: 'category_id': int.parse(category).
without this, the transaction would be added without any issues, BUT, the transaction would not get added back to the transaction list and the notifyListeners() wouldn't run and update the widget list, so it looked like it wasn't added (it was added in the DB)
Yes, that sounds exactly like what I saw. And one of the possible quick fixes. Still feels a bit flaky that we are merely hoping the server ends up sending an int response because of this, rather than having that return type explicit as part of the API spec. Hence my preference for a cast on the server side.