Like I said, WordPress create URLs for each of the terms in the taxonomy. These pages automatically only show the posts for a specific term.
What you are doing here is overriding the query that WP has already done
$posts = get_posts(array(
'post_type' => 'dt_portfolio',
'posts_per_page' => -1,
'meta_key' => 'sub_seminars_0_start_date',
'orderby' => 'meta_value',
'order' => 'ASC'
));
You either need to add a taxonomy query to this in order to recreate what WP is already doing… this is a waste, will take more work, and will slow down your site because you’re doing multiple queries when they are not needed….
… or you can set up a pre_get_posts
filter https://codex.wordpress.org/Plugin_API/Action_Reference/pre_get_posts to add the new sort order to the query that WP is already performing.
// add to functions.php
function order_dt_portfolio_by_repeater($query=false) {
// only used on main query on front end of site
if (is_admin() || !$query || !is_a($query, 'WP_Query') ||
!$query->is_main_query()) {
return;
}
// modfiy a custom post type to show and order by meta value
if (isset($query->query_vars['post_type']) &&
$query->query_vars['post_type'] == 'dt_portfolio') {
$query->set('meta_key', 'sub_seminars_0_start_date');
$query->set('orderby', 'meta_value');
$query->set('order', 'ASC');
$query->set('posts_per_page', -1);
}
} // end function blunt_pre_get_posts
add_action('pre_get_posts', 'order_dt_portfolio_by_repeater');
WP will automatically sort posts based on taxonomy/term by using the correct link and instead of creating a custom query to get the posts you use a pre_get_posts filter https://codex.wordpress.org/Plugin_API/Action_Reference/pre_get_posts.
The main problem you’re going to have is sorting the posts by a repeater field. You say that the repeater sub field is sub_seminars_0_start_date
, but if you have multiple repeater rows then you will also have sub_seminars_1_start_date
, sub_seminars_2_start_date
, sub_seminars_3_start_date
, etc. This is where you’re going to have a problem. It is virtually impossible to sort posts based on a repeater sub field value, unless you repeater will only ever have a single row or if you only care about the first row.
You can’t… Well, I should say, you can’t easily. The taxonomy field only stores the term ID. In order to get the term you must know the taxonomy, which ACF only stores as part of the taxonomy. Also, for the “Save Terms” and “Load Terms” options, the taxonomy must also be know, again, that information is not part of the value of the field.
In order to do this you’d have to have a field that includes information about the taxonomy and the term. You could possibly build this with a select field and dynamically generate the value for it https://www.advancedcustomfields.com/resources/dynamically-populate-a-select-fields-choices/, for the values you could include the taxonomy name and term ID, or you could potentially use the WP term_taxonomy_is from the wp_term_taxonomy table. But if you want to save and load these terms when saving a post you’d also need to work out the details of that yourself as well and usin wp_set_post_terms() https://codex.wordpress.org/Function_Reference/wp_set_post_terms inside of an acf/save_post filter https://www.advancedcustomfields.com/resources/acf-save_post/. If you have a lot of terms the select field would be huge.
I’m not sure what you’re asking. I will tell you that custom taxonomies can be one of the most difficult things in WP for some. But if I understand, then yes. Each select field that you want to be able to list posts for would be a different custom taxonomy.
I guess it would depend on how many of these sorting/filtering fields you want to have for these posts. If you’re going to end up with a lot of them then you might want to look into a search plugin to add to your site. https://facetwp.com/ is a fairly good one and there are others if you look. It really depends on how complicated it will be and what you want for the end result.
Thanks John! I looked into your links.
Do I need to register a taxonomy for every “dropdown”-field I have in my ACF? In this case I need to fill out twice everything, correct? So for example I have to select “2 years” in the acf-dropdown and select “2 years” in my newly created taxonomy “duration”.
There is not template in WP that will show a list of posts based on an ACF field. For this you would need to create your own query and url to use. This is explained in the Dynamic $_GET parameters section of this page https://www.advancedcustomfields.com/resources/query-posts-custom-fields/
Since you are new to WP I will make a suggestion. WP has a build in mechanism for doing what you want to. https://codex.wordpress.org/Function_Reference/register_taxonomy.
Hi John,
I have tried the code outside the loop and it is working.
It seems that I was missing a global variable in my function, hence why the previous code was returning NULL. However this still does not solve my problem..
I have isolated some of the code to identify what could possibly my error. I am now sure it is not taxonomy issue as you suggested.
The foreach loop is repeating the last item of the array. I am not sure if this has anything to do with ACF but I followed instructions from other forums regarding the rest of the code and it still does not work. Hope you can help.
$select_fields = array( 'boat-bow', 'boat-stern',);
foreach ( $select_fields as $var ){ add_filter('acf/load_field/name=' . $var . '-select', load_select ); }
function load_select () {
global $var;
var_dump($var);
}
I believe that ‘catagory_name’ only works with posts in the ‘Post’ post type, but I could be wrong.
Have you attached the standard WP category taxonomy to the custom post type?
Thanks Edir, you’ve inspired me to write a more general version.
This version is based on “post_id” provided by ACF, which is actually an ACF-post_id (not a real wp_posts table ID)
and also based on get_field()
documentation, which is pretty steady in ACF-post_id formats.
(Dis)advantage: uses global variable for taxonomy caching.
//returns BOOLEAN false or a term id (int)
$GLOBALS['taxonomies_for_acf_term_meta_filter'] = [];
function my_acf_get_term_id_from_acf_post_id( $acf_post_id ){
if( is_numeric($acf_post_id) ) return false; //not a special ID...
if( empty($GLOBALS['taxonomies_for_acf_filter']) )
$GLOBALS['taxonomies_for_acf_filter'] = get_taxonomies();
$taxonomies = &$GLOBALS['taxonomies_for_acf_filter']; //shorthand
$term_id = 0;
foreach( $taxonomies as $taxonomy ){
//special id is in format somestring_ID
$term_id = str_replace( $taxonomy.'_', '', $acf_post_id );
//If not numeric at all, or something is removed while cast type to int (must use "!=" !!!!!)
//=> not a proper ID... => nothing to do here...
if( ! is_numeric($term_id) || ( ((int)$term_id) != $term_id ) ) continue;
$term_id = (int)$term_id;
break;
}
if( $term_id < 1 ) return false; //not a proper ID...
return $term_id;
}
function my_acf_update_term_meta($value, $post_id, $field) {
//FILTER! ===> MUST ALWAYS RETURN $value !
$term_id = my_acf_get_term_id_from_acf_post_id( $post_id );
if( $term_id === false ) return $value; //not a proper ID... MUST USE "===" !!!!!
update_term_meta($term_id, $field['name'], $value);
return $value;
}
add_filter('acf/update_value', 'my_acf_update_term_meta', 10, 3);
add_filter('acf/update_value', 'my_acf_update_term_meta', 10, 3);
function my_acf_load_term_meta($value, $post_id, $field) {
//FILTER! ===> MUST ALWAYS RETURN $value !
$term_id = my_acf_get_term_id_from_acf_post_id( $post_id );
if( $term_id === false ) return $value; //not a proper ID... MUST USE "===" !!!!!
$value = get_term_meta($term_id, $field['name'], true);
return $value;
}
add_filter('acf/load_value', 'my_acf_load_term_meta', 10, 3);
add_filter('acf/load_value', 'my_acf_load_term_meta', 10, 3);
Thank you very much. I’m doing a similar thing at the moment:
– I use ACF hooks in order to run callback when an image field value is changed
– Then I read the image field data,
– Then I read the EXIF data from the image field,
– Then I update other fields values based on the EXIF data.
So, as long as it’s just a matter of updating existing fields, it’s not a big problem. But if I wanted to create some taxonomy fields on the fly, then it would be tricky, because there’s no way to re-render a field and/or a field group.
I will probably end up with writing my own ACF custom field that will handle taxonomies update. This will be a simpler and a cleaner solution because I want to have as little DOM traversing in my code as possible. I don’t want my code to break if ACF HTML structure will change.
Thank you for your help anyway! I hope that in future ACF will be more flexible in this area. I would love to see a real, non-DOM-dependant JS API. I would love to be able to render ACF fields anywhere, with a custom HTML templates, etc. But I know how much work it requires. At the moment this part of ACF code is very monolithic and DOM-oriented. I dream of a MVC-like architecture where models and collections are separated from the view layer (like Backbone Models/Collection/Views).
If it was implemented then ACF could become something more than just a powerful custom fields manager. If ACF has such an API and architecture then It would be even possible to build something like a page builder (like visual composer) on top of ACF!
Sorry for the offtopic 🙂
This is a piece of code I wrote:
import $ from 'jquery';
export default () => {
const $$ = {
template: $('#js-exif-data')
};
const nonce = $$.template.data('nonce');
// ACF fields identifiers
const imageInputName = 'acf[field_56d99d7de115e]';
const orientationInputName = 'acf[field_56d8b43f73de1]';
const allowedExifData = {
created_timestamp: 'Data',
iso: 'ISO',
focal_length: 'Ogniskowa',
shutter_speed: 'Migawka',
aperture: 'Przysłona',
keywords: 'Tagi'
};
const utils = {
/**
* @param {int} exifValue
* @returns {string} - <code>landscape</code> or <code>portrait</code>
*/
getOrientationByExifValue(exifValue) {
const orientations = [{
name: 'landscape',
values: [1, 2, 3, 4]
}, {
name: 'portrait',
values: [5, 6, 7, 8]
}];
const orientation = orientations
.filter(orientation => {
return orientation.values.indexOf(exifValue) !== -1;
});
return orientation.length === 0 ? 'unknown' : orientation[0].name;
},
/**
* @param {int} attachmentId
* @returns {Promise}
*/
loadExifData(attachmentId) {
return $.ajax({
type: 'post',
url: acf.o.ajaxurl,
data: {
nonce,
attachmentId,
action: 'get_image_meta',
}
});
},
/**
* @returns {Promise}
*/
getOrientationTerms() {
return $.ajax({
type: 'post',
url: acf.o.ajaxurl,
data: {
nonce,
action: 'get_orientation_terms',
}
});
},
/**
* @param {object} response
*/
renderExifData(response) {
$.each(response.data, (k, v) => {
if (!allowedExifData.hasOwnProperty(k)) {
return true;
}
// Convert array values to coma-separated string.
const value = (function () {
if (v.constructor === Array) {
return v.join(', ');
}
return v;
}());
const $p = $('<li />').html(<code><strong>${allowedExifData[k]}</strong>: ${value}</code>);
$$.template.append($p);
});
},
/**
* @param {object} response
*/
setOrientation(response) {
if (!response.data.hasOwnProperty('orientation')) {
return;
}
utils.getOrientationTerms().done(termsResponse => {
const wpTerms = termsResponse.data;
if (wpTerms.constructor !== Array) {
return;
}
const exifOrientation = parseInt(response.data.orientation, 10);
const orientationHumanReadable = utils.getOrientationByExifValue(exifOrientation);
const wpTerm = wpTerms.filter(term => {
return term.name === orientationHumanReadable;
});
if (wpTerm.length === 0) {
return;
}
const orientationTermId = parseInt(wpTerm[0].id, 10);
const $radios = $(<code>input[name="${orientationInputName}"]</code>);
const $radio = $radios.filter((index, el) => {
return parseInt($(el).val(), 10) === orientationTermId;
});
$radio
.prop('checked', true)
.trigger('click')
});
}
};
const handlers = {
imageOnLoad() {
const $input = $(<code>input[name="${imageInputName}"]</code>);
const attachmentId = $input.val();
if ($input.length === 0 || attachmentId === '') {
return;
}
$$.template.empty();
utils.loadExifData(attachmentId).done(utils.renderExifData);
},
/**
* @param {object} $input - an array of jQuery DOM objects
*/
imageInputOnChange($input) {
const attachmentId = $input.val();
if ($input.context.name !== imageInputName) {
return;
}
$$.template.empty();
if (attachmentId === '') {
return;
}
utils.loadExifData(attachmentId).done(response => {
utils.renderExifData(response);
utils.setOrientation(response);
})
}
};
acf.add_action('change', handlers.imageInputOnChange);
acf.add_action('load', handlers.imageOnLoad);
}
The ACF taxonomy field stores an array of term ID values, you can’t search based on the term name or use ‘=’ for the comparison.
You need to use the term id and you need to use LIKE, something like this
'search' => '*'.esc_attr( $search ).'*',
'meta_query' => array(
array(
'key' => organization_category,
'value' => '"'.$term->term_id.'"',
'compare' => 'LIKE'
)
).......
This might be helpful to you. https://support.advancedcustomfields.com/forums/topic/send-additional-field-for-ajax-update-event-on-selecttaxonomy/
Thank you.
So, this is what I ended up with. It works well.
Do you see any issues there? Thanks again.
<?php $terms = get_terms(array( 'taxonomy' => 'artist', 'meta_key' => 'hide_artist', 'meta_value' => 'active' )); ?>
<?php foreach($terms as $term) {$term_link = get_term_link( $term ); echo '<li><a href="' . esc_url( $term_link ) . '">' . $term->name . '</a></li>';} ?>
Scratch that, on closer inspection, the get_field( ‘block_links’ ) isn’t returning a post object but rather a post ID.
So, my field definition
register_field_group(array (
'id' => 'acf_page-extensions',
'title' => 'Page Extensions',
'fields' => array (
array (
'key' => 'field_5800d28886448',
'label' => 'Block links',
'name' => 'block_links',
'type' => 'post_object',
'instructions' => 'Select pages to display in link blocks.',
'post_type' => array (
0 => 'post',
1 => 'page',
),
'taxonomy' => array (
0 => 'all',
),
'allow_null' => 0,
'multiple' => 1,
),
),
'location' => array (
array (
array (
'param' => 'post_type',
'operator' => '==',
'value' => 'page',
'order_no' => 0,
'group_no' => 0,
),
),
),
'options' => array (
'position' => 'normal',
'layout' => 'default',
'hide_on_screen' => array (
),
),
'menu_order' => 0,
));
The retrieval code
$blocks = get_field( "block_links" );
Is returning a post ID rather than the post object.
My field definitions:
register_field_group(array (
'id' => 'acf_page-extensions',
'title' => 'Page Extensions',
'fields' => array (
array (
'key' => 'field_5800d28886448',
'label' => 'Block links',
'name' => 'block_links',
'type' => 'post_object',
'instructions' => 'Select pages to display in link blocks.',
'post_type' => array (
0 => 'post',
1 => 'page',
),
'taxonomy' => array (
0 => 'all',
),
'allow_null' => 0,
'multiple' => 1,
),
array (
'key' => 'field_5805e3f350cf1',
'label' => 'Latest Offers',
'name' => 'latest_offers',
'type' => 'post_object',
'instructions' => 'Select the pages you wish to link to as special offers.',
'post_type' => array (
0 => 'page',
),
'taxonomy' => array (
0 => 'all',
),
'allow_null' => 0,
'multiple' => 1,
),
),
'location' => array (
array (
array (
'param' => 'post_type',
'operator' => '==',
'value' => 'page',
'order_no' => 0,
'group_no' => 0,
),
),
),
'options' => array (
'position' => 'normal',
'layout' => 'default',
'hide_on_screen' => array (
),
),
'menu_order' => 0,
));
My display code:
$blocks = get_field( "block_links" );
foreach ($blocks as $blockPost) {
setup_postdata( $blockPost );
$blockTitle = get_field( "block_title", $blockPost->ID );
if(empty(trim($blockTitle))) {
$blockTitle = $blockPost->post_title;
}
?>
<div class="small-12 medium-6 columns post-block">
<div class="img">
<h4><a>ID); ?>" class="hvr-sweep-to-right"><?php echo $blockTitle; ?></a></h4>
</div>
</div>
<?php
$count++;
}
wp_reset_postdata();
?>
In the version you are using, terms are saved in the options table. The “option_name” will be
"{$taxonomy}_{$term_id}_{$field_name}"
If you update ACF Pro then the value will be saved in the term meta table and the “meta_key” will be the name of your field.
Is “categorias” a select field or a taxonomy field?
Does the field allow only one selection or multiple selections?
My advice would be to not use a repeater field for this. A better solution would be to create a custom post type for your press release section https://codex.wordpress.org/Function_Reference/register_post_type. This would also allow you to create a custom taxonomy in order to categorize your posts if you wanted https://codex.wordpress.org/Function_Reference/register_taxonomy. While adding custom rewrite rules and other code you could also create date archives for your custom post type, with a little work or could even be done using a plugin https://wordpress.org/plugins/custom-post-type-date-archives/
If you really want to use a repeater for this I would suggest looking at this turorial https://www.advancedcustomfields.com/resources/how-to-sorting-a-repeater-field/. Creating date archives for a repeater field is going to be complicated. I would suggest starting with the Dynamic $_GET parameters section of this page https://www.advancedcustomfields.com/resources/query-posts-custom-fields/
Thanks for this @radgh . I used the ‘select2_ajax_data’ filter and checked the data.field_key to determine what field triggered the event and when to pass additional params:
if (typeof acf !== 'undefined') {
acf.add_filter('select2_ajax_data', (data) => {
// field_587843455430a is the post select field containing the model
if (data.field_key === 'field_587843455430a') {
// field_586ac90dfd68b contains the make value
data.make_id = document.getElementById('acf-field_586ac90dfd68b').value;
}
return data;
});
}
Then on the backend I used the ‘acf/fields/post_object/query/key={field_key}’ filter to filter the post query based on make_id:
// filter models based on their make meta_field
function my_post_object_query( $args, $field, $post_id ) {
$make_id = $_POST['make_id']; //the new query param from client-side
// add additional constraints to the post query
$args['meta_key'] = 'make';
$args['meta_value'] = $make_id;
return $args;
}
add_filter('acf/fields/post_object/query/key=field_587843455430a', 'my_post_object_query', 10, 3);
I’m using a slightly different approach since I’m using a separate post object field for make but should work similarly with Taxonomy Relationship fields if you use the ‘acf/fields/taxonomy/query’ filter on the backend instead.
No.
You cannot make permalinks like this and mix “/“, “–” without heavy PHP magic.
/grade-11/english to /grade-11/english-eng3u. Your field value (all of them) would need to have this sign “-“.
You dont have so many language courses ? Even theoretically it they are in hundreds WordPress now manages Terms well. As long you dont use to many Select (dropdown) options Terms on shared hosting.
Do it like this:
– “grade 11” = (you use WooCommerce) Products main Category/Taxonomy/Term.
– “english” = subcategory/Term of “grade11” main category.
– “eng3u” = sub category/Term of “english” subcategory.
Just simple nested categories and Terms. This is very reusable everywhere in WordPress for future website building, not like using fields. And Permalinks are generated naturally, no tweaks needed.
Still dont get it.
It looks like perfect job for Terms (taxonomy terms). You can use ACF for other manipulation(s), Therms select field, etc…
You dont have to worry about permalinks when working with Terms.
Hi John
Thanks for posting. I should have posted here a little earlier. I also posted this to WordPress yesterday and got a resolution. I think there is a bug in WordPress which is evident when comparing custom fields and custom taxonomies that contain an apostrophe:
https://wordpress.org/support/topic/wp_query-custom-taxonomy-custom-field-comparison/#post-8806020
Applying the code from the above thread to the WordPress core (I know … but it makes sense) fixed my issue alongside wrapping the return I get from an ACF field in html_entity_decode before comparing it with my custom taxonomy.
Once again, thank you.
Regards
Mike
It works now. I peeked a bit in the code of the class \acf_form_taxonomy
. There are two methods which were added since version 5, add_term
and edit_term
. There are also two actions defined in which you can hook into: {$taxonomy}_add_form_fields
and {$taxonomy}_edit_form
.
I am using now the actions to determine which term is currently viewed. I save the current term in the $GLOBAS
variable (current hotfix only).
Next, I am using the acf/load_field/name={$field_name}
filter which checks the previously defined term in the $GLOBALS
variable on which level we are and to determine if we display the current field or not.
Hey John, thanks for your reply.
I meant term, not the taxonomy.
I have some required fields defined in my custom fields group. I want these fields only to be applied on the first level (Level 1
) of my terms:
* Root Term
* * Level 1 Term <- Required fields needed and displayed
* * * Level 2 Term <- Required fields not needed here but are displayed
Level 2
has to be still editable but without the required fields.
It seems that in ACF4 the custom fields for the terms were loaded over an xhr request which is no longer the case in ACF5.
ACF5 is build completely differently than ACF4. To be honest, I can’t tell by looking at the function what in ACF5 may be similar, or even if there is anything similar.
I’m not really sure what this means
to determine if a taxonomy has parents or not
Can you explain more about what you need to do and why?
Welcome to the Advanced Custom Fields community forum.
Browse through ideas, snippets of code, questions and answers between fellow ACF users
Helping others is a great way to earn karma, gain badges and help ACF development!
We use cookies to offer you a better browsing experience, analyze site traffic and personalize content. Read about how we use cookies and how you can control them in our Privacy Policy. If you continue to use this site, you consent to our use of cookies.