Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

kendrick's avatar

BadMethodCallException: Method update does not exist

Trying to update my Auth:user() Product with update method, but always results in Error. User has many products() / Product belongsTo business().

$this->validate($request, [

            'title'=> 'max:25',
            'brand'=> 'max:25',
            'sku'=> 'max:6',
            'description'=> 'max:140',
            'price'=> 'max:10',
            'availability'=> 'max:10',

        ]);

        $product = auth()->user()->products()->findOrFail($id);

        $product->update([

            'title' => $request->input('title'),
            'brand' => $request->input('brand'),
            'sku' => $request->input('sku'),
            'description' => $request->input('description'),
            'price' => $request->input('price'),
            'availability' => $request->input('availability'),

        ]);

Am I missing something within my Controller? Btw...

Auth::user()->products()->update([
            'title' => $request->input('title'),
            'brand' => $request->input('brand'),
            'sku' => $request->input('sku'),
            'description' => $request->input('description'),
            'price' => $request->input('price'),
            'availability' => $request->input('availability'),
]);

...updated every User related product to the same values. So the problem is within the Product specification.

Product.php

public function business()
    {
        return $this->belongsTo(Business::class, 'business_id');
    }

Business.php

public function products(){
        return $this->hasMany(Product::class);
    }

Tried it this way, too, which results in: BadMethodCallException: Method fill does not exist.

$product = auth()->user()->products()->findOrFail($id);

$product->fill([

    'title' => $request->input('title'),
    'brand' => $request->input('brand'),
    'sku' => $request->input('sku'),
    'description' => $request->input('description'),
    'price' => $request->input('price'),
    'availability' => $request->input('availability'),

])->save($product);

Any other suggestions? Thank you

0 likes
48 replies
MiguelBarros's avatar

This way doesn´t work ?

$user = auth()->user();
$product = $user->products()->findOrFail($id);

$product->update([

    'title' => $request->input('title'),
    'brand' => $request->input('brand'),
    'sku' => $request->input('sku'),
    'description' => $request->input('description'),
    'price' => $request->input('price'),
    'availability' => $request->input('availability'),

]);

If this don´t work you could do something like this:


$product->title = request()->input('title');
$product->brand= request()->input('brand');
$product->sku= request()->input('sku');
$product->description= request()->input('description');
$product->price= request()->input('price');
$product->availability= request()->input('availability');

$user->push();

1 like
kendrick's avatar

First thanks @jorpedito

It doesn't work.

These are my routes

Route::get('products/{product}/edit', [
    'uses' => 'EditController@getProductEdit', 
    'as' => 'products.edit',
    'middleware' => ['auth'],
    ]);
Route::post('/edit', [
    'uses' => 'EditController@postProductEdit', 
    'middleware' => ['auth'],
    ]);

Form:

<form class="form-vertical" role="form" action="/edit" method="POST">

Anything obvious?

mfoote's avatar

Did you run php artisan route:list and verify the routes are valid? /edit seems lacking. it is trying to post to a method called update() when your route calls postProductEdit(). To me it appears to be using the wrong route.

2 likes
kendrick's avatar

@mfoote You are right. The post method route for /edit is not listed.

This is the Controller with postProductEdit


    public function postProductEdit(Request $request, Product $id){
        $this->validate($request, [

            'title'=> 'max:140',
            'brand'=> 'max:140',
            'sku'=> 'max:140',
            'description'=> 'max:140',
            'price'=> 'max:140',
            'availability'=> 'max:140',
        ]);

        $product = Auth::user()->products()->findOrFail($id);

        $product->update([

            'title' => $request->input('title'),
            'brand' => $request->input('brand'),
            'sku' => $request->input('sku'),
            'description' => $request->input('description'),
            'price' => $request->input('price'),
            'availability' => $request->input('availability'),
        ]);

        return redirect()
            ->route('products.edit', compact('product'));
}
mfoote's avatar

What version of Laravel are you on? if 5+ try

Route::group(['middleware' => 'auth'], function () {
  Route::post('/product/{product}/update', 'EditController@postProductEdit');
});

and in your form...

<form class="form-vertical" role="form" action="/product/<the id of product>/update" method="POST">
public function postProductEdit(Request $request, Product $product){
        $this->validate($request, [

            'title'=> 'max:140',
            'brand'=> 'max:140',
            'sku'=> 'max:140',
            'description'=> 'max:140',
            'price'=> 'max:140',
            'availability'=> 'max:140',
        ]);

        $product->update($request->all());

        return redirect()
            ->route('products.edit', compact('product'));
}

You should look into the resourcing built into routes in laravel, it builds the routes for you in a standard format. With it you can do...

Route::resource('product', 'ProductController')

And it will build all the routes needed to manage a resource.

MiguelBarros's avatar

@splendidkeen u need to pass the product on that route

The way i normally do routes i would do :

Route::resource('product', 'ProductController');

If you use this way the form would be this:

<form class="form-vertical" role="form" action="{{route('product.store', [$product->id])}}" method="POST">

{{method_field('PUT')}}
mfoote's avatar

I did some editing after I looked at your controller more.

Seriously though, look at route resourcing, its amazing. you can create a model and while you do it, prebuild your migration and controller/methods with one command.

Route::resource('product', 'ProductController')

and

php artisan make:model Product -mcr

you are half done!

MiguelBarros's avatar

@splendidkeen see the example i give above , don´t forget to put that {{ method_field('PUT') }} otherwise will not work on update a product

mfoote's avatar

@jorpedito They aren't resourcing the route, so it has to be done in a non-standard way, splendidkeen's controller is not called ProductController. My mention of resourcing was just so splendidkeen was aware it could be done that way.

1 like
Snapey's avatar

Hang on, just skimming this.

If you have the product ID, why complicate things? Just get the product by its id and update it ?

$this->validate($request, [

            'title'=> 'max:25',
            'brand'=> 'max:25',
            'sku'=> 'max:6',
            'description'=> 'max:140',
            'price'=> 'max:10',
            'availability'=> 'max:10',

        ]);

        $product = Product::findOrFail($id);

        $product->update([

            'title' => $request->input('title'),
            'brand' => $request->input('brand'),
            'sku' => $request->input('sku'),
            'description' => $request->input('description'),
            'price' => $request->input('price'),
            'availability' => $request->input('availability'),

        ]);
2 likes
Snapey's avatar

Also,

If you want to also check of the user owns this product you can put a constraint on it

$product = Product::where('user_id',Auth::id())->findOrFail($id);
1 like
kendrick's avatar

Thank you @Snapey This results in undefined variable: id When I add (Request $request, Product $id) it will get back to Method update() does not exist.

public function postProductEdit(Request $request) {

        
$this->validate($request, [

            'title'=> 'max:25',
            'brand'=> 'max:25',
            'sku'=> 'max:6',
            'description'=> 'max:140',
            'price'=> 'max:10',
            'availability'=> 'max:10',

        ]);

        $product = Product::findOrFail($id);

        $product->update([

            'title' => $request->input('title'),
            'brand' => $request->input('brand'),
            'sku' => $request->input('sku'),
            'description' => $request->input('description'),
            'price' => $request->input('price'),
            'availability' => $request->input('availability'),

        ]);

        return redirect()
            ->route('products.edit', compact('product'));
}
Route::get('/products/{product}/edit', [
    'uses' => 'ProductEditController@getProductEdit', 
    'as' => 'products.edit',
    'middleware' => ['auth'],
    ]);
Route::post('/edit', [
    'uses' => 'ProductEditController@postProductEdit', 
    'middleware' => ['auth'],
    ]);
Snapey's avatar

because you are not passing the product ID in the edit route. Is it in a hidden form field?

I would expect more like;

Route:

Route::post('/edit/{id}', [
    'uses' => 'ProductEditController@postProductEdit', 
    'middleware' => ['auth'],
    ]);

Controller;

public function postProductEdit(Request $request, $id) {

        
$this->validate($request, [

            'title'=> 'max:25',
            'brand'=> 'max:25',
            'sku'=> 'max:6',
            'description'=> 'max:140',
            'price'=> 'max:10',
            'availability'=> 'max:10',

        ]);

    $product = Product::where('user_id',Auth::id())->findOrFail($id);

        $product->update([

            'title' => $request->input('title'),
            'brand' => $request->input('brand'),
            'sku' => $request->input('sku'),
            'description' => $request->input('description'),
            'price' => $request->input('price'),
            'availability' => $request->input('availability'),

        ]);

        return redirect()
            ->route('products.edit', compact('product'));
}

But I see that others have already posted similar

1 like
kendrick's avatar

@Snapey

Now its giving me:

Call to undefined method Illuminate\Database\Query\Builder::getNameOrUsername()

the error occurs on a navigation.blade.php, which is not even used on this Auth: system side. This is weird

Snapey's avatar

Did the record get updated?

1 like
kendrick's avatar

No @Snapey, just returns the Call to undefined method Illuminate\Database\Query\Builder Error.

Snapey's avatar

is getNameOrUsername() something in your code?`

Just make sure your user is still logged in

1 like
kendrick's avatar

Yes, getNameOrUsername() is used, when I'm logged in as a normal User. But now I am logged in as a Business, with protected routes. That is why I am wondering, because it returns this error from the navigation.blade.php of normal users. @Snapey

Also logged-in/out again, but still returns the same

Snapey's avatar

Changing this controller should have no effect.

Perhaps you are failing validation and then something is upset when the page is reloaded.

1 like
kendrick's avatar

Where could the validation fail? At the beginning it changed the values but for every business related product, that is why I am wondering about the error.

@Snapey

Snapey's avatar

I can't explain how changing something here breaks something totally unrelated in your code.

Its your code, without the whole project I can't guess how they might be related.

If you really are stuck, dd in this controller at the start, and then move to the end to see if it hits it before the error is thrown.

1 like
kendrick's avatar

Understand @Snapey

So if I dd, after validate

dd($request);

it will have a ParameterBag with the array of the new values

Next step is below $product

$product = Product::where('business_id', Auth::id())->findOrFail($id);

dd($product);

results in the same Error, again.

This is the url: /edit/%7Bid%7D

Snapey's avatar

What does your route look like?

1 like
kendrick's avatar

Sorry for coming back @Snapey I might have found the problem.

When I dd($product) like so

$product = Auth::user()->products()->first();

dd($product);

it returns the correct product information and also updates the correct product (always the first one)

When I dd($product) like so

$product = Auth::user()->products()->findOrFail($id);

dd($product);

it returns a Collection of items[]

Both with Routes:

Route::get('/product/{product}/edit', [
    'uses' => 'ProductController@getProductEdit', 
    'as' => 'partner.profile.editproduct',
    'middleware' => ['auth:partner'],
    ]);

Route::post('/edited', [
    'uses' => 'ProductController@postProductEdit', 
    'middleware' => ['auth:partner'],
    ]);

But, when I change the POST Route like so

Route::post('/edited/{id}'

it ends up on my (User) system as a logged in User, with the info that this page could not be found, even though the Route is protected with the Partner guard.

mfoote's avatar

Just do, $product = Product::findOrFail($id); unless I am missing something, why do you need the user?

Snapey's avatar

One of us has lost the plot...

$product = Auth::user()->products()->first();

should make $product an instance of Product object equivalent to the first of the logged in user's products.

$product = Auth::user()->products()->findOrFail($id);

should make $product an instance of Product object equivalent to the product with the $id passed, but only if that product belongs to the user.(@mfoote)

Neither should return a collection of items

When you post the form data to the server you need to state what product is being updated so your Route::post('/edited', will not know which product to update.

If you are confused you should try and stick to the REST resource principles as these will tell you what to do in each circumstance. Check the table under this heading https://laravel.com/docs/5.5/controllers#resource-controllers

If you edit a product, you should;

PUT to /products/{product} and call anupdate method on your controller passing in $product

1 like
kendrick's avatar

Thank you again @Snapey Updated the routes according to the Resource Controller Table

Route::get('/product/{product}/edit', [
    'uses' => 'ProductController@getProductEdit', 
    'as' => 'products.edit',
    'name' => 'products.edit',
    'middleware' => ['auth:partner'],
    ]);

Route::post('/product/{product}', [
    'uses' => 'ProductController@postProductEdit', 
    'name' => 'products.update',
    'middleware' => ['auth:partner'],
    ]);

It will again return the error: Method update does not exist: at Collection->__call('update', array(array('title' => 'Title Collection'.......

Controller:

$product = Product::findOrFail($id);
                $product->update([

                    'title' => $request->input('title'),
                    'brand' => $request->input('brand'),
                    'sku' => $request->input('sku'),
                    'description' => $request->input('description'),
                    'price' => $request->input('price'),
                    'availability' => $request->input('availability'),

                ]);

I am not sure, what the problem is, but could there be a potential of failure because my Users will access the products via this route:


// Product Route

Route::get('/products/{product}', [
    'uses' => 'ProductController@show', 
    'as' => 'products.show',
    
]);

Next

Please or to participate in this conversation.