Woocommerce 3.1 product variation meta data - wordpress

I have currently added metadata to a product variation woocommerce_product_after_variable_attributes and woocommerce_save_product_variation Guide here.
function custom_woocommerce_product_after_variable_attributes($loop, $variation_data, $variation)
{
woocommerce_wp_select([
'id' => 'field_1['.absint($variation->ID).']',
'label' => 'Field 1 ',
'value' => get_post_meta(absint($variation->ID), 'field_1', true),
'options' => [
'1' => '1',
'2' => '2',
'3' => '3',
],
]);
}
add_action('woocommerce_product_after_variable_attributes', 'custom_woocommerce_product_after_variable_attributes', 10, 3);
function custom_woocommerce_save_product_variation($post_id)
{
$field1 = $_POST['field_1'][$post_id];
if (! empty($field1)) {
update_post_meta(absint($post_id), 'field_1', esc_html($field1));
}
}
add_action('woocommerce_save_product_variation', 'custom_woocommerce_save_product_variation', 10, 2);
Then in the js hooked into single_variation_wrap when the variation was changed. This was working fine in 3.0.5 but since updating to 3.1.1 in the js i am no longer getting the custom meta_data for variations.
$('.single_variation_wrap').on('show_variation', function(event, variation) {
console.log(variation.meta_data);
});
The meta_data information no longer exists.
How can this be fixed?

I was able to fix this by adding a filter.
function custom_woocommerce_available_variation($variations, $product, $variation)
{
$metadata = $variation->get_meta_data();
if (!empty($metadata)) {
$variations = array_merge($variations, [
'meta_data' => $metadata,
]);
}
return $variations;
}
add_filter('woocommerce_available_variation', 'custom_woocommerce_available_variation', 10, 3);

Related

Adding graphql support for custom posts

I have installed WP GraphQL plugin and it works fine for pages and posts. However its not working for custom posts.
for example,
query getPages{
pages(first: 1000) {
edges {
node {
uri
}
}
}
}
works.
query getPastEvents{
past-events(first: 1000) {
edges {
node {
uri
}
}
}
}
not works.
I have read this doc https://www.wpgraphql.com/docs/custom-post-types/ but still not understood exact process.
update: i added the below code in wp-includes/plugin.php and its now showing careCenter, careCenterPages in WP GraphQL IDE but its returning no data
// WP GRAPH QL
add_action( 'init', function() {
register_post_type( 'care_center', [
'show_ui' => true,
'labels' => [
//#see https://developer.wordpress.org/themes/functionality/internationalization/
'menu_name' => __( 'CareCenter', 'care_center' ),
],
'show_in_graphql' => true,
'hierarchical' => true,
'graphql_single_name' => 'careCenter',
'graphql_plural_name' => 'careCenterPages',
] );
} );
add_filter( 'register_post_type_args', function( $args, $post_type ) {
// Change this to the post type you are adding support for
if ( 'care_center' === $post_type ) {
$args['show_in_graphql'] = true;
$args['graphql_single_name'] = 'careCenter';
$args['graphql_plural_name'] = 'careCenterPages';
}
return $args;
}, 10, 2 );
am attaching few screenshots below for more understanding...
I want graphql support for custom posts just like pages & posts.

Can't change INDEXBY for findAll() and findBy() functions

With Doctrine and Symfony 5, I want to change INDEX BY for my queries with findAll() and findBy() functions. I would like the keys of the result table to be simply the IDs.
// Current result
[
0 => Post {
'id' => 5,
'name' => 'Test 5',
},
1 => Post {
'id' => 6,
'name' => 'Test 6',
},
]
// Desired result :
[
5 => Post {
'id' => 5,
'name' => 'Test 5',
},
6 => Post {
'id' => 6,
'name' => 'Test 6',
},
]
I know that I can use createQueryBuilder('post', 'post.id'), but in this case I lose the fetch = EAGER since I make the queries myself.
Maybe the only solution is to do foreach () to change the keys of each findAll() and findBy() request, but I find it heavy.
You can alter the findBy method or findAll method:
Create a foreach ($results as $result) as follows:
$newResult = [];
$results = $repo->findAll();
foreach ($results as $result) {
$newResult[$result->getId()] = $result;
}

wp_insert_post called via cron duplicates posts instead of updating them

I'm currently stuck on a problem I can't figure out on a wordpress side project. Here's the thing :
I made a plugin that queries an API, and processes its data to insert posts.
The problem I face is that, when the main function is triggered manually (i.e. via a route or cron manually launched) posts are correctly created or updated, but when the cron executed automatically, posts are always duplicated. I may have narrowed the problem down to capabilities (I check if posts exists by querying the cpt for a unique meta-field), and when the cron executes on its own, it find no ids.
Here are code samples :
public function insertPosts() {
set_current_user(1); // <- here I tried setting the user as admin for the script, but it didn't work
// First, get all prestations from the api
$prestations = $this->api->getPrestations();
/*
* Then :
* we remove sessions of formations initiales, which are useless (gamme id 10 = formations initiales)
* I could have "only" taken the ids I need, but unsetting useless array entries allows for a lighter dataset
*/
$prestations_info = [];
foreach ($prestations as $key => $prestation) {
if ($prestation['gamme']['id'] == 10) {
// Removes the useless $prestation and breaks out of the loop so its ID's not stored
unset($prestations[$key]);
continue;
}
$prestations_info[] = [
'fid' => $prestation['id'],
'cat_name' => $prestation['gamme']['name'],
'cat_id' => $prestation['gamme']['id']
];
}
/*
* Then :
* Parse the array to process every id individually and query the api for core informations for the singles
*/
foreach ($prestations_info as $presta) {
// Meta query to check if a post with the formation's id already exists
$args = [
'post_type' => 'formations',
'posts_per_page' => -1,
'meta_query' => [
[
'key' => 'api_fid',
'value' => $presta['fid'],
'compare' => '='
]
]
];
$posts = new WP_Query($args);
if ($posts->have_posts()) {
while ($posts->have_posts()) {
// If a post with that formation id already exists, we retrieve its ID:
// this way, we can update existing post and avoid duplicates
$posts->the_post();
$post_id = get_the_ID();
}
}
$body = $this->api->getSessionsByPrestationsId($presta['fid']);
$post_array = [];
$locations = [];
$nb_places = [];
foreach ($body as $key => $item) {
if (!empty($item['seances'])) {
$duration = BOUtilities::getFormationDuration($item['seances']);
$dates = BOUtilities::getDatesPerSessions($item['seances']);
}
if ($dates) {
$session_date = [];
if (is_array($dates)) {
foreach ($dates as $i => $date) {
$session_date[$i] = [
'dates' => $date['start'] .' - ' . $date['end'],
];
}
} else {
$session_date[] = [
'dates' => $dates,
];
}
}
if (!empty($item['location'])) {
$locations[] = strtolower($item['location']);
$session[] = [
'sessions_location' => $item['location'],
'sessions_dates' => $session_date,
];
}
if (!empty($item['nbplace']) && $item['nbplace'] != 0) {
if (!in_array($item['nbplace'], $nb_places)) {
$nb_places[] = $item['nbplace'];
}
}
if ($item['price'] != 0) {
$prices[] = $item['price'];
}
}
$formatted_cost = BOUtilities::setPrices($prices);
$effectifs = BOUtilities::setEffectifs($nb_places);
if ($post_id) {
$post_array = [
'ID' => $post_id,
'post_type' => 'formations',
'post_title' => $item['prestation']['name'],
'meta_input' => [
'api_fid' => $presta['fid'],
'aside_cost' => $formatted_cost,
'aside_duration' => ($duration > 1 ? $duration . ' jours' : $duration . ' jour'),
'aside_effectifs' => $effectifs,
]
];
$inserted_post = wp_update_post($post_array);
}
else {
$post_array = [
'post_type' => 'formations',
'post_title' => $item['prestation']['name'],
'meta_input' => [
'api_fid' => $presta['fid'],
'aside_cost' => $formatted_cost,
'aside_duration' => ($duration > 1 ? $duration . ' jours' : $duration . ' jour'),
'aside_effectifs' => $effectifs,
]
];
$inserted_post = wp_insert_post($post_array);
}
// The cron does not have the capability of adding taxonomy terms, they need to be be added separately
$inserted_terms = wp_set_object_terms($inserted_post, $presta['cat_name'], 'categories_formations');
$inserted_terms = wp_set_object_terms($inserted_post, $locations, 'ville');
update_field('aside_sessions', $session, $inserted_post);
unset($dates);
unset($session);
unset($formatted_cost);
unset($nb_places);
unset($prices);
}
}
This is very much work in progress, but, for now, I am stumped.

Wordpress Buddypress always show members list of group

I'm working on a Wordpress/Buddypress project and I would like to make the members list of group always visible whether or not the group is private and the logged member belong to the group.
I was thinking I had to change access and visibility of the nav items so I did that:
function change_access_group_nav_tabs() {
if(bp_is_group()) {
buddypress()->groups->nav->edit_nav( array('visibility' => 'public'), 'members', bp_current_item() );
buddypress()->groups->nav->edit_nav( array('access' => 'anyone'), 'members', bp_current_item() );
}
}
add_action( 'bp_actions', 'change_access_group_nav_tabs' );
But it didn't work…
Any suggestion how to proceed?
I found a workaround, I'am quite not completely satisfied but...
First of all, disable members list on group:
function change_access_group_nav_tabs() {
if(bp_is_group()) {
buddypress()->groups->nav->edit_nav( array( 'user_has_access' => false ), 'members', bp_current_item() );
}
}
add_action( 'bp_actions', 'change_access_group_nav_tabs' );`
(btw, setting the value to true actually make the the nav items always here, but we still can't access the group list on click)
And then I simply add a custom BP Group Extension to make my own members list:
class Group_Extension_List_Members extends BP_Group_Extension {
function __construct() {
$args = array(
'slug' => 'members-list',
'name' => 'Membres',
'access' => array( 'anyone'),
'show_tab' => array( 'anyone'),
'nav_item_position' => 12,
'screens' => array(
'create' => array(
'enabled' => false
),
'edit' => array(
'enabled' => false
),
)
);
parent::init( $args );
}
function display( $group_id = NULL ) {
//Remove user who do not belong to the group on members loop
function filter_for_groups( $members_template_has_members, $members_template, $r ) {
for ($i=sizeof($members_template->members)-1; $i >= 0 ; $i--) {
$user_id = $members_template->members[$i]->id;
if(!groups_is_user_member( $user_id, bp_get_group_id() )){
$members_template->member_count = $members_template->member_count-1;
array_splice($members_template->members, $i, 1);
}
}
if ($members_template->member_count <= 0) {
return '';
} else {
return $members_template_has_members;
}
};
add_filter( 'bp_has_members', 'filter_for_groups', 10, 3 );
require('/Your/theme/custom/members/loop/members-loop.php');
}
}
bp_register_group_extension( 'Group_Extension_List_Members' );
Hope it will help other in the future, And I'm still open to know the good way to proceed.

Drupal autocomplete, callback with multiple parameters

I am adding some autocomplete on a form alter. The problem is that in the callback, only the string in the textfield The autocomplete is on, is available. I also want to access a value from another textfield in the callback. How is this possible ?
/**
* Implements hook_form_alter().
*/
function webform_conversion_jquery_form_webform_client_form_1_alter(&$form, &$form_state, $form_id) {
//Load some extra function to process data
module_load_include('inc', 'webform_conversion_jquery', '/includes/dataqueries');
//Add extra js files
drupal_add_js(drupal_get_path('module', 'webform_conversion_jquery') . '/js/conversionform.js');
$form['submitted']['correspondentadress']['cor_street']['#autocomplete_path'] = 'conversionform/conversion_street';
}
}
/**
* Implements hook_menu().
*/
function webform_conversion_jquery_menu() {
$items = array();
$items['conversionform/conversion_street'] = array(
'title' => 'Conversion street autocomplete',
'page callback' => 'conversion_street_autocomplete',
'access callback' => 'user_access',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Retrieve a JSON object containing autocomplete suggestions for streets depending on the zipcode.
*/
function conversion_street_autocomplete($street = '') {
$street = "%" . $street . "%";
$matches = array();
$result = db_select('conversion_adresslist')
->fields('conversion_adresslist', array('street'))
->condition('street', $street, 'like')
->execute();
foreach ($result as $street) {
$matches[$street->street] = $street->street;
}
drupal_json_output($matches);
}
I just want to be able to post extra information in the function:
conversion_street_autocomplete($street = '', $extraparameter)
I had the same problem and have figured out a way, which is not too strenuous. It involves overriding the textfield theme and then passing your parameter to the theme function.
First create declare your theme function:
function mymodule_theme() {
$theme_hooks = array(
'my_module_autocomplete' => array(
'render element' => 'element',
),
);
return $theme_hooks;
}
Next we need to add the theme and the variable to our form element. In my case, the form element is part of a field widget:
function my_module_field_widget_form($form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
if($instance['widget']['type'] == 'my_module_field_type') {
$element['my_module_field'] = array(
'#type' => 'textfield',
'#autocomplete_path' => 'my-module/autocomplete',
// THIS IS THE IMPORTANT PART - ADD THE THEME AND THE VARIABLE.
'#theme' => 'my_module_autocomplete',
'#my_module_variable' => $field['field_name'],
);
}
return $element;
}
Then implement the theme function. This is a copy of theme_textfield from includes/form.inc with one important difference - we append the variable to the autocomplete path:
function theme_my_module_autocomplet($variables) {
$element = $variables['element'];
$element['#attributes']['type'] = 'text';
element_set_attributes($element, array('id', 'name', 'value', 'size', 'maxlength'));
_form_set_class($element, array('form-text'));
$extra = '';
if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'])) {
drupal_add_library('system', 'drupal.autocomplete');
$element['#attributes']['class'][] = 'form-autocomplete';
$attributes = array();
$attributes['type'] = 'hidden';
$attributes['id'] = $element['#attributes']['id'] . '-autocomplete';
// THIS IS THE IMPORTANT PART. APPEND YOUR VARIABLE TO THE AUTOCOMPLETE PATH.
$attributes['value'] = url($element['#autocomplete_path'] . '/' . $element['#my_module_variable'], array('absolute' => TRUE));
$attributes['disabled'] = 'disabled';
$attributes['class'][] = 'autocomplete';
$extra = '<input' . drupal_attributes($attributes) . ' />';
}
$output = '<input' . drupal_attributes($element['#attributes']) . ' />';
return $output . $extra;
}
Now the variable will be available as the first parameter on the autocomplete callback function:
function _my_module_autocomplete($my_module_variable, $search_string) {
// Happy days, we now have access to our parameter.
}
Just in case anyone is still having trouble with this I found a great solution while trying to figure out how to do this. I had a year select list and that dictated what data was displayed in the autocomplete field. The solution basically has an ajax callback function for the select list that can then update the autocomplete field with an extra parameter in the url. Anyways, it is really well explained in the following article.
http://complexdan.com/passing-custom-arguments-drupal-7-autocomplete/
*A note of caution, I was going crazy trying to figure out why it did not work and it turns out you can't have the same form on the page twice (I needed to because I was displaying it differently for mobile devices) because you are using an id for the ajax callback. I added an extra argument to accomplish that. It is called uniqueid in the below example.
function report_cards_comparison_form($form, &$form_state, $uniqueid) {
$curryear = t('2012');
$form['year_select'] = array(
'#title' => t('School Year'),
'#type' => 'select',
'#options' => array(
'2012' => t('2012'),
'2013' => t('2013'),
'2014' => t('2014'),
'2015' => t('2015'),
),
'#default_value' => $curryear,
'#ajax' => array(
'callback' => 'report_cards_comparison_form_callback',
'wrapper' => $uniqueid,
'progress' => array(
'message' => 'Updating Schools...',
'type' => 'throbber'
),
),
);
$form['choice'] = array(
//'#title' => t('Search By: School Name'),
'#type' => 'textfield',
'#attributes' => array(
'class' => array('school-choice'),
'placeholder' => t('Start Typing School Name...'),
),
'#required' => TRUE,
'#autocomplete_path' => 'reportcards/autocomplete/' . $curryear,
'#prefix' => '<div id="' . $uniqueid . '">',
'#suffix' => '</div>',
);
$form['submit'] = array(
'#type' => 'submit',
'#prefix' => '<div class="submit-btn-wrap">',
'#suffix' => '</div>',
'#value' => t('Search'),
'#attributes' => array('id' => 'add-school-submit'),
);
return $form;
}
/**
* Ajax Callback that updates the autocomplete ajax when there is a change in the Year Select List
*/
function report_cards_comparison_form_callback($form, &$form_state) {
unset($form_state['input']['choice'], $form_state['values']['choice']);
$curryear = $form_state['values']['year_select'];
$form_state['input']['choice'] = '';
$form['choice']['#value'] = '';
$form['choice']['#autocomplete_path'] = 'reportcards/autocomplete/' . $curryear;
return form_builder($form['#id'], $form['choice'], $form_state);
}
and I can call the form by doing this...
print render(drupal_get_form('report_cards_comparison_form', 'desktop-schoolmatches'));
You can do it by overriding methods from autocomplete.js in your own js. Here is example:
(function($) {
Drupal.behaviors.someModuleOverrideAC = {
attach: function(context, settings) {
// Next is copied and adjusted method from autocomplete.js
Drupal.jsAC.prototype.populatePopup = function() {
var $input = $(this.input);
var position = $input.position();
// Show popup.
if (this.popup) {
$(this.popup).remove();
}
this.selected = false;
this.popup = $('<div id="autocomplete"></div>')[0];
this.popup.owner = this;
$(this.popup).css({
top: parseInt(position.top + this.input.offsetHeight, 10) + 'px',
left: parseInt(position.left, 10) + 'px',
width: $input.innerWidth() + 'px',
display: 'none'
});
$input.before(this.popup);
// Do search.
this.db.owner = this;
if ($input.attr('name') === 'field_appartment_complex') {
// Overriden search
// Build custom search string for apartments autocomplete
var $wrapper = $('div.apartments-autocomplete');
var $elements = $('input, select', $wrapper);
var searchElements = {string: this.input.value};
$elements.each(function() {
searchElements[$(this).data('address-part')] = $(this).val();
});
var string = encodeURIComponent(JSON.stringify(searchElements));
this.db.search(string);
}
else {
// Default search
this.db.search(this.input.value);
}
};
}
};
}(jQuery));
In your server callback:
function some_module_autocomplete_ajax($string) {
// Decode custom string obtained using overriden autocomplete js.
$components = drupal_json_decode(rawurldecode($string));
// Do you search here using multiple params from $components
}
Ok, for as far as I can see it is not possible. maybe you can roll your own with the ajax functionality in fapi http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7#ajax
For now I solved it by implementing jquery.ui.autocomplete which is included in drupal 7

Resources