Need Help Extending WooCommerce Class

I need to extend woocommerce class WC_Order.  I have tried many permutations but none get me past the error

Fatal error:  Call to undefined function formatted_shipping_address() in C:\xampp\apps\wordpress\htdocs\wp-content\themes\flatsome-child\functions.php on line 30

Open in new window


Here is the function I want to extend in the class WC_Order
https://gyazo.com/4a93b504bd0a915ad986099a81a24aaf

Here is my function
class POBox_Shipping extends WC_Order {

function formatted_shipping_address($order)
{
    return
        $order->shipping_address_1 . ', ' . 
        $order->shipping_address_2 . ' ' .
        $order->shipping_city      . ', ' .
        $order->shipping_state     . ' ' .
        $order->shipping_postcode;
}
}
echo formatted_shipping_address($order);


$order = new POBox_Shipping($order_id); // Order id
$shipping_address = $order->get_address('shipping'); 

echo "<pre>";
print_r($shipping_address);
echo "</pre>";

Open in new window


I am new to OOP and I realize I am only doing the "bare bones" needed.  However, this is only to get the shipping details to show on the checkout page.
sharingsunshineAsked:
Who is Participating?
 
Scott Fell, EE MVEDeveloper & EE ModeratorCommented:
The actual class is more complex in that it has many objects and methods.  But what I have shown you is how to extend the class. It is over simplistic to demonstrate.

Back to this one a42484205, the class WC_Order has an object called $address.  $address is set to "1234 maple ave"

To extend the class, you have this part correct, "class FormatAddress extends WC_Order "  What you had wrong is how to access the object inside of the extended class FormatAddress.  You were trying to access it directly and you can't do that.  In my example, if you want to return the address in all caps, you first have to instantiate.  $test = new FormatAddress();  Once you do that, you can access the original object $address by using, echo $test->address; to return, "1234 maple ave".  Now to access the all caps formatted, echo $test->formatted_shipping_address();  and that will return1234 MAPLE AVE

In your next example, a42484468 it will error as is because you are calling echo $test->has_shipping_address; and that relies on $this->has_shipping_address_1() which is not currently defined in part because that line should be "$this->has_shipping_address_1" (no parentheses) and in part you didn't define has_shipping_address_1.  I believe has_shipping_address_1 is Boolean.  So if we wanted to set up a test case where it is determined that has_shipping_address_1 is true, it could look like below.  

class WC_Order{

   //TEST CASE THAT SHOWS has_shipping_address_1 IS TRUE
   public $has_shipping_address_1; 
    
    public function __construct() 
    { 
      
        $this -> has_shipping_address_1 = true; 
    } 

}

class POBox_Shipping extends WC_Order {

    function has_shipping_address()
    {
	    $address2 = $this->has_shipping_address_1;
        return $address2;
	
    }
}
  
$test = new POBox_Shipping();  // instantiate the class

if($test->has_shipping_address()){
    echo "has_shipping_address_1 is set to true";
} else {
      echo "has_shipping_address_1 is set to false";
}

Open in new window


The reason to use a simple test case http://sscce.org/ is to make it easier to understand. If there is an error, you are working with minimal information and it is easier to pinpoint.

Your actual WC_Order class contains many items with objects that inherited methods from other classes and would be very difficult to test.  In the last two examples. I am simply creating a hard coded variable if you will and showing how to use your extended class and access the data from the parent class and then access the data from the extended class in your code.

Lets go back to the code you had in the original question.  Since it would be too hard for testing like this to set the data, I hard coded the values for the address in the WS_Order class.  Then in your extended class, POBox_Shipping  you are concatenating and that part stays as is.

class WC_Order{

    public $shipping_address_1;
    public $shipping_address_2;
    public $shipping_city;
    public $shipping_state;
    public $shipping_postcode;
    
    public function __construct() 
    { 
      
        $this -> shipping_address_1 = '1234 maple ave'; 
        $this -> shipping_address_2 = '';
        $this -> shipping_city = 'Anytown';
        $this -> shipping_state = 'XX';
        $this -> shipping_postcode = '99999';
    } 
 

}



class POBox_Shipping extends WC_Order {

    function formatted_shipping_address($order)
    {
        return
        $this->shipping_address_1 . ', ' .    // 
        $this->shipping_address_2 . ' ' .
        $this->shipping_city      . ', ' .
        $this->shipping_state     . ' ' .
        $this->shipping_postcode;
    }
}

$order_id = '1111';
$order = new POBox_Shipping(); 
//$shipping_address = $order->get_address('shipping'); 
$shipping_address = $order->formatted_shipping_address($order_id);
echo $shipping_address;        //1234 maple ave, Anytown, XX 99999

Open in new window

The output will be the concatenation as expected. I hope this makes sense.
0
 
Scott Fell, EE MVEDeveloper & EE ModeratorCommented:
On your line 13 you have echo formatted_shipping_address($order);

I believe that should come after you create the $order = new POBox_Shipping($order_id); // Order id
0
 
sharingsunshineAuthor Commented:
I put if after and it didn't make any difference.
0
Cloud Class® Course: Microsoft Windows 7 Basic

This introductory course to Windows 7 environment will teach you about working with the Windows operating system. You will learn about basic functions including start menu; the desktop; managing files, folders, and libraries.

 
Scott Fell, EE MVEDeveloper & EE ModeratorCommented:
Are you sure it is not a different error?
0
 
sharingsunshineAuthor Commented:
It just changed the line number
Fatal error: Call to undefined function formatted_shipping_address() in C:\xampp\apps\wordpress\htdocs\wp-content\themes\flatsome-child\functions.php on line 72

Open in new window


This is a screenshot to show you line 72


This is the code
class POBox_Shipping extends WC_Order {

function formatted_shipping_address($order)
{
    return
        $order->shipping_address_1 . ', ' . 
        $order->shipping_address_2 . ' ' .
        $order->shipping_city      . ', ' .
        $order->shipping_state     . ' ' .
        $order->shipping_postcode;
}
}



$order = new POBox_Shipping($order_id); // Order id

echo formatted_shipping_address($order);
$shipping_address = $order->get_address('shipping'); 


echo "<pre>";
print_r($shipping_address);
echo "</pre>";

Open in new window

0
 
sharingsunshineAuthor Commented:
0
 
Scott Fell, EE MVEDeveloper & EE ModeratorCommented:
I don't have time to load up a Woo site to test.  But here is some example code that may help.  I tested this and it works.

class WC_Order{}

class POBox_Shipping extends WC_Order {

function formatted_shipping_address($order)
{
    return "testing 123";
}
}


$test = new POBox_Shipping();  // instantiate the class
print_r($test->formatted_shipping_address('1')); access data from the function

Open in new window


This is a good resource to help you out as well http://www.phptherightway.com/pages/Design-Patterns.html
0
 
sharingsunshineAuthor Commented:
I ran the code but the place order button is grayed out for some reason.  The "testing 123 is showing at the top left corner.  Here is a screenshot:

https://gyazo.com/c2e684ef4cb0b1df5d047e0f26608300
0
 
Scott Fell, EE MVEDeveloper & EE ModeratorCommented:
I was only showing the idea that you have to instantiate to access the function.

You were doing below which created an error
$order = new POBox_Shipping($order_id); // Order id

echo formatted_shipping_address($order);
$shipping_address = $order->get_address('shipping'); 

Open in new window

Then my suggestion is to instantiate the class and then you can hit the function
$test = new POBox_Shipping();  // instantiate the class
print_r($test->formatted_shipping_address('1')); access data from the function

Open in new window


The code I changed in the class was just for quick testing.  You can put what you had back.  The button that is showing up as disabled is going to be a different issue or perhaps somewhat related.  At least we have the main idea fixed and now that just happens to rear up another issue.  Probably best to follow up with a new question. However, try and view the source of the rendered html, then paste that code to a static html file that you can upload to your site and obfuscate private data.

Otherwise, you just need to use the browser dev tools to track which class is disabling and then find in your php code/class where that is being done.
0
 
sharingsunshineAuthor Commented:
I want to get the code correct before I tackle another question about the grayed out box.  Simply, because it may clear up if I have the code correct.  I initiated the question to find out how to extend an existing woocommerce class.  This is a function out of the WC_Order class.
 public function get_formatted_shipping_address( $empty_content = '' ) {
                $address = '';

                if ( $this->has_shipping_address() ) {
                        $address = apply_filters( 'woocommerce_order_formatted_shipping_address', $this->get_address( 'shipping' ), $this );
                        $address = WC()->countries->get_formatted_address( $address );
                }

                return $address ? $address : $empty_content;
        }

Open in new window


In my novice way of thinking towards oop I should be able to extract the value of the shipping address that has been filled out in the checkout fields.  Once I can do that then I will know that I have extended a class.  If this incorrect please let me know.

Here is my code to do just that

class WC_Order{}

class POBox_Shipping extends WC_Order {

function get_formatted_shipping_address($order)
{
    return "testing 123";
	
}
}
  
$test = new POBox_Shipping();  // instantiate the class
//print_r($address->get_formatted_shipping_address('1')); //access data from the function
print_r($address);

Open in new window


This doesn't show "testing 123" anymore and doesn't show the address that is in the checkout field either.
0
 
Scott Fell, EE MVEDeveloper & EE ModeratorCommented:
I don't have woo installed and can't give you exact details for this specific case. But it appears you changed what I gave you. The sample code $address does not exist and that probably gave you an error.

I have built a test case you can work from and perhaps this will help
class WC_Order{
    
    public $address; 
    
    public function __construct() 
    { 
      
        $this -> address = '1234 maple ave'; 
    } 
    

}

class FormatAddress extends WC_Order {

   function formatted_shipping_address()
   {
       $newAddress = strtoupper($this-> address);
       return $newAddress;
   }
}


$test = new FormatAddress();  // instantiate the class
echo $test->address; // 1234 maple ave
echo "<hr>";
echo $test->formatted_shipping_address(); //1234 MAPLE AVE

Open in new window

http://php.net/manual/en/language.oop5.basic.php
http://php.net/manual/it/keyword.extends.php

If you can create a very simple test case like I have here, I can help you. I just don't have time to dig into the full class for Wool
0
 
sharingsunshineAuthor Commented:
Thanks for the test case and I I have created a test case and included the function in WC_Order I am trying to use in my new class.
class WC_Order{

  /*public function has_shipping_address() {
                return $this->get_shipping_address_1() || $this->get_shipping_address_2();
        }*/


}

class POBox_Shipping extends WC_Order {

function has_shipping_address()
{
	$address2 = ($this->has_shipping_address_1());
    return $address2;
	
}
}
  
$test = new POBox_Shipping();  // instantiate the class
//print_r($test->get_formatted_shipping_address('0')); //access data from the function
//print_r($address);
echo $test->has_shipping_address; 

Open in new window


My sole objective is to retrieve the ship to address I have in the checkout fields.  In which case I may have the wrong function.  

I realize you are too busy to investigate woo.  However, I can wait or I can contact the moderator again and ask for more experts.  Just let me know which you prefer.  You have been great to work with and I prefer waiting if you are amenable to that.

I am not sure if this link would help you to see the classes and functions quicker or not.  https://docs.woocommerce.com/wc-apidocs/

The reason I need this address is to test via regex if people are selecting UPS and trying to ship to a PO Box.
1
 
Scott Fell, EE MVEDeveloper & EE ModeratorCommented:
I think we need to redefine what you are asking. Originally, what I think I saw was getting the error for calling formatted_shipping_address. We solved that problem In my post a42483321

I have a feeling that solving that issue, just brought us to the next. That's why it's best to ask this in multiple questions and you can always reference back to this thread.  

So now the issue is the button is disabled and maybe the assumption is testing for the shipping address but it sounds like your end goal is to really know if people are selecting UPS and trying to ship to a PO Box.

So there are multiple issues here.

I looked in the docs for WC_Order https://docs.woocommerce.com/wc-apidocs/source-class-WC_Order.html and it looks like there is already a function for has_shipping_address that looks to see if there is a value in ship address 1 or 2.

 
 /**
     * Returns true if the order has a shipping address.
     *
     * @since  3.0.4
     * @return boolean
     */
    public function has_shipping_address() {
        return $this->get_shipping_address_1() || $this->get_shipping_address_2();
    }

Open in new window


There is also a shipping method class too.   Then when all this is figured out, then work on the button being disabled.

Is your real question then how to detect UPS and make sure it is going to a valid address and not a PO box?
0
 
sharingsunshineAuthor Commented:
My original sentence was
I need to extend woocommerce class WC_Order.  I

Open in new window

 I only mention the PO Box so you could have a frame of reference.

I don't have a  problem with greyed out anymore.

I just need to retreive the address from the WC_Order class via my function.  Or, if it is easier then another function.  I just want to see how to extend a class and prove it is extended by retrieving a value too.

Yes, I realize I need to have other questions once I can see how to extend an actual woo class.

Your example was too simplistic compared to what I need to tackle with the existing woo classes.
0
 
sharingsunshineAuthor Commented:
Thanks for the help and the explanations.  Your examples do help but I do need to find out how to actually extend WC_Order and use the attributes that are in the class.  So any suggestions on a link that teaches that more in depth.  I have 2 courses at udemy but neither one of them really address that problem.
0
 
Scott Fell, EE MVEDeveloper & EE ModeratorCommented:
I think the confusion is more about understanding how this works rather than the actual class.  All my examples I hard coded the data as if it were posted to create a test.

You can pull any object from the class in the same manner.  Looking at the docs https://docs.woocommerce.com/wc-apidocs/class-WC_Order.html 

Take get_shipping_address_1
  /**
     * Get shipping address line 1.
     *
     * @param  string $context What the value is for. Valid values are view and edit.
     * @return string
     */
    public function get_shipping_address_1( $context = 'view' ) {
        return $this->get_address_prop( 'address_1', 'shipping', $context );
    }

Open in new window


or get_formatted_shipping_address
/**
     * Get a formatted shipping address for the order.
     *
     * @param string $empty_content Content to show if no address is present. @since 3.3.0.
     * @return string
     */
    public function get_formatted_shipping_address( $empty_content = '' ) {
        $address = '';

        if ( $this->has_shipping_address() ) {
            $address = apply_filters( 'woocommerce_order_formatted_shipping_address', $this->get_address( 'shipping' ), $this );
            $address = WC()->countries->get_formatted_address( $address );
        }

        return $address ? $address : $empty_content;
    }

Open in new window


You can access get_formatted_shipping_address from the actual WC_order class in the same manner as what we have above using formatted_shipping_address.

If you are trying to determine if there is a po box being used, there is a sample https://docs.woocommerce.com/document/dont-allow-po-box-shipping/ 
add_action('woocommerce_after_checkout_validation', 'deny_pobox_postcode');

function deny_pobox_postcode( $posted ) {
  global $woocommerce;

  $address  = ( isset( $posted['shipping_address_1'] ) ) ? $posted['shipping_address_1'] : $posted['billing_address_1'];
  $postcode = ( isset( $posted['shipping_postcode'] ) ) ? $posted['shipping_postcode'] : $posted['billing_postcode'];

  $replace  = array(" ", ".", ",");
  $address  = strtolower( str_replace( $replace, '', $address ) );
  $postcode = strtolower( str_replace( $replace, '', $postcode ) );

  if ( strstr( $address, 'pobox' ) || strstr( $postcode, 'pobox' ) ) {
    wc_add_notice( sprintf( __( "Sorry, we cannot ship to PO BOX addresses.") ) ,'error' );
  }
}

Open in new window

You can use this portion to create your own function
global $woocommerce;

  $address  = ( isset( $posted['shipping_address_1'] ) ) ? $posted['shipping_address_1'] : $posted['billing_address_1'];
  $postcode = ( isset( $posted['shipping_postcode'] ) ) ? $posted['shipping_postcode'] : $posted['billing_postcode'];

  $replace  = array(" ", ".", ",");
  $address  = strtolower( str_replace( $replace, '', $address ) );
  $postcode = strtolower( str_replace( $replace, '', $postcode ) );

  if ( strstr( $address, 'pobox' ) || strstr( $postcode, 'pobox' ) ) {
    wc_add_notice( sprintf( __( "Sorry, we cannot ship to PO BOX addresses.") ) ,'error' );
  }

Open in new window


This line "global $woocommerce; " is accessing the woocommerce variable already set. For your own learning, it would be good to simply run
global $woocommerce;
print_r($woocommerce);
die();

Open in new window

Then that will show you the array of data and make it easier to understand why the rest of the code looks like
  $address  = ( isset( $posted['shipping_address_1'] ) ) ? $posted['shipping_address_1'] : $posted['billing_address_1'];
  $postcode = ( isset( $posted['shipping_postcode'] ) ) ? $posted['shipping_postcode'] : $posted['billing_postcode'];

  $replace  = array(" ", ".", ",");
  $address  = strtolower( str_replace( $replace, '', $address ) );
  $postcode = strtolower( str_replace( $replace, '', $postcode ) );

  if ( strstr( $address, 'pobox' ) || strstr( $postcode, 'pobox' ) ) {
    wc_add_notice( sprintf( __( "Sorry, we cannot ship to PO BOX addresses.") ) ,'error' );
  }

Open in new window

For this part
  if ( strstr( $address, 'pobox' ) || strstr( $postcode, 'pobox' ) ) {
    wc_add_notice( sprintf( __( "Sorry, we cannot ship to PO BOX addresses.") ) ,'error' );
  }

Open in new window

You will want to replace the line, "  wc_add_notice( sprintf( __( "Sorry, we cannot ship to PO BOX addresses.") ) ,'error' );" with your own code as needed and perhaps also add your test for shipping type that matches UPS
0
 
sharingsunshineAuthor Commented:
this was a great addendum and I appreciate you finding the function on the PO Boxes.  I am going to post another question about testing for the shipping method.  I found their id's but most examples go by the name and I need to go by the shipping_method number.  Because there are actually 5 UPS shipping numbers based on the amount in the cart the rate changes.

Here is the new question:

https://www.experts-exchange.com/questions/29086970/Need-Syntax-Test-For-Shipping-Method-In-WooCommerce.html#questionAdd
1
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.