Doesn’t seem to add anything in the second repeater using that. The only way I can get it to add rows is using the code in my question. It feels extremely close but it’s just not iterating through the last repeater, so the first row is always copied over.
The problem here is that you have multiple nested queries. This comes up often. When you have multiple nested queries you cannot loop over the posts in the 3rd, 4th, etc, queries using the standard WP loop
while ($query->have_post()) {
.... etc
The reason for this is that wp_reset_postdata()
always resets the query and the post data to the main WP query and not the previous WP query.
see my notes and changes
<?php
get_header();
// main WP query happens before here
// this is the 1st nested query
$args = array(
'post_type' => 'sm_indexsections',
'posts_per_page' => -1
);
$query = new WP_Query($args);
// The custom post type loop
if($query->have_posts()):
while ($query->have_posts()):
$query->the_post();
// ACF Flexible content loop
if(have_rows('acfIndCont')) {
while(have_rows('acfIndCont')) {
the_row();
switch(get_row_layout()) {
// Show blog-posts where a certain criteria is met
case 'acfIndSecBlogPosts':
// This is the second nested query
$postsArgs = array(
'posts_per_page' => 5,
'post_type' => 'post',
'meta_key' => 'acfKnowHubPostCaseShow',
'meta_value' => true
);
$postsQuery = new WP_Query($postsArgs);
// you must access the posts in this query useing other methods
/* NOT THIS
if($postsQuery->have_posts()) {
while($postsQuery->have_posts()) {
$postsQuery->the_post();
// Display all blog-posts where this criteria is met
}
}
END NOT THIS */
// USE SOMETHING LIKE THIS
// also do not use the global $post varaible
if (count($postsQuery->posts)) {
foreach ($postsQuery->posts as $nested_post) {
// and exampele
echo get_the_title($nested_post->ID);
}
}
// DO NOT USE EITHER OF THESE FUNCTIONS
// IN 2ND AND AFTER NESTED QUERIES
// wp_reset_query();
//wp_reset_postdata();
break;
// To put this (or other flexible content) before the custom loop works.
//But not after.
// there is no loop inside this case.
case 'acfIndSecBtn':
// Other content to display
break;
}
}
}
endwhile;
endif;
wp_reset_postdata();
get_footer();
?>
"save_post_{$post_type}"
runs before "save_post"
. ACF does not set the values of the field until the second hook. So you are trying to get values from a field that has not been set yet. When you update the post the second time (and after) you are using values that were saved the previous time the the post was saved and it will not reflect changes correctly if the first image in the gallery has been changed.
Whenever dealing with ACF you need to use the acf/save_post hook. https://www.advancedcustomfields.com/resources/acf-save_post/
I know that you are trying to limit your filter to a specific post type, but you can do this in your acf/save_post filter
add_action( 'acf/save_post', 'set_featured_from_gallery', 20);
set_featured_from_gallery ($post_id) {
if (get_post_type($post_id) != 'galleries') {
return;
}
// code continues...
}
I had to rework my code and use some of new 5.7 JavaScript APIs. For example they changed acf.prepare_for_ajax
to acf.prepareForAjax
. The way the data element is constructed also need changing as well as abandoning the acf.ajax.extend methodology.
Below is an attempt to rewrite your example using what I did as a basis. I make no guarantees that all my changes will make your example work, but they got my code back up and running. Hope it helps. I also left both the ready and change events in there in case people wanted to use e.type
to branch the code based on the event type.
jQuery(document).ready(function($){
if (typeof acf == 'undefined') { return; }
var acf_field = acf.getField('field_56e1cf2e7ad26');
acf_field.on("change ready", function(e){
// bail early if no ajax
if( !acf.get('ajax') ) return;
// abort XHR if it's already running
if( this.request ) {
this.request.abort();
}
// set the ajax action that's set up in php
var data = {
action: 'get_sub_terms',
key_name: acf_field.val(); //The key_name needs to be the name of the parameter in your action
}
this.request = $.ajax({
url: acf.get('ajaxurl'), // ajax url, acf already sets this
data: acf.prepareForAjax(data), // another included acf function
type: 'post', // posted values, look at $_POST in your php ajax action function
dataType: 'json', // how we want the date returned
success: function( json ){
// get the field we want to populate
var $select = $('.acf-field-56e2b71dde0c5 select');
// this stores the currenlty selected value in that field
// so that we can mark it as selected, this is important when loading
// and exsiting post
var $selected = $select.val();
// clear all of the exsiting options from the field
$select.empty();
// rebuild the child select field
$select.append('<option data-i="" selected="selected" value="">- Select -</option>');
var count = json.length;
for (i=0; i<count; i++) {
var $item = '<option value="'+json[i]['value']+'"'
if ($selected == json[i]['value']) {
$item += ' selected="selected"';
}
$item += '>'+json[i]['label']+'</option>';
$select.append($item);
}
}
}); // end ajax
});
// trigger the ready action to load the field
// this is important for existing posts
$('#acf-field_56e1cf2e7ad26').trigger('ready');
});
What is the return value of the field set to? Your code assumes values, but you might want to check it. It could be somewhere in you if/elseif statements.
I would simplify it like this
<?php
if (have_rows('add_info_exam_grid')) {
$addclassexamgrid = array();
$checkboxarray = get_field('add_age_exam_grid');
foreach ($checkboxarray as $value) {
switch ($value) {
case 'Option1':
$addclassexamgrid[] = 'class1';
break;
case 'Option2':
$addclassexamgrid[] = 'class2';
break;
case 'Option3':
$addclassexamgrid[] = 'class3';
break;
} // end switch
while (have_rows('add_info_exam_grid')) {
the_row();
?>
<div class="grid-item <?php echo implode(' ', $addclassexamgrid); ?> col-md-4 col-sm-6 col-xs-12">
//Some code here
</div>
<?php
} // end while have_rows
} // end foreach
} // end if have_rows
?>
Hi HQ80, thanks for your reply. I had deleted the new field right after the problem occurred, in case it was something about the field that I had messed up. Not sure how to go about getting the code for that page.
In any case, it turned out that the template used for that page just needed to be reset in WordPress and the problem was solved. I’m not sure why or how the template got changed, but that’s what finally solved the problem.
So it may or may not have been anything to do with ACF. Maybe by changing the field group for that page (adding, then deleting, a new field), it reset a bunch of settings, including the page template? There were other settings for the field group that had to be reset too, as I recall.
Thanks very much for offering your help.
It appears that just updating acf.ajax.extend
to acf.Field.extend
fixes the problem. Thanks for your quick response.
Apologies if this is a duplicate reply. The system hasn’t accepted previous submits.
__
I instead chose to run the __search_by_title_only function only when a user is logged in, which satisfies my requirement.
Here’s the exact code I used:
function __search_by_title_only( $search, $wp_query )
{
global $wpdb;
$q = $wp_query->query_vars;
$n = ! empty( $q[‘exact’] ) ? ” : ‘%’;
$search =
$searchand = ”;
foreach ( (array) $q[‘search_terms’] as $term ) {
$term = esc_sql( like_escape( $term ) );
$search .= “{$searchand}($wpdb->posts.post_title LIKE ‘{$n}{$term}{$n}’)”;
$searchand = ‘ AND ‘;
}
if ( ! empty( $search ) ) {
$search = ” AND ({$search}) “;
}
return $search;
}
// call this function ONLY if user is logged in (e.g. admin, editor)
if ( is_user_logged_in() ) {
add_filter(‘posts_search’, ‘__search_by_title_only’, 500, 2);
}
FYI:
I omitted the “&” from the 2nd parameter because it was throwing errors sitewide: “Parameter 2 to __search_by_title_only( ) expected to be a reference”
Produced errors:
function __search_by_title_only( $search, &$wp_query )
Working:
function __search_by_title_only( $search, $wp_query )
Thanks for your answer.
Hmm. Nope. didn’t work.
Forgot to mention that i did try to move the call inside if($postsQuery->have_posts())
earlier.
Thanks for your help John.
I’m marking your response as resolved because it’s helped me come to a decision, and made me realise how the site should have been built. However, due to time restraints I can’t explore your proposed solutions, and I don’t think the time it would take me to find the best alternative (potentially a day) would be re-gained in any time saved in future deployments.
I don’t get how currently what I do works and copied changes from site to site are isolated between database tables but when you apply json sync it overwrites due to the field groups being the same.
After much thought it’s evident that the structure of the field groups in the site I’m working on is beyond repair unless I wanted to do a couple of days data entry in re-creating posts after re-grouping fields and then re-write all the field name look ups in the php, not to mention all the testing.
The site was built a couple of years ago (not by me), quite a lot of posts have been created using field groups that have been shared and subsequent fields have been added in the same field group. In the main post there are 22 fields in one field group for instance, and in the parallel site there are 25. When I activated json sync I was horrified to find that it overwrote the fields and subtly different settings such as number of rows in repeater fields and which fields were set as required. This is because from the very beginning the ACF export option had been used to copy across fields and then the appropriate modifications were made, all in complete isolation. It appears you have more control over manually copying and then modifying/removing field groups between sites from the out of the box database way than using json sync. I guess this is because ACF is not built in mind for multisite and any solutions for multisite are work arounds.
To summarise, the option of splitting unique field groups up by defining them within child themes could work, but in my circumstance of just two sites (but potentially more in time) and 90% of field groups being shared, but with subtle differences I’ll have more control doing things the manual way and double checking than relying on json sync.
The lesson learned would be – when working on future sites (especially wordpress multisite) – to make field groups as smaller, more portable, specific use components rather than cramming everything into one mega field group. Same principle as when writing functions when programming!
You could wrap it in an if
statement to check if $projects
is empty.
<?php
$projects = get_field('projects');
if ( $projects ) :
?>
<h2>Supported projects</h2>
<hr class=”bg__yellow”>
<div class="posts">
<?php foreach( $projects as $post) : setup_postdata($post); ?>
// Your code here...
<?php endforeach; wp_reset_postdata(); ?>
</div>
<?php endif; ?>
Try moving your wp_reset_postdata()
calls inside your if($query->have_posts())
and if($postsQuery->have_posts())
statements.
https://developer.wordpress.org/reference/functions/wp_reset_postdata/#comment-2325
Have you tried debugging this by removing the “tax_query” and then removing the “meta_key” and “orderby” to see if the query is returning all the properties you’re expecting?
This should help push you in the right direction.
https://www.advancedcustomfields.com/resources/query-posts-custom-fields/
Hello,
If you would like to use custom post fields (generated by ACF) in URL structure without modyfying the WP_Query you can use the plugin:
https://github.com/athlan/wordpress-custom-fields-permalink-plugin
Cheers,
Athlan
Yes,
The way I would do this is first add a filter for ACF, in that filter I would add the filter than modifies the query, then after modifying the query I would remove the filter something like this.
add_filter('acf/fields/post_object/query/name=your-field-name', 'modify_my_field_name_query', 10, 3);
function modify_my_field_name_query($args, $field, $post_id) {
//using the example from the original link I posted
add_filter('posts_search', '__search_by_title_only', 500, 2);
return $args
}
function __search_by_title_only($search, $wp_query) {
remove_filter('posts_search', '__search_by_title_only', 500, 2);
/// modify the query as shown in the other post
return $search
}
Hi John, thanks for your reply. I misspoke. It’s not searching meta but rather post_content, and I ONLY want it to search post_title.
Can I alter the query to only apply to ACF cases (acf/fields/post_object/query)? I certainly don’t want to alter it sitewide.
I don’t know why it is searching meta values as well as the title and content. This usually requires a plugin or adding code to the WP search query to include meta values. Whatever is causing this it is not ACF.
post_content and post_title are not meta values and are stored in the post table.
ACF uses a standard WP search query to return values when searching. This automatically searches the title and content. You would need to alter this query using the filter you suggest. There are examples of this, for example this one https://stackoverflow.com/questions/9468804/make-wordpress-search-only-in-post-title. All of the examples require altering the actual SQL query and not the WP_Query().
No there isn’t. ACF uses use update_post_meta() or update_metadata(). Every update requires at least 2 db queries and sometimes 3. This means that it can take 4 to 6 queries to update a single ACF field.
WP does not supply any way to update more than a single meta value at a time in a single query.
There is a way, but it would require accessing the database directly and constructing the correct SQL query to do multiple updates or inserts to the correct table in a single query. You would also need to account for both the field value and the acf field reference value as well.
You should probably submit this as a bug by opening new support ticket for his here https://support.advancedcustomfields.com/new-ticket/ or maybe here https://www.advancedcustomfields.com/contact/, not quite sure which is the right one to use any more with recent changes to the site.
I don’t think that there is any way to do this, but to be honest, I don’t know.
You would somehow need to capture the user ID that was just created and before ACF tries to save the values.
I don’t know if any of this will work.
use "new_user"
for the post ID in your form.
https://www.advancedcustomfields.com/resources/acf-pre_save_post/
// i'm using a class here so I can store a value in one action/filter
// and retrieve it in a different action/filter
// you could also do this using a global variable
class my_custom_acf_save_user_form {
private $user_id = false; // will store the user id just saved
public function __construct() {
add_action('user_register', array($this, 'user_register', 10, 1);
add_filter('acf/pre_save_post', array($this, 'pre_save_post', 10, 1);
} // end __construct
public function user_register($user_id) {
// capture the id of the user just created
$this->user_id = $user_id;
} // end user_register
public function pre_save_post($post_id) {
if ($post_id == 'new_user' && $this->user_id) {
$post_id = 'user_'.$this->user_id;
}
return $post_id;
} // end pre_save_post
} // end class
new my_custom_acf_save_user_form();
Either there is a JavaScript error or there is a PHP error during the AJAX request that is interfering with the request or the response of the request. You need to track down where the error is happening. It you don’t see a JS error then use the WP error log to look for errors during the AJAX request https://codex.wordpress.org/WP_DEBUG
Hi, could you tell me what is 10 & 3 from
add_filter( 'acf/fields/post_object/query/name=my_field', [ $this, 'my_filter' ], 10, 3 );
It’s a year later but thought I’d post a solution – was trying to combine the wrong methods, and it’s actually a lot simpler than I’d thought. The issue was knowing how to insert the style select, but I didn’t how it was referred to in ACF arrays.
1) Find out the names of the toolbar elements for reference later on. In functions.php add the following:
add_filter( 'acf/fields/wysiwyg/toolbars' , 'my_toolbars' );
function my_toolbars( $toolbars )
{
echo '< pre >';
print_r($toolbars);
echo '< /pre >';
die;
}
2) Look at the bottom of the WP admin area and copy the array into a text file for future reference. We now know the names of the buttons.
< pre >Array
(
[Full] => Array
(
[1] => Array
(
[0] => formatselect
[1] => bold
[2] => italic
[3] => bullist
[4] => numlist
[5] => blockquote
[6] => alignleft
[7] => aligncenter
[8] => alignright
[9] => link
[10] => unlink
[12] => spellchecker
[14] => wp_adv
)
[2] => Array
(
[0] => styleselect
[1] => strikethrough
[2] => hr
[4] => removeformat
[5] => charmap
[6] => outdent
[7] => indent
[8] => undo
[9] => redo
)
[3] => Array
(
)
[4] => Array
(
)
)
[Basic] => Array
(
[1] => Array
(
[0] => bold
[1] => italic
[2] => underline
[3] => blockquote
[4] => strikethrough
[5] => bullist
[6] => numlist
[7] => alignleft
[8] => aligncenter
[9] => alignright
[10] => undo
[11] => redo
[12] => link
[13] => unlink
[14] => fullscreen
)
)
)
< /pre >
3) Remove or comment out the code above that displays the button array. Create your new custom toolbar.
add_filter( 'acf/fields/wysiwyg/toolbars' , 'custom_toolbars' );
function custom_toolbars( $toolbars )
{
// Add a new toolbar called "Custom" - only 1 row of buttons
$toolbars['Custom' ] = array();
$toolbars['Custom' ][1] = array('styleselect','bold','italic');
// return $toolbars
return $toolbars;
}
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.