upload image in laravel 8 - API

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company
# Uploading Images in Laravel 8 API: Fixing the `BadMethodCallException` and Mastering Polymorphic Relationships Building a robust API that handles file uploads and complex relational data, like one-to-one polymorphic setups, is a common task in modern web development. When you integrate file handling with Eloquent models in Laravel, developers often run into subtle errors related to how the framework manages requests versus direct file system operations. This post dives deep into the specific error you encountered while trying to upload an image and link it to a post, showing you exactly where the mistake lies and how to implement this functionality using idiomatic Laravel practices. ## The Problem: Why `Call to undefined method Image::move()` Fails You are attempting to perform a file system operation directly on your Eloquent model instance: `$image->move(public_path('images'), $imageName);`. This approach is incorrect for several reasons: 1. **Model Responsibility:** Eloquent models should primarily handle database interactions (CRUD operations). File storage and handling belong in the controller or a dedicated service layer, where you manage the incoming request data. 2. **Method Absence:** The `Image` model does not inherently possess a `move()` method. You are attempting to call a custom function on an object that hasn't been designed to handle file system operations. 3. **Security and Scope:** Directly manipulating paths like `public_path('images')` inside the controller bypasses Laravel’s robust file storage mechanisms. The error `BadMethodCallException: Call to undefined method App\Models\Image::move()` is a clear signal that you are mixing database logic with file system logic incorrectly. ## The Solution: Correct File Upload and Polymorphic Linking The correct way to handle file uploads in Laravel involves using the methods provided by the incoming `$request` object and leveraging Laravel’s storage facade. We need to separate three distinct steps: 1. Validate the request (including the file). 2. Store the uploaded file securely on the disk. 3. Save the record into the database, establishing the polymorphic link. ### Step 1: Setting Up the Models (Review) Your polymorphic setup using `morphTo` and `morphOne` is conceptually sound for linking posts to images: **Image Model:** ```php class Image extends Model { use HasFactory; protected $fillable = [ 'image' // Assuming this stores the file path/name ]; public function imageable(){ // This correctly defines the polymorphic relationship back to the parent model (Post) return $this->morphTo(); } } ``` **Post Model:** ```php class Post extends Model { use HasFactory; // ... other traits/methods public function image(){ // This correctly defines the one-to-one polymorphic relationship return $this->morphOne(Image::class , 'imageable'); } } ``` ### Step 2: Refactoring the Controller Logic We will use the `storeAs` or `store` methods on the uploaded file object to handle storage, and then save the resulting ID to the post record. We should leverage Laravel’s filesystem abstraction for this, which aligns perfectly with best practices advocated by developers working with frameworks like those found at [laravelcompany.com](https://laravelcompany.com). Here is the corrected `store` method: ```php use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; use App\Models\Post; use App\Models\Image; // Import the Image model public function store(Request $request) { // 1. Validation $validator = Validator::make($request->all(), [ 'user_id' => 'required', 'category_id' => 'required', 'title' => 'required|max:150|unique:posts', 'body' => 'required', 'study_time' => 'required', 'tags' => 'nullable|string', 'image' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048', // Crucial file validation ]); if ($validator->fails()) { return response()->json(['error' => $validator->errors(), 'error' => 'Validation failed'], 422); } // 2. Handle File Upload Safely $imageFile = $request->file('image'); // Store the image on the disk and get the path/filename // 'public' disk is standard for web-accessible files $path = $imageFile->store('post_images', 'public'); // 3. Create Image Record $image = Image::create([ 'image' => $path, // Store the path in the database ]); // 4. Create Post Record $post = Post::create([ 'user_id' => $request->user_id, 'category_id' => $request->category_id, 'title' => $request->title, 'body' => $request->body, 'study_time' => $request->study_time, // ... other fields ]); // 5. Establish Polymorphic Relation $post->image()->associate($image); // Link the post to the newly created image $post->save(); // Save tags (assuming you handle this separately or via a package) $tags = explode(",", $request->tags); $post->tags()->sync($tags); // Use sync for managing many-to-many if applicable, or create/assign appropriately. return response()->json([ 'success' => true, 'message' => 'Post and image uploaded successfully.', 'data' => $post ], 201); } ``` ## Conclusion The core lesson here is to avoid trying to implement file system logic directly on Eloquent models. Instead, treat the request data as input, use Laravel’s built-in storage utilities (like `$request->file()->store()`), and then use those results to create and link your database records. By focusing on clear data flow between the HTTP layer (Request), the persistence layer (Database/Eloquent), and the file system, you ensure your API is not only functional but also secure and maintainable. Always strive for these organized patterns when building sophisticated applications based on frameworks like Laravel.