All you need is to store the post IDs of the products together with the order (easiest is through post_meta
). Then you can run a WP_query
to fetch all the products.
The example below is a system that grants users access to a custom post type and charge credits, and gives options to send e-mail on update, set the access status and manually charge credits.
Though its not quite the same as yours, the same principles are at play.
I am assuming you are already confortable with WordPress hooks, CPTs metaboxes etc.
You need to:
- Add products to a form (Search and fetch the IDs of the products using ajax)
- Hook into
update_post
to save the form intopost_meta
- List items from
post_meta
onto the page.
Imagine you have a metabox with the form:
<form>
<?php
// lets get all your products saved as a serialized list
// more on how thats done later
$product_ids = get_post_meta( $post_id, 'product_ids', true );
// if no value, set default empty array
if ( empty( $product_ids ) ) {
$product_ids = array();
} else {
// otherwise, deserialize the value
$product_ids = explode( $product_ids, ',' );
}
/*
Two things to note about this code:
1. We are saving the IDs as a string, with ids separated by ','
which we explode into an array to use
2. We are using the post_meta 'single' argument instead of
saving multiple metas and retrieving as array.
You could do save multitple meta values, but it will use up more of
your database and is generally harder to maintain
*/
?>
<table id="product_list">
<?php foreach( $product_ids as $p_id ) : ?>
<tr>
<td><input type="text" name="product_id[]" value="<?php echo $p_id; ?>"></td>
</tr>
<?php endforeach; ?>
<!--
- Each row has an input with the same name ( id[] )
- Each input value must be a valid ID
You have to use javascript to populate this table with new items
which can be fetched using ajax through an input elsewhere in the page
Note that to remove a field, just remove it from the DOM
When saving, we'll overwrite the whole array, so any missing
items won't be saved ... as if they were never there!
-->
</table>
</form>
You can hook into save_post
. $_POST[ 'product_id' ]
now has an array with all posts IDs. Handy!
function yourdomain_save_post( $post_id ) {
// check we are updating your product CPT
if ( get_post_type( $post_id ) != 'your_products_cpt' ) { return; }
// Parse the product id
// make sure to check the values receieved, nonces, sanitization etc!
$product_ids = join( $_POST[ 'product_id' ], ',' );
// this will serialize your product IDs: "2,34,92,3"
// So it can be used with the display code in the previous example
// save the list
update_post_meta( $post_id, 'product_ids', $product_ids, true );
}
Now, if you want to pull each product information (say its name), you can use a WP_Query
:
// get comma separated string with product IDs
$product_ids = get_post_meta( $post_id, 'product_ids', true );
// create WP_Query
$args = array(
'post_type' => 'your_products_cpt',
'post__in' => $product_ids // you can use a comma separated list
// of IDs here ... coincidence..?
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
// pretty standard loop from now on
// You're a pro at this I am sure!
}
That’s it really!
I know its a bit rushed, but hope its enough to get your started.
Once these items are in place you can start adding better functionality and UX, such as using jQuery-ui autocomplete with Ajax to search for product names, use names (with ID as hidden field) in the table, show product pictures … the sky’s the limit.
If you want a quick way to get it done, check out ACF relationship fields.
Also spend some time in the codex for metaboxes and save post hooks
For Ajax, I always refer to an excelent article on Smashingmag
And remember to check your IDs, sanitize input and protect your site with NONCEs!
Cheers!