I am sending data from ionic project using api. And receiving from laravel. My "Add" product api is working fine. But "Update" product api is not working.
I can see from browser inspection tool that Request payload got data which is being send to laravel. So, data is being send but laravel is not able to real that data.
This is update function for api.
public function update(Request $request, Product $product)
{
// Validate request
$validatedData = $request->validate([
'account_type' => 'required|in:1,2',
'name' => 'required|string|max:255',
'description' => 'nullable|string',
'price' => 'required|numeric|min:0'
]);
// Update product
$product->update($validatedData);
return response()->json($product);
}
This is giving following error
account_type [ "The account type field is required." ]
name [ "The name field is required." ]
price [ "The price field is required." ]
and so on..
Laravel unable to read data for some reason.
Update
<form [formGroup]="productForm" (ngSubmit)="onSubmit()">
<!-- Add Type -->
<ion-item>
<ion-label>Add Type <span class="required">*</span></ion-label>
<ion-radio-group formControlName="account_type">
<ion-item>
<ion-label>Individual</ion-label>
<ion-radio slot="start" value="1"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Business</ion-label>
<ion-radio slot="start" value="2"></ion-radio>
</ion-item>
</ion-radio-group>
</ion-item>
<!-- Ad Title -->
<ion-item>
<ion-label position="floating">Ad Title <span class="required">*</span></ion-label>
<ion-input formControlName="name" type="text"></ion-input>
</ion-item>
<!-- Submit Buttons -->
<div class="ion-padding">
<ion-button type="submit" expand="block" [disabled]="productForm.invalid">
{{ isEditMode ? 'Update Product' : 'Submit Now!' }}
</ion-button>
<ion-button expand="block" color="light">Preview</ion-button>
</div>
Didn’t add all field of form here to make it short.
TS FILE
onSubmit() {
if (this.productForm.valid) {
const formData = new FormData();
const formValues = this.productForm.value;
// Append all form data
Object.keys(formValues).forEach((key) => {
if (key !== 'image' && key !== 'additionalImages') {
formData.append(key, formValues[key]);
}
});
// Handle file inputs
const mainImage = document.querySelector('[formControlName="image"]') as HTMLInputElement;
if (mainImage && mainImage.files && mainImage.files[0]) {
formData.append('image', mainImage.files[0]);
}
const additionalImages = document.querySelector('[formControlName="additionalImages"]') as HTMLInputElement;
if (additionalImages && additionalImages.files) {
Array.from(additionalImages.files).forEach((file) => {
formData.append('additionalImages[]', file);
});
}
// Differentiate between add and update
if (this.isEditMode) {
const productId = Number(this.route.snapshot.paramMap.get('productId'));
this.productService.updateProductPost(productId, formData).subscribe(
(response) => {
console.log('Product updated successfully:', response);
this.router.navigate(['/success']);
},
(error) => {
console.error('Error updating product:', error);
}
);
} else {
// Get the query string parameter
const categoryId = this.route.snapshot.paramMap.get('categoryId'); // Extract categoryId from query string
console.log('Category ID:', categoryId);
// Get the sessionStorage value
const userId = sessionStorage.getItem('userId'); // Extract userId from sessionStorage
// Append query string and sessionStorage data to the formData
if (categoryId) {
formData.append('category_id', Number(categoryId).toString());
}
if (userId) {
formData.append('seller_id', Number(userId).toString());
}
this.productService.addProduct(formData).subscribe(
(response) => {
console.log('Product added successfully:', response);
this.router.navigate(['/success']);
},
(error) => {
console.error('Error adding product:', error);
}
);
}
} else {
console.error('Form is invalid');
}
}
PRODUCT.SERVICE.TS
//Add product (is working)
addProduct(product: FormData): Observable<Product> {
return this.http.post<Product>(`${this.apiUrl}/products`, product);
}
//update producd
updateProductPost(productId: number, product: FormData): Observable<Product> {
return this.http.put<Product>(`${this.apiUrl}/products-update/${productId}`, product);
}
Laravel route in api.php
Route::put('products-update/{id}', [ProductApi::class, 'update']);
2
Answers
This is the case of incorrectly defined
implicit model-binding
.When you use implicit
model-binding
you need to use the samename
for the Route parameter and for the injected model instance into the controller method(in your caseupdate(Request $request, Product $product)
).So change you route defination like this.
N:B :-
implicit model-binding
.The http method
PUT
doesnt necessarily carry a body by RFC standards.Library like axios or even servers like apache can completely ignore processing the body in a put request.
there are 2 workarounds:
1- Use POST for updates too:
As you can see, the 2 url structure are different from insert and update since one needs an id, so you can use post to update too
2- Use POST with the input
_method
:In laravel you can send a
POST
request and have it treated like aPUT
one if it has the input_method=PUT
.PRODUCT.SERVICE.TS
Any of the two solutions would work for your case.