Fix WooCommerce fatal error

User
<script>
jQuery(document).ready(function ($) {

      //Check for an entry cookie in the product's category
      <?php
      $product_cat = false;
      $product_id = $product->get_id();
      $product_cats = get_the_terms( $product->get_id(), 'product_cat' );
      if ( $product_cats ) {
        $product_cat = $product_cats[0]->term_id;
      }
      if ( $product_cat && !get_post_meta( $product_id, '_pmm_consultation_form_id', true ) ): ?>
        var categoryId = '<?php echo absint($product_cat); ?>';
        if ( Cookies.get('entry_category-' + categoryId) || ( document.cookie.indexOf('wp_user_logged_in') !== -1 && Cookies.get('logged_in_entry_category-' + categoryId) ) ) {
          $('.single_add_to_cart_button').text('Select your product');
        }
      <?php endif; ?>

      //Disable enter-to-submit on quantity input (stops adding to cart without going through consultation first)
      $('input[name=quantity]').on('keypress', function(){
        if (event.which == '13') {
          event.preventDefault();
          return false;
        }
      });

      //Custom add to cart button that requires consultation form
      $('#consult-first').on( 'click', function(e) {
         e.preventDefault();
         e.stopImmediatePropagation();

         //check that the button isn't disabled, and that we have a variation selected (this would be false in a re/loading state which sometimes happens on longer variation product loads)
         if ( !$(this).hasClass("disabled") && $('input[name="variation_id"]').val() > 0 ) {
            $(this).parents('form').submit();
        }
        //if someone clicks a disabled/loading button don't do anything
       return false;
      });

      if ( $('#form_id_override').length > 0 ) {
        $('.single_add_to_cart_button').on( 'click', function(event){
          event.preventDefault();
          event.stopImmediatePropagation();

          $(this).attr('name', '');
          $(this).parents('form').submit();
        });
      }

    });
</script>
<script>
jQuery(document).ready(function ($) {
          //Make sure we scroll to the top for the consultation entry display
          setTimeout(function(){
            if ( $('.returning').is(':visible') ) {
              window.onunload = function(){ window.scrollTo(0,0); }
            }
          },1);


          $('.reveal-consultation').on('click', function(event){
            event.preventDefault();
            $('.consultation-container').fadeIn();
            $('.changed-condition').fadeIn();
            $('.condition-choice').slideUp();
            return false;
          });

            //validate Gform
            var form = $('.gform_wrapper > form');
            $('.entry-content').on("click", "input[type=radio]", function (e) {

                if ( $('#product_id').val() != undefined ) {
                    //If any incorrect choices are visible it means the form is invalid so disable the submit button
                    if ( $('.choice_incorrect').is(':visible') == true ) {
                        $('.gform_button').addClass("disabled");
                    } else {
                        $('.gform_button').removeClass("disabled");
                    }
                }
            });

            /**
             * Show error message
             * @param {string} msg
             * @returns {void}
             */
            function showError(msg) {
              $('.validation_error').empty();
	            $('.validation_error').css({borderTop: "none", borderBottom: "none", padding: 0});
              $('.gform_heading').append('<div class="validation_error">' + msg + '</div>');
            }

			<?php
			//Set up a var that allows us to handle things differently if this is a pingdom bot
			$is_pingdom = isset( $_SERVER['HTTP_USER_AGENT'] ) ? stripos( $_SERVER['HTTP_USER_AGENT'], 'pingdom' ) : false;
			$variation_id = isset( $_POST['variation_id'] ) ? intval($_POST['variation_id']) : ''; ?>

        //Form submission
        form.find('input[type=submit]').click(function (e) {
            e.preventDefault();

            if (!formFilled()) {
                showError('Please fill all (*) required fields.')
                $('html, body').animate({scrollTop: form.offset().top - 100}, 'slow');
            } else if (!hasIncorrects()) {
					<?php
					//Redirect straight to add-to-cart without generating an entry if this is pingdom
					if ( $is_pingdom !== false) { ?>
						window.location = "<?php echo site_url("/cart/?add-to-cart=" . $variation_id ); ?>";
					<?php } else {

            //If a GET request, replace the history with GET params so we can keep our nice clean POST URL
            if ( !( isset($_GET['cat']) || isset($_GET['product_id']) ) || isset( $_POST['form_id_override'] ) ) {
              $url = add_query_arg( pmm_get_product_data('select_treatment') ); ?>
              history.replaceState(null, "Consultation Form: Submitted", "<?php echo esc_url_raw($url); ?>");
            <?php } ?>
            $('.gform_footer').addClass('loading');
						form.submit();
					<?php } ?>
                } else {
                    $('.gform_heading .validation_error').hide();
                }
            });

            //check each radio has checked
              //check each radio has checked
            function formFilled() {
    let isValid = true;

    form.find('.gfield_contains_required:visible').each(function () {
        const field = $(this);

        // Skip hidden fields
        if (!field.is(':visible')) return;

        // Get the actual input element(s)
        const input = field.find('input, select, textarea').first();

        // If it's radio or checkbox, check if any are selected
        if (input.is('[type=radio]') || input.is('[type=checkbox]')) {
            if (field.find('input:checked').length === 0) {
                isValid = false;
                return false; // Break out of .each loop
            }
        } else {
            // For text, textarea, select — check value
            if (!input.val() || input.val().trim() === '') {
                isValid = false;
                return false;
            }
        }
    });

    return isValid;
}

      			//Ensure that when a label is clicked, the associated checkbox is checked
      			//e.g. if a user selects text inside the label
      			$('.ginput_container_choice label').on('click', function(){
      				var radioEl = $(this).siblings('input[type="radio"]');
      				var isChecked = radioEl.prop('checked');
      				if (!isChecked) {
      					radioEl.prop('checked', true);
      				}
      			});

            /**
             * Exclusions logic
             */

            var availableProductsEl = $('#available_products');
            var isOverride = $('#add-to-cart').length > 0;
            if ( !isOverride ) {
              //Store a master copy of the available products list, so we can restore exclusions when customers switch choices
              var availableProductsMaster = availableProductsEl.length && $('#available_products').val().split(',');
              var availableProducts = availableProductsEl.length && $('#available_products').val().split(',');

              //Run a full check and applying of exclusions of all selected options on every new selection
              $('.gfield_choice label').on('click', function() {

                  //Collect (unique) exclusions from all checked options
                  var exclusions = [];

                  $('.gfield_choice input:checked').each(function() {
                      var choiceExclusions =  $(this).parent().data('exclusions');

                      if ( choiceExclusions ) {
                        for (i = 0; i < choiceExclusions.length; ++i ) {
                          if ( exclusions.indexOf(choiceExclusions[i]) === -1 ) {
                            exclusions.push(choiceExclusions[i]);
                          }
                        }
                      }
                  })

                  //Search for the exclusions in the availableProductsMaster and return as a new array
                  availableProducts = $.grep(availableProductsMaster, function(value) {
                      return $.inArray(value, exclusions) < 0;
                  });

                  //Update the hidden input element
                  availableProductsEl.val(availableProducts.join(','));

                  //Handle no products being left in the available list
                  //Disable the submit button and display the error modal
                  if (availableProducts.length === 0) {
                    var noProductsMessage = '<div class="choice_incorrect choice_no_products">Sorry based on your answers there are no suitable treatments we recommend for you. Please contact us on 0208 894 6080 to discuss this.</div>';

                    if ( $(this).parents('.gfield').find('.choice_no_products').length === 0 ) {
                      $(this).parents('.gfield').children('.choice_left').append(noProductsMessage);
                    } else {
                      $(this).parents('.gfield').find('.choice_no_products').show();
                    }

                    $('.gform_button').addClass("disabled");
                  } else {
                    $('.choice_no_products').hide();
                    $('.gform_button').removeClass("disabled");
                  }
              });
            }

            //check has a incorrect answer
            function hasIncorrects() {
                var goTo = false;
                $.each(form.find('.choice_incorrect:visible'), function (i) {
        						//figure out if we need to offset the scrollTo position (for non-mobile)
        						var headerHeight = $('#et_mobile_nav_menu').is(':visible') ? 0 : $('#et-main-area').offset().top;

        						//Set target element based on element height vs window height (stops very long questions appearing wrongly on mobile)
        						if ( $(this).parent().parent().outerHeight() > $(window).outerHeight() ) {
        							goTo = $(this);
        							headerHeight = $(window).outerHeight() / 4;
        						} else {
        							goTo = $(this).parent().parent();
        						}

                    $('html, body').animate({
                        scrollTop: $(goTo).offset().top - headerHeight
                    }, 1000);
                    return false;
                });
              return goTo ? true : false
            }

<?php if ( $is_pingdom === false ) { ?>
(function ($) {
  // Rebind safely (GF can re-render DOM)
  $(document)
    .off('click.gfAutoScroll', '.gfield_choice:not(:last) label')
    .on('click.gfAutoScroll', '.gfield_choice:not(:last) label', function () {
      // Let GF update conditional logic first
      $(this).siblings('input').trigger('click');

      var $sourceBlockEl = $(this).closest('.gfield');
      var $form          = $sourceBlockEl.closest('form');
      if (!$form.length) $form = $(document); // fallback

      // next visible field (skip hidden)
      var $nextBlockEl = $sourceBlockEl.nextAll('.gfield:visible').first();

      function computeHeaderHeight() {
        return $('#et_mobile_nav_menu').is(':visible') ? 0 : ($('#et-main-area').offset() ? $('#et-main-area').offset().top : 0);
      }
      var headerHeight = computeHeaderHeight();

      // Only treat as "correct" if not flagged incorrect AND gating allows
      var isCorrectClick =
        ((($(this).attr('onclick') || '').indexOf('incorrect') === -1)) &&
        ((typeof isOverride !== 'undefined' && isOverride) ||
         (typeof availableProducts !== 'undefined' && availableProducts.length > 0));

      // --- helpers (scoped to this form) ---
      function isIncorrect($f) {
        return $f.hasClass('gfield_error') ||
               $f.hasClass('gquiz-field-incorrect') ||
               $f.find('.gquiz-incorrect-choice:visible, .choice_incorrect:visible').length > 0;
      }
      function isRequired($f) {
        return $f.hasClass('gfield_contains_required') ||
               $f.find('[aria-required="true"], [required]').length > 0;
      }
      function hasInputContainer($f) {
        return $f.find('.ginput_container').length > 0;
      }
      function isAnswered($f) {
        if (!hasInputContainer($f)) return true; // non-input sections treated as answered/skip

        // choices
        if ($f.find('.ginput_container_choice, .ginput_container_radio, .ginput_container_checkbox').length) {
          var anyChecked = $f.find('input:radio:checked, input:checkbox:checked').length > 0;
          if (anyChecked) return true;
        }

        // text-ish
        var textFilled = $f.find('input[type="text"], input[type="email"], input[type="number"], input[type="tel"], input[type="url"], textarea')
          .filter(function () {
            var v = $.trim($(this).val());
            return v !== '' && v.toLowerCase() !== 'other'; // ignore placeholder-like "Other"
          }).length > 0;

        var selectFilled = $f.find('select').filter(function () {
          var v = $(this).val();
          return v !== '' && v !== null;
        }).length > 0;

        var dateTimeFilled = $f.find('input[type="date"], input[type="time"]')
          .filter(function () { return $.trim($(this).val()) !== ''; }).length > 0;

        return textFilled || selectFilled || dateTimeFilled;
      }
      // --- end helpers ---

      var $scrollTo = $(); // empty jQuery

      if (isCorrectClick) {
        var $visibleFields = $form.find('.gfield:visible');
        var idx = $visibleFields.index($sourceBlockEl);

        // 1) UP: only REQUIRED fields (incorrect OR unanswered)
        for (var i = idx - 1; i >= 0; i--) {
          var $f = $visibleFields.eq(i);
          if (!isRequired($f)) continue;
          if (isIncorrect($f) || !isAnswered($f)) { $scrollTo = $f; break; }
        }

        // For tall targets, aim at a meaningful sub-element
        if ($scrollTo.length && $scrollTo.outerHeight() > $(window).outerHeight()) {
          var $sub = $scrollTo.find('.gquiz-incorrect-choice:visible, .choice_incorrect:visible').first();
          if (!$sub.length) $sub = $scrollTo.find('input, select, textarea').filter(':visible').first();
          if ($sub.length) $scrollTo = $sub;
          headerHeight = $(window).outerHeight() / 4;
        }

        // 2) DOWN: include OPTIONAL questions again (unanswered below current)
        if (!$scrollTo.length) {
          // a) immediate next visible block if unanswered (required or optional)
          if ($nextBlockEl.length && !isAnswered($nextBlockEl)) {
            $scrollTo = $nextBlockEl;
          } else {
            // b) first unanswered field BELOW current (required OR optional)
            for (var j = idx + 1; j < $visibleFields.length; j++) {
              var $f2 = $visibleFields.eq(j);
              if (!hasInputContainer($f2)) continue;
              if (!isAnswered($f2)) { $scrollTo = $f2; break; }
            }
            // c) if none remain, go to the submit/footer
            if (!$scrollTo.length) {
              $scrollTo = $form.find('.gform_footer:visible').first();
              if (!$scrollTo.length) $scrollTo = $('.gform_footer:visible').first(); // global fallback
              headerHeight = computeHeaderHeight() + ($(window).outerHeight() / 4);
            }
          }
        }
      }

      // No scroll on incorrect clicks
      if ($scrollTo && $scrollTo.length) {
        var elOffset = $scrollTo.is(':visible')
          ? $scrollTo.offset().top
          : ($sourceBlockEl.offset().top + $sourceBlockEl.outerHeight());

        $('html, body').animate({ scrollTop: elOffset - headerHeight }, 1000);
      }
    });
})(jQuery);


<?php } ?>

        });
</script>
<script>
jQuery(document).ready(function($){

        //Proceed to checkout bar - sticky on scroll
        if ( $('.treatment-continue').length > 0 ) {
          var $treatmentContinue = $('.treatment-continue');
          var elPos = $treatmentContinue.offset();
          $(window).on('scroll', function() {
            if ( $(window).scrollTop() > elPos.top ) {
              $treatmentContinue.addClass('sticky');
            } else {
              $treatmentContinue.removeClass('sticky');
            }
          }).trigger('scroll');
        };

        //Disable input on selected products
        $('.selected select, .selected .qty').prop('disabled','disabled');

        function selectTreatment(e) {
           var $selectButton = $(e.target);

          //check that the button isn't disabled, and that we have a variation selected (this would be false in a re/loading state which sometimes happens on longer variation product loads)
          if ( !$selectButton.hasClass("disabled") && $selectButton.siblings('input[name="variation_id"]').val() > 0 ) {
            var form = $selectButton.parents('form');
            form.submit();

            $selectButton.removeClass('added');
            $selectButton.addClass('loading');

          }

          //if someone clicks a disabled/loading button don't do anything
          return false;
        }

        function deselectTreatment( $removeButton ) {
          var cartItemKey = $removeButton.data('remove_item');
          $removeButton.removeClass('can-update').removeClass('deselect').removeClass('added').addClass('loading');
          $.post( '/?wc-ajax=remove_from_cart', 'cart_item_key=' + cartItemKey, function(response){
            if ( ! response || ! response.fragments ) {
              window.location.reload(true);
              return;
            }

            //Reset parent element
            $removeButton.parents('.product').removeClass('selected');
            $removeButton.parents('.product').find('select, .qty').prop('disabled', false);

            //Reset button
            $removeButton.removeClass('loading').addClass('added').removeClass('deselect');
            $removeButton.text('Removed');

            //Hide continue section if we've deselected the last item
            if ( $('.deselect').length === 0 ) {
              $('.treatment-continue').slideUp();
            }

            setTimeout(function(){
              $removeButton.text('Select').removeClass('added');
              $( document.body ).trigger( 'removed_from_cart', [ response.fragments, response.cart_hash, $removeButton ] );
              $removeButton.on('click', function(e){ selectTreatment(e) });
              $('.select_treatment.deselect').one( 'click', function(){
                deselectTreatment( $(this) );
              });
            }, 1500);
          } );
          return false;
        }


        $('.select_treatment').on('click', function(event){
          event.preventDefault();
          event.stopImmediatePropagation();

          if ( $(this).hasClass('deselect') ) {
            deselectTreatment( $(this) );
          } else {
            //Custom AJAX submission of the "Select" button
            selectTreatment(event);
          }

        });

        $('.view-more').on( 'click', function(){
          $(this).toggleClass('opened');
          $(this).siblings('.woocommerce-product-details__short-description').slideToggle();
        });

      });
</script>

Consultation Form

Select a Consultation Form…

<script>
jQuery(document).ready(function($){
          $('body').on('show_variation', function(){
              $(this).find('button.single_add_to_cart_button').removeClass('disabled wc-variation-is-unavailable');
          });
        });
</script>
<script>
jQuery("#forceResumbission").on("change", function(){
    if ( jQuery(this).is(":checked") ) {
        form.forceResumbission = true;
    } else {
        form.forceResumbission = null;
    }
  });
</script>

';
return $button_pre . $button;
}

//Set the force update time using PHP functions for consistency
add_filter( 'gform_form_update_meta', 'pmm_force_update_meta_save' );

function pmm_force_update_meta_save( $meta ) {
if ( isset( $meta['forceResumbission'] ) && $meta['forceResumbission'] == true ) {
$now = new DateTime();
$meta['forceResumbission'] = $now->format('U');
}
return $meta;
}

/**
* Check if an entry needs to be submitted again
* This would be either due to being from the previous plugin, or because of the form being updated with "force resubmission" ticked
*/

function pmm_entry_needs_resubmitting( $entry_id, $form_id, $product_id ) {

$force_resubmission = false;

//Check for the available_products meta – if it's not there this must be an entry from the previous plugin
//Also check if a consultation product level override is in place, then we skip this check
if ( !gform_get_meta( $entry_id, 'available_products' ) && !get_post_meta( $product_id, '_pmm_consultation_form_id', true ) ) {
//echo 'DEBUG: old form';
$force_resubmission = true;
} else {

//Check form's meta for a force resubmit
$form = GFAPI::get_form($form_id);
$entry = GFAPI::get_entry($entry_id);
if ( $form && !is_wp_error( $entry ) ) {

//If the entry's form id doesn't match the passed form id, it's automatically invalid
if ( absint($form_id) !== absint($entry['form_id']) ) {
//echo 'DEBUG: entry's form id does not match passed form id';
return true;
}

if ( isset($form['forceResumbission']) ) {

$force_time = DateTime::createFromFormat( 'U', $form['forceResumbission'] );
$entry_time = DateTime::createFromFormat( 'Y-m-d H:i:s', $entry['date_created'] );

//If forceResubmission date is greater (newer) than the entry submission time we need to force a new entry
if ( $force_time > $entry_time ) {
//echo 'DEBUG: form updated with force resubmit entry (ID:' . $entry_id . ') ' . $entry_time->format('Y-m-d H:i:s') . ' vs. ' . $force_time->format('Y-m-d H:i:s');
$force_resubmission = true;
}
}
}
}
return $force_resubmission;
}
undefined method get_id()

Assistant
You're getting **“Call to undefined method get_id()”** because in a few places the code assumes `$product` (or `$variation`) is always a valid **`WC_Product`** object. In reality, WooCommerce can return `false` (e.g., product deleted/unavailable) or a different type, so calling `->get_id()` on it fatals.

Below are **safe guards** for the most likely hotspots in your file. Drop these **as-is replacements** in your plugin (they’re minimal, no behavior change except avoiding fatals).

### 1) Reorder button on the Order details page
**Function:** `pmm_order_item_reorder_button`

function pmm_order_item_reorder_button( $item, $order ) {
  $entry_id = $item->get_meta( 'entry_id', true );
  $product  = $item->get_product();

  // Guard: product can be false (deleted/trashed)
  if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
    return;
  }

  $product_id = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id();

  if ( $entry_id && $product_id && pmm_product_reordering_allowed( $product_id ) ): ?>
    <td class="reorder-button">
      <form action="/consultation-form/" method="post">
        <input type="hidden" name="reorder_item_id" value="<?php echo absint($item->get_id()); ?>">
        <input type="hidden" name="product_id" value="<?php echo absint($product_id);?>">
        <input type="hidden" name="variation_id" value="<?php echo absint($product->get_id());?>">
        <input type="hidden" name="quantity" value="<?php echo absint($item->get_quantity()); ?>">
        <?php if ( $override_id = get_post_meta( $product_id, '_pmm_consultation_form_id', true ) ): ?>
          <input type="hidden" name="form_id_override" value="<?php echo absint($override_id); ?>">
          <input type="hidden" name="add-to-cart-id" value="<?php echo absint($product->get_id()); ?>">
        <?php endif;
        if ( !pmm_product_consultation_required( $product ) ): ?>
          <input type="hidden" name="entry_id" value="<?php echo absint($entry_id); ?>">
          <input type="hidden" name="add-to-cart" value="<?php echo absint($product->get_id()); ?>">
        <?php endif; ?>
        <button type="submit" class="button">Reorder</button>
      </form>
  	</td>
<?php endif;
}

### 2) Reorder column on My Account → Orders list
**Hooks:** `pmm_reorder_order_column`

function pmm_reorder_order_column( $order ) {
  $order_items = $order->get_items( apply_filters( 'woocommerce_purchase_order_item_types', 'line_item' ) );

  foreach ( $order_items as $item ) {
    $entry_id = $item->get_meta( 'entry_id', true );
    $product  = $item->get_product();

    if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
      continue;
    }

    $product_id = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id();

    if ( $entry_id && $product && pmm_product_reordering_allowed( $product_id ) ) { ?>
      <a href="<?php echo esc_url( $order->get_view_order_url() ); ?>" class="button">Reorder</a>
    <?php break;
    }
  }
}

### 3) “Return to Select Treatments” link in cart
**Function:** `pmm_cart_return_to_select_treatments`

function pmm_cart_return_to_select_treatments( $cart_item ) {
  if ( empty( $cart_item['entry_id'] ) ) {
    return;
  }

  $entry_id = absint( $cart_item['entry_id'] );
  $entry    = GFAPI::get_entry( $entry_id );
  if ( is_wp_error( $entry ) ) {
    return;
  }

  $product = isset( $cart_item['data'] ) ? $cart_item['data'] : false;

  // Guard: ensure WC_Product
  if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
    return;
  }

  $product_id   = $cart_item['product_id'];
  $variation_id = $product->get_id();

  // Skip if product has individual form override
  if ( get_post_meta( $product_id, '_pmm_consultation_form_id', true ) ) {
    return;
  }

  $product_cats = get_the_terms( $product_id, 'product_cat' );
  $product_cat  = $product_cats ? $product_cats[0] : false;

  $query_args = array(
    'product_id'   => absint( $product_id ),
    'variation_id' => absint( $variation_id ),
    'entry_id'     => absint( $entry_id ),
  );

  if ( isset( $cart_item['reorder'] ) ) {
    $query_args['reorder'] = absint( $cart_item['reorder'] );
  }

  $url = add_query_arg( $query_args, home_url('/consultation-form/') );

  if ( $url && $product_cat ) {
    printf(
      '<p>Looking for another treatment?<br>You can see other %s treatments you are eligible for <a href="%s">here</a>.</p>',
      esc_html( $product_cat->name ),
      esc_url_raw( $url )
    );
  }
}

### 4) Select-treatment product loop output
**Hook:** `pmm_select_treatment_variations_output`

function pmm_select_treatment_variations_output() {
  if ( ! pmm_is_select_treatment_page() ) return;

  global $product;

  // Guard: make sure it’s a WC_Product
  if ( ! $product || ! is_a( $product, 'WC_Product' ) ) return;

  if ( $product->is_type('variable') ) {
    woocommerce_variable_add_to_cart();
  } else {
    woocommerce_simple_add_to_cart();
  }
}

### 5) Product-page footer JS bootstrap
**Function:** `pmm_single_product_js`

function pmm_single_product_js() {
  if ( ! is_product() ) return;

  global $product;

  // Guard: ensure WC_Product
  if ( ! $product || ! is_a( $product, 'WC_Product' ) ) return;
  ?>
  <!-- existing JS output unchanged -->
  <?php
}

### 6) Stock check helper using global $product
**Function:** `pmm_check_for_instock`

function pmm_check_for_instock() {
  global $product;

  if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
    return;
  }

  //Exclude products with consultation overrides
  $has_override = $product->get_meta( '_pmm_has_override', true );

  if ( pmm_is_select_treatment_page() || $has_override ) {
    return;
  }

  $category_ids = $product->get_category_ids();
  if ( $category_ids ) {
    $category_id = $category_ids[0];

    if ( pmm_category_has_instock_consult_products( $category_id ) ) { ?>
      <script>
        jQuery(document).ready(function($){
          $('body').on('show_variation', function(){
            $(this).find('button.single_add_to_cart_button').removeClass('disabled wc-variation-is-unavailable');
          });
        });
      </script>
    <?php }
  }
}

### Why this fixes it
– **`$item->get_product()` can be `false`** when the product doesn’t exist anymore.
– **`global $product`** can be unset or another type if the loop context isn’t what you expect.
– Adding **`is_a( $obj, 'WC_Product' )`** guards before calling **`->get_id()`** (or other product methods) prevents the fatal and degrades gracefully.

### Quick grep to find any other risky calls
Search your file for these and wrap similarly if needed:
– `->get_id(`
– `->get_parent_id(`
– `->get_children(`
– any place you call **WooCommerce product methods** on a variable that might not be a `WC_Product`.

If you still see the fatal after these patches, share the **exact stack trace line number**—I’ll pinpoint that spot next.

User
give me only changes need to made
Assistant
Here are the **exact replacements**. Paste each one over your existing function.

**1) `pmm_order_item_reorder_button`**

function pmm_order_item_reorder_button( $item, $order ) {
  $entry_id = $item->get_meta( 'entry_id', true );
  $product  = $item->get_product();

  if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
    return;
  }

  $product_id = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id();

  if ( $entry_id && $product_id && pmm_product_reordering_allowed( $product_id ) ): ?>
    <td class="reorder-button">
      <form action="/consultation-form/" method="post">
        <input type="hidden" name="reorder_item_id" value="<?php echo absint($item->get_id()); ?>">
        <input type="hidden" name="product_id" value="<?php echo absint($product_id);?>">
        <input type="hidden" name="variation_id" value="<?php echo absint($product->get_id());?>">
        <input type="hidden" name="quantity" value="<?php echo absint($item->get_quantity()); ?>">
        <?php if ( $override_id = get_post_meta( $product_id, '_pmm_consultation_form_id', true ) ): ?>
          <input type="hidden" name="form_id_override" value="<?php echo absint($override_id); ?>">
          <input type="hidden" name="add-to-cart-id" value="<?php echo absint($product->get_id()); ?>">
        <?php endif;
        if ( !pmm_product_consultation_required( $product ) ): ?>
          <input type="hidden" name="entry_id" value="<?php echo absint($entry_id); ?>">
          <input type="hidden" name="add-to-cart" value="<?php echo absint($product->get_id()); ?>">
        <?php endif; ?>
        <button type="submit" class="button">Reorder</button>
      </form>
  	</td>
<?php endif;
}

**2) `pmm_reorder_order_column`**

function pmm_reorder_order_column( $order ) {
  $order_items = $order->get_items( apply_filters( 'woocommerce_purchase_order_item_types', 'line_item' ) );

  foreach ( $order_items as $item ) {
    $entry_id = $item->get_meta( 'entry_id', true );
    $product  = $item->get_product();

    if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
      continue;
    }

    $product_id = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id();

    if ( $entry_id && pmm_product_reordering_allowed( $product_id ) ) { ?>
      <a href="<?php echo esc_url( $order->get_view_order_url() ); ?>" class="button">Reorder</a>
    <?php break;
    }
  }
}

**3) `pmm_cart_return_to_select_treatments`**

function pmm_cart_return_to_select_treatments( $cart_item ) {
  if ( empty( $cart_item['entry_id'] ) ) return;

  $entry_id = absint( $cart_item['entry_id'] );
  $entry    = GFAPI::get_entry( $entry_id );
  if ( is_wp_error( $entry ) ) return;

  $product = isset( $cart_item['data'] ) ? $cart_item['data'] : false;
  if ( ! $product || ! is_a( $product, 'WC_Product' ) ) return;

  $product_id   = $cart_item['product_id'];
  $variation_id = $product->get_id();

  if ( get_post_meta( $product_id, '_pmm_consultation_form_id', true ) ) return;

  $product_cats = get_the_terms( $product_id, 'product_cat' );
  $product_cat  = $product_cats ? $product_cats[0] : false;

  $query_args = array(
    'product_id'   => absint( $product_id ),
    'variation_id' => absint( $variation_id ),
    'entry_id'     => absint( $entry_id ),
  );
  if ( isset( $cart_item['reorder'] ) ) {
    $query_args['reorder'] = absint( $cart_item['reorder'] );
  }

  $url = add_query_arg( $query_args, home_url('/consultation-form/') );
  if ( $url && $product_cat ) {
    printf(
      '<p>Looking for another treatment?<br>You can see other %s treatments you are eligible for <a href="%s">here</a>.</p>',
      esc_html( $product_cat->name ),
      esc_url_raw( $url )
    );
  }
}

**4) `pmm_select_treatment_variations_output`**

function pmm_select_treatment_variations_output() {
  if ( ! pmm_is_select_treatment_page() ) return;

  global $product;
  if ( ! $product || ! is_a( $product, 'WC_Product' ) ) return;

  if ( $product->is_type('variable') ) {
    woocommerce_variable_add_to_cart();
  } else {
    woocommerce_simple_add_to_cart();
  }
}

**5) `pmm_single_product_js` (just the guards at the top)**

function pmm_single_product_js() {
  if ( !is_product() ) return;

  global $product;
  if ( ! $product || ! is_a( $product, 'WC_Product' ) ) return;

  ?>
  <!-- existing JS output unchanged -->
  <?php
}

**6) `pmm_check_for_instock` (guard at top)**

function pmm_check_for_instock() {
  global $product;

  if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
    return;
  }

  $has_override = $product->get_meta( '_pmm_has_override', true );
  if ( pmm_is_select_treatment_page() || $has_override ) return;

  $category_ids = $product->get_category_ids();
  if ( $category_ids ) {
    $category_id = $category_ids[0];
    if ( pmm_category_has_instock_consult_products( $category_id ) ) { ?>
      <script>
        jQuery(document).ready(function($){
          $('body').on('show_variation', function(){
            $(this).find('button.single_add_to_cart_button').removeClass('disabled wc-variation-is-unavailable');
          });
        });
      </script>
    <?php }
  }
}

Leave a Comment

Your email address will not be published. Required fields are marked *