Skip to content

Moving a WooCommerce shop to Laravel with its products, orders and customers ​

A WooCommerce shop is spread across the whole WordPress database. Products are product posts with their price, SKU and stock in meta (_regular_price, _sale_price, _price, _sku, _stock, _stock_status and many more). Variations are product_variation posts under them. Global attributes sit in woocommerce_attribute_taxonomies, with their values as terms of pa_* taxonomies. On a current store, orders live in the HPOS tables: wc_orders, wc_order_addresses, wc_order_operational_data and wc_orders_meta, with the lines in woocommerce_order_items and woocommerce_order_itemmeta. Coupons are shop_coupon posts. Shipping zones and tax rates have tables of their own, and the settings are woocommerce_* options. Customers are WordPress users, with their addresses in billing_* and shipping_* user meta and their passwords as WordPress hashed them.

A usual move to Laravel picks a shop package with its own schema and writes an importer. Each step can lose something: an attribute that does not map, the tax per rate on an order line, a coupon's usage count, a refund, a saved address. Customers reset their passwords, and the importer runs again at the switch for the orders placed meanwhile.

How kitloom reads it ​

kitloom runs on the WordPress database as it is. kitloom/shop takes WooCommerce's tables, meta keys and options as its own data model, so nothing is imported and there is nothing to lose in an importer. A live WooCommerce shop opens in kitloom as it is:

  • Products: simple and variable, with categories, tags, global and custom attributes, prices, SKUs, stock, upsells and cross-sells, images and reviews (comments of type review with their rating).
  • Orders: from the HPOS tables, with their lines, fees, shipping, taxes per rate, coupons, notes and refunds.
  • Customers: read from the orders, one per billing email, guests included. A customer with an account signs in with their WordPress password and finds their orders and saved addresses in My account.
  • Coupons, shipping zones and their methods (flat rate, free shipping, local pickup), tax classes and rates, from WooCommerce's own tables.
  • Settings: the store's address, where it sells and ships, the checkout options, currency, taxes and the emails, from the woocommerce_* options.
  • Offline payments: bank transfer and cash on delivery keep their titles and instructions, read from woocommerce_bacs_settings and woocommerce_cod_settings.

The addresses stay too: /product/…, /product-category/… and /product-tag/…, or the bases in woocommerce_permalinks. The Cart, Checkout and My account pages keep working, whether they hold WooCommerce's shortcodes or its Cart and Checkout blocks. The cart and checkout answer where WooCommerce's Store API lives, under /wp-json/wc/store/v1/.

In code, a product and an order are Eloquent models with plain method names:

php
if ($product->isVariable()) {
    $product->priceRange();              // ['min' => 18.0, 'max' => 24.0]

    foreach ($product->variations as $variation) {
        $variation->sku();
        $variation->stockQuantity();
    }
}

$order->totalRefunded();

We compare the shop with a real WooCommerce store on the same data: a suite of scenarios (coupons, taxes, shipping, stock) runs the same steps on a WooCommerce store and on kitloom over the same data and compares the totals.

What the admin shows ​

Both admins, Filament and Nova, have one Shop section: Orders, Coupons, Customers, the products with their categories, tags and attributes, Shipping, Shipping classes, Tax rates and Settings. The product form has WooCommerce's Product data tabs: General, Inventory, Shipping, Linked products, Advanced, and Variations for a variable product.

An order's page changes its status, adds notes (for the staff or for the customer), edits the addresses and, while the order is pending payment, on hold or failed, its items. Refund sends the money back through the gateway or records a refund made some other way. Add order takes an order by hand, and Send email sends one of the order's emails again.

The screens ask WooCommerce's own capabilities: edit_shop_orders, manage_woocommerce, edit_products, edit_shop_coupons. WooCommerce's Shop manager role is not created for you; make one on the roles screen if you need it.

With kitloom/ai installed, an AI agent can read and save products, list orders, move an order to another status, leave internal notes and read sales reports, each with the rights of the user it acts as.

What changes and what is not covered ​

  • The move is one way. kitloom writes arrays as JSON (_product_attributes, an order line's _line_tax_data, list options), and WooCommerce cannot read those back. kitloom reads WooCommerce's serialized values.
  • Orders are read from the HPOS tables only. A shop that still keeps its orders as shop_order posts shows none of them until WooCommerce has moved them to HPOS. Do that in WooCommerce before the move.
  • WooCommerce's lookup and analytics tables (wc_product_meta_lookup, wc_order_stats and the rest) are not kept up to date, and the admin has no Analytics screen. An agent's sales reports read from the orders.
  • Refunds are given in the admin. An agent may change an order's status, but not to refunded.
  • Online payment gateways are set up again in kitloom/payments (Stripe, Paddle, LiqPay, Monobank, WayForPay, Hutko).
  • Downloadable products keep their files in meta, and paid orders get their download permissions. The shop does not serve the downloads yet, and the product form does not edit the files.
  • Grouped and external products keep their type but cannot go into the cart, as in WooCommerce. An external product's button leads to the site that sells it. A grouped product's page does not list its products yet.
  • Shipping methods from other plugins (table rates and the like) are listed but not offered. WooCommerce's Geolocate option is not supported.
  • Extensions are plugins of their own. Their data stays in the database, but without a kitloom module nothing uses it.
  • Plugin code does not run. The shop is kitloom's own code, written from WooCommerce's data model. The storefront is drawn by a kitloom theme, and there is no Gutenberg.

The full list, table by table, is in the Shop guide.

Run the shop with an AI agent ​

This is where kitloom differs most from a shop you moved by hand. With kitloom/ai installed, Claude, Cursor or your own agent connects to the site and works the shop through its own tools: products and their attributes (products_list, product_save, attribute_save), orders (orders_list, order_get, order_set_status, order_add_note), customers, coupons (coupon_save), and the numbers (sales_report, top_products). You ask in plain words:

  • "Which orders have been on hold for more than three days? Add a note to each and list them for me."
  • "How did the shop do last month, and which five products sold best?"
  • "Write descriptions for the products that have none, as drafts."
  • "Make a 10% coupon for the spring collection that ends on Sunday."

You stay in control. The agent acts as the user whose key it holds, so a shop manager's key does what a shop manager may do and a read-only key only reads. A new product is a draft until a person publishes it. A completed or refunded order is not reopened, and refunds stay in the admin. Every change is in the activity log, and revoking the key stops the agent at once. How to connect one: Run your site from Claude or Cursor.

Check it on your own site ​

You can see your shop in kitloom before anything changes. Create a MySQL user that can only read, point kitloom at the live database, and run compare-sites.php against both sites. It compares status codes, titles, canonicals, content and the lists on archive pages, address by address. Products, categories and the Shop page are drawn from the same data at the same moment.

The cart, the checkout and signing in write to the database, so they need a copy of it. Load the copy, run php artisan migrate, sign in with your WordPress password and open a few orders. At the switch, the shop takes no orders until the final copy is in place. The steps are in Migrate from WordPress.

Try it ​

Open a sandbox in one click, with no sign-up: Filament or Nova. When it fits, see Pricing.

The same approach covers the rest of a WooCommerce site's plugins: ACF fields, Contact Form 7 and Flamingo and Rank Math. An overview is in your plugin data after moving to Laravel.

Last updated:

kitloom — modules and themes for Laravel.
Pricing · Services · License · Refunds · Privacy · Community · [email protected]