I am relatively new to Post Meta Data in the WordPress backend using PHP. I have written the code that creates the Meta Data. I need help saving the data for which I have written. It will also need to allow me to edit the data once saved.
In this case its for a text field.
I have created the Meta Data for the input field which displays well in the back-end WordPress admin area.
add_action( 'add_meta_boxes', 'cd_meta_box_add' );
function cd_meta_box_add()
{
add_meta_box( 'my-meta-box-id', 'Job Title', 'cd_meta_box_cb', 'people', 'normal', 'high' );
/* Save post meta on the 'save_post' hook. */
add_action( 'save_post', 'cd_meta_box_add', 10, 2 );
}
function cd_meta_box_cb()
{
echo "<input type='text' name='jobtitle'>";
}
I just need assistance with the code that will save the above Meta Data to the DB and allow for editing and revisions
You were almost there.
The final part of the puzzle is the function that saves the metadata, but first we need to make a few adjustments to your existing code:
add_action( 'save_post', 'cd_meta_box_add', 10, 2 ); has to be moved outside cd_meta_box_add(), and
Change add_action( 'save_post', 'cd_meta_box_add', 10, 2 ); into add_action( 'save_post', 'cd_meta_box_add' ); as this action hook only receives one parameter (the post ID), and
You need to define the function that will process the data (and it can't be cd_meta_box_add as you have it now so we'll create a new one called save_cd_meta_box_data).
/* Save post meta on the 'save_post' hook. */
add_action( 'save_post', 'save_cd_meta_box_data' );
function save_cd_meta_box_data( $post_id ) {
// Autosaving, bail.
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
// #TODO
// You should add some additional security checks here
// eg. nonce, user capabilities, etc, to prevent
// malicious users from doing bad stuff.
/* OK, it's safe for us to save the data now. */
// Make sure that it is set.
if ( ! isset( $_POST['jobtitle'] ) ) {
return;
}
// Sanitize user input.
$my_data = sanitize_text_field( $_POST['jobtitle'] );
// Update the meta field in the database.
update_post_meta( $post_id, '_job_title', $my_data );
}
Now that we're successfully saving the metadata into the database, let's allow the user to view it / edit it:
function cd_meta_box_cb( $post )
{
$job_title = get_post_meta( $post->ID, '_job_title', true );
echo "<input type='text' name='jobtitle' value='" . esc_attr( $job_title ) . "'>";
}
The final code should look like this:
/* Register and display metabox */
add_action( 'add_meta_boxes', 'cd_meta_box_add' );
function cd_meta_box_add()
{
add_meta_box( 'my-meta-box-id', 'Job Title', 'cd_meta_box_cb', 'people', 'normal', 'high' );
}
function cd_meta_box_cb( $post )
{
$job_title = get_post_meta( $post->ID, '_job_title', true );
echo "<input type='text' name='jobtitle' value='" . esc_attr( $job_title ) . "'>";
}
/* Save post meta on the 'save_post' hook. */
add_action( 'save_post', 'save_cd_meta_box_data' );
function save_cd_meta_box_data( $post_id ) {
// Autosaving, bail.
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
// #TODO
// You should add some additional security checks here
// eg. nonce, user capabilities, etc, to prevent
// malicious users from doing bad stuff.
/* OK, it's safe for us to save the data now. */
// Make sure that it is set.
if ( ! isset( $_POST['jobtitle'] ) ) {
return;
}
// Sanitize user input.
$my_data = sanitize_text_field( $_POST['jobtitle'] );
// Update the meta field in the database.
update_post_meta( $post_id, '_job_title', $my_data );
}
Related
I'm trying to learn WooCommerce and WordPress plugins so I'm tweaking around. I'm trying to create a plugin that redirects customer to a custom page after checkout. The custom page/url can be defined when I create the product. Here is my code:
<?php
/*
Plugin Name: Custom Redirect After Sale
Description: Redirects customers to a custom page after a successful sale.
*/
// Register a new meta field for products
add_action( 'add_meta_boxes', 'custom_redirect_meta_box' );
function custom_redirect_meta_box() {
add_meta_box( 'custom_redirect_meta_box', 'Custom Redirect URL', 'custom_redirect_meta_box_callback', 'product', 'side' );
}
function custom_redirect_meta_box_callback( $post ) {
$value = get_post_meta( $post->ID, '_custom_redirect_url', true );
echo '<label for="custom_redirect_url">Custom Redirect URL:</label>';
echo '<input type="text" id="custom_redirect_url" name="custom_redirect_url" value="' . esc_attr( $value ) . '" style="width:100%">';
}
// Save the meta field value when the product is saved
add_action( 'save_post_product', 'save_custom_redirect_meta_box_data' );
function save_custom_redirect_meta_box_data( $post_id ) {
if ( isset( $_POST['custom_redirect_url'] ) ) {
update_post_meta( $post_id, '_custom_redirect_url', sanitize_text_field( $_POST['custom_redirect_url'] ) );
}
}
// Redirect to the custom page after a successful sale
add_action( 'woocommerce_payment_complete', 'custom_redirect_after_sale' );
function custom_redirect_after_sale( $order_id ) {
$order = wc_get_order( $order_id );
//$order->update_status( 'completed' );
$items = $order->get_items();
// Get the first product in the order
$product = reset($items);
// Get the custom redirect URL for the product
//$redirect_url = get_post_meta( $product->get_product_id(), '_custom_redirect_url', true );
$redirect_url = get_post_meta( $product->get_id(), '_custom_redirect_url', true );
//echo "Meta retrieved: " . $redirect_url;
//error_log("callback fired");
//echo "Payment complete ho ho ho";
if( $redirect_url ) {
wp_redirect( $redirect_url );
exit;
}
}
It seems the woocommerce_payment_complete hook is not firing. I tried to echo out the redirect url and text but it doesn't seem to work.
I'm on localhost and I'm using the cash on delivery payment method.
Basing this answer on the great https://rudrastyh.com/ - specifically this tutorial https://rudrastyh.com/woocommerce/thank-you-page.html#redirects this is the code that should work for what you are trying to do.
First, you hook into the template_redirect action to determine the URL where the customer needs to go
Getting the Order ID, you can get the products purchased for that order
Once you have the purchased products, you can get their ID and meta data, the redirect URL you saved for each. Note that while you use WP functions for handling meta, when working with WooCommerce it is best practice to use its CRUD methods. In case in the future they port products to custom tables, your code will continue working.
Implement the redirect with the WP function wp_safe_redirect
Note that what you are trying to achieve will have problems if customers purchase orders with more than 1 product, and you have more than 1 redirect URL. In this implementation, the first product in the order that has a saved redirect URL will override all others
add_action( 'template_redirect', 'purchased_product_redirect');
function purchased_product_redirect(){
if( !function_exists( 'is_wc_endpoint_url' )){
return;
}
// do nothing if we are not on the order received page
if( ! is_wc_endpoint_url( 'order-received' ) || empty( $_GET[ 'key' ] ) ) {
return;
}
// Get the order ID
$order_id = wc_get_order_id_by_order_key( $_GET[ 'key' ] );
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Get and Loop Over Order Items
foreach ( $order->get_items() as $item_id => $item ) {
$product_id = $item->get_product_id();
$product = wc_get_product($product_id);
if(!$product){
continue;
}
//Get the first product's redirect URL
$product_redirect_url = $product->get_meta('_custom_redirect_url');
if(!$product_redirect_url){
continue;
}
wp_safe_redirect( $product_redirect_url );
exit; // always exit after using wp_safe_redirect
}
}
I've got this code snippet in my functions.php file:
add_action( 'woocommerce_checkout_create_order_line_item', 'add_custom_field_to_order_item_meta', 10, 4 );
function add_custom_field_to_order_item_meta( $item, $cart_item_key, $values, $order ) {
$custom_field_value = get_post_meta( $item->get_product_id(), 'supplier_sku', true );
if ( ! empty($custom_field_value) ){
$item->update_meta_data( __('Supplier SKU', 'woocommerce'), $custom_field_value );
}
}
It pulls in the custom field on products, called Supplier SKU and then adds it to the WooCommerce email notifications. Which is fine, but I want to exclude it from the customer email notification and only have it display in the admin email notification.
How can I achieve this?
You could use the woocommerce_display_item_meta hook and return an empty string
function filter_woocommerce_display_item_meta ( $html, $item, $args ) {
$html = '';
return $html;
}
add_filter( 'woocommerce_display_item_meta', 'filter_woocommerce_display_item_meta', 10, 3 );
While the above would work, there would be some issues, namely:
The hook doesn't run just for email notifications, so it wouldn't show up anywhere
Even if this hook would only be executed for email notifications, we would still need to specify that this should only be the case for certain email notifications. However, this hook does not offer a solution for it by default to make this distinction
So a workaround will be needed, this can be done by creating a global variable through another hook that applies only to email notifications
Step 1) creating and adding a global variable
// Setting global variable
function action_woocommerce_email_before_order_table( $order, $sent_to_admin, $plain_text, $email ) {
$GLOBALS['email_id'] = $email->id;
}
add_action( 'woocommerce_email_before_order_table', 'action_woocommerce_email_before_order_table', 1, 4 );
Step 2) In the hook woocommerce_display_item_meta, add and check for specific conditions
Only for email notifications
Only for specific meta data
Only for admin 'new order' email
function filter_woocommerce_display_item_meta ( $html, $item, $args ) {
// For email notifications and specific meta
if ( ! is_wc_endpoint_url() && $item->is_type( 'line_item' ) && $item->get_meta( 'Supplier SKU' ) ) {
// Getting the email ID global variable
$ref_name_globals_var = isset( $GLOBALS ) ? $GLOBALS : '';
$email_id = isset( $ref_name_globals_var['email_id'] ) ? $ref_name_globals_var['email_id'] : '';
// NOT empty and targeting specific email. Multiple statuses can be added, separated by a comma
if ( ! empty ( $email_id ) && ! in_array( $email_id, array( 'new_order' ) ) ) {
$html = '';
}
}
return $html;
}
add_filter( 'woocommerce_display_item_meta', 'filter_woocommerce_display_item_meta', 10, 3 );
I succeed in creating a metabox with custom field inside, and I restrict it to appear in a custom post type.
//define metabox
function product_info_en() {
add_meta_box( 'english_info', 'English Info', 'english_product_name_callback', array('product'), 'normal', 'high' );
}
//add to hook
add_action( 'add_meta_boxes', 'product_info_en' );
The code to display it in the product page:
// display in add product admin page
function english_product_name_callback( $post ) {
//ob_start();
$content = esc_attr( get_post_meta( get_the_ID(), 'product_desc_en', true ) );
//here goes the custom field
echo '<fieldset><div><label><b>English Product Name:</b></label><br/>';
echo '<input id="product_name_en" type="text" name="product_name_en" style="width:100%; margin:10px 0px"';
echo ' value="';
echo esc_attr( get_post_meta( get_the_ID(), 'product_desc_en', true ) );
echo '"></div></fieldset>';
//here goes the wp_editor
echo '<fieldset><div><label><b>English Product Content Info:</b></label><div><br/>';
echo '<div>';
wp_editor($content, 'product_desc_en', array(
'wpautop' => true,
'media_buttons' => true,
'textarea_rows' => 10
)
);
echo '</div></fieldset>';
}
Here goes the code that do the saving job:
//save
function enginfo_save_meta_box( $post_id ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
if ( $parent_id = wp_is_post_revision( $post_id ) ) {
$post_id = $parent_id;
}
$fields = [
'product_name_en',
];
foreach ( $fields as $field ) {
if ( array_key_exists( $field, $_POST ) ) {
update_post_meta( $post_id, $field, sanitize_text_field( $_POST[$field] ) );
}
}
update_post_meta( $post_id,'product_desc_en', wp_kses_post( $_POST['product_desc_en'] ) );
}
add_action( 'save_post', 'enginfo_save_meta_box' );
However, the custom created field that supposed to be going only into the newly created metabox, will always show up in the default "custom field". And this happens to all post type.
As shown below, What could possibly be the issue here?
To hide and not show your custom fields there in default box, please prefix your custom fields with underscore _ , so product_desc_en will become _product_des_en
I mean the names of your custom fields should be prefixed with underscore and WordPress default custom metabox will ignore them and not show in WordPress default GUI, but you can use and display them in your own custom metaboxes by calling with there new Underscore prefixed names.
I am trying to add a new field on my cart.php file.
I actually want to insert a URL field, so user can set a URL for each order item.
I tried to use a code from another post here but I can't get it to work.
The first and the second functions are working but when it comes to the third one, 'woocommerce_get_item_data' the $cart_item['url'] doesn't contain anything even if I add something in the field and I press Update Cart.
$cart_totals[ $cart_item_key ]['url'] from the first function is outputting the right value when the page load.
I don't know what to do now, thanks for any help.
Here is the code
Add the field
cart/cart.php
<td class="product-url">
<?php
$html = sprintf( '<div class="url"><input type="text" name="cart[%s][url]" value="%s" size="4" title="Url" class="input-text url text" /></div>', $cart_item_key, esc_attr( $values['url'] ) );
echo $html;
?>
</td>
functions.php
// get from session your URL variable and add it to item
add_filter('woocommerce_get_cart_item_from_session', 'cart_item_from_session', 99, 3);
function cart_item_from_session( $data, $values, $key ) {
$data['url'] = isset( $values['url'] ) ? $values['url'] : '';
return $data;
}
// this one does the same as woocommerce_update_cart_action() in plugins\woocommerce\woocommerce-functions.php
// but with your URL variable
// this might not be the best way but it works
add_action( 'init', 'update_cart_action', 9);
function update_cart_action() {
global $woocommerce;
if ( ( ! empty( $_POST['update_cart'] ) || ! empty( $_POST['proceed'] ) ) ) {
$cart_totals = isset( $_POST['cart'] ) ? $_POST['cart'] : '';
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
if ( isset( $cart_totals[ $cart_item_key ]['url'] ) ) {
$woocommerce->cart->cart_contents[ $cart_item_key ]['url'] = $cart_totals[ $cart_item_key ]['url'];
}
}
}
}
}
// this is in Order summary. It show Url variable under product name. Same place where Variations are shown.
add_filter( 'woocommerce_get_item_data', 'item_data', 10, 2 );
function item_data( $data, $cart_item ) {
if ( isset( $cart_item['url'] ) ) {
$data['url'] = array('name' => 'Url', 'value' => $cart_item['url']);
}
return $data;
}
// this adds Url as meta in Order for item
add_action ('woocommerce_add_order_item_meta', 'add_item_meta', 10, 2);
function add_item_meta( $item_id, $values ) {
woocommerce_add_order_item_meta( $item_id, 'Url', $values['url'] );
}
Add a textarea field to a WooCommerce cart item
First, we just need to add the textarea field. We use the woocommerce_after_cart_item_name hook so our textarea will appear after the product name.
<?php
/**
* Add a text field to each cart item
*/
function prefix_after_cart_item_name( $cart_item, $cart_item_key ) {
$notes = isset( $cart_item['notes'] ) ? $cart_item['notes'] : '';
printf(
'<div><textarea class="%s" id="cart_notes_%s" data-cart-id="%s">%s</textarea></div>',
'prefix-cart-notes',
$cart_item_key,
$cart_item_key,
$notes
);
}
add_action( 'woocommerce_after_cart_item_name', 'prefix_after_cart_item_name', 10, 2 );
/**
* Enqueue our JS file
*/
function prefix_enqueue_scripts() {
wp_register_script( 'prefix-script', trailingslashit( plugin_dir_url( __FILE__ ) ) . 'update-cart-item-ajax.js', array( 'jquery-blockui' ), time(), true );
wp_localize_script(
'prefix-script',
'prefix_vars',
array(
'ajaxurl' => admin_url( 'admin-ajax.php' )
)
);
wp_enqueue_script( 'prefix-script' );
}
add_action( 'wp_enqueue_scripts', 'prefix_enqueue_scripts' );
´´´
At the moment, the user will be able to enter text into the field but the text won’t save. We are going to use some AJAX to save the text.
The code above not only adds the textarea to the cart item, it also enqueues a JavaScript file ready for our AJAX.
It’s assumed that you’re using the code on this page to create a new plugin. If so, you should create a new JS file with the code below and place the file in the root directory of your plugin.
However, if you’ve added the PHP above to your theme functions.php or as a snippet on your site, you’ll need to change the location of the JS file by updating line 21 of the snippet above to identify the location of the JS file.
´´´
(function($){
$(document).ready(function(){
$('.prefix-cart-notes').on('change keyup paste',function(){
$('.cart_totals').block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
var cart_id = $(this).data('cart-id');
$.ajax(
{
type: 'POST',
url: prefix_vars.ajaxurl,
data: {
action: 'prefix_update_cart_notes',
security: $('#woocommerce-cart-nonce').val(),
notes: $('#cart_notes_' + cart_id).val(),
cart_id: cart_id
},
success: function( response ) {
$('.cart_totals').unblock();
}
}
)
});
});
})(jQuery);
´´´
Now, when the user types anything, the contents of the text field get sent back to the server ready to be saved as meta data to the cart item.
´´´
<?php
/**
* Update cart item notes
*/
function prefix_update_cart_notes() {
// Do a nonce check
if( ! isset( $_POST['security'] ) || ! wp_verify_nonce( $_POST['security'], 'woocommerce-cart' ) ) {
wp_send_json( array( 'nonce_fail' => 1 ) );
exit;
}
// Save the notes to the cart meta
$cart = WC()->cart->cart_contents;
$cart_id = $_POST['cart_id'];
$notes = $_POST['notes'];
$cart_item = $cart[$cart_id];
$cart_item['notes'] = $notes;
WC()->cart->cart_contents[$cart_id] = $cart_item;
WC()->cart->set_session();
wp_send_json( array( 'success' => 1 ) );
exit;
}
add_action( 'wp_ajax_prefix_update_cart_notes', 'prefix_update_cart_notes' );
function prefix_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
foreach( $item as $cart_item_key=>$cart_item ) {
if( isset( $cart_item['notes'] ) ) {
$item->add_meta_data( 'notes', $cart_item['notes'], true );
}
}
}
add_action( 'woocommerce_checkout_create_order_line_item', 'prefix_checkout_create_order_line_item', 10, 4 );
´´´
The prefix_update_cart_notes function does a security check using the WooCommerce cart nonce then saves the content of the textarea as meta data in the cart item. You can check out this article for more information about updating cart meta for items that have already been added to the cart.
Add the custom text to the order meta
Finally, we want to pass our meta data to the order so that we can use it after the customer has checked out. The prefix_checkout_create_order_line_item function takes care of that, iterating through each item and saving notes when it finds them.
https://pluginrepublic.com/how-to-add-an-input-field-to-woocommerce-cart-items/
I've created a meta box. The code is:
// Create your custom meta box
add_action( 'add_meta_boxes', 'hotel_amenities' );
// Add a custom meta box to a post
function hotel_amenities( $post ) {
add_meta_box(
'Meta Box Amenities', // ID, should be a string
'Amenities', // Meta Box Title
'amenities_content', // Your call back function, this is where your form field will go
'post', // The post type you want this to show up on, can be post, page, or custom post type
'normal', // The placement of your meta box, can be normal or side
'high' // The priority in which this will be displayed
);
}
// Content for the custom meta box
function amenities_content( $post ) {
echo '<label>Bed room</label>';
echo '<input type="text" name="amenity_bed_room" value="" />';
}
// Save your meta box content
add_action( 'save_post', 'save_amenities' );
// save newsletter content
function save_amenities(){
global $post;
// Get our form field
if( $_POST ) :
$amenities_meta = esc_attr( $_POST['amenity_bed_room'] );
// Update post meta
update_post_meta($post->ID, '_amenities_custom_meta', $amenities_meta);
endif;
}
It shows a meta box on admin post page with a text field. but it gets blank if I save or update the post after I put some thing on the text field.
Seems function save_amenities() is not working. What I am doing wrong in this code?
Also for getting that value I use the function below. Is that correct?
//get amenities meta box values
function get_amenities_meta_box() {
global $post;
$meta_values = get_post_meta($post->ID, '_amenities_custom_meta', true);
}
There are a few things going wrong there. The final value that you want to see will be displayed by the value attribute in the amenities_content function. Right now it is just displaying an empty string (""). Try putting any value in that attribute and you should see it show up in the meta box (value="this is a test").
The save_amenities function should take $post_id as a parameter. You'll need that to update the post meta-data and give a real value for the amenities_content function to echo back to the admin screen.
The amenities_content function should really have a nonce field that should then be verified by the save_amenities function. And user input should be sanitized before it is saved (I'm doing it both when I save it and when I display it. I'm not sure if that's necessary.)
try this out for the amenities_content function:
function amenities_content( $post ) {
// This is the value that was saved in the save_amenities function
$bed_room = get_post_meta( $post->ID, '_amenity_bed_room', true );
wp_nonce_field( 'save_amenity', 'amenity_nonce' );
echo '<label>Bed room</label>';
echo '<input type="text" name="amenity_bed_room"
value="' . sanitize_text_field( $bed_room ) . '" />';
}
and this for the save_amenities function:
function save_amenities( $post_id ) {
// Check if nonce is set
if ( ! isset( $_POST['amenity_nonce'] ) ) {
return $post_id;
}
if ( ! wp_verify_nonce( $_POST['amenity_nonce'], 'save_amenity' ) ) {
return $post_id;
}
// Check that the logged in user has permission to edit this post
if ( ! current_user_can( 'edit_post' ) ) {
return $post_id;
}
$bed_room = sanitize_text_field( $_POST['amenity_bed_room'] );
update_post_meta( $post_id, '_amenity_bed_room', $bed_room );
}