I have been searching for 2 days how to do this and really struggling. I am fairly new to PHP so this maybe really simple, but whatever I try it just doesnt work.
I have a custom field in each product called 'Profit'. All i want to do is call that next to the name of the product in the woocommerce sales Report Email plugin. !
Here is the code from the class-wc-sre-row-top-sellers.php file I need to edit.
But it doesnt work, where am I going wrong?
UPDATE: I have now added what was suggested by still no luck. You can see the line I added '// Set Profit by Oli and added the
. ' - £) . $Profit
at the end of the $value = at the bottom.
UPDATE2: I have now added the code as I was kindly told below but it is still not working. The profit is being called but nothing is displayed after. The way I have entered the Custom Field is on the products under the Custom Fields tab
What am I doing wrong?
<?php
if ( !defined( 'ABSPATH' ) ) {
exit;
} // Exit if accessed directly
class WC_SRE_Row_Top_Sellers extends WC_SRE_Report_Row {
/**
* The constructor
*
* #param $date_range
*
* #access public
* #since 1.0.0
*/
public function __construct( $date_range ) {
parent::__construct( $date_range, 'top-sellers', __( 'Top Sellers', 'woocommerce-sales-report-email' ) );
}
/**
* Prepare the data
*
* #access public
* #since 1.0.0
*/
public function prepare() {
// Create a Report Manager object
$report_manager = new WC_SRE_Report_Manager( $this->get_date_range() );
// Set the default order types
$order_types = array( 'shop_order' );
// wc_get_order_types() is a 2.2+ function
if ( function_exists( 'wc_get_order_types' ) ) {
$order_types = wc_get_order_types( 'order-count' );
}
// Get top sellers
$top_sellers = $report_manager->get_order_report_data( array(
'data' => array(
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id'
),
'_qty' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_qty'
)
),
'order_by' => 'order_item_qty DESC',
'group_by' => 'product_id',
'limit' => 12,
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => $order_types,
) );
$value = 'n/a';
// Fill the $value var with products
if ( count( $top_sellers ) > 0 ) {
$value = '';
foreach ( $top_sellers as $product ) {
// THIS IS WHERE THE NEW CODE IS AND $PROFIT IS CALLED AFTER ' - £' . BELOW
$Profit = array_shift(woocommerce_get_product_terms($product->product_id, 'Profit', 'names'));
$value .= $product->order_item_qty . 'x : ' . get_the_title( $product->product_id ) . ' - £' . $Profit . '<br/>';
}
}
$this->set_value( $value );
}
}
Here is the way to get the custom variable added to woo commerce products:
$Profit = array_shift(woocommerce_get_product_terms($product->product_id, 'Profit', 'names'));
Related
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 have a custom post type "cs_portfolio" for my project. What I want to achieve is to be able the user to choose a specific page to display all the data of "cs_portfolio".
Example of this is the blog post when you set in Settings > Reading the static page of post. It automatically use the index.php to loop all the blog post.
In my case I already figure out how to add a dropdown field in Settings > Reading and save it.
Settings > Reading
1: https://i.stack.imgur.com/w6ybw.png
Here's the code for adding dropdown field in Settings > Reading.
/**
* Adds a custom field: "Projects page"; on the "Settings > Reading" page.
*/
add_action( 'admin_init', function () {
$id = 'page_for_projects';
// add_settings_field( $id, $title, $callback, $page, $section = 'default', $args = array() )
add_settings_field( $id, 'Projects Page:', 'settings_field_page_for_projects', 'reading', 'default', array(
'label_for' => 'field-' . $id, // A unique ID for the field. Optional.
'class' => 'row-' . $id, // A unique class for the TR. Optional.
) );
} );
function settings_field_page_for_projects( $args ) {
$id = 'page_for_projects';
wp_dropdown_pages( array(
'name' => $id,
'show_option_none' => '— Select —',
'option_none_value' => '0',
'selected' => get_option( $id ),
) );
}
add_filter( 'whitelist_options', function ( $options ) {
$options['reading'][] = 'page_for_projects';
return $options;
} );
And then in list of Page I already set the display posts states.
Page List
2: https://i.stack.imgur.com/bVvWo.png
Here's the code for setting display post state.
/**
* Filters the post states on the "Pages" edit page. Displays "Projects Page"
* after the post/page title, if the current page is the Projects static page.
*
* #param array $states
* #param WP_Post $post
*/
add_filter( 'display_post_states', function ( $states, $post ) {
if ( intval( get_option( 'page_for_projects' ) ) === $post->ID ) {
$states['page_for_projects'] = __( 'Projects Page' );
}
return $states;
}, 10, 2 );
My problem now is what should be the file to be used by Portfolio Page to be called to loop all the data in "cs_portfolio" ? and maintain the slug of "cs_portfolio" ?
How to add custom field in shipping method using woocommerce wordpress
Please see my screenshot
Please try this code.
if ( ! function_exists( 'dimative_shipping_instance_form_fields_filters' ) ) {
/**
* Shipping Instance form add extra fields.
*
* #param array $settings Settings.
* #return array
*/
function dimative_shipping_instance_form_add_extra_fields( $settings ) {
$settings['shipping_extra_field_title'] = array(
'title' => esc_html__( 'Shipping method title', 'zincheco' ),
'type' => 'text',
'placeholder' => esc_html__( 'Please add shipping method title', 'zincheco' ),
'description' => '',
);
$settings['shipping_extra_field_description'] = array(
'title' => esc_html__( 'Shipping method description', 'zincheco' ),
'type' => 'textarea',
'placeholder' => esc_html__( 'Please add your shipping method description', 'zincheco' ),
'description' => '',
);
return $settings;
}
/**
* Shipping instance form fields.
*/
function dimative_shipping_instance_form_fields_filters() {
$shipping_methods = WC()->shipping->get_shipping_methods();
foreach ( $shipping_methods as $shipping_method ) {
add_filter( 'woocommerce_shipping_instance_form_fields_' . $shipping_method->id, 'dimative_shipping_instance_form_add_extra_fields' );
}
}
add_action( 'woocommerce_init', 'dimative_shipping_instance_form_fields_filters' );
}
In case if someone is looking how to get the value using #Өлзийбат-Нансалцог code example. I was able to retrieve it this way:
//Get Shipping rate object
//$method->id is the ID of the shipping method.
//In my case I used that in cart-shipping.php template
$WC_Shipping_Rate = new WC_Shipping_Rate($method->id);
// Returned value is flat_rate:1
$shipping_rate_id = $WC_Shipping_Rate->get_id();
//Replacing : with underscore to have the id in format of flat_rate_1
$shipping_rate_id = str_replace(':', '_', $shipping_rate_id);
//Get the description field value
//Since the value is stored inside options table use get_option() method
get_option('woocommerce_'.$shipping_rate_id.'_settings')['shipping_extra_field_description']
Here is the code for getting the value.
if ( ! function_exists( 'dimative_shipping_method_description' ) ) {
/**
* Function will print shipping method description.
*
* #param object $method Shipping method object.
* #param int $index Shipping method index.
* #return void
*/
function dimative_shipping_method_description( $method, $index ) {
$dimative_method_fields = get_option( 'woocommerce_' . $method->method_id . '_' . $method->instance_id . '_settings' );
$dimative_method_allowed_html = array(
'p' => array(
'class' => array(),
),
'strong' => array(),
'b' => array(),
'a' => array(
'href' => array(),
),
'span' => array(
'class' => array(),
),
);
// Shipping method custom description.
if ( $dimative_method_fields['shipping_extra_field_description'] ) {
?>
<div class="method-description"><?php echo wp_kses( $dimative_method_fields['shipping_extra_field_description'], $dimative_method_allowed_html ); ?></div>
<?php
}
}
add_action( 'woocommerce_after_shipping_rate', 'dimative_shipping_method_description', 1, 2 );
}
I've been struggling a bit with this. I have one custom post type that I created called "pt_initiatives" and one that The Event Calendar created called "tribe_events". With Advanced Custom Fields, I added a relationship field to both post types which is also bi-directional. The relationship field is called "bi_initiatives-events".
I have The Events Calendar Pro with the Filter Bar add on. I am trying to create a filter that shows the pt_initiatives posts as options in a multi-checkbox filter. (There are only 9 of these posts). I have that part working so far. I am a bit lost on how to update the list of events when one of these pt_initiatives are chosen.
I've been playing with the wpdb queries with no success. I'm not sure how to tell it to look at tribe_events posts to find which has posts in common. I've been scouring the internet and haven't come across anything too helpful and The Event Calendar folks don't seem too keen on helping since this is considered "Customization". However, they offer the ability to add filters as a feature... They have a very vague page on how to do this but I personally did not find it too helpful.
I am not too familiar with writing wpdb queries, hence you can see me adding a post loop to achieve the same thing. At least, I think it's the same output as some examples I came across.
Thanks in advance!
class Tribe__Events__Filterbar__Filters__Initiative extends Tribe__Events__Filterbar__Filter {
public $type = 'checkbox';
public function get_admin_form() {
$title = $this->get_title_field();
$type = $this->get_multichoice_type_field();
return $title.$type;
}
protected function get_values() {
/** #var wpdb $wpdb */
global $wpdb;
// get initiative IDs associated with published posts
// $initiative_ids = $wpdb->get_col(
// $wpdb->prepare(
// "SELECT DISTINCT m.meta_value
// FROM {$wpdb->postmeta} m
// INNER JOIN {$wpdb->posts} p
// ON p.ID=m.post_id
// WHERE p.post_type=%s,
// AND p.post_status='publish'
// AND m.meta_key='bi_initiatives-events'
// AND m.meta_value > 0", $post_type
// )
// );
$initiative_ids = array();
// WP_Query arguments
$args = array(
'post_type' => array( 'pt_initiatives' ),
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'bi_initiatives-events',
// 'value' => '',
'compare' => 'EXISTS'
)
)
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
$initiative_ids[] = get_the_ID();
}
}
wp_reset_postdata();
array_filter( $initiative_ids );
if ( empty( $initiative_ids ) ) {
return array();
}
/**
* Filter Total Initiatives in Filter Bar
* Use this with caution, this will load initiatives on the front-end, may be slow
* The base limit is 200 for safety reasons
*
*
* #parm int 200 posts per page limit
* #parm array $initiative_ids ids of initiatives attached to events
*/
$limit = apply_filters( 'tribe_events_filter_bar_pt_initiatives_limit', 200, $initiative_ids );
$initiatives = get_posts( array(
'post_type' => 'pt_initiatives',
'posts_per_page' => $limit,
'suppress_filters' => false,
'post__in' => $initiative_ids,
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC',
) );
$initiatives_array = array();
foreach ( $initiatives as $initiative => $value ) {
$initiatives_array[] = array(
'name' => $value->post_title,
'value' => $value->ID,
);
}
return $initiatives_array;
}
protected function setup_join_clause() {
add_filter( 'posts_join', array( 'Tribe__Events__Query', 'posts_join' ), 10, 2 );
global $wpdb;
$this->joinClause .= " LEFT JOIN {$wpdb->postmeta} AS initiatives_filter ON ({$wpdb->posts}.ID = initiatives_filter.post_id AND initiatives_filter.meta_key = 'bi_initiatives-events')";
}
protected function setup_where_clause() {
if ( is_array( $this->currentValue ) ) {
$initiative_ids = implode( ',', array_map( 'intval', $this->currentValue ) );
} else {
$initiative_ids = esc_attr( $this->currentValue );
}
$this->whereClause = " AND initiatives_filter.meta_value IN ($initiative_ids) ";
}
}
new Tribe__Events__Filterbar__Filters__Initiative( __( 'Initiatives', 'tribe-events-filter-view' ), 'initiatives_filter' );
I am using the example from the CMB2 snippets library to add a theme options page in WordPress
/**
* Hook in and register a metabox to handle a theme options page and adds a menu item.
*/
function yourprefix_register_main_options_metabox() {
/**
* Registers main options page menu item and form.
*/
$args = array(
'id' => 'yourprefix_main_options_page',
'title' => 'Main Options',
'object_types' => array( 'options-page' ),
'option_key' => 'yourprefix_main_options',
'tab_group' => 'yourprefix_main_options',
'tab_title' => 'Main',
);
// 'tab_group' property is supported in > 2.4.0.
if ( version_compare( CMB2_VERSION, '2.4.0' ) ) {
$args['display_cb'] = 'yourprefix_options_display_with_tabs';
}
$main_options = new_cmb2_box( $args );
/**
* Options fields ids only need
* to be unique within this box.
* Prefix is not needed.
*/
$main_options->add_field( array(
'name' => 'Site Background Color',
'desc' => 'field description (optional)',
'id' => 'bg_color',
'type' => 'colorpicker',
'default' => '#ffffff',
) );
/**
* Registers secondary options page, and set main item as parent.
*/
$args = array(
'id' => 'yourprefix_secondary_options_page',
'menu_title' => 'Secondary Options', // Use menu title, & not title to hide main h2.
'object_types' => array( 'options-page' ),
'option_key' => 'yourprefix_secondary_options',
'parent_slug' => 'yourprefix_main_options',
'tab_group' => 'yourprefix_main_options',
'tab_title' => 'Secondary',
);
// 'tab_group' property is supported in > 2.4.0.
if ( version_compare( CMB2_VERSION, '2.4.0' ) ) {
$args['display_cb'] = 'yourprefix_options_display_with_tabs';
}
$secondary_options = new_cmb2_box( $args );
$secondary_options->add_field( array(
'name' => 'Test Radio',
'desc' => 'field description (optional)',
'id' => 'radio',
'type' => 'radio',
'options' => array(
'option1' => 'Option One',
'option2' => 'Option Two',
'option3' => 'Option Three',
),
) );
/**
* Registers tertiary options page, and set main item as parent.
*/
$args = array(
'id' => 'yourprefix_tertiary_options_page',
'menu_title' => 'Tertiary Options', // Use menu title, & not title to hide main h2.
'object_types' => array( 'options-page' ),
'option_key' => 'yourprefix_tertiary_options',
'parent_slug' => 'yourprefix_main_options',
'tab_group' => 'yourprefix_main_options',
'tab_title' => 'Tertiary',
);
// 'tab_group' property is supported in > 2.4.0.
if ( version_compare( CMB2_VERSION, '2.4.0' ) ) {
$args['display_cb'] = 'yourprefix_options_display_with_tabs';
}
$tertiary_options = new_cmb2_box( $args );
$tertiary_options->add_field( array(
'name' => 'Test Text Area for Code',
'desc' => 'field description (optional)',
'id' => 'textarea_code',
'type' => 'textarea_code',
) );
}
add_action( 'cmb2_admin_init', 'yourprefix_register_main_options_metabox' );
/**
* A CMB2 options-page display callback override which adds tab navigation among
* CMB2 options pages which share this same display callback.
*
* #param CMB2_Options_Hookup $cmb_options The CMB2_Options_Hookup object.
*/
function yourprefix_options_display_with_tabs( $cmb_options ) {
$tabs = yourprefix_options_page_tabs( $cmb_options );
?>
<div class="wrap cmb2-options-page option-<?php echo $cmb_options->option_key; ?>">
<?php if ( get_admin_page_title() ) : ?>
<h2><?php echo wp_kses_post( get_admin_page_title() ); ?></h2>
<?php endif; ?>
<h2 class="nav-tab-wrapper">
<?php foreach ( $tabs as $option_key => $tab_title ) : ?>
<a class="nav-tab<?php if ( isset( $_GET['page'] ) && $option_key === $_GET['page'] ) : ?> nav-tab-active<?php endif; ?>" href="<?php menu_page_url( $option_key ); ?>"><?php echo wp_kses_post( $tab_title ); ?></a>
<?php endforeach; ?>
</h2>
<form class="cmb-form" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="POST" id="<?php echo $cmb_options->cmb->cmb_id; ?>" enctype="multipart/form-data" encoding="multipart/form-data">
<input type="hidden" name="action" value="<?php echo esc_attr( $cmb_options->option_key ); ?>">
<?php $cmb_options->options_page_metabox(); ?>
<?php submit_button( esc_attr( $cmb_options->cmb->prop( 'save_button' ) ), 'primary', 'submit-cmb' ); ?>
</form>
</div>
<?php
}
/**
* Gets navigation tabs array for CMB2 options pages which share the given
* display_cb param.
*
* #param CMB2_Options_Hookup $cmb_options The CMB2_Options_Hookup object.
*
* #return array Array of tab information.
*/
function yourprefix_options_page_tabs( $cmb_options ) {
$tab_group = $cmb_options->cmb->prop( 'tab_group' );
$tabs = array();
foreach ( CMB2_Boxes::get_all() as $cmb_id => $cmb ) {
if ( $tab_group === $cmb->prop( 'tab_group' ) ) {
$tabs[ $cmb->options_page_keys()[0] ] = $cmb->prop( 'tab_title' )
? $cmb->prop( 'tab_title' )
: $cmb->prop( 'title' );
}
}
return $tabs;
}
This works great, but I can't figure out how to actually get one of these values and display it in a theme. Anyone have an example?
You can use cmb2_get_option()
It takes 3 parameters: cmb2_get_option( $option_key, $field_id, $default );
So in your case:
$option_key = yourprefix_main_options,
$field_id = bg_color,
$default = the default value (in case you set it).
It should look like this:
cmb2_get_option( 'yourprefix_main_options', 'bg_color' );
cmb2_get_option( 'yourprefix_secondary_options', 'radio' );
and so on ..
Also, You can use your own function with Fallback:
/**
* Wrapper function around cmb2_get_option
* #since 0.1.0
* #param string $key Options array key
* #param mixed $default Optional default value
* #return mixed Option value
*/
function myprefix_get_option( $key = '', $default = false ) {
if ( function_exists( 'cmb2_get_option' ) ) {
// Use cmb2_get_option as it passes through some key filters.
return cmb2_get_option( 'yourprefix_main_options', $key, $default );
}
// Fallback to get_option if CMB2 is not loaded yet.
$opts = get_option( 'yourprefix_main_options', $default );
$val = $default;
if ( 'all' == $key ) {
$val = $opts;
} elseif ( is_array( $opts ) && array_key_exists( $key, $opts ) && false !== $opts[ $key ] ) {
$val = $opts[ $key ];
}
return $val;
}
Use it like this:
myprefix_get_option( bg_color )
Please use the following code :
get_option("<your id>");
Like :
get_option('bg_color');
Hope this will work for you.
You can try using array system with get_option() function.
First time you need to use get_option('your-option-key');
like your option-key is "yourprefix_main_options" so you need to use get_option('yourprefix_main_options');
then you can keep your value on variable. like
$main_option_data = get_option('yourprefix_main_options');
then you can use $main_option_data and your filed id as a array. like your one filed id is bg_color so you can use $main_option_data['bg_color'];
You can check my one previous work, I think it will be help you.
// Theme Options
add_action( 'cmb2_admin_init', 'mosc_panel_theme_options_metabox' );
/**
* Hook in and register a metabox to handle a theme options page and adds a menu item.
*/
function mosc_panel_theme_options_metabox() {
/**
* Registers options page menu item and form.
*/
$mos_option_panel = new_cmb2_box( array(
'id' => 'mosc_theme_options_page',
'title' => esc_html__( 'Theme Options', 'cmb2' ),
'parent_slug' => 'themes.php', // Make options page a submenu item of the themes menu.
'object_types' => array( 'options-page' ),
'option_key' => 'mosc_theme_options', // The option key and admin menu page slug.
'icon_url' => 'dashicons-palmtree', // Menu icon. Only applicable if 'parent_slug' is left empty.
) );
// Regular text field
$mos_option_panel->add_field( array(
'name' => __( 'Contact Form Heading', 'mosc' ),
'id' => 'mosc-contact-form-heading',
'type' => 'text',
'default' => __('Feel free to drop me a line', 'mosc'),
) );
}
then I've used this code for show my field value.
$mosc_contact_heading = get_option('mosc_theme_options');
if(!empty($mosc_contact_heading['mosc-contact-form-heading'])) {
echo esc_html($mosc_contact_heading['mosc-contact-form-heading']);
}
I've used this code to show my field value and working perfectly:
echo cmb2_get_option( '_key_options', 'text_text');