4

I am trying to build a REST API with Laravel where users need to update their image. But, If I use PUT method in postman it does not update the image or store the image in designated folder. If I use POST instead it does update the image and also saves in the folder. But it does not save the file in database as I customize the name to store in DB. What could be the reason. Help is really necessary. TIA.

Update Code

public function update(Request $request, $id)
{
    $found = Partner::find($id);
    if (!$found) {
        return Response::json(['message' => 'Id not found'], 404);
    }
    $validatedData = Validator::make($request->all(), [
        'company_logo' => 'sometimes|mimes:jpg,png,jpeg|max:3048',
        'company_name' => 'sometimes|max:130',
        'company_email' => 'sometimes|email',
        'password'  =>  'sometimes|min:6',
        'phone'  =>  'sometimes|min:6|max:11',
        'address'  =>  'sometimes',
        'city'  =>  'sometimes|string',
        'country' =>  'sometimes|string' ,
        'business_category'  =>  'sometimes|string',
        'website_link' =>  'nullable|string',
        'facebook_page' =>  'nullable|string'
    ]);
    if ($validatedData->fails()) {
        return Response::json(['success' => false, 'message' => $validatedData->errors()], 400);
    }

    if ($request->hasFile('company_logo')) {
        $logo = $request->company_logo;
        $fileName = date('Y') . $logo->getClientOriginalName();
        $request->company_logo->storeAs('company_logo', $fileName, 'public');
        $found['company_logo'] = $fileName;
    }
    
    $found->update($request->all());

    return Response::json(['success' => true, 'message' => 'Partner updated successfully!', 
                           'updated_data' => $found], 200);
}

My route

Route::group(['prefix' => 'partner'], function () {
Route::post('store', [APIMemberController::class, 'store']);
Route::post('update/{id}', [APIPartnerController::class, 'update']); // This works
// Route::put('update/{id}', [APIPartnerController::class, 'update']); // This don't why?
});

Storing partner

public function store(Request $request)
{
    $validatedData = Validator::make($request->all(), [
        'company_logo' => 'required|mimes:jpg,png,jpeg|max:3048',
        'company_name' => 'required|max:130',
        'company_email' => 'required|email|unique:partners',
        'password'  =>  'required|min:6',
        'phone'  =>  'required|min:6|max:11',
        'address'  =>  'required',
        'city'  =>  'required|string',
        'country' =>  'required|string' ,
        'business_category'  =>  'required|string',
        'website_link' =>  'nullable|string',
        'facebook_page' =>  'nullable|string'
    ]);

    if ($validatedData->fails()) {
        return Response::json(['success' => false, 'message' => $validatedData->errors()], 400);
    }

    // $saved = Partner::create($request->all());

    if ($request->hasFile('company_logo')) {
        $logo = $request->file('company_logo');
        $fileName = date('Y') . $logo->getClientOriginalName();
        $request->company_logo->storeAs('company_logo', $fileName, 'public');
    }

    $partner = new Partner();
    
    $partner->company_logo = $request->company_logo->getClientOriginalName();
    $partner->company_name = $request->company_name;
    $partner->company_email = $request->company_email;
    $partner->password = $request->password;
    $partner->phone = $request->phone;
    $partner->address = $request->address;
    $partner->city = $request->city;
    $partner->country = $request->country;
    $partner->business_category = $request->business_category;
    $partner->website_link = $request->website_link;
    $partner->facebook_page = $request->facebook_page;
    $partner->save();
    return Response::json(['success' => 'Partner created successfully!', 'created_partner' => $partner], 201);


}
Jakaria Ridoy
  • 142
  • 2
  • 9

1 Answers1

4

I can see two changes which should get you the desired result with the update function

public function update(Request $request, $id)
{
    $found = Partner::find($id);
    if (!$found) {
        return Response::json(['message' => 'Id not found'], 404);
    }
    $validatedData = Validator::make($request->all(), [
        'company_logo' => 'sometimes|mimes:jpg,png,jpeg|max:3048',
        'company_name' => 'sometimes|max:130',
        'company_email' => 'sometimes|email',
        'password'  =>  'sometimes|min:6',
        'phone'  =>  'sometimes|min:6|max:11',
        'address'  =>  'sometimes',
        'city'  =>  'sometimes|string',
        'country' =>  'sometimes|string' ,
        'business_category'  =>  'sometimes|string',
        'website_link' =>  'nullable|string',
        'facebook_page' =>  'nullable|string'
    ]);
    if ($validatedData->fails()) {
        return Response::json(['success' => false, 'message' => $validatedData->errors()], 400);
    }

    if ($request->hasFile('company_logo')) {
        $logo = $request->company_logo;
        $fileName = date('Y') . $logo->getClientOriginalName();

    //Get the path to the folder where the image is stored 
    //and then save the path in database
        $path = $request->company_logo->storeAs('company_logo', $fileName, 'public');
        $found['company_logo'] = $path;
    }
    
    //update with all fields from $request except 'company_logo'

    $found->update($request->except('company_logo'));

    return Response::json(['success' => true, 'message' => 'Partner updated successfully!', 
                           'updated_data' => $found], 200);
}

Just to include it in the answer here:

If someone wants to use a PUT or PATCH request for form containing file uploads

<form action="/foo/bar" method="POST">
    @method('PUT')

    ...
</form>

via any javascript framework like vue

let data = new FormData;
data.append("_method", "PUT")

axios.post("some/url", data)

I have done that. But my question is now why PUT does not work for Laravel in update method?

It's not because of Laravel, it's a PHP thing/limitation. PHP doesn't populate the super global $_FILES during a PUT request as it does during a POST request.

To access a file upload via Put/ method in PHP see features.file-upload.put-method

Hence it's probably easier to make a POST request for file-uploads and use method spoofing to let Laravel know that the request is for a route corresponding to PUT method

Donkarnash
  • 12,433
  • 5
  • 26
  • 37
  • I have done that. But my question is now why PUT does not work for Laravel in update method? – Jakaria Ridoy Nov 18 '20 at 06:35
  • Though Thanks Very much for your answer – Jakaria Ridoy Nov 18 '20 at 06:36
  • If the given changes work for you to get the desired output, you can mark it as accepted answer or upvote it for the benefit of subsequent visitors. – Donkarnash Nov 18 '20 at 06:39
  • The post with put method must include id of the data otherwise it raised method not allowed `axios.post("some/url/{id}", data)` – Nurkartiko Feb 15 '21 at 17:40
  • @Nurkartiko Say if PUT route is defined as `Route::put('some/url/{id}', [SomeController::class, 'update']);`& POST request is made via axios with data containing `_method=PUT` like `axios.post('some/url', data)`. When Laravel tries to infer the incoming request, it will evaluate the url & try to match against declared routes, POST route for store will be matched, then it reads the `_method=PUT` key and tries to find a put route with the given url which is not found. So it will give the error that PUT method is not allowed for the given url "some/url" without id – Donkarnash Feb 15 '21 at 18:28
  • @Nurkartiko Url passed to `axios.post()` method must correspond to route declared for PUT method in routes file. In most cases PUT/PATCH routes are for updating an existing resource, so the route will most likely have a url like `/{id}` or `/{resourceId}//{id}` as per normal REST conventions :- `` can be the plural of a resource like users or posts `` may be plural for a nested resource like comments in case of Post comments. **id** is a route parameter used to identify which record of the resource is being updated – Donkarnash Feb 15 '21 at 18:31