Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to support HPOS #71

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

maxfrederiksen-mondido
Copy link

@maxfrederiksen-mondido maxfrederiksen-mondido commented Feb 6, 2024

Function

(WIP) Refactor and make our plugin use latest HPOS so it still works in the future when they remove old way.

Business value

Making sure the plugins doesn't break when they deprecate current methods.

Security considerations

Not sure.

Implementation strategies

Not sure.

Rollback strategies

Rollback to latest version

Test

Tested locally connected to prod mondido admin.
Tested happy flow with cards.
Made sure syntax is correct and not breaking.

Steps taken

None yet.

Expected result

Working with new HPOS.

@nuttmeister
Copy link

@maxfrederiksen-mondido is this ready for review or still WIP?

@maxfrederiksen-mondido
Copy link
Author

@nuttmeister I would say WIP since I can't test it yet locally. However if we can find a way to test on staging then yes.

Copy link

@eric-thelin eric-thelin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditionals can be eliminated by introducing polymorphism. This will have the following benefits:

  1. The client code, i.e. the callers, will be less complex
  2. All code related to a particular order storage technology (HPOS or CPT) will be in a single location and not spread out across the code base.
  3. Less duplicated code

One polymorphic solution we could use is to introduce an abstract OrderStorageTechnology base class and have concrete implementations HPOSOrderStorage and CPTOrderStorage. A client could then call it like so:

OrderStorageTechnology::current()->delete_meta_data($order, '_mondido_invoice_address');
OrderStorageTechnology::current()->update_meta_data($order, '_mondido_invoice_address', $address);
OrderStorageTechnology::current()->save($order);

The CPTOrderStorage::save method would have an empty implementation since saving seems to be implied by each individual mutation. I guess this is one reason for the lower performance of the legacy API.

To reduce the duplication further we could even introduce our own OrderStorage class and use it like so:

OrderStorage::get().replace_meta_data($order, '_mondido_invoice_address', $address);

The implementation would reuse the OrderStorageTechnology:

class OrderStorage
{
	// ... Static factory method, technology field and constructor here

	public function replace_meta_data($order, $key, $value)
	{
		$this->technology->delete_meta_data($order, $key);
		$this->technology->update_meta_data($order, $key, $value);
		$this->technology->save($order);
	}

	// More useful methods would go here
}

With this implementation, the only if-statement is in OrderStorageTechnology::current(), the client code is much simpler and each participating class has a small and well defined scope.

Copy link

@eric-thelin eric-thelin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good. Please consider my comments.

includes/class-wc-gateway-mondido-hw.php Outdated Show resolved Hide resolved
includes/class-wc-mondido-hpos-compatibility.php Outdated Show resolved Hide resolved
includes/class-wc-mondido-hpos-compatibility.php Outdated Show resolved Hide resolved
includes/class-wc-mondido-subscriptions.php Outdated Show resolved Hide resolved
@@ -58,21 +58,9 @@ public function __construct( $the_order ) {
if ( FALSE === $the_order ) {
$the_order = $post;
} elseif ( is_numeric( $the_order ) ) {
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we remove the conditional here?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file will be removed after upgrade so no need to add any if checks.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is upgrading WooCommerce a prerequisite for using HPOS?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

October 2023
From WooCommerce 8.2, released on October 2023, High-Performance Order Storage (HPOS) is officially flagged as stable and will be enabled by default for new installations.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tested everything on the latest version 8.7. So ideally if we get merchants to upgrade to the same one we should be safe. Hopefully the newer versions will also work, aka 9.0, but we should encourage our merchants to use 8.7 for now.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will happen if the merchant uses our updated plugin without updating their WooCommerce version? Do we really want to enforce all merchants to upgrade their WooCommerce version to use the latest version of our plugin?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is for us to be able to support HPOS and CPT at the same time, which is what this PR is about. Our merchants should till be able to run really old woo and it will work since it's backwards compatible. However, IMHO, i don't think it's good for them to be on old versions, like 2.2 etc. So if our plan is to ask those merchants to upgrade we might as well ask everyone.

Merchants that are using versions 8.2 or later will be able to activate HPOS directly. However they will need to also do some admin work since they need to sync orders between the tables etc, it's an easy process but still should be monitored by them and if any errors should come up they would need to contact us or Woocommerce.

includes/abstracts/class-wc-gateway-mondido-abstract.php Outdated Show resolved Hide resolved
includes/class-wc-gateway-mondido-hw.php Outdated Show resolved Hide resolved
includes/class-wc-mondido-hpos-compatibility.php Outdated Show resolved Hide resolved
includes/class-wc-mondido-admin-actions.php Show resolved Hide resolved
@@ -31,6 +31,11 @@ public function __construct() {
register_deactivation_hook( __FILE__, array( $this, 'flush_rewrite_rules' ) );

// Actions
add_action( 'before_woocommerce_init', function() {
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this call should move to either OrderStorageTechnology or OrderUtil so that we have all mentions of the custom orders table in a single location. However, if we move the logic we must pass __FILE__ as an argument to preserve the behavior, e.g. OrderStorageTechnology::declare_compatibility(__FILE__);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like it fits here since it's something that should be added before woocommerce init.
A very similar add_action call is made further down in that function:
"// WC_Order Compatibility for WC < 3.0
add_action( 'woocommerce_init', CLASS . '::add_compatibility' );"

Which is also for a one-off compatibility file, so i feel like it fits here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea was just to move the mention of the custom orders table to either OrderStorage or OrderUtil since that is an implementation detail of HPOS. Thinking of it now, an even better location is probably HPOSOrderStorage. The call to add_action is probably best kept here.

add_action( 'before_woocommerce_init', function() {
	HPOSOrderStorage::declare_compatibility(__FILE__);
}

However, if you prefer the current solution I am fine with that.

Copy link

@laura-mondido laura-mondido left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the "WIP" tag from title before merging. It'd confuse anyone going through commits in the future.

Copy link

@jonaholsson jonaholsson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good comments by @eric-thelin. I have nothing to add


use Automattic\WooCommerce\Utilities\OrderUtil;

interface OrderStorageInterface {
Copy link

@eric-thelin eric-thelin Apr 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having Interface in the name is ugly and violates the IS-A relationship to implementing classes. For instance, HPOSOrderStorage implements OrderStorageInterface but it is wrong to say that it "is a" OrderStorageInterface. HPOSOrderStorage is not an interface, it is a kind of order storage. The same is true for CPTOrderStorage. Since both those classes are examples of order storage, it makes sense to use the name OrderStorage for the shared interface.

If the problem is that we already use the name OrderStorage as a placeholder for the current() method, my suggestion is that we merge OrderStorageInterface and OrderStorage into an abstract class named OrderStorage.

abstract class OrderStorage
{
	public static function current(): OrderStorage
	{
		if (OrderUtil::custom_orders_table_usage_is_enabled()) {
			return new HPOSOrderStorage();
		} else {
			return new CPTOrderStorage();
		}
	}

	public abstract function get_meta_data($order, $meta_key, $meta_value);
	public abstract function add_meta_data($order, $meta_key, $meta_value);
	public abstract function update_meta_data($order, $meta_key, $meta_value);
	public abstract function delete_meta_data($order, $meta_key);
	public abstract function save($order);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's exactly the issue I saw with clashing names. Thanks for the input, learning a lot about OOP best practices here, great stuff :)

@maxfrederiksen-mondido maxfrederiksen-mondido changed the title (WIP) Refactor to use HPOS Refactor to support HPOS Apr 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

5 participants