I'm using the following code in my theme functions.php file to add a additional data field to the product inventory tab:
// Add Custom Field to woocommerce inventory tab for product
add_action('woocommerce_product_options_inventory_product_data', function() {
woocommerce_wp_text_input([
'id' => '_number_in_package',
'label' => __('Number of Pages', 'txtdomain'),
'type' => 'number',
]);
});
add_action('woocommerce_process_product_meta', function($post_id) {
$product = wc_get_product($post_id);
$num_package = isset($_POST['_number_in_package']) ? $_POST['_number_in_package'] : '';
$product->update_meta_data('_number_in_package', sanitize_text_field($num_package));
$product->save();
});
add_action('woocommerce_product_meta_start', function() {
global $post;
$product = wc_get_product($post->ID);
$num_package = $product->get_meta('_number_in_package');
if (!empty($num_package)) {
printf('<div class="custom-sku">%s: %s</div>', __('Number of Pages', 'txtdomain'), $num_package);
}
});
add_filter('woocommerce_product_data_tabs', function($tabs) {
$tabs['additional_info'] = [
'label' => __('Additional info', 'txtdomain'),
'target' => 'additional_product_data',
'class' => ['hide_if_external'],
'priority' => 25
];
return $tabs;
});
However, on the single product page, the custom field is added before category and ISBN. I want to place the custom field at the end after the product ISBN. Any advice?
Some comments/suggestions regarding your code attempt
To save fields you can use the woocommerce_admin_process_product_object hook, opposite the outdated woocommerce_process_product_meta hook
WooCommerce contains by default no ISBN field, but it looks like the woocommerce_product_meta_end hook will answer your question
So you get:
// Add custom field
function action_woocommerce_product_options_inventory_product_data() {
woocommerce_wp_text_input( array(
'id' => '_number_in_package',
'label' => __( 'Number of Pages', 'woocommerce' ),
'description' => __( 'This is a custom field, you can write here anything you want.', 'woocommerce' ),
'desc_tip' => 'true',
'type' => 'number'
) );
}
add_action( 'woocommerce_product_options_inventory_product_data', 'action_woocommerce_product_options_inventory_product_data' );
// Save custom field
function action_woocommerce_admin_process_product_object( $product ) {
// Isset
if ( isset( $_POST['_number_in_package'] ) ) {
// Update
$product->update_meta_data( '_number_in_package', sanitize_text_field( $_POST['_number_in_package'] ) );
}
}
add_action( 'woocommerce_admin_process_product_object', 'action_woocommerce_admin_process_product_object', 10, 1 );
// Display on single product page
function action_woocommerce_product_meta_end() {
global $product;
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Get meta
$number = $product->get_meta( '_number_in_package' );
// NOT empty
if ( ! empty ( $number ) ) {
echo '<p>' . $number . '</p>';
}
}
}
add_action( 'woocommerce_product_meta_end', 'action_woocommerce_product_meta_end', 10 );
i cannot figure out what i am missing.
I created a custom textfield at the woocommerce checkout with a predefined value. Function is pretty standard:
function one_more_field(){
$product_name='';
global $product;
foreach( WC()->cart->get_cart() as $cart_item ){
$productvariation = $cart_item['variation_id'];
}
woocommerce_form_field( 'variatie', array(
'type' => 'text',
'value' => $productvariation,
),
$productvariation );
}
add_action( 'woocommerce_after_checkout_billing_form', 'one_more_field' );
I can see the field in the checkout and the variation ID is nicely inserted.
Then step 2: saving the value to the user meta data:
function reigel_woocommerce_checkout_update_user_meta( $customer_id, $posted ) {
$dob4 = 'test';
if (isset($posted['variatie'])) {
$dob4 = $posted['variatie'];
update_user_meta( $customer_id, 'variatie', $dob4);
}
add_action( 'woocommerce_checkout_update_user_meta', 'reigel_woocommerce_checkout_update_user_meta', 10, 2 );
However, the value is not saved to the user meta data.
What am i missing here?
Oh it turns out to be very straightforward: just add required => yes to the array of field arguments and it is saved. Cant really understand why though...
So new code (step 1) is:
function one_more_field(){
$product_name='';
global $product;
foreach( WC()->cart->get_cart() as $cart_item ){
$productvariation = $cart_item['variation_id'];
}
woocommerce_form_field( 'variatie', array(
'type' => 'text',
'value' => $productvariation,
'required' => 'true'
),
$productvariation );
}
add_action( 'woocommerce_after_checkout_billing_form', 'one_more_field' );
I am trying to create a PowerApp in which I want to integrate the post meta fields. I inserted this code in the functions.php file. But I can read and write the data for default WordPress fields, but I am not able to update the post custom fields.
add_action( 'rest_api_init', 'create_api_posts_meta_field' );
function create_api_posts_meta_field() {
// register_rest_field ( 'name-of-post-type', 'name-of-field-to-return', array-of-callbacks-and-schema() )
register_rest_field( 'post', 'post-meta-fields', array(
'get_callback' => 'get_post_meta_for_api',
'update_callback' => 'get_post_meta_update_for_api',
'schema' => null,
)
);
}
function get_post_meta_for_api( $object ) {
//get the id of the post object array
$post_id = $object['id'];
//return the post meta
return get_post_meta( $post_id );
}
function get_post_meta_update_for_api( $value, $object, $field_name ) {
return update_post_meta( $object[ 'id' ], $field_name, $value );
}
I have a gravity form form on my WP site and I recently changed a free text field into a drop down field.
The website is a store which hold several categories of goods and I want my drop-down to show the user all the possible categories he can choose from.
Please assist in how to "pull" the categories into the drop-down list.
Thanks in advance.
You can do using some filters of gravity form, code is following
// Here 1 is form id
add_filter( 'gform_pre_render_1', 'populate_category' );
add_filter( 'gform_pre_validation_1', 'populate_category' );
add_filter( 'gform_pre_submission_filter_1', 'populate_category' );
add_filter( 'gform_admin_pre_render_1', 'populate_category' );
function populate_category( $form ) {
foreach ( $form['fields'] as &$field ) {
if ( $field->type != 'select' || strpos( $field->cssClass, 'populate-category' ) === false ) {
continue;
}
// Get category list
$categories = get_categories( array(
'orderby' => 'name',
'order' => 'ASC'
) );
$choices = array();
foreach( $categories as $category ) {
$choices[] = array( 'text' => $category->name, 'value' => $category->name );
}
$field->placeholder = 'Select a Category';
$field->choices = $choices;
}
return $form;
}
This is working perfectly its tested code.
I use wp_list_categories() to get the list of all the categories and generate the navigation bar. Is there a way to order these categories in a particular order other than alphabetical ordering.
eg: Connect, News & Views, Q&A, Hello Startup, Startup 101...
Most themes don't use the description of the category for anything. Easy workaround I did was to use numbers in description. The top post here currently has some jQuery hack from here, it's unneeded.
You can add custom order fields I suppose as well.
Just
$categories = get_categories( array(
'orderby' => 'description',
'order' => 'ASC'
) );
Technical approach
The problem in wordpress core is that the table wp_terms has no term_order column. That means, standard wordpress does not support the custom term order. If you look at this WP database structure you can find the table wp_term_relationships. This table is responsible for the relationships between posts and the taxonomy (your categories) AND this table has a term_order column.
Now, with a simple SQL statement ALTER TABLE wp_terms ADD term_order INT(11) NOT NULL DEFAULT 0 (not forget the $wpdb->wp_terms variable) you can add a column to the table, which is responsible for your custom category order. Then you can put your custom category order in this two columns of wp_term_relationships and wp_terms. When all is finished, you can hook into the filter of get_terms_args and change the orderby to term_order.
Here a list of all relevant links for the technical approach:
https://codex.wordpress.org/Database_Description for the wp database structure
https://codex.wordpress.org/Class_Reference/wpdb for the $wpdb->wp_terms
https://developer.wordpress.org/reference/hooks/get_terms_args/ for the WP filter
A plugin can do the job for you
Check my plugin to solve this: WordPress Real Categories Management. WP RCM creates an extra field term_order on the wp terms table. It also brings a lot of other useful features as you can see in the screenshot below. It allows you to organize your wordpress categories in a nice way. It is easy to use, just drag&drop your categories and move it to a specific order. The plugin works in all Custom post types.
From the product description i can quote. If you want to try the plugin, there is also a demo on the plugin page.
There are a lot of free plugins
This can be solved with a lot of free plugins available within the wordpress.org plugin repository. Simply search for "category order" in your Wordpress Dashboard > Plugins > Install.
This is inbuilt in wordpress_wp_list_categories
wp_list_categories('orderby=name');
I think that would help you out
I did it generating several term lists. I call it later by my own order. I'm a PHP beginner.
First, I store, in a different variable, the ID for each category term:
$terms = get_terms('my_taxonomy', 'hide_empty=0');
foreach ( $terms as $term ) {
${$term->slug} = get_term_by('slug', $term->slug, 'product_cat');
${$term->slug.'_array'} = (array)${$term->slug};
${$term->slug.'_array_id'} =${$term->slug.'_array'}['term_id'];
};
Then, I create several args for each wp_list_categories() excluding, with this variable the terms I want to:
$args = array(
'taxonomy' => 'my_taxonomy',
'orderby' => 'name',
'show_count' => true,
'pad_counts' => false,
'hierarchical' => true,
'title_li' => '',
'hide_empty' => 0,
'show_option_all' => 'Show all',
'exclude' => array( $term1_array_id, $term2_array_id )
);
$args_1 = array(
'taxonomy' => 'my_taxonomy',
'orderby' => 'name',
'show_count' => true,
'pad_counts' => false,
'hierarchical' => true,
'title_li' => '',
'hide_empty' => 0,
'exclude' => array( $term3_array_id, $term4_array_id, $term1_array_id )
);
$args_2 = array(
'taxonomy' => 'my_taxonomy',
'orderby' => 'name',
'show_count' => true,
'pad_counts' => false,
'hierarchical' => true,
'title_li' => '',
'hide_empty' => 0,
'exclude' => array( $term1_array_id, $term4_array_id, $term5_array_id )
);
Finally, I can call separately each term list:
<ul>
<?php wp_list_categories( $args ); ?>
<?php wp_list_categories( $args_1 ); ?>
<?php wp_list_categories( $args_2 ); ?>
</ul>
Use Category Order and Taxonomy Terms Order free plugin
I didn't find anything so I constructed my own method. I abstracted it away in an abstract class for my plugin, hence the extra code, but you can pull the methods.
The main method to look at is format_hierarchy()
// The parent class
abstract class Taxonomy {
protected bool $_terms_loaded;
protected array $terms;
protected array $formatted_terms;
public function __get( $property ) {
if ( $property === 'formatted_terms' ) {
if ( !isset( $this->formatted_terms ) ) $this->format_hierarchy();
return $this->formatted_terms;
}
if ( substr( $property, 0, 1 ) === '_' ) die( 'Cannot get private properties' );
if ( property_exists( $this, $property ) )
return $this->$property;
}
/**
* Formats the taxonomy's terms into a hierarchy of term_blocks and saves the value as a property to the class
*
* #return array an array of `[term_taxonomy_id:number] => term_block` like:
* array(
* array( // parent $term_block
* 'term' => WP_Term,
* 'children' => array( $term_blocks… )
* ),
* …$term_blocks…
* )
*/
public function format_hierarchy():array {
if ( !$this->_load_terms() ) return [];
// Holds a reference to every category, parents and children
$term_blocks = [];
// Holds a reference to every top most level category
$parents = [];
foreach ( $this->terms as $term ) {
// Add itself to the list of all categories
$term_block = [
'children' => [],
'term' => $term
];
// Add itself to the array of all categories
if ( !isset( $term_blocks[ $term->term_taxonomy_id ] ) )
$term_blocks[ $term->term_taxonomy_id ] =& $term_block;
// If it's a child category…
if ( $term->parent !== 0 ) {
// If the parent hasn't been created yet, create it
if ( !isset( $term_blocks[ $term->parent ] ) )
$term_blocks[ $term->parent ] = [
'children' => [],
'term' => null
];
$term_blocks[ $term->parent ][ 'children' ][] =& $term_block;
} else
// Otherwise it's a parent
$parents[ $term->term_taxonomy_id ] =& $term_blocks[ $term->term_taxonomy_id ];
// set the term block's WP_Term property
$term_blocks[ $term->term_taxonomy_id ][ 'term' ] =& $term;
// This is needed so that the loop doesn't readd the same reference over and over again
unset( $term ); unset( $term_block );
}
return $this->formatted_terms = $parents;
}
/**
* Given a WP_Term property value, and a property key, recursively searches through all of the terms for it
*
* #property $term_val mixed The property value to find
* #property $prop string The property key for the value
* #property $with_parent ?boolean Whether to return the top level parent as well
* #property $term_blocks ?array Array of term blocks
* #return array If $with_parent is true, returns an [ $found_term_block, $top_level_parent ]
* Otherwise returns only the found term block
*/
public function find_term_by(
$term_val,
string $prop,
bool $with_parent = false,
$term_blocks = null
):?array {
if ( is_null( $term_blocks ) ) $term_blocks = $this->formatted_terms;
foreach ( $term_blocks as $term_block ) {
if ( $term_block[ 'term' ]->{$prop} === $term_val ) return $term_block;
if ( count( $term_block[ 'children' ] ) &&
( $found = $this->find_term_by( $term_val, $prop, false, $term_block[ 'children' ] ) )
) return $with_parent ? [ $found, $term_block ] : $found;
}
return null;
}
/**
* Loads the taxonomy terms once from the DB
*/
protected function _load_terms():bool {
if ( isset( $this->_terms_loaded ) ) return $this->_terms_loaded;
$this->terms = get_terms(
array(static::$taxonomy),
array(
'hide_empty' => false,
)
);
if ( !$this->terms ) {
ClassErrorHandler::handle_exception(
new \WP_Error( 500, 'Failed to load category terms: \'' . static::$taxonomy . '\'' )
);
}
return $this->_terms_loaded = !!$this->terms;
}
}
// The Implementation
class TaxonomyProductService extends Taxonomy {
public static string $taxonomy;
public static string $slug;
/**
* To be called upon taxonomy registration long before any instance is required
*/
public static function define_taxonomy( string $slug, string $taxonomy ) {
static::$slug = $slug;
static::$taxonomy = $taxonomy;
}
}
Right after registering the custom taxonomy I call
TaxonomyProductService::define_taxonomy( 'url-slug', 'product-service' );
And finally how it's used
$tax = new TaxonomyProductService();
$terms = $tax->formatted_terms;
// search for a term whose slug === `my-term`, and return the parent category
list( $current_term, $parent_term ) = $tax->find_term_by( 'my-term', 'slug', true );
For the benefit of future visitors, here’s the easy solution to this problem:
There are now a number of plugins that allow you to order categories or other custom taxonomies in WordPress. You can see some of them in the WordPress plugin directory’s “category order” tag page. I can personally confirm that Custom Taxonomy Order NE plugin does the job.