I'm using the following code to add attributes with terms:
$taxonomy = 'pa_' . $attr['name']; // The attribute taxonomy
if (!taxonomy_exists($taxonomy)) {
global $wpdb;
$insert = $wpdb->insert(
$wpdb->prefix . 'woocommerce_attribute_taxonomies',
array(
'attribute_name' => $attr['name'],
'attribute_label' => $attr['name'],
'attribute_public' => 0
),
array('%s', '%s', '%d')
);
if (is_wp_error($insert)) {
throw new WC_API_Exception('woocommerce_api_cannot_create_product_attribute', $insert->get_error_message(), 400);
}
// Clear transients
delete_transient('wc_attribute_taxonomies');
}
if (!term_exists($attr['value'], $attr['name'])) {
wp_insert_term($attr['value'], $attr['name']);
}
$term_slug = get_term_by('name', $attr['value'], $attr['name'])->slug; // Get the term slug
wp_set_post_terms($product_id, $attr['value'], $attr['name'], true);
// Set/save the attribute data in the product variation
update_post_meta($variation_id, 'attribute_' . $taxonomy, $term_slug);
// Assign to the product
wp_set_object_terms($product_id, $attr['value'], $taxonomy, true);
$att = array($taxonomy => array(
'name' => $taxonomy,
'value' => $attr['value'],
'is_visible' => '1',
'is_variation' => '1',
'is_taxonomy' => '1',
));
update_post_meta($product_id, '_product_attributes', $att);
However, the first time the code runs it adds the attributes without terms. If I run it a second time, only then does it add the terms to the previously added attributes.
Why is that?
Edit: The problem seems to start at the following line:
$term_slug = get_term_by('name', $attr['value'], $attr['name'])->slug
It simply doesn't yet recognize the newly created taxonomy. Only at the next run.
But why? Is there a function that can be used to "refresh" the attributes, or the $wp_attributes variable, which seems to be closely related?
Thanks!
It is hard to debug without looking at the live site. However, I think it could be something to do with this code:
if (!term_exists($attr['value'], $attr['name'])) {
wp_insert_term($attr['value'], $attr['name']);
}
On first request the term is inserted yet it will not be part of WordPress main query until further requests.
Again, this is mostly guessing as I need to see the actual implementation.
I use such a method to create a taxonomy.
public function setAttribute($attributeArgs)
{
$attributeId = wc_attribute_taxonomy_id_by_name($attributeArgs['slug']);
if ($attributeId === 0) {
$attributeId = wc_create_attribute($attributeArgs);
register_taxonomy('pa_' . $attributeArgs['slug'], ['product'], []);
} else {
$attributeId = wc_update_attribute($attributeId, $attributeArgs);
}
return [
'id' => $attributeId,
'args' => $attributeArgs,
];
}
An important line in this code is register_taxonomy('pa_' . $attributeArgs['slug'], ['product'], []);
Without this line, I had the same problem.
Related
I am attempting to order my products by Length and Width, where it sorts by length first and, if two products have the same value, it will sort by Width (and so on). I would like to add height if I can get this working first.
So far the issue I'm running into is that it orders the products by length and only length (plus a bug where even the length is incorrect, i.e. the product order is 2.1, 2, and 2.5).
This is as far as I've gotten with the query:
function woocommerce_catalog_orderby( $args ) {
$args['meta_query'] = array(
'relation' => 'AND',
'length_clause' => array(
'key' => '_length',
'type' => 'DECIMAL',
'orderby' => 'meta_value_num',
),
'width_clause' => array(
'key' => '_width',
'type' => 'DECIMAL',
'orderby' => 'meta_value_num',
),
);
$args['orderby'] = array(
'length_clause' => 'ASC',
'width_clause' => 'ASC',
);
return $args;
}
add_filter('woocommerce_get_catalog_ordering_args', 'woocommerce_catalog_orderby');
I'm convinced I'm missing something obvious that I just can't see at the moment. Any help is much appreciated!
In your case you must add priority when you add a filter for woocommerce.
Here is the working code for sorting by meta values:
/**
Save product attributes to post metadata when a product is saved.
#param int $post_id The post ID.
#param post $post The post object.
#param bool $update Whether this is an existing post being updated or not.
Source: https://www.webhat.in/article/woocommerce-tutorial/custom-order-by-attributes/
Reference: https://codex.wordpress.org/Plugin_API/Action_Reference/save_post/
*/
'''
function wh_save_product_custom_meta($post_id, $post, $update) {
$post_type = get_post_type($post_id);
// If this isn't a 'product' post, don't update it.
if ($post_type != 'product')
return;
if (!empty($_POST['attribute_names']) && !empty($_POST['attribute_values'])) {
$attribute_names = $_POST['attribute_names'];
$attribute_values = $_POST['attribute_values'];
foreach ($attribute_names as $key => $attribute_name) {
switch ($attribute_name) {
//for color (string)
case 'pa_color':
//it may have multiple color (eg. black, brown, maroon, white) but we'll take only the first color.
if (!empty($attribute_values[$key][0])) {
update_post_meta($post_id, 'pa_color', $attribute_values[$key][0]);
}
break;
//for lenght (int)
case 'pa_length':
if (!empty($attribute_values[$key][0])) {
update_post_meta($post_id, 'pa_length', $attribute_values[$key][0]);
}
break;
default:
break;
}
}
}
}
add_action( 'save_post', 'wh_save_product_custom_meta', 10, 3);
'''
Custom sorting can be added using: please follow the step according and replace your meta value
https://docs.woocommerce.com/document/custom-sorting-options-ascdesc/
i am new to v2, i use v1 for long time, currently upgrade to v2, i try to get all the terms belong to specific custom taxonomy.
In v1 i can do this to get terms
/taxonomies/location_category/terms
but in v2 i try
/taxonomies/terms
it return json error "code":"rest_no_route","message":"No route was found matching the URL and request method","data":{"status"
:404}}
if just /taxonomies/location_category/ it didn't show anything terms belong to taxonomy.
i search the question on google for few hours didn't show any result, anyone can help please, thank you
end out to write the custom code here
add blow code to functions.php
class all_terms
{
public function __construct()
{
$version = '2';
$namespace = 'wp/v' . $version;
$base = 'all-terms';
register_rest_route($namespace, '/' . $base, array(
'methods' => 'GET',
'callback' => array($this, 'get_all_terms'),
));
}
public function get_all_terms($object)
{
$return = array();
// $return['categories'] = get_terms('category');
// $return['tags'] = get_terms('post_tag');
// Get taxonomies
$args = array(
'public' => true,
'_builtin' => false
);
$output = 'names'; // or objects
$operator = 'and'; // 'and' or 'or'
$taxonomies = get_taxonomies($args, $output, $operator);
foreach ($taxonomies as $key => $taxonomy_name) {
if($taxonomy_name = $_GET['term']){
$return = get_terms($taxonomy_name);
}
}
return new WP_REST_Response($return, 200);
}
}
add_action('rest_api_init', function () {
$all_terms = new all_terms;
});
and enter url http://youdomain.com/wp-json/wp/v2/all-terms?term=you_taxonomy
so term = you_taxonomy, will get terms belong to job_category.
Taxonomy terms are simply called this way:
https://yoursite.com/wp-json/wp/v2/the-taxonomy-slug
For instance, to answer your question:
https://yoursite.com/wp-json/wp/v2/location_category
From terminal:
curl -X GET -i http://www.example.com/wp-json/wp/v2/location_category
For custom taxonomies, be sure that you're setting the 'show_in_rest' argument to be true (default is false) in your register_taxonomy() call.
The register_taxonomy() call is also where you can set the 'rest_base' argument (default will be the taxonomy name, /location_category/ in your example).
TLDR
Use the flag show_in_rest when you register the taxonomy. That's all.
Details
List all available taxonomies
All default taxonomies are available via REST API. Use the following endpoint to get a list of all available taxonomies:
https://exmaple.org/wp-json/wp/v2/taxonomies
It will tell you the correct endpoint for each taxonomy in the wp:items tag, e.g.
..., "wp:items":[{"href":"https://example.com/wp-json/wp/v2/categories"}], ...
..., "wp:items":[{"href":"https://example.com/wp-json/wp/v2/tags"}], ...
Adding new taxonomies to the REST endpoint
In case your taxonomy is not listed in this taxonomy overview, you need to enable the REST endpoint when calling register_taxonomy. You can do this by adding the argument 'show_in_rest' => true:
<php
register_taxonomy( 'location_category', 'post', [
// ...
'show_in_rest' => true, // ← make sure you have this line in the taxonomy args!
] );
Note: If you do NOT use show_in_rest, then the taxonomy is not available in the Block Editor ("Gutenberg").
I strongly advise against creating custom REST endpoints to duplicate built-in logic.
I.e. don't do this or this
Seems some confusion for some devs even for me.
Correct URL is
https://example.com/wp-json/wp/v2/{your_taxonomy}
Not
https://example.com/wp-json/wp/v2/taxonomies/{your_taxonomy}
"/taxonomies" not required
The accepted answer mostly worked for me. This is what I got
<?php
// your_theme/functions.php
/**
* how to list all taxonomy terms
*/
class all_terms
{
public function __construct()
{
$version = '2';
$namespace = 'wp/v' . $version;
$base = 'all-terms';
register_rest_route($namespace, '/' . $base, array(
'methods' => 'GET',
'callback' => array($this, 'get_all_terms'),
));
}
public function get_all_terms($object)
{
$args = array(
'public' => true,
'_builtin' => false
);
$output = 'names'; // or objects
$operator = 'and'; // 'and' or 'or'
$taxonomies = get_taxonomies($args, $output, $operator);
foreach ($taxonomies as $key => $taxonomy_name) {
if ($taxonomy_name = $_GET['term']) {
$return[] = get_terms(array(
'taxonomy' => $taxonomy_name,
'hide_empty' => false,
));
}
}
return new WP_REST_Response($return, 200);
}
}
add_action( 'rest_api_init', get_all_terms);
?>
matches the docs more closely https://developer.wordpress.org/reference/functions/get_terms/
If anyone is reading this in the future, I ran into an issue where the default WP category was outputting a parent key of 0, 1, 2 etc for each term object, which is a problem in itself, but a bigger problem when custom taxonomies do not have this parent value on objects
To solve this amend the ticked example with this:
foreach ($taxonomies as $key => $taxonomy_name) {
if($taxonomy_name = $_GET['term']){
$return = get_terms( array(
'taxonomy' => $taxonomy_name,
'hide_empty' => false,
));
}
}
I'm new at Drupal 7, so I have a question.
I have my own content type Writers that includes such fields as Title, Years of life, Photo, Description.
I have a task to display 3 random Writers at page. Actual I've done it with help of Views module, but I want to do it myself.
So I created my own module random_content like that:
<?php
function random_content_help($path, $arg) {
switch ($path) {
case "admin/help#random_content":
return '<p>'. t("Displays random content") .'</p>';
break;
}
}
function random_content_block_info() {
$blocks['random_content'] = array(
'info' => t('Random content'),
'cache' => DRUPAL_CACHE_PER_ROLE,
);
return $blocks;
}
function random_content_contents() {
$query = db_select('node', 'n')
->fields('n', array('nid', 'title'))
->condition('type', 'writers')
->orderBy('rand()')
->range(0,3)
->execute();
return $query;
}
function random_content_block_view($delta = '') {
switch($delta){
case 'random_content':
$block['subject'] = t('Random content');
if(user_access('access content')) {
$result = random_content_contents();
$items = array();
foreach ($result as $node){
$items[] = array(
'data' => l($node->title, 'node/' . $node->nid) . '</br>',
);
}
if (empty($items)) {
$block['content'] = t('No data availible.');
} else {
$block['content'] = theme('item_list', array(
'items' => $items));
}
}
}
return $block;
}
As you can see, I've learned only to add links to particular content. But how can I display full information including Title, Years of life, Photo and Description?
To display the full node, or parts of it you need to load the node. E.g.
$my_node = node_load($nid);
$render_array = array();
$render_array['title'] = array(
'#type' => 'markup',
'#markup' => $my_node->title
);
$author = field_get_items('node', $my_node, 'field_author','und');
$render_array['author'] = array(
'#type' => 'markup',
'#markup' => $author[0]['safe_value']
);
// or as some like to do it
$render_array['author'] = array(
'#type' => 'markup',
'#markup' => $my_node->field_author['und'][0]['value']
);
echo drupal_render($render_array);
Note the 'und' constant means the language is undefined. If you have translation/language enabled and different content for different languages you would have to use 'en', 'de' etc. for the appropriate language.
You can also let drupal render the node and then manipulate or retrieve individual items. Like this
$my_node = node_load($nid);
$build = node_view($my_node,'full');
$build['body'][0]['#markup'] = $build['body'][0]['#markup'].' some addition';
$build['field_author'][0]['#markup'] = $build['field_author'][0]['#markup'].' my favorite';
echo drupal_render($build);
The advantage of using this latter method is that then the whole themeing engine kicks in, and all hooks that are set to act on the content etc. Of course, if you only want to retrieve values you don't need that.
Note also, I assume your author field is named field_author. You should check that in the field edit window for the content type.
I am currently using the Multi Post Thumbnails plugin for Wordpress, but I only want the extra thumbnails provided by the plugin to show on one specific page. The plugin does not appear to natively support this functionality but it seems like something that would be pretty easy to add, I'm just not sure of the right way to go about it as I'm fairly new to Wordpress development.
The code for Multi Post Thumbnails is the following, which simply goes in functions.php:
if (class_exists('MultiPostThumbnails')) {
new MultiPostThumbnails(
array(
'label' => 'Secondary Image',
'id' => 'secondary-image',
'post_type' => 'page'
)
);
new MultiPostThumbnails(
array(
'label' => 'Tertiary Image',
'id' => 'tertiary-image',
'post_type' => 'page'
)
);
}
It seems to me it would just be a simple case of wrapping this in a check so that it only runs for a specific page ID, but I'm not quite sure how to go about doing that.
This is probably somewhat of a hack. To my knowledge post/page id's are not accessible from inside functions.php.
// get the id of the post/page based on the request uri.
$url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$post_id = url_to_postid($url);
// the id of the specific page/post.
$specific_post_id = 3;
// check if the requested post id is identical to the specific post id.
if ($post_id == $specific_post_id) {
if (class_exists('MultiPostThumbnails')) {
new MultiPostThumbnails(
array(
'label' => 'Secondary Image',
'id' => 'secondary-image',
'post_type' => 'page'
)
);
new MultiPostThumbnails(
array(
'label' => 'Tertiary Image',
'id' => 'tertiary-image',
'post_type' => 'page'
)
);
}
}
This is also probably a hack but it worked for me. I got stung by the AJAX 'post_id' back to the admin page once the image has been selected. My usage was for a slug but the function could easily be modified for a post ID.
function is_admin_edit_page( $slug ){
if( ( isset($_GET) && isset($_GET['post']) ) || ( isset($_POST) && isset($_POST['post_id']) ) )
{
$post_id = 0;
if(isset($_GET) && isset($_GET['post']))
{
$post_id = $_GET['post'];
}
else if(isset($_POST) && isset($_POST['post_id']))
{
$post_id = $_POST['post_id'];
}
if($post_id != 0)
{
$c_post = get_post($post_id);
if( $c_post->post_name == $slug )
{
return true;
}
}
}
return false;
}
if( is_admin_edit_page('work') ) {
new MultiPostThumbnails(
array(
'label' => 'Hero 1 (2048px x 756px JPEG)',
'id' => 'am-hero-1',
'post_type' => 'page'
)
);
}
I'm working on a drupal 7 module, where I wish to print out infos on a page (MENU_LOCAL_TASK node/%node/something), with ajax filters.
I created a form and added 2 checkboxes, 1 is on default other is not. I want to show to the user the information according to wich checkbox is checked. 1 is on table row 1 is displayed, 2 is on table row 2 is displayed. If some of it is off, than that table row is off. Did I mentioned, that I want to solve it without submit and reload, only ajax.
I added to the two 'checkbox'es the following 'ajax' => array('callback' => 'my_module_callback')
. Here is the rest of the code, simplefied.
function my_module_callback($form, $form_state) {
$data = array();
$nid = 1;
if ($form_state['values']['checkbox1']) {
$data += load_data($nid, "checkbox1");
}
if ($form_state['values']['checkbox1']) {
$data += load_data($nid, "checkbox2");
}
$commands[] = ajax_command_html("#here", my_module_table($data));
return array('#type' => 'ajax', '#commands' => $commands);
}
function my_module_table($data){
//do some stuff with the data in a foreach
return theme("my_module_fancy_table",array("data" => $data));
}
function theme_my_module_fancy_table($data){ //registered with my_module_theme()
// putting html into $output in a foreach
return $output;
}
function my_module_page_callback_from_menu_function($nid){
$output = drupal_render(drupal_get_form('my_module_custom_ajax_form'));
$output .= "adding other stuffs including div#here";
return $output;
}
First of all is this the 'good way' to do this, cause I kind of lost confident:)
Second question, how to show the data on page load, rigth now one checkbox needs to be changed to see some infos.
Thanks and sorry for the short description :)
You should not really be doing the processing in the callback, it should be done in the form building function. The callback usually only returns the part of the form that has changed. Also, I don't think there is not need for setting commands[] in this case as returning part of the form will automatically replace the content set by 'wrapper'.
function my_module_form($form, $form_state){
$data = array();
$nid = 1;
if ($form_state['values']['checkbox1']) {
$data += load_data($nid, "checkbox1");
}
if ($form_state['values']['checkbox2']) {
$data += load_data($nid, "checkbox2");
}
$form = array();
$form['checkbox1'] = array(
'#type' => 'checkbox',
'#ajax' => array(
'callback' => 'my_module_callback'
'wrapper' => 'mydata',
'event' => 'change',
),
);
$form['checkbox2'] = array(
'#type' => 'checkbox',
'#ajax' => array(
'callback' => 'my_module_callback'
'wrapper' => 'mydata',
'event' => 'change',
),
);
$form['mydata'] = array(
'#prefix' => '<div id="mydata">',
'#suffix' => '</div>',
'#markup' => my_module_table($data),
);
return $form;
}
function my_module_callback($form, $form_state){
// $form_state['rebuild'] = true; may have to be set because the form has not been submitted and wont be rebuilt...I think, I cant remember for sure.
return $form['mydata'];
}
To show the data on page load, you just have to change the logic of setting data in the form build function.
Also, fyi, there is a stack site specifically for drupal at: http://drupal.stackexchange.com