I am working on a simple university project, a movie catalog, but I am stuck on a problem that I can’t find a solution for. When trying to update a movie, I get a 422 error, and I can’t figure out the problem. I created my API using Laravel 10, and I’m sharing my controller with you, specifically the update method, as that’s where I’m getting the error, along with my route:
MovieController.php
public function updateMovie(Request $request, $id)
{
Log::info('Request data:', $request->all());
Log::info('Request files:', $request->file());
if (!$request->has('title')) {
Log::error('Title is missing');
}
if (!$request->has('synopsis')) {
Log::error('Synopsis is missing');
}
if (!$request->has('year')) {
Log::error('Year is missing');
}
$validatedData = $request->validate([
'title' => 'required|string|max:255',
'synopsis' => 'required|string',
'year' => 'required|integer|min:1900|max:' . date('Y'),
'cover' => 'nullable|file|mimes:jpeg,png,jpg,gif|max:2048',
]);
api.php
Route::put('/movies/{id}', [MovieController::class, 'updateMovie']);
I think the problem is not with my API but with my client. I’m using Angular 16 for the frontend. Now, I’m going to show you my edit component and the configuration of my service:
movie.edit.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { MovieService } from '../../services/movie.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-movie-edit',
templateUrl: './movie-edit.component.html',
styleUrls: ['./movie-edit.component.scss']
})
export class MovieEditComponent implements OnInit {
movieForm: FormGroup;
movieId!: number;
movie: any;
constructor(
private route: ActivatedRoute,
private router: Router,
private movieService: MovieService,
private fb: FormBuilder
) {
this.movieForm = this.fb.group({
title: ['', Validators.required],
synopsis: ['', Validators.required],
year: ['', [Validators.required, Validators.min(1900), Validators.max(new Date().getFullYear())]],
cover: [null]
});
}
ngOnInit(): void {
this.movieId = Number(this.route.snapshot.paramMap.get('id'));
this.getMovieDetails();
}
getMovieDetails(): void {
this.movieService.getMovieById(this.movieId).subscribe({
next: (data) => {
this.movie = data;
this.movieForm.patchValue({
title: this.movie.title,
synopsis: this.movie.synopsis,
year: this.movie.year
});
},
error: (error) => {
console.error('Error fetching movie details', error);
}
});
}
onFileChange(event: any): void {
if (event.target.files.length > 0) {
const file = event.target.files[0];
this.movieForm.patchValue({
cover: file
});
}
}
onSubmit(): void {
if (this.movieForm.valid) {
const formData = new FormData();
const title = this.movieForm.get('title')?.value || '';
const synopsis = this.movieForm.get('synopsis')?.value || '';
const year = this.movieForm.get('year')?.value;
const cover = this.movieForm.get('cover')?.value;
console.log('Title:', title);
console.log('Synopsis:', synopsis);
console.log('Year:', year);
console.log('Cover:', cover);
formData.append('title', title);
formData.append('synopsis', synopsis);
if (year !== undefined && year !== null) {
formData.append('year', year.toString());
}
if (cover) {
formData.append('cover', cover);
}
formData.forEach((value, key) => {
console.log(key, value);
});
this.movieService.updateMovie(this.movieId, formData).subscribe({
next: () => {
this.router.navigate(['/movie', this.movieId]);
},
error: (error) => {
console.error('Error updating movie', error);
}
});
}
}
}
movie.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class MovieService {
private apiUrl = 'http://localhost:8000/api/movies';
constructor(private http: HttpClient) {}
getAllMovies(): Observable<any> {
return this.http.get(this.apiUrl);
}
getMovieById(id: number): Observable<any> {
return this.http.get(`${this.apiUrl}/${id}`);
}
createMovie(movie: any): Observable<any> {
const formData = new FormData();
formData.append('title', movie.title);
formData.append('synopsis', movie.synopsis);
formData.append('year', movie.year.toString());
formData.append('cover', movie.cover);
return this.http.post(this.apiUrl, formData);
}
updateMovie(id: number, formData: FormData): Observable<any> {
return this.http.put(`${this.apiUrl}/${id}`, formData);
}
deleteMovie(id: number): Observable<any> {
return this.http.delete(`${this.apiUrl}/${id}`);
}
}
It seems that the data is not reaching the backend; there appears to be an issue with how the FormData is being sent from Angular.
This is what it shows me in the log file:
[2024-10-08 00:33:17] local.INFO: Request data:
[2024-10-08 00:33:17] local.INFO: Request files:
[2024-10-08 00:33:17] local.ERROR: Title is missing
[2024-10-08 00:33:17] local.ERROR: Synopsis is missing
[2024-10-08 00:33:17] local.ERROR: Year is missing
Please help, I need to submit this activity in two weeks.
3
Answers
Since you’re using
FormData
, I think you need to set theContent-Type
header for yourPOST
andPUT
requests.And for future debugging, you can output variables into the log file.
You could try POST method with parameter _method="PUT" when submit as PHP not recognize the multipar-form-data correctly for uploading files with PUT method.
so change your submitting form method to POST and add in
formData.append(‘_method’, ‘PUT’);
please use Json data for submitting data from angular instead of formData…
{
"year": document.getElementByID(" year"),
….
….
}
Like this