简体   繁体   中英

An exception has been thrown during the rendering of a template (“Unable to resolve dependency [Parameter #0 [ $listing ]]”)

I have decided to upgrade the Laravel framework version to resolve the critical security flaws. I have successfully upgraded from 5.8 to 6 and then to 6.20 (exploit fixed). But unfortunately I have encountered an error starting from Laravel framework 6.19.0 which throws an exception in my class (BuyWidget.php) which is related to BoundMethod.php.I have checked through the internet and found out they implemented check for non specified types in BoundMethod.php (Illuminate\Container\BoundMethod).

I tried to change all the functions in BuyWidget.php accordingly: public function run($listing) to public function run(Listing $listing) etc.

BuyWidget.php

<?php

namespace App\Widgets\Order;

use Arrilot\Widgets\AbstractWidget;
use App\Models\Listing;

class BuyWidget extends AbstractWidget
{
    protected $config = [];
    
    public function calculate_price($listing, $params) {
        $fee_percentage = setting('marketplace_percentage_fee');
        $fee_transaction = setting('marketplace_transaction_fee');
        
        $quantity = isset($params['quantity'])?$params['quantity']:1;
        $variants = isset($params['variant'])?$params['variant']:null;
        $shipping = isset($params['shipping_option'])?$params['shipping_option']:null;
        $additional_options = isset($params['additional_option'])?$params['additional_option']:[];
        $additional_options_meta = isset($params['additional_options_meta'])?$params['additional_options_meta']:[];
        
        $listing_price = $listing->price;
        
        #calculate additional variant cost
        $selected_variant = null;
        $error = false;

        $user_choice = [];
        $user_choice[] = ['group' => 'general', 'name' => 'quantity', 'value' => $quantity];

        if($variants) {
            $variant_pricing = $listing->variants;
            foreach($variants as $k => $v) {
                $variant_pricing = $variant_pricing->where("meta.$k", $v);
                $user_choice[] = ['group' => 'variant', 'name' => $k, 'value' => $v];
            }
            
            if($variant_pricing->count() == 1) {
                $selected_variant = $variant_pricing->first();
                $listing_price += $selected_variant->price;
                if($quantity > $selected_variant->stock) {
                    $error = __('Insufficient stock. Please lower the quantity.');
                }               
                if($selected_variant->stock < 1) {
                    $error = __('Out of Stock');
                }
            }
        }
        
        #calculate shipping cost
        $selected_shipping_price = null;
        if(!is_null($shipping)) {
            $selected_shipping_method = $listing->shipping_options->firstWhere('id', $shipping)?:null;
            if($selected_shipping_method) {
                $selected_shipping_price = $selected_shipping_method->price;
            }
            $user_choice[] = ['group' => 'shipping', 'name' => 'Shipping', 'value' => $selected_shipping_method->name, 'price' => $selected_shipping_method->price];
        }

        #additional pricing
        $additional_options_price = $listing->additional_options->reduce(function ($carry, $item) use($additional_options, $additional_options_meta) {
            if(in_array($item->id, array_keys($additional_options))) {
                $price = $item->price;
                $quantity = 1;
                if(in_array($item->id, array_keys($additional_options_meta)) && isset($additional_options_meta[$item->id]['quantity'])) {               
                    $quantity = (int) $additional_options_meta[$item->id]['quantity'];
                }               
                return $carry + ($price*$quantity);
            }
                        
            return $carry;
        }, 0);

        $number = 0;
        foreach($listing->additional_options as $k => $item) {
            if(in_array($item->id, array_keys($additional_options))) {
                $number++;
                $user_choice[] = ['group' => 'additional_options', 'name' => 'Option '.($k+1), 'value' => $item->name, 'price' => $item->price];
            }
        }
        
        //date, time, qty
        $subtotal = ($quantity * $listing_price) + $additional_options_price;
        $service_fee_percentage = $subtotal * ($fee_percentage/100);
        $service_fee = (float) $service_fee_percentage + (float) $fee_transaction;
        $total = $subtotal + $service_fee + $selected_shipping_price;

        if($quantity > $listing->stock) {
            $error = __('Insufficient stock. Please lower the quantity.');
        }
        if($listing->stock < 1) {
            $error = __('Out of Stock');
        }
        
        //now check if we have any slots left for this time
        $price_items = [
            [
                'key'   => 'price',
                'label' => __(':price x :quantity :unit_label', ['price' => format_money($listing_price, $listing->currency), 'quantity' => $quantity, 'unit_label' => $listing->unit]),
                'price' => ($quantity * $listing_price)
            ]
        ];
        if($selected_shipping_price) {
            $price_items[] = [
                'key'   => 'service',
                'label' => __('Shipping'),
                'price' => $selected_shipping_price,
            ];
        }

        if($additional_options_price) {
            $price_items[] = [
                'key'   => 'additional',
                'label' => __('Additional options'),
                'price' => $additional_options_price,
            ];
        }

        if($service_fee > 0) {
            $price_items[] = [
                'key' => 'service',
                'label' => __('Service fee'),
                'price' => $service_fee,
                'notice' => __('This fee helps cover the costs of operating the website'),
            ];
        }
        
        return [
            'user_choice'   =>  $user_choice,
            'error'         =>  $error,
            'total'         =>  $total,
            'service_fee'   =>  $service_fee,
            'price_items'   =>  $price_items,
        ];
    
    }
    
    public function decrease_stock($order, $listing)
    {
        $quantity = $order->listing_options['quantity'];
        $listing->decrement( 'stock', $quantity );

        if(isset($order->listing_options['variant'])) {
            $variants = $order->listing_options['variant'];

            $listing_variants = $listing->variants;
            foreach($variants as $k => $v) {
                $listing_variants = $listing_variants->where("meta.$k", $v);
            }

            if($listing_variants->count() == 1) {
                $listing_variant = $listing_variants->first();
                $listing_variant->decrement( 'stock', $quantity );
            }
        }
    }

    public function validate_payment($listing, $request)
    {
        $result = $this->calculate_price($listing, request()->all());
        return $result;
    }

    
    /**
     * Treat this method as a controller action.
     * Return view() or other content to display.
     */
    public function run($listing)
    {
        //
        $total = 0;
        $quantity = request('quantity', 1);

        $result = $this->calculate_price($listing, request()->all());
        
        return view('listing.widgets.buy_widget', [
            'config'        => $this->config,
            'listing'       => $listing,
            'qs'            => http_build_query(request()->all()),
            'error'         => $result['error'],
            'total'         => $result['total'],
            'service_fee'   => $result['service_fee'],
            'price_items'   => $result['price_items'],
        ]);
    }
}

However after applying these changes the app does not work properly, the parameters are always empty and the product is shown as Out Of Stock.

The initial exception has been thrown at sidebar.twig file which builds up the widget based on the listing type.

"An exception has been thrown during the rendering of a template ("Unable to resolve dependency [Parameter #0 [ $listing ]] ")

sidebar.twig

{% if listing.pricing_model %}
    {{ Widget.run('Order.'~(listing.pricing_model.widget)~'Widget', {}, listing) | raw }}
{% endif %}

Code works flawlessly on any other versions before Laravel 6.19.0. If I manually revert the changes from file BoundMethod.php the code works as expected even on never vesions. I would like to find another solution to avoid modifying vendor files, since it's not the best approach.

web.php

include "admin.php";
include "payments.php";

Route::get('/cp', function () {
    if(env('DEMO_PANEL')) {
        \Auth::loginUsingId(1, true);
        return redirect("/panel");
    }
});

Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => 'jailBanned'], function()
{
    Auth::routes();
    Route::get('email-verification', 'Auth\EmailVerificationController@sendEmailVerification')->name('email-verification.send');
    Route::get('email-verification/error', 'Auth\EmailVerificationController@getVerificationError')->name('email-verification.error');
    Route::get('email-verification/check/{token}', 'Auth\EmailVerificationController@getVerification')->name('email-verification.check');

    Route::get('/', 'HomeController@index')->name('home');
    Route::get('/browse', 'BrowseController@listings')->name('browse');
    Route::get('/categories', 'BrowseController@categories')->name('categories');

    Route::get('/pages/{slug?}', 'PageController@index')->name('page');
    Route::get('/contact', 'ContactController@index')->name('contact');
    Route::post('/contact', 'ContactController@postIndex')->name('contact.post');

    Route::get('/profile/{user}', 'ProfileController@index')->name('profile'); //PROFILE
    Route::get('/profile/{user}/follow', 'ProfileController@follow')->name('profile.follow'); //PROFILE

    //LISTINGS
    Route::group(['prefix' => 'listing'], function()
    {
        Route::get('/{listing}/{slug}', 'ListingController@index')->name('listing');
        Route::get('/{listing}/{slug}/card', 'ListingController@card')->name('listing.card');
        Route::get('/{listing}/{slug}/spotlight', 'ListingController@spotlight')->middleware('auth.ajax')->name('listing.spotlight');
        Route::get('/{listing}/{slug}/verify', 'ListingController@verify')->middleware('auth.ajax')->name('listing.verify');
        Route::get('/{listing}/{slug}/star', 'ListingController@star')->middleware('auth.ajax')->name('listing.star');
        Route::get('/{listing}/{slug}/edit', 'ListingController@edit')->name('listing.edit');
        #Route::get('/{listing}/{slug}/availability', 'AvailabilityController@availability')->name('listing.availability');
        Route::any('/{id}/update', 'ListingController@update')->name('listing.update');

    });


    //ACCOUNT
    Route::group(['middleware' => ['auth', 'isVerified'], 'prefix' => 'account', 'as' => 'account.', 'namespace' => 'Account'], function()
    {
        Route::get('/', function () {
            return redirect(route('account.edit_profile.index'));
        });
        Route::resource('change_password', 'PasswordController');
        Route::resource('edit_profile', 'ProfileController');

        Route::resource('purchase-history', 'PurchaseHistoryController');
        Route::resource('favorites', 'FavoritesController');

        Route::resource('listings', 'ListingsController');
        Route::resource('orders', 'OrdersController');

        Route::get('payments/{id}/unlink', 'BankAccountController@unlink')->name('payments.unlink');
        Route::resource('bank-account', 'BankAccountController');

        Route::get('paypal/connect', 'PayPalController@connect')->name('paypal.connect');
        Route::get('paypal/callback', 'PayPalController@callback')->name('paypal.callback');

    });

    //REQUIRES AUTHENTICATION
    Route::group(['middleware' => ['auth', 'isVerified']], function () {

        //INBOX
        Route::resource('inbox', 'InboxController')->middleware('talk'); //Inbox
        Route::get('/inbox/messages/{id}', 'InboxController@messages')->name('inbox.messages');

        //CREATE LISTING
        Route::resource('create', 'CreateController');
        Route::any('/create/{listing}/session', 'CreateController@session')->name('create.session');
        Route::get('/create/{listing}/images', 'CreateController@images')->name('create.images');
        Route::get('/create/{listing}/additional', 'CreateController@additional')->name('create.additional');
        Route::get('/create/{listing}/pricing', 'CreateController@pricing')->name('create.pricing');
        Route::get('/create/{listing}/times', 'CreateController@getTimes')->name('create.times');
        Route::post('/create/{listing}/times', 'CreateController@postTimes')->name('create.times');
        Route::get('/create/{listing}/boost', 'CreateController@boost')->name('create.boost');

        Route::post('/create/{listing}/uploads', 'CreateController@upload')->name('create.upload');
        Route::delete('/create/{listing}/image/{uuid?}', 'CreateController@deleteUpload')->name('create.delete-image');

        //CHECKOUT
        Route::get('/checkout/error', 'CheckoutController@error_page')->name('checkout.error');
        Route::get('/checkout/{listing}', 'CheckoutController@index')->name('checkout');
        Route::post('/checkout/{listing}', 'CheckoutController@store')->name('checkout.store');
        Route::get('/checkout/{session}/callback', 'CheckoutController@callback')->name('checkout.callback');
        Route::any('/checkout/process/{listing}', 'CheckoutController@process')->name('checkout.process');
        #Route::any('/checkout/test', 'CheckoutController@test')->name('checkout.test');
        #Route::resource('stripe', 'StripeController');
        #Route::any('/stripe/connect', 'StripeController@connect')->name('stripe.connect');

        Route::any('/paypal/{listing}/start', 'PaypalController@start')->name('paypal.start');
        Route::any('/paypal/cancel', 'PaypalController@cancel')->name('paypal.cancel');
        Route::any('/paypal/callback', 'PaypalController@callback')->name('paypal.callback');
        Route::any('/paypal/confirm', 'PaypalController@confirm')->name('paypal.confirm');
        #Route::any('/paypal/create_agreement', 'PaypalController@create_agreement')->name('paypal.create_agreement');


    });

    //REQUIRES AUTHENTICATION
    Route::group(['middleware' => ['auth']], function () {

        Route::get('email-verification', 'Auth\EmailVerificationController@index')->name('email-verification.index');
        Route::get('resend-verification', 'Auth\EmailVerificationController@resend')->name('email-verification.resend');
        Route::get('email-verified', 'Auth\EmailVerificationController@verified')->name('email-verification.verified');

    });

    Route::get('login/facebook', 'Auth\LoginController@redirectToProvider');
    Route::get('login/facebook/callback', 'Auth\LoginController@handleProviderCallback');


});

#errors
Route::get('/suspended',function(){
    return 'Sorry something went wrong.';
})->name('error.suspended');

public function run(Listing $listing)

And you included a class in that namespace that matches Listing? Or is it available through service providers?

Remove parameter from function:

public function run();

and add the following line to the function:

$listing = func_get_arg(0);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM