Ben
Ben

Reputation: 315

Laravel 5.2 Controller method not found but appears in artisan route:list

I have an issue with one of my Laravel 5.2 routes/controllers, specifically I get the error of Controller method not found.

Route:

Route::get( 'guest/shop/{product}', 'GuestShopController@show' )->name( 'guest.shop.show' );

Controller and method:

class GuestShopController extends ShopController {
    public function __construct(  ) {
        $this->middleware( 'guest' );
    }
}

abstract class ShopController extends Controller {
    protected function singularProductData( $product ) {
        $thumbnails = $product->thumbnails();

        return [
            'product'        => $product,
            'thumbnails'     => $thumbnails,
            'main_thumbnail' => head( $thumbnails ),
        ];
    }

    protected function getProducts() {
        return Cache::remember(
            'products',
            3600,
            function () {
                return Product::active()->get();
            }
        );
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index() {
        return view( 'pages.shop.index' )->with(
            [
                'products'     => $this->getProducts(),
                'organisation' => request()->attributes->get( 'organisation' ),
            ]
        );
    }

    /**
     * Display the specified product.
     *
     * @param  string $slug
     * @param null $product
     *
     * @return \Illuminate\Http\Response
     */
    public function show( $slug, $product = null ) {
        if( ! is_a( $product, Product::class ) ) {
            $product = Product::active()->where( 'slug', $slug )->firstOrFail();
        }

        return view( 'pages.shop.product' )->with( $this->singularProductData( $product ) );
    }

    /**
     * Display the specified product modal.
     *
     * @param  int $id
     *
     * @return \Illuminate\Http\Response
     */
    public function modal( $id ) {
        $product = Product::active()->findOrFail( $id );

        if( request()->ajax() ) {
            return view( '_partials.shop.modal-content' )->with( $this->singularProductData( $product ) );
        }

        return $this->show( $product->slug, $product );
    }
}

Things I've already done when debugging:

Upvotes: 1

Views: 1106

Answers (2)

tnash
tnash

Reputation: 385

What url are you putting in the browser to be exact? You have

Route::get('guest/shop/{product}', 'GuestShopController@show')->name('guest.shop.show');

but show method expects 2 parameters $slug and an optional $product so the route should be

Route::get('guest/shop/{slug}/{product?}', 'GuestShopController@show')->name('guest.shop.show');

Otherwise if you only need the product the method and route should be as below:

    Route::get('guest/shop/{product?}', 'GuestShopController@show')->name('guest.shop.show');

public function show($product = null)
{

}

Upvotes: 2

Ben
Ben

Reputation: 315

@chikurubhi's answer was mostly correct. It led me to change the url structure and slightly re-jig the controller methods.

Route::get( 'guest/shop/modal/{productId}', 'GuestShopController@modal' )->name( 'guest.shop.modal' );
Route::get( 'guest/shop/{slug}', 'GuestShopController@show' )->name( 'guest.shop.show' );

And on abstract ShopController:

/**
 * Display the specified product.
 *
 * @param  string $slug
 * @param null $product
 *
 * @return \Illuminate\Http\Response
 */
public function show( $slug ) {
    $product = Product::active()->where( 'slug', $slug )->firstOrFail();

    return view( 'pages.shop.product' )->with( $this->singularProductData( $product ) );
}

/**
 * Display the specified product modal.
 *
 * @param  int $id
 *
 * @return \Illuminate\Http\Response
 */
public function modal( $productId ) {
    $product = Product::active()->findOrFail( $productId );

    if( request()->ajax() ) {
        return view( '_partials.shop.modal-content' )->with( $this->singularProductData( $product ) );
    }

    return redirect()->action( "{$this}@show", [ $product->slug ] );
}

I've now changed this to {slug} as the route parameter. After recently upgrading from 5.1 to 5.2, this may be a change in the framework that I wasn't aware of, that parameter names in the controller methods and routes must match? Anyway, fixed now and happy.

Because I have a route for modals that uses the same path, appended with modal, I've also changed this around so Laravel can't get confused by it. The modal route is now /guest/shop/modal/{productId}. The modal controller method now only looks up the product via the $productId parameter and if the request does not come via ajax, it redirects the user to the show method via the redirect()->action() helper.

Because of the similar structure of the modal and show routes, I ensured I placed the modal route first, otherwise it would always return a 404.

Upvotes: 0

Related Questions