Repository navigation
Add object forms for WooCommerce order edit screens - #35
Merged
Merged
Conversation
Object forms on order screens need a store over WooCommerce's order CRUD, because orders in WooCommerce's order tables have no metadata API type. It must read only the keys an order has, so a stored empty string or empty list never reads as missing, and write a form's values in one order save, so WooCommerce's order hooks and caches see them. OrderMetaStoreTest runs each row with orders in WooCommerce's order tables and in the posts table: - only the keys the order has come back, with a stored empty string and empty list among them; - text keeps its backslashes; - a stored empty string and empty list read back as stored; - one write saves the order once, and woocommerce_update_order sees the new values. OrderFixturesTrait moves the test process to the posts table with a pre_option filter, because update_option() refuses the switch while orders are out of sync. Nothing was outdated, so this branch has no package update: composer outdated --direct and npm outdated listed nothing, composer update and npm update left both locks unchanged, and the configs workflows already pin configs trunk 6302f1f. Assisted-by: Claude Code:claude-opus-5-5
Orders in WooCommerce's order tables have no metadata API type, so an object form on an order screen had no store for its values. OrderMetaStore reads and writes through WooCommerce's order CRUD, so it works wherever WooCommerce stores orders: - it reads a key only when the order has it, with the edit context, so a stored empty value is returned and WooCommerce's view filters do not change what the form shows; - it writes every value of one save to the order and saves the order once, so WooCommerce's order hooks and caches see the change. The test helper now reindexes the meta values it reads, because WooCommerce keeps their array positions, and a row pins the edit-context read. Assisted-by: Claude Code:claude-opus-5-5
Object forms could show on post, profile and term screens, but not on the screens that edit WooCommerce orders. WooCommerce draws those through its own page when orders live in its order tables, and through post.php when they live in the posts table, and passes add_meta_boxes the screen id and a WC_Order on the first. OrderMetaBoxTest drives WooCommerce's order edit screen, admin.php?page=wc-orders, with orders in the order tables: - a box serves one form, is added on add_meta_boxes and saves at 35 on woocommerce_process_shop_order_meta, before WooCommerce's order data save at 40; - the definition is evaluated once on the edit screen and never on the dashboard; - the box shows, titled and with its nonce, only for orders of its types, and in its context; - a shop manager without the form's capability gets no box; - a single control is labelled by its row and a group by the row's visible legend; - controls carry the stored value, a stored empty value or the default; - per-object fields receive the order id, and a contributed field renders. The request fixture gains the order host and a switch that keeps one request's orders in the posts table. Assisted-by: Claude Code:claude-opus-5-5
An object form had no host on WooCommerce's order screens, whose ids and objects depend on where WooCommerce stores orders. OrderMetaBox takes the order types whose edit screens show it, a context and a priority: - on add_meta_boxes it adds the box when the screen id is one that wc_get_page_screen_id() gives for its types and the form shows for the order, reading the order id from the WC_Order or WP_Post WooCommerce passes; - the object capability is edit_post, which WooCommerce maps to the order type's capabilities in both storages; - the default store is OrderMetaStore; - rows stack as PostMetaBox's do, with a group named by a visible legend. The hook row now covers add_meta_boxes only; the save hook gets its row with saving. Assisted-by: Claude Code:claude-opus-5-5
The order meta box rendered the form but nothing stored it when WooCommerce saved the order. OrderMetaBoxTest adds rows through WooCommerce's order edit and new-order screens: - the box saves at 35 on woocommerce_process_shop_order_meta; - Update stores accepted values as the order's meta: text unslashed, an unchecked checkbox as no, a cleared list as an empty list, and an unposted field keeps its value; - a worded and a silent rejection keep the value and show once, as text, on the edit screen WooCommerce redirects to, and not after; - a status change in the same save sees the stored value, because WooCommerce changes the status at 40; - the new-order screen renders defaults and its first save stores them; - a nonce minted for another order or none, a user without the form's capability, and an order saved in code store nothing. The three guard rows pass until saving exists. The request fixture records what woocommerce_order_status_changed sees under a meta key. A spacing error from the rendering tests is fixed. Assisted-by: Claude Code:claude-opus-5-5
The order meta box rendered the form, but nothing stored it. OrderMetaBox now calls the form's save() on woocommerce_process_shop_order_meta at 35, which WooCommerce fires only from its order edit form, in both storages, after its own nonce check. The form stores before WooCommerce saves the order's data at 40 and its actions at 50, so a status change and the emails it sends see the form's values. The save does not check the order type: WooCommerce fires the hook for every order type, but only the box's own screens print the form's nonce, which is bound to the order. Assisted-by: Claude Code:claude-opus-5-5
WooCommerce edits orders stored in the posts table on post.php, passes add_meta_boxes the post type and a WP_Post, and reaches woocommerce_process_shop_order_meta through save_post. The rows so far ran only with orders in WooCommerce's order tables. Three rows switch both the test process and the request to the posts table: - the box shows on post.php with its nonce and the stored value; - Update stores the accepted values; - a rejection keeps the value and shows on the screen WordPress redirects to. They pass on arrival, because the box already reads its screen ids from wc_get_page_screen_id() and its order id from either object. Each fails when the box drops the WP_Post order id or matches only the order-table screen id. Assisted-by: Claude Code:claude-opus-5-5
The file map did not list the order classes, and limitation 6 named only core's Custom Fields box as a write that skips an object form field's validate. The file map lists Orders/ with OrderMetaBox and OrderMetaStore. Limitation 6 adds order CRUD writes and WooCommerce's Custom Fields box on the order tables' edit screen, and says that core's box covers orders stored as posts, whose post type supports custom-fields. Assisted-by: Claude Code:claude-opus-5-5
The add_meta_box() docblock said the hook passes a WC_Order or a WP_Post, but add_meta_boxes passes other objects on other screens, such as a comment, which the box ignores. The parameter now says that the object is a WC_Order or a WP_Post on an order screen. Assisted-by: Claude Code:claude-opus-5-5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Object forms could show on post, profile and term screens, but not where WooCommerce edits orders. WooCommerce draws the order screen itself when orders live in its order tables and through
post.phpwhen they live in the posts table, with different screen ids and objects, and orders in its tables have no metadata API type to store into.OrderMetaBoxhosts anObjectFormin a meta box on the edit screens of the given order types (shop_orderby default), in both storages, with the same stacked rows asPostMetaBox. It matches the screen idswc_get_page_screen_id()gives for its types, so it also follows custom order types and the screen of a user without WooCommerce's menu.woocommerce_process_shop_order_metaat 35, which WooCommerce fires only from its order edit form after its own nonce check, and before it saves the order's data at 40 and its actions at 50, so a status change and the emails it sends see the form's values.OrderMetaStorekeeps the values as order meta through WooCommerce's order CRUD: it reads only the keys the order has, with the edit context, and writes one save's values in one order save, so WooCommerce's order hooks and caches see them.edit_post, which WooCommerce maps to the order type's capabilities in both storages.AGENTS.md records the file map, and that an object form field's
validatealso does not run for order CRUD writes or a Custom Fields box on order screens.Affected packages
wp-framework-bootstrapwp-framework-sharedwp-framework-corewp-framework-settingswp-framework-woocommerceChecklist
tests/Integration/WooCommerce/OrderMetaBoxTest.php(24 rows) drives WooCommerce's order edit and new-order screens with orders in its order tables, andpost.phpwith orders in the posts table, each request in its own PHP process, with forms posted back as a browser serializes them;OrderMetaStoreTest.php(9) runs in both storages.composer quality-checkpasses locally (Unit 204, Integration 970), pluscomposer test:integration:multisite(29) and PHP 7.4php -loverpackages/bootstrapand the woocommerce uninstall filesREADME.md(the package READMEs stay pointer-form; AGENTS.md carries the file map)