How can I save a custom field of an attribute in Woocommerce? - wordpress

Old
I am trying to create a custom field in the product attributes in Woocommerce. This to be able to select if an attribute is highlighted or not. For example:
https://i.stack.imgur.com/Ge76B.png
What I want to do is that the field that I specify in backend as highlighted is displayed in a certain way in the frontend.
So far I have been able to add the field, but I have not managed to figure out how to save it. This is what I have:
add_action('woocommerce_after_product_attribute_settings','wcb_add_product_attribute_is_highlighted', 10, 2);
add_filter( 'woocommerce_admin_meta_boxes_prepare_attribute', 'wcb_admin_meta_boxes_prepare_attribute', 10, 3);
function get_attribute_highlighted($id, $i) {
return get_post_meta( 1, "attribute_".$id."_highlighted_".$i, true);
}
function wcb_add_product_attribute_is_highlighted($attribute, $i=0) {
$value = get_attribute_highlighted($attribute->get_id(), $i); ?>
<tr>
<td>
<div class="enable_variation show_if_canopytour show_if_variable_canopytour">
<label><input type="checkbox" class="checkbox" <?php checked( $value, true ); ?> name="attribute_highlighted[<?php echo esc_attr( $i ); ?>]" value="1" /> <?php esc_html_e( 'Highlight attribute', $this->wcb ); ?></label>
</div>
</td>
</tr>
<?php
}
function wcb_admin_meta_boxes_prepare_attribute($attribute, $data, $i=0) {
// updated
if(array_key_exists("attribute_highlighted", $data) && is_array($data["attribute_highlighted"])) {
update_post_meta( 1, "attribute_".$attribute->get_id()."_highlighted_".$i, wc_string_to_bool($data["attribute_highlighted"][$i]) );
}
}
I tried the woocommerce_admin_meta_boxes_prepare_attribute filter using the offsetSet and offsetGet methods of the WC_Product_Attribute class but I can not understand how it works. I could not make it save my custom value.
Upgrade
I changed the woocommerce_admin_meta_boxes_prepare_attribute filter by the wp_ajax_woocommerce_save_attributes action and it seems to work. Now I have the problem that it is not updated after saving for the first time.
I explain the current problem: I have a checkbox that when clicked is activated, I save and when reloading the state it is maintained. If now I want to deactivate it, after saving the state it is not kept as disabled, it is put back as activated.
This is the updated code:
add_action('woocommerce_after_product_attribute_settings', 'wcb_add_product_attribute_is_highlighted', 10, 2);
add_action('wp_ajax_woocommerce_save_attributes', 'wcb_ajax_woocommerce_save_attributes', 10);
function get_attribute_highlighted($id, $i) {
global $post;
$id = sanitize_title($id);
$id = strtolower($id);
$val = get_post_meta( $post->ID, "attribute_".$id."_highlighted_".$i, true);
return !empty($val) ? $val : false;
}
function wcb_add_product_attribute_is_highlighted($attribute, $i=0) {
$value = get_attribute_highlighted($attribute->get_name(), $i); ?>
<tr>
<td>
<div class="enable_highlighted show_if_canopytour show_if_variable_canopytour">
<label><input type="checkbox" class="checkbox" <?php checked( $value, true ); ?> name="attribute_highlighted[<?php echo esc_attr( $i ); ?>]" value="1" /> <?php esc_html_e( 'Highlight attribute', $this->wcb ); ?></label>
</div>
</td>
</tr>
<?php
}
function wcb_ajax_woocommerce_save_attributes() {
check_ajax_referer( 'save-attributes', 'security' );
parse_str( $_POST['data'], $data );
$post_id = absint( $_POST['post_id'] );
if(array_key_exists("attribute_highlighted", $data) && is_array($data["attribute_highlighted"])) {
foreach($data["attribute_highlighted"] as $i => $val) {
$attr_name = sanitize_title($data["attribute_names"][$i]);
$attr_name = strtolower($attr_name);
update_post_meta( $post_id, "attribute_".$attr_name."_highlighted_".$i, wc_string_to_bool($val) );
}
}
}

Check Ink's answer
Outdated
I have found the solution to my problem. I share the code in case someone is useful. Regards!
add_action('woocommerce_after_product_attribute_settings', 'wcb_add_product_attribute_is_highlighted', 10, 2);
add_action('wp_ajax_woocommerce_save_attributes', 'wcb_ajax_woocommerce_save_attributes', 10);
function get_attribute_highlighted($id, $i) {
global $post;
$id = sanitize_title($id);
$id = strtolower($id);
$val = get_post_meta( $post->ID, "attribute_".$id."_highlighted_".$i, true);
return !empty($val) ? $val : false;
}
function wcb_add_product_attribute_is_highlighted($attribute, $i=0) {
$value = get_attribute_highlighted($attribute->get_name(), $i); ?>
<tr>
<td>
<div class="enable_highlighted">
<label><input type="hidden" name="attribute_highlighted[<?php echo esc_attr( $i ); ?>]" value="0" /><input type="checkbox" class="checkbox" <?php checked( $value, true ); ?> name="attribute_highlighted[<?php echo esc_attr( $i ); ?>]" value="1" /> <?php esc_html_e( 'Highlight attribute', $this->wcb ); ?></label>
</div>
</td>
</tr>
<?php
}
function wcb_ajax_woocommerce_save_attributes() {
check_ajax_referer( 'save-attributes', 'security' );
parse_str( $_POST['data'], $data );
$post_id = absint( $_POST['post_id'] );
if(array_key_exists("attribute_highlighted", $data) && is_array($data["attribute_highlighted"])) {
foreach($data["attribute_highlighted"] as $i => $val) {
$attr_name = sanitize_title($data["attribute_names"][$i]);
$attr_name = strtolower($attr_name);
update_post_meta( $post_id, "attribute_".$attr_name."_highlighted_".$i, wc_string_to_bool($val) );
}
}
}
In the end the only thing I had to add to my code was a hidden input with the same name as the checkbox but with a value of 0: <input type="hidden" name="attribute_highlighted[<?php echo esc_attr( $i ); ?>]" value="0" />
Here an image of the result (https://i.stack.imgur.com/VscT1.jpg). By clicking on save the value of the checkbox is maintained. The value is saved in a post_meta of the post that you are modifying. This is useful if you want to highlight a specific attribute in the front end.
I appreciate the help of #LoicTheAztec :)

As I struggled with the same issue I have tried using the code from Jesus Magallon. However, it did not work.
I found out that the priority of wp_ajax_woocommerce_save_attributes needed to be set to 0, otherwise the callback is not run for some reason.
Forthermore, if we only use $post->ID for get_post_meta() the correct results state of the checkbox will only be visible on page reload. To make sure the selected state is also visible direclty after ajax saving/reloading, we need to use $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : $post->ID; instead.
Here the final version that should work perfectly for everyone:
add_action( 'woocommerce_after_product_attribute_settings', 'wcb_add_product_attribute_is_highlighted', 10, 2);
add_action( 'wp_ajax_woocommerce_save_attributes', 'wcb_ajax_woocommerce_save_attributes', 0);
function get_attribute_highlighted( $attribute_name, $i ) {
global $post;
// ID for either from ajax or from post
$post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : $post->ID ;
$attribute_name = strtolower( sanitize_title( $attribute_name ) );
$val = get_post_meta( $post_id, 'attribute_' . $attribute_name . '_highlighted_' . $i, true );
return !empty( $val ) ? $val : false;
}
function wcb_add_product_attribute_is_highlighted( $attribute, $i=0 ) {
$value = get_attribute_highlighted($attribute->get_name(), $i); ?>
<tr>
<td>
<div class="enable_highlighted">
<label>
<input type="hidden" name="attribute_highlighted[<?php echo esc_attr( $i ); ?>]" value="0" />
<input type="checkbox" class="checkbox" <?php checked( $value, true ); ?> name="attribute_highlighted[<?php echo esc_attr( $i ); ?>]" value="1" />
<?php esc_html_e( 'Highlight attribute', 'textdomain' ); ?>
</label>
</div>
</td>
</tr>
<?php
}
function wcb_ajax_woocommerce_save_attributes() {
check_ajax_referer( 'save-attributes', 'security' );
parse_str( $_POST['data'], $data );
$post_id = absint( $_POST['post_id'] );
if( array_key_exists( 'attribute_highlighted', $data ) && is_array( $data['attribute_highlighted'] ) ) {
foreach( $data['attribute_highlighted'] as $i => $val ) {
$attr_name = sanitize_title( $data['attribute_names'][$i] );
$attr_name = strtolower( $attr_name );
update_post_meta( $post_id, 'attribute_' . $attr_name . '_highlighted_' . absint( $i ), wc_string_to_bool( $val ) );
}
}
}

#ink almost got it, but it's not working when you click on Update button of whole product directly. You always need to click on Save attributes button firstly and that's wrong, because when you try other checkboxes then you'll see it works also on Update click.
So I would completely remove that add_action( 'wp_ajax_woocommerce_save_attributes', 'wcb_ajax_woocommerce_save_attributes', 0); and wcb_ajax_woocommerce_save_attributes function and use this instead:
add_filter( 'woocommerce_admin_meta_boxes_prepare_attribute', function( $attribute, $data, $i ){
global $post;
if( is_object( $post ) && isset( $post->ID ) ){
$post_id = $post->ID;
}else{
$post_id = absint( $_POST['post_id'] );
}
if( array_key_exists( 'attribute_highlighted', $data ) && is_array( $data['attribute_highlighted'] ) ){
foreach( $data['attribute_highlighted'] as $i => $value ){
$attr_name = sanitize_title( $data['attribute_names'][ $i ] );
update_post_meta( $post_id, 'attribute_' . $attr_name . '_highlighted_' . absint( $i ), wc_string_to_bool( $value ) );
}
}
return $attribute;
}, 10, 3 );

perfect code but change the hook
add_action('wp_ajax_woocommerce_save_attributes', 'wcb_ajax_woocommerce_save_attributes', 10);
to
add_action('wp_ajax_woocommerce_save_attributes', 'wcb_ajax_woocommerce_save_attributes', 0);

#Jesús Magallón's
Answer need little modifications.
Remove the function get_attribute_highlighted() and replace this line
$value = get_attribute_highlighted($attribute->get_name(), $i);
TO
global $post;
$post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : $post->ID ;
$value = get_post_meta($post_id , "attribute_description".$i, true);
It will show ajax saved value instance of empty;
And Change hooks
add_action('wp_ajax_woocommerce_save_attributes', 'wcb_ajax_woocommerce_save_attributes', 10);
TO
add_action('wp_ajax_woocommerce_save_attributes', 'wcb_ajax_woocommerce_save_attributes', 0);

Related

How to make an array to clear the checkbox in "register_setting"?

There is a ready form.
2 checkboxes, everything works, but how to make an array when there are a lot of checkboxes?
<?php
add_action( 'admin_menu', 'add_page' );
function add_page() {
add_options_page( 'Test Settings', 'Title', 'manage_options', 'page_slug', 'callback_output' );
}
function callback_output() {
?>
<div class="wrap">
<form action="options.php" method="post">
<?php
settings_fields( 'option_group' );
do_settings_sections( 'field_page' );
submit_button();
?>
</form>
</div>
<?php
}
add_action( 'admin_init', 'settings' );
function settings() {
register_setting( 'option_group', 'db_settings', 'sinitize' );
add_settings_section( 'sect_id', 'Settings', '', 'field_page' );
add_settings_field( 'field1', 'NAME FIELD 1', 'fill_field1', 'field_page', 'sect_id' );
add_settings_field( 'field2', 'NAME FIELD 2', 'fill_field2', 'field_page', 'sect_id' );
}
function fill_field1() {
$val = get_option( 'db_settings' );
$val = $val ? $val['checkbox1'] : false;
?>
<label><input type="checkbox" name="db_settings[checkbox1]" value="1" <?php checked( 1, $val ); ?> /> checkbox 1</label>
<?php
}
function fill_field2() {
$val = get_option( 'db_settings' );
$val = $val ? $val['checkbox2'] : false;
?>
<label><input type="checkbox" name="db_settings[checkbox2]" value="1" <?php checked( 1, $val ); ?> /> checkbox 2</label>
<?php
}
**// Sinitize. Need array.**
function sinitize( $options ) {
foreach ( $options as $name => & $val ) {
if ( 'checkbox1' === $name ) {
$val = int( $val );
} elseif ( 'checkbox2' === $name ) {
$val = int( $val );
}
}
return $options;
}
How can I just do this without changing the main code, just changing the function: sinitize()
Thanks in advance for your replies!
To start with, I don't believe you're using register_setting correctly - the third parameter should be an array of args, not a callback reference.
Within the $args array, you can specify 'type' as 'array' - that may be what you're looking for.

Add Image Upload to Woocommerce Registration Form And Display it in the Admin Backend

Hi I am trying to add an upload image field to the woocommerce register form and then get this to display in the admin area in the backend. I am using the code below which give the image upload on register form but having trouble trying to get it to display within the admin area.
//// ADD IMAGE UPLOAD TOO REGSITER
// Add enctype to form to allow file upload
function AddEnctypeCustomRegistrationForms() {
echo 'enctype="multipart/form-data"';
}
add_action('woocommerce_register_form_tag','AddEnctypeCustomRegistrationForms');
// Add file input html to register form
add_action('woocommerce_register_form','AddImageField');
function AddImageField() {
?>
<p class="form-row validate-required" id="pro_image" data-priority="">
<label for="pro_image" class="">Upload Picture ID<abbr class="required" title="required">*</abbr></label>
<span class="woocommerce-input-wrapper">
<input type='file' name='pro_image' accept='image/*,.pdf' required>
</span>
</p>
<?php
}
// Validate new fields
function ValidateImageField( $errors, $username, $email ) {
if ( isset( $_POST['pro_image'] ) && empty( $_POST['pro_image'] ) ) {
$errors->add('pro_image_error', __( 'Please provide a valid image', 'woocommerce' ) );
}
return $errors;
}
add_filter('woocommerce_registration_errors','ValidateImageField',10,3 );
// Save new field
function SaveImageField( $customer_id ) {
if ( isset( $_FILES['pro_image'] ) ) {
require_once( ABSPATH . 'wp-admin/includes/image.php' );
require_once( ABSPATH . 'wp-admin/includes/file.php' );
require_once( ABSPATH . 'wp-admin/includes/media.php' );
$attach_id= media_handle_upload('pro_image', 0 );
if ( is_wp_error( $attach_id) ) {
update_user_meta( $customer_id,'pro_image', $_FILES['pro_image'] . ": " . $attach_id->get_error_message() );
} else {
update_user_meta( $customer_id,'pro_image', $attach_id);
}
}
}
add_action('user_register','SaveImageField',1);
I have tried a few different things but not having any luck, here is one of them.
function add_SaveImageField( $user ) {
?>
<h3><?php _e('ID','woocommerce' ); ?></h3>
<table class="form-table">
<tr>
<th><label for="SaveImageField_field"><?php _e( 'ID', 'woocommerce' ); ?></label></th>
<td><input type="image" name="SaveImageField_field" value="<?php echo esc_attr( get_the_author_meta( 'pro_image', $user->ID )); ?>" class="pro_image" /></td>
</tr>
</table>
<br />
<?php
}
add_action( 'show_user_profile', 'add_SaveImageField', 10, 1 );
add_action( 'edit_user_profile', 'add_SaveImageField', 10, 1 );
Any help would be much apricated
Thanks
You have to do some changes to your code
// Save new field
function SaveImageField( $customer_id ) {
if ( isset( $_FILES['pro_image'] ) ) {
require_once( ABSPATH . 'wp-admin/includes/image.php' );
require_once( ABSPATH . 'wp-admin/includes/file.php' );
require_once( ABSPATH . 'wp-admin/includes/media.php' );
//Code changes
foreach ($_FILES as $file => $array){
if ($file == 'pro_image') {
if (!empty($array['name'])) {
$attach_id = media_handle_upload($file,$new_post);
update_user_meta( $customer_id, $file, $attach_id );
}
}
}
//End Code changes
}
}
add_action('user_register','SaveImageField',1);
function add_SaveImageField( $user ) {
//Code changes
$thumbnail_id = get_the_author_meta( 'pro_image', $user->ID );
$image_url = '';
if (!empty($thumbnail_id) && $thumbnail_id) {
$image_size = 'full'; // (thumbnail, medium, large, full or custom size)
$image_attachment_data_arr = wp_get_attachment_image_src( $thumbnail_id, $image_size );
$image_url = $image_attachment_data_arr[0];
}
//EndCode changes
?>
<h3><?php _e('ID','woocommerce' ); ?></h3>
<table class="form-table">
<tr>
<th><label for="SaveImageField_field"><?php _e( 'ID', 'woocommerce' ); ?></label></th>
<td>
<!--Code changes-->
<?php if (!empty($image_url)) { ?>
<img src="<?php echo $image_url;?>" style="width: 200px;">
<?php }else{ ?>
<h3>Image Not uploaded</h3>
<?php } ?>
<!--End Code changes-->
</td>
</tr>
</table>
<br />
<?php
}
add_action( 'show_user_profile', 'add_SaveImageField', 10, 1 );
add_action( 'edit_user_profile', 'add_SaveImageField', 10, 1 );

Get product custom attribute Woocommerce

I have a single product custom variation table template in functions.php and I'm trying to get product custom attribute. I have two them (date and location).
I'm found a lot of solutions, but none didn't work for me.
Woocommerce version 3.2.6.
With this code below I get
Notice: Only variables should be passed by reference.
And I get type 'Null' of $date.
<td class="date">
<?php
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
$date = array_shift( wc_get_product_terms( $product_id, 'pa_date', array( 'fields' => 'names' ) ) );
?>
</td>
This is my full product variation table code in function.php
function woocommerce_variable_add_to_cart(){
global $product, $post, $woocommerce;
$attributes = $product->get_attributes();
$variations = find_valid_variations();
// Check if the special 'price_grid' meta is set, if it is, load the default template:
if ( get_post_meta($post->ID, 'price_grid', true) ) {
wp_enqueue_script( 'wc-add-to-cart-variation' );
wc_get_template( 'single-product/add-to-cart/variable.php', array(
'available_variations' => $product->get_available_variations(),
'attributes' => $product->get_variation_attributes(),
'selected_attributes' => $product->get_variation_default_attributes()
) );
return;
}
?>
<table class="variations variations-grid" cellspacing="0">
<thead>
<tr>
<td> Date | Location </td>
<td> Price </td>
<td> Quantity </td>
<td> Availability </td>
<td> </td>
</tr>
</thead>
<tbody>
<?php
foreach ($variations as $key => $value) {
if( !$value['variation_is_visible'] ) continue;
?>
<tr>
<td class="date">
<?php
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id; // Added WC 3+ support
$date = array_shift( wc_get_product_terms( $product_id, 'pa_date', array( 'fields' => 'names' ) ) );
?>
</td>
<td class="price">
<?php echo '<span>£</span>' . $product->get_price(); ?>
</td>
<td class="quantity">
<?php woocommerce_quantity_input(); ?>
</td>
<td class="stock">
<?php if (!$value['is_in_stock'] ) { ?>
<p class="stock out-of-stock"><?php _e( 'Places Not Available', 'woocommerce' ); ?></p>
<?php } else { ?>
<p class="stock in-stock"><?php _e( 'Places Available', 'woocommerce' ); ?></p>
</td>
<td class="add-to-cart">
<form class="cart" action="<?php echo esc_url( $product->add_to_cart_url() ); ?>" method="post" enctype='multipart/form-data'>
<?php
if(!empty($value['attributes'])){
foreach ($value['attributes'] as $attr_key => $attr_value) {
?>
<input type="hidden" name="<?php echo $attr_key?>" value="<?php echo $attr_value?>">
<?php
}
}
?>
<button type="submit" class="single_add_to_cart_button button alt"><span class="glyphicon glyphicon-tag"></span> Add to cart</button>
<input type="hidden" name="variation_id" value="<?php echo $value['variation_id']?>" />
<input type="hidden" name="product_id" value="<?php echo esc_attr( $post->ID ); ?>" />
<input type="hidden" name="add-to-cart" value="<?php echo esc_attr( $post->ID ); ?>" />
</form>
<?php } ?>
</td>
</tr>
<?php } ?>
</tbody>
</table>
<?php
}
function find_valid_variations() {
global $product;
$variations = $product->get_available_variations();
$attributes = $product->get_attributes();
$new_variants = array();
foreach( $variations as $variation ) {
// Peruse the attributes.
// 1. If both are explicitly set, this is a valid variation
// 2. If one is not set, that means any, and we must 'create' the rest.
$valid = true; // so far
foreach( $attributes as $slug => $args ) {
if( array_key_exists("attribute_$slug", $variation['attributes']) && !empty($variation['attributes']["attribute_$slug"]) ) {
// Exists
} else {
// Not exists, create
$valid = false; // it contains 'anys'
foreach( explode( '|', $attributes[$slug]['value']) as $attribute ) {
$attribute = trim( $attribute );
$new_variant = $variation;
$new_variant['attributes']["attribute_$slug"] = $attribute;
$new_variants[] = $new_variant;
}
}
}
// This contains ALL set attributes, and is itself a 'valid' variation.
if( $valid )
$new_variants[] = $variation;
}
return $new_variants;
}
UPDATE
I got this code working, but I get both attribute values (date location) in one tag.
How to seperate attributes?
<td>
<?php
foreach($value['attributes'] as $key => $val ) {
$val = str_replace(array('-','_'), ' ', $val);
printf( '<span class="attr attr-%s">%s</span>', $key, ucwords($val) );
}
?>
</td>
UPDATE
Finally I get date and location but with extra warnings, notices, and single characters.
Code
<?php
foreach($value as $date ) {
printf($date['attribute_date']);
}
?>
** SOLVED **
Maybe not the best method, but it's working.
<td class="date">
<?php
$i = 0;
foreach($value['attributes'] as $key => $val ) {
if($i == 0 ) {
echo $val;
}
$i++;
}
?>
</td>
<td class="location">
<?php
$i = 0;
foreach($value['attributes'] as $key => $val ) {
if($i !== 0) {
echo $val;
}
$i++;
}
?>
</td>
Use the method get_attributes() and function wc_get_product_terms() described here
<?php
// Get the attributes
$attributes = $product->get_attributes();
// Start the loop
foreach ( $attributes as $attribute ) : ?>
<?php
// Check and output, adopted from /templates/single-product/product-attributes.php
if ( $attribute['is_taxonomy'] ) {
$values = wc_get_product_terms( $product->id, $attribute['name'], array( 'fields' => 'names' ) );
echo apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values );
} else {
// Convert pipes to commas and display values
$values = array_map( 'trim', explode( WC_DELIMITER, $attribute['value'] ) );
echo apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values );
}
?>
<?php endforeach; ?>
If you look at the template \templates\single-product\product-attributes.php you can see how to get the individual attribute names.
if ( $attribute->is_taxonomy() ) {
$attribute_taxonomy = $attribute->get_taxonomy_object();
$attribute_values = wc_get_product_terms( $product->get_id(), $attribute->get_name(), array( 'fields' => 'all' ) );
foreach ( $attribute_values as $attribute_value ) {
$value_name = esc_html( $attribute_value->name );
if ( $attribute_taxonomy->attribute_public ) {
$values[] = '' . $value_name . '';
} else {
$values[] = $value_name;
}
}
}
Here's how to get specific Custom Attribute:
// Get the custom attribute
$svd_attribute = array_shift( wc_get_product_terms( $product->id, 'your_custom_attribute', array( 'fields' => 'names' ) ) );
// Display if String
if ( is_string( $svd_attribute ) ) {
echo wp_kses_post( "$svd_attribute" );
}
Just replace "your_custom_attribute"
Working on WooCommerce: 3.8.0

Custom Meta Boxes in Wordpress are not updating - need help identifying the errors in this function

I created a small plugin to add a Color meta box to a custom post type. The box displays fine but I can't seem to get it to save the data. When I type something in the field and click update, the field returns blank. I researched this and followed many tutorials, and each offered a slightly different approach. The code below was the easiest for me to follow, so I would really appreciate any help with identifying the error(s) in it. This is my first question so sorry if I omitted anything relevant.
Here is how I added the box:
add_action( 'add_meta_boxes', 'addmeta' );
function addmeta() {
$post_types = array ('post', 'ev');
foreach ( $post_types as $post_type ) {
add_meta_box (
'color_box',
'Color',
'display_meta_box',
$post_type,
'side'
);
}
}
add_action ( 'add_meta_boxes', 'addmeta');
Function to display the meta box:
function display_meta_box() {
$value = get_post_meta( $post->ID, '_mykey', true);
wp_nonce_field( basename( __FILE__ ), 'my_nonce' );
?>
<label for="color_box"><strong>Color:</strong> </label>
<input type="text" name="my_text" id="my_text" />
<?php
}
And this is the save function:
function save_meta_box ( $post_id ) {
$is_autosave = wp_is_post_autosave( $post_id );
$is_revision = wp_is_post_revision( $post_id );
$wp_valid_nonce = false;
if( isset( $_POST[ 'my_nonce' ] ) ) {
if ( wp_verify_nonce( $_POST['my_nonce'], basename( __FILE__ ) ) ) {
$is_valid_nonce = true;
}
}
if ( $is_autosave || $is_revision || !$is_valid_nonce ) return;
if( array_key_exists( 'color_box', $_POST ) ) {
update_post_meta(
$post_id,
'_mykey',
sanitize_text_field( $_POST[ 'color_box' ] )
);
}
}
add_action( 'save_post', 'save_meta_box' );
Thank you!
you need to define ID. Check this code it will work definitively.
function display_meta_box( ) {
$value = get_post_meta( get_the_ID(), '_mykey', true);
wp_nonce_field( basename( __FILE__ ), 'my_nonce' );
?>
<label for="color_box"><strong>Color:</strong> </label>
<input type="text" name="color_box" value="<?php echo $value;?>" id="my_text" />
<?php
}
You are putting wrong name in input box in your function display_meta_box. Replace your function with below function.
function display_meta_box() {
global $post;
$value = get_post_meta( $post->ID, '_mykey', true);
wp_nonce_field( basename( __FILE__ ), 'my_nonce' );
?>
<label for="color_box"><strong>Color:</strong> </label>
<input type="text" name="color_box" value="<?php echo $value;?>" id="my_text" />
<?php
}
Hope this will help. Please let me know if any issue.

Custom Post Type ID Issue - When Querying Other Post Types

I have quite a complex problem to solve which has baffled me for the last day or so.
Basically, I have two custom post types 'Sessions' and 'Recipes'. These are showing fine in the backend of WordPress.
Attached to the session are several meta boxes, 'Introduction Video', 'Session video', 'Goals', 'Fitness Level' and 'Featured Recipes'.
The 'Featured Recipes' meta box is special, as in it queries the WordPress database to get all posts within the 'recipe' custom post type and then use these values to populate a select box.
Now as you can see from the screen shot attached, the select box for the recipe custom post type is pulling through the correct items and ID's, and it also saves the data and pulls back the correct selection and associates it with the correct post ID for the 'session' post type. Great!
The problem arises with the other fields within the post, they seem to be using the first recipe ID as the value for pulling through the data for each field, rather than the 'session' custom post type ID.
Now I can get this to work by writing various while loops based on specific query arguments but that creates another issue. The data is saved in the correct place but it uses the first 'session' cpt ID to pull back the data for all posts.
Now, I believe it is something to do with the 'recipe' meta box creation, as as soon as you remove this function and it's related functions from the functions.php file, everything works as it should.
I think I should be using for loops instead of while loops, I'm going to try this now.
Hopefully some of you PHP gurus out there will be able to point me in the right direction. I've included both my functions.php file and a screen shot from the admin. Cheers.
<?php
add_action( 'init', 'tfp_session_cpt' );
function tfp_session_cpt() {
register_post_type( 'session',
array(
'labels' => array(
'name' => __( 'Sessions' ),
'singular_name' => __( 'Session' )
),
'public' => true,
'has_archive' => true,
)
);
}
add_action( 'init', 'tfp_recipe_cpt' );
function tfp_recipe_cpt() {
register_post_type( 'recipe',
array(
'labels' => array(
'name' => __( 'Recipes' ),
'singular_name' => __( 'Recipe' )
),
'public' => true,
'has_archive' => true,
)
);
}
/*add_action( 'init', 'tfp_tip_cpt' );
function tfp_tip_cpt() {
register_post_type( 'tip',
array(
'labels' => array(
'name' => __( 'Tips' ),
'singular_name' => __( 'Tip' )
),
'public' => true,
'has_archive' => true,
)
);
}*/
add_action( 'add_meta_boxes', 'tfp_add_introduction_video' );
add_action( 'add_meta_boxes', 'tfp_add_session_video' );
add_action( 'add_meta_boxes', 'tfp_add_goals' );
add_action( 'add_meta_boxes', 'tfp_add_levels' );
add_action( 'add_meta_boxes', 'tfp_add_recipes' );
/*add_action( 'add_meta_boxes', 'tfp_add_tips' );*/
add_action( 'save_post', 'tfp_save_introduction_video');
add_action( 'save_post', 'tfp_save_session_video');
add_action( 'save_post', 'tfp_save_goals');
add_action( 'save_post', 'tfp_save_levels');
add_action( 'save_post', 'tfp_save_recipes');
/*add_action( 'save_post', 'tfp_save_tips');*/
function tfp_add_introduction_video() {
add_meta_box('tfp_introduction_video', 'Introduction Video', 'tfp_introduction_video_html', 'session');
}
function tfp_add_session_video() {
add_meta_box('tfp_session_video', 'Session Video', 'tfp_session_video_html', 'session');
}
function tfp_add_goals() {
$types = array ('session');
foreach ($types as $type) {
add_meta_box('tfp_goals', 'Goals', 'tfp_goals_html', $type);
}
}
function tfp_add_levels() {
add_meta_box('tfp_levels', 'Fitness Levels', 'tfp_levels_html', 'session');
}
function tfp_add_recipes() {
add_meta_box('tfp_recipes', 'Select a Featured Recipe', 'tfp_recipes_html', 'session', 'side');
}
function tfp_add_tips() {
add_meta_box('tfp_tips', 'Tips', 'tfp_tips_html', 'session', 'side');
}
function tfp_introduction_video_html($post) {
// Nonce field for some validation
wp_nonce_field( plugin_basename( __FILE__ ), 'session' );
global $post;
$introductionVideo = get_post_meta( $postID, 'introduction_video_embed_code', true );
echo $post->ID;
?>
<label for="introduction_video_embed_code">YouTube Embed Code</label>
<textarea class="large-text code" id="introduction_video_embed_code" name="introduction_video_embed_code" value="<?php echo $introductionVideo; ?>"><?php echo get_post_meta( $post->ID, 'introduction_video_embed_code', true ); ?></textarea>
<?php }
function tfp_session_video_html($post) {
// Nonce field for some validation
wp_nonce_field( plugin_basename( __FILE__ ), 'session' );
global $post;
$sessionVideo = get_post_meta( $post->ID, 'session_video_embed_code', true ); ?>
<?php echo $post->ID; ?>
<label for="session_video_embed_code">YouTube Embed Code</label>
<textarea class="large-text code" id="session_video_embed_code" name="session_video_embed_code" value="<?php echo $sessionVideo; ?>"><?php echo $sessionVideo; ?></textarea>
<?php }
function tfp_goals_html($post) {
// Nonce field for some validation
wp_nonce_field( plugin_basename( __FILE__ ), 'session' );
$goals = get_post_meta( $post->ID, 'goals', true ); ?>
<?php echo $post->ID; ?>
<label for="weight_loss">
<input type="checkbox" name="goal[]" id="weight_loss" value="Weight Loss" <?php if (in_array('Weight Loss', $goals)) echo "checked='checked'"; ?> />
<?php _e("Weight Loss"); ?>
</label>
<br />
<label for="improve_fitness">
<input type="checkbox" name="goal[]" id="improve_fitness" value="Improve Fitness" <?php if (in_array('Improve Fitness', $goals)) echo "checked='checked'"; ?> />
<?php _e("Improve Fitness"); ?>
</label>
<br />
<label for="improve_health">
<input type="checkbox" name="goal[]" id="improve_health" value="Improve Health" <?php if (in_array('Improve Health', $goals)) echo "checked='checked'"; ?> />
<?php _e("Improve Health"); ?>
</label>
<br />
<?php }
function tfp_levels_html($post) {
// Nonce field for some validation
wp_nonce_field( plugin_basename( __FILE__ ), 'session' );
$levels = get_post_meta( $post->ID, 'levels', true ); ?>
<?php echo $post->ID; ?>
<label for="beginner">
<input type="checkbox" name="level[]" id="beginner" value="Beginner" <?php if (in_array('Beginner', $levels)) echo "checked='checked'"; ?> />
<?php _e("Beginner"); ?>
</label>
<br />
<label for="advanced">
<input type="checkbox" name="level[]" id="advanced" value="Advanced" <?php if (in_array('Advanced', $levels)) echo "checked='checked'"; ?> />
<?php _e("Advanced"); ?>
</label>
<br />
<?php }
function tfp_recipes_html($post) {
// Nonce field for some validation
wp_nonce_field( plugin_basename( __FILE__ ), 'session' );
global $post;
$recipes = get_post_meta( $post->ID, 'recipe_name', true );
$recipeArgs = array(
'post_type' => 'recipe'
);
$recipeQuery = new WP_Query($recipeArgs); ?>
<?php echo $post->ID; ?>
<select name="recipe_names" id="recipes">
<option value="null">Select a Featured Recipe</option>
<?php while ( $recipeQuery->have_posts() ) : $recipeQuery->the_post(); ?>
<option id="<?php echo $post->ID; ?>" value="<?php the_title(); ?>" <?php $title = get_the_title(); if($recipes == $title) echo "selected='selected'";?>><?php the_title(); ?></option>
<?php endwhile; ?>
</select>
<?php }
/*function tfp_tips_html($post) {
// Nonce field for some validation
wp_nonce_field( plugin_basename( __FILE__ ), 'session' );
global $post;
$tipArgs = array(
'post_type' => 'tip'
);
$tipQuery = new WP_Query($tipArgs); ?>
<?php echo $post->ID; ?>
<?php while ( $tipQuery->have_posts() ) : $tipQuery->the_post();
$tipID = 'tip_' . $post->ID;
$tips = get_post_meta( $post->ID, $tipID, true );?>
<?php echo $tipID; ?>
<label for="<?php echo $tipID; ?>">
<input type="checkbox" name="<?php echo $tipID; ?>" id="<?php echo $tipID; ?>" value="1" <?php if ($tips == 1) echo "checked='checked'"; ?> />
<?php the_title(); ?>
</label>
<br />
<?php endwhile;
}*/
function tfp_save_introduction_video($post_id) {
if ( !current_user_can( 'edit_post', $post_id ) ) { return false; }
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
update_post_meta($post_id, 'introduction_video_embed_code', $_POST['introduction_video_embed_code']);
}
function tfp_save_session_video($post_id) {
if ( !current_user_can( 'edit_post', $post_id ) ) { return false; }
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
update_post_meta($post_id, 'session_video_embed_code', $_POST['session_video_embed_code']);
}
function tfp_save_goals($post_id) {
if ( !current_user_can( 'edit_post', $post_id ) ) { return false; }
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
update_post_meta($post_id, 'goals', $_POST['goal']);
}
function tfp_save_levels($post_id) {
if ( !current_user_can( 'edit_post', $post_id ) ) { return false; }
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
update_post_meta($post_id, 'levels', $_POST['level']);
}
function tfp_save_recipes($post_id) {
if ( !current_user_can( 'edit_post', $post_id ) ) { return false; }
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
update_post_meta($post_id, 'recipe_name', $_POST['recipe_names']);
}
/*function tfp_save_tips($post) {
if ( !current_user_can( 'edit_post', $post_id ) ) { return false; }
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
if ( ! wp_verify_nonce( $_POST['custom_post_type'], plugin_basename(__FILE__) ) ) return;
global $post;
$tipArgs = array(
'post_type' => 'tip'
);
$tipQuery = new WP_Query($tipArgs);
while ( $tipQuery->have_posts() ) : $tipQuery->the_post();
$tipID = 'tip_' . $post->ID;
update_post_meta($post->ID, $tipID, $_POST[$tipID]);
endwhile;
}*/
// Extra user profile fields, specifically goal and fitness level checkboxes and radio buttons
add_action( 'show_user_profile', 'gaf_user_profile_fields' );
add_action( 'edit_user_profile', 'gaf_user_profile_fields' );
// Function to generate field HTML
function gaf_user_profile_fields( $user ) { ?>
<h3><?php _e("Goals and Fiteness Level", "blank"); ?></h3>
<table class="form-table">
<tr>
<th scope="row"><?php _e("Goals"); ?></th>
<td>
<fieldset>
<legend class="screen-reader-text">
<span><?php _e("Goals"); ?></span>
</legend>
<?php $single = true; ?>
<?php $goals = get_user_meta( $user->ID, 'goals', $single ); ?>
<label for="weight_loss">
<input type="checkbox" name="goal[]" id="weight_loss" value="Weight Loss" <?php if (in_array('Weight Loss', $goals)) echo "checked='checked'"; ?> />
<?php _e("Weight Loss"); ?>
</label>
<br />
<label for="improve_fitness">
<input type="checkbox" name="goal[]" id="improve_fitness" value="Improve Fitness" <?php if (in_array('Improve Fitness', $goals)) echo "checked='checked'"; ?> />
<?php _e("Improve Fitness"); ?>
</label>
<br />
<label for="improve_health">
<input type="checkbox" name="goal[]" id="improve_health" value="Improve Health" <?php if (in_array('Improve Health', $goals)) echo "checked='checked'"; ?> />
<?php _e("Improve Health"); ?>
</label>
<br />
</fieldset>
</td>
</tr>
<tr>
<?php $fitnessLevel = get_the_author_meta('fitness_level', $user->ID); ?>
<th scope="row"><?php _e("Fitness Level"); ?></th>
<td>
<fieldset>
<legend class="screen-reader-text">
<span><?php _e("Fitness Level"); ?></span>
</legend>
<input class="tog" type="radio" name="fitness_level" value="beginner" <?php if ($fitnessLevel == 'beginner') { ?>checked="checked"<?php } ?> /><label>Beginner</label>
<br />
<input class="tog" type="radio" name="fitness_level" value="advanced" <?php if ($fitnessLevel == 'advanced') { ?>checked="checked"<?php } ?> /><label>Advanced</label>
<br />
</fieldset>
</td>
</tr>
</table>
<?php }
// Function and actions to save the data from the fields
add_action( 'personal_options_update', 'save_gaf_user_profile_fields' );
add_action( 'edit_user_profile_update', 'save_gaf_user_profile_fields' );
function save_gaf_user_profile_fields( $user_id ) {
if ( !current_user_can( 'edit_user', $user_id ) ) { return false; }
update_user_meta( $user_id, 'goals', $_POST['goal'] );
update_user_meta( $user_id, 'fitness_level', $_POST['fitness_level'] );
}
?>
[EDIT]
I'm certainly getting closer with this function but it's not quite right as it's pulling through the same title.
function tfp_recipes_html($post) {
// Nonce field for some validation
wp_nonce_field( plugin_basename( __FILE__ ), 'session' );
$recipeArgs = array(
'post_type' => 'recipe'
);
$recipeQuery = get_posts($recipeArgs);
$recipeName = get_post_meta( $post->ID, 'recipe_name', true );
$recipeTitle[] = get_query_var('name');
echo $recipeTitle;
?>
<select name="recipe_names" id="recipes">
<option value="null">Select a Featured Recipe</option>
<?php foreach ( $recipeQuery as $recipe ) : setup_postdata($recipeQuery); ?>
<option id="<?php echo $recipe->ID; ?>" value="<?php echo $recipeTitle; ?>" <?php if($recipeName == $recipeTitle) echo "selected='selected'";?>><?php echo $recipeName; ?></option>
<?php endforeach; ?>
</select>
<?php }
OK Guys, I solved my issue using the following function.
function tfp_recipes_html($post) {
// Nonce field for some validation
wp_nonce_field( plugin_basename( __FILE__ ), 'session' );
global $post;
$recipeArgs = array(
'post_type' => 'recipe'
);
$recipes = get_posts($recipeArgs);
$recipeName = get_post_meta( $post->ID, 'recipe_name', true );
?>
<select name="recipe_names" id="recipes">
<option value="null">Select a Featured Recipe</option>
<?php foreach ( $recipes as $recipe ) : setup_postdata($recipe); ?>
<option id="<?php echo $recipe->ID; ?>" value="<?php $recipeTitle = $recipe->post_title; echo $recipeTitle; ?>" <?php if($recipeName == $recipeTitle) echo "selected='selected'";?>><?php echo $recipeTitle; ?></option>
<?php endforeach; ?>
</select>
<?php }
Though now I'm having another issue saving another custom metabox. The function to add the metabox is:
function tfp_tips_html($post) {
// Nonce field for some validation
wp_nonce_field( plugin_basename( __FILE__ ), 'session' );
global $post;
echo $post->ID;
$tipArgs = array(
'post_type' => 'tip'
);
$tips = get_posts($tipArgs);
$tipData = get_post_meta( $post->ID, $tipID, true );
foreach ( $tips as $tip ) : setup_postdata($tip);
$tipID = 'tip_' . $tip->ID; ?>
<label for="<?php echo $tipID; ?>">
<input type="checkbox" name="<?php echo $tipID; ?>" id="<?php echo $tipID; ?>" value="1" <?php if ($tipData == 1) echo "checked='checked'"; ?> />
<?php $tipTitle = $tip->post_title; echo $tipTitle; ?>
</label>
<br />
<?php endforeach;
}
Similar to the recipe function except this adds checkboxes each with a specific ID. Now the function to save it is:
function tfp_save_tips($post_id) {
if ( !current_user_can( 'edit_post', $post_id ) ) { return false; }
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
if ( ! wp_verify_nonce( $_POST['custom_post_type'], plugin_basename(__FILE__) ) ) return;
global $tipID;
add_post_meta($post->ID, $tipID, $_POST[$tipID], true) or update_post_meta($post->ID, $tipID, $_POST[$tipID]);
}
The problem is it's not saving the data, and is going to a blank page.
[EDIT]
OK, I have the data saving, as well as the checkboxes being checked correctly when you go back to the post. The only issue is it's posting to a blank page, which is no good. Needs to refresh the editor.
The function 'm now using is:
function tfp_save_tips($post_id) {
if ( !current_user_can( 'edit_post', $post_id ) ) { return false; }
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
if ( ! wp_verify_nonce( $_POST['session'], plugin_basename(__FILE__) ) ) return;
$tipArgs = array(
'post_type' => 'tip'
);
$tips = get_posts($tipArgs);
foreach ( $tips as $tip ) : setup_postdata($tip);
$tipID = 'tip_' . $tip->ID;
endforeach;
add_post_meta($post_id, $tipID, $_POST[$tipID], true) or update_post_meta($post_id, $tipID, $_POST[$tipID]);
}

Resources