skip to Main Content

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


  1. Since you’re using FormData, I think you need to set the Content-Type header for your POST and PUT requests.

    createMovie(movie: any): Observable<any> {
        ...
        const options = {
            headers: new HttpHeaders({ "Content-Type": "multipart/form-data" }),
        };
    
        return this.http.post(this.apiUrl, formData, options);
    }
    
    updateMovie(id: number, formData: FormData): Observable<any> {
        const options = {
            headers: new HttpHeaders({ "Content-Type": "multipart/form-data" }),
        };
    
        return this.http.put(`${this.apiUrl}/${id}`, formData, options);
    }
    

    And for future debugging, you can output variables into the log file.

    Log::debug($request->all());
    
    Login or Signup to reply.
  2. 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’);

    Login or Signup to reply.
  3. please use Json data for submitting data from angular instead of formData…
    {
    "year": document.getElementByID(" year"),
    ….
    ….
    }
    Like this

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search