Creating Laravel DTO from request with optional fields - laravel

I'm using Spatie Laravel-data to create data objects to pass in and out of an API. I'm hitting an issue trying to create a DTO from a POST request - there are certain fields that won't be passed in for an INSERT, the obvious one being ID.
I'm failing validation each time, with the following error
{
"message": "The given data was invalid.",
"errors": {
"id": [
"The id field is required."
]
}
}
My DTO class follows these instructions and looks like this:
class MemberData extends Data
{
public function __construct(
public int|Optional $id,
#[Max(255)]
public string $first_name,
#[Max(255)]
public string $last_name,
)
{}
}
How can I get past validation without providing an ID? Or should I be creating a different DTO for an INSERT?

Problem solved. If I had listened to the complaints from my IDE, I would have realised that Optional is a class that needs to be included.
Same goes for the attribute validation (Max)
Adding this to the top of the data object fixed the issue.
use Spatie\LaravelData\Optional;
use Spatie\LaravelData\Attributes\Validation\Max;

Related

Laravel BelongsTo works fine but in Nova not displaying data

I have an application with many question responses. I am not using the traditional foreign key naming conventions but when I query either object, the relationships pull up correctly. Using the current Laravel Nova v4.
class Application extends Model
{
public function questionResponses(): HasMany
{
return $this->hasMany(QuestionResponse::class, 'application_ec_id', 'ec_id');
}
}
class QuestionResponse extends Model
{
public function application(): BelongsTo
{
return $this->belongsTo(Application::class, 'application_ec_id', 'ec_id');
}
}
Within Laravel Nova, when I open an Application, I see the references to multiple question responses. However when I open an individual QuestionResponse, the application relationship just shows a hyphen like it's missing; it does not display the Application reference.
This is from the Application Nova Resource
HasMany::make('Question Responses'), // WORKS
But this does not work in the QuestionResponse Resource
BelongsTo::make('Application'), // does not work
BelongsTo::make('Application', 'application', Application::class), // doesn't work either
Text::make('application ec id'), // just to make sure the correct ID is in place
Even when I check the browser console, I see this request pulling the correct data. I am not sure what else to check.
// this URL returns a fields array and the correct record ID for application is present
domain.test/nova-api/question-responses/58649?relationshipType=

Passing exact parameters to Web API in ASP.NET Core

I have written a Web API in ASP.NET Core, for which I need to pass 2 parameters; of them one is a string with grade, the other is of type list of studentInfo as shown here:
[HttpPost]
[Route("UpdateActiveStudents")]
public Response UpdateActiveStudents(string grade, [FromBody] List<StudentsInfo> lststudents)
{
try
{
// My Logic
}
catch(Exception ex)
{
resp.flag = false;
resp.message = ex.Message;
}
return resp;
}
To test this API, I used ARC (Advanced Rest Client). I passed the data as like this in a POST request:
{
"grade": "B",
"lststudents": [
{ "StudentName": "abcdef", "RollNo": "user1"},
{ "StudentName": "abcdef", "RollNo": "user1"}
]
}
It throws a HTTP 400 status error with the following message :
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[SchoolHub.Model.StudentList]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'lststudents', line 2, position 13.
I'm unaware of this exception.
You have this issue because you are not sending the data in the format at which ASP.Net Web API expects. ASP.net Web API needs some special format when dealing with a value like string and value type (int, bool, etc) parameter are marked with FromBody attribute.
Just remove FromBody it will work. For better understanding go with this link.
Why do we have to specify FromBody and FromUri?
In Web API, general parameter binding rules for POST method are as follows -
Query string -> Primitive type
Request body -> Complex type
Now if you want to use POST method with Mixed parameters i.e in your case you are passing primitive (string) and complex (List), Web API will get the grade parameter from query string and student parameter from the request body.
Possible solutions to try -
In the ARC request it seems you are passing grade in request body instead of as a query string parameter. Try passing grade as a query string parameter.
Also add a class viz. StudentInfoRequest to wrap List<StudentsInfo> lststudents and then use StudentInfoRequest object to pass as parameter to UpdateActiveStudents method.
You dont need to mention [FromBody] in UpdateActiveStudents method as by default complex parameters are read from the request body by Web API.
Hope this helps!
You must add [ApiController] in the controller level. Code is as follows
[ApiController]
[Route("[controller]")]
public class studentController : ControllerBase
{
[HttpPost]
[Route("UpdateActiveStudents")]
public Response UpdateActiveStudents(string grade, List<StudentsInfo>
lststudents)
{
//code
}
}

Spring Data JPA and Spring Web Updating Entities Avoiding Associations

I have the following entities:
Area
Listing
They are both many-to-many:
An area can have many listings
A listing can have many areas
Both Area and Listing have other fields like name, domain, etc.
I'm using Spring Web RestController as a way to update the entities.
For example:
#PutMapping("/{id}")
public Area update(#PathVariable Long id, #RequestBody Area update) {
return areaRepository.save(update);
}
However, as an Area can have many thousands of Listings, it's not practical to pass them all in the update request when I just want to update the Area name and other basic fields in my web application.
For example, the update json in the http request would be:
{
"id" : 69,
"name" : "San Francisco",
"domain" : "foo",
...
}
When serialised, the area instance above will have a listings field equal to null (obviously) and then when saved, all association are remove from this Area.
I'm thinking that I could do a select-then-update set of operations and only update the values necessary but that is cumbersome - especially when there are many dozens of non-association fields.
The question would be: how can I try to keep to the above code and http request and not remove all of the existing Listing associations when saving the input area? Is this possible? I want to update the name and other basic fields but not the association fields.
You can use the BeanUtilBean(org.apache.commons.beanutils.BeanUtilsBean).
Step 1: Create custom beanutilbean class.
#Component
public class CustomBeanUtilsBean extends BeanUtilsBean {
#Override
public void copyProperty(Object dest, String name, Object value)
throws IllegalAccessException, InvocationTargetException {
if(value==null)return;
super.copyProperty(dest, name, value);
}
}
Step 2: In your controller while updating. first get the Area from database & use copyProperties method as shown in below.
#PutMapping("/{id}")
public Area update(#PathVariable Long id, #RequestBody Area update) {
Area areaDB = areaRepository.findOne(update.getId());
customBeanUtilsBean.copyProperties(areaDB,update);//it will set not null field values of update to areaDB.
return areaRepository.save(areaDB);
}
Hope this will helps you..:)
Since you are using spring-data-jpa repository you can write a new method that takes the changed values of Area object with #Modifying and #Query annotations on it.
In the #Query you specify the HQL query with update statement as in here

Why does Eloquent change relationship names on toArray call?

I'm encountering an annoying problem with Laravel and I'm hoping someone knows a way to override it...
This is for a system that allows sales reps to see inventory in their territories. I'm building an editor to allow our sales manager to go in and update the store ACL so he can manage his reps.
I have two related models:
class Store extends Eloquent {
public function StoreACLEntries()
{
return $this->hasMany("StoreACLEntry", "store_id");
}
}
class StoreACLEntry extends Eloquent {
public function Store()
{
return $this->belongsTo("Store");
}
}
The idea here is that a Store can have many entries in the ACL table.
The problem is this: I built a page which interacts with the server via AJAX. The manager can search in a variety of different ways and see the stores and the current restrictions for each from the ACL. My controller performs the search and returns the data (via AJAX) like this:
$stores = Store::where("searchCondition", "=", "whatever")
->with("StoreACLEntries")
->get();
return Response::json(array('stores' => $stores->toArray()));
The response that the client receives looks like this:
{
id: "some ID value",
store_ac_lentries: [
created_at: "2014-10-14 08:13:20"
field: "salesrep"
id: "1"
store_id: "5152-USA"
updated_at: "2014-10-14 08:13:20"
value: "salesrep ID value"
]
}
The problem is with the way the StoreACLEntries name is mutilated: it becomes store_ac_lentries. I've done a little digging and discovered it's the toArray method that's inserting those funky underscores.
So I have two questions: "why?" and "how do I stop it from doing that?"
It has something in common with automatic changing camelCase into snake_case. You should try to change your function name from StoreACLEntries to storeaclentries (lowercase) to remove this effect.

Play Framework: automatic validation of controller methods applied?

I have some issue with validation of parameters passed to a controller method.
Following the suggestion from the tutorial, I am using the same controller method for "save" and "create new" of an entity. See example in
http://www.playframework.org/documentation/1.2.4/guide9
So, my controller method looks like:
public static void saveEntity(long l, Long itemId,
#Required(message="error.shouldspecifyname") String name,
#Required(message="error.shouldspecifycategory") String category)
If 'itemId' is not part of the data sent via an HTTP request - it is supposed to be set to 'null'.
Unfortunately, it seems like "Play" is automatically adding a validation error on the "missing" parameter.
When looking into the validation errors' list, every time 'itemId' is 'null' - I am getting an error Incorrect value for field itemId
Is it a documented behavior? Any way to override it, or "get rid" of the error.
I am handling the errors simply using redirection, like:
if(validation.hasErrors() )
{
validation.keep();
showSomePage();
}
So the errors are displayed "out of the context" they get generated. This is the reason the "automatic" error bothers me.
Thanks for any hint.
Most likely it fails to validate itemId because it's declared as Long, are you sure you have "Long" there ant not just "long"? We are using validation with controllers every where and it works with #Required and passed "null" to "Long" values.
Worst case you can remove error from validation object based on "itemId" key, also if you're using controller to save model object, you might want to use:
public static void saveEntity(#Required #Valid MyEntity entity) {
if(validation.hasErrors() ) {
validation.keep();
showSomePage();
}
entity.save();
}
It will automaticly hook your changes inside existing entity if you pass ID from page with:
<input type="hidden" name="myEntity.id" value="${myEntity.id}">

Resources