[Resolved] Testing the value of checkbox in intermediate post for many-to-many relationship
This thread is resolved. Here is a description of the problem and solution.
Problem: I would like to display a View of related posts in an M2M relationship. There is checkbox in the intermediary post type (in the relationship) and I would like to display only those related posts where the checkbox is unchecked.
Solution: This requires some custom code, and it also requires that the View is configured to display the intermediary post type. Add the following code snippet to filter by an unchecked checkbox:
add_filter( 'wpv_filter_query', 'unchecked_ints',99,3 );
function unchecked_ints( $query_args,$views_settings, $view_id) {
global $post;
$field_slug = 'release-artist-checked';
$views = array( 48324 ); /* "Collaborations" */
$ints_args = array(
'query_by_role' => 'child',
'role_to_return' => 'intermediary',
'limit' => 1000,
'offset' => 0,
'args' => [
'meta_key' => 'wpcf-' . $field_slug,
'meta_value' => 1,
'meta_compare' => '='
]
);
$ints = toolset_get_related_posts( $post->ID, 'supporting-artists', $ints_args);
// you should not need to edit below this line
if ( in_array( $view_id, $views ) ){
$query_args['post__not_in'] = $ints;
}
return $query_args;
}
I have two Relationships set up between 'Albums' (WooCommerce Products) and 'Artists' (Toolset CPT). The Relationship between Artlsts and their own albums - "Release Artist" - is one-to-many. Artists also sometimes perform on each other's albums, so the second Relationship - "Performing On" is many-to-many, and has an intermediate post 'support-artist' with a checkbox field 'Release Artist' that stores the value 1 if checked.
On the template for the single 'Artist' posts I have two views. The content for the 'Artist's Releases' View's loop is the 'Release Artist' relationship. The content for the 'Collaborations' View's loop is the 'Performing On' relationship and needs a second filter to not include albums where the Artist is the 'Release Artist', i.e. the intermediate post checkbox field needs to be unchecked.
I've tried all sorts of combinations of string, number, and boolean field types; equals, not like, less than, etc.; 0, 1, empty - everything I've tried as a second filter has resulted in "no items displayed".
Can you steer me in the right direction to get a View that displays Albums that an Artists plays on that are not the Artist's own Albums?
Hello, it sounds like you want a way to filter out posts that have a value of '1' for this custom field, and only show posts that have no value set for this custom field. There is a quirk in WordPress custom field queries that makes this type of filter a bit more complex than others, because normally if you're testing for a custom field value and a post has no value for that custom field, that post is excluded from the query results. That's exactly what you want to accomplish though - get those posts with no value. So you'll need a small custom code snippet to overcome this quirk. I assume this View is set up to query the intermediary post type. If not, you should select the intermediary post type in the Content Selection area of this View. After that, you should add a Query Filter based on the checkbox field, and configure it as shown in the screenshot here - testing for a Constant value of 1 as a string. This screenshot is from a View built in the Block Editor, but you could do something similar in the legacy editor if you've built your View using the legacy editor experience.
I assume this is the only custom field-based Query Filter applied in the View? If not, I'll need more information about the other custom field Query Filters applied here to give you an updated snippet.
Once you've added the custom field Query Filter, the following custom code can be used to display only those posts with no checkbox checked:
/* ----------------------------------------------------------------
* FILTER INTERMEDIARIES BY UNCHECKED CHECKBOX
* In a View of Ints, only show ints with an unchecked checkbox field
* Toolset Support forum ticket: https://toolset.com/forums/topic/testing-the-value-of-checkbox-in-intermediate-post-for-many-to-many-relationship/
*/
add_filter( 'wpv_filter_query', 'unchecked_ints',99,3 );
function unchecked_ints( $query_args,$views_settings, $view_id) {
$field_slug = 'your-checkbox-field-slug';
$views = array( 123, 456 );
// you should not need to edit below this line
if ( in_array( $view_id, $views ) ){
if( !isset( $query_args['meta_query'] ) ) {
$query_args['meta_query'] = array();
}
$query_args['meta_query'][0] = array(
'key' => 'wpcf-' . $field_slug,
'compare' => 'NOT EXISTS'
);
}
return $query_args;
}
Replace your-checkbox-field-slug with the slug of the checkbox field from wp-admin. Replace 123, 456 with the numeric ID of the View of intermediary posts, or a comma-separated list of IDs if there are multiple similar Views. If you have used the Block Editor to create the View, the numeric View ID will not be obvious. To find the numeric ID in this case, you must enable the legacy View editor experience temporarily. Go to Toolset > Settings > General tab and activate the option "Show both the legacy and Blocks interface and let me choose which to use for each item I build". Reload the page after the option is saved, and you'll find a new Views menu item under the main Toolset menu. Go to Toolset > Views to find a list of all the Views you've created so far, and you'll be able to find the ID corresponding to the View of intermediary posts you created in the Block Editor. After you have that ID, you can deactivate the legacy Views experience if you'd like to continue using the Block Editor exclusively for Views designs.
You can add this custom PHP snippet in your child theme's functions.php file, or you can create a new custom code snippet in Toolset > Settings > Custom Code. Set the snippet to run everywhere and be sure to activate the snippet.
Once you've added the custom code, the View should return only those intermediary posts with an unchecked checkbox. If you'd like to display information about the related post in the View instead of information about the intermediary post, use the dynamic content feature to select the related post as the source of any dynamic content you choose. For example, you could use a Single Field Block to display the related post's title and link instead of the title and link of the intermediary post. Or you could display information from custom fields in the related post if you'd prefer.
Let me know if you have questions about this setup, and I can provide additional guidance.
Hello Christian - thank you for your detailed reply. Your first assumption - regarding the View querying the Intermediate post in the Performing On Relation - is incorrect; the Content Selection is Products ('Albums'). The first query filter is the Performing On Relationship, which results in the display of all the albums performed on by the particular artist. However, the includes the artist's own albums, which is not desirable for this View, which is of 'Collaborations'. So there's a second filter for the 'Release Artist' checkbox in the Intermediate posts.
Since new Album-Artist Performing On Relationships will automatically save a '0' as the default if 'Release Artist' is not explicitly checked, I realized the test for empty only applied to my existing records, so I simply edited them with appropriate 0 or 1 values.
Since I want to display Albums where the Release Artist checkbox value is '0', I was hoping/expecting that "The field Release Artist is a String that is equal to 0" would do the trick. But it doesn't - any attempt to add a further filter results in no albums being displayed... 🙁
Your first assumption - regarding the View querying the Intermediate post in the Performing On Relation - is incorrect; the Content Selection is Products ('Albums').
Okay, understood. Regardless of how it was originally configured the instructions are still the same, for technical reasons. In order to filter any View by a custom field, that custom field must be part of the post type being queried in Content Selection. In other words, you cannot filter a View of post type "A" by a custom field in a related post type "B", it won't work for technical reasons related to how WordPress queries are implemented. To filter by a custom field from post type A, the View must be set up to query post type A. To filter by a custom field from post type B, the View must be set up to query post type B. So in your case, the View should be configured to query the Intermediary post type, regardless of the information you want to display in the list of results. So you should select the intermediary post type in the Content Selection area of the View editor. Then in the loop of the View, by default it will display information from the intermediary post type. I take it you would prefer to display information from the related post type instead. So when choosing the source for a block's dynamic content, choose the related post type in the dynamic source fields.
Since new Album-Artist Performing On Relationships will automatically save a '0' as the default if 'Release Artist' is not explicitly checked, I realized the test for empty only applied to my existing records, so I simply edited them with appropriate 0 or 1 values.
We are instructed to advise against using the option that saves 0 when a checkbox is unchecked. That option is known to cause problems in custom search filters, and I suspect it will also cause problems in conditionals. Views will interpret unchecked checkboxes as checked in some circumstances when this option is used, and our developers have determined that this issue must be resolved on the data entry side by using the default setting "When unchecked, don't save anything to the database" (screenshot here). I don't recommend changing that default selection. I wish the developers would remove the option entirely, but for now it is what it is. Erratum post about this issue: https://toolset.com/errata/checkboxes-that-save-0-are-interpreted-as-checked-by-views/
We should be able to get this working with the custom code snippet and a View of the intermediary post type, and avoid the issue mentioned in the erratum post. If you're having trouble getting the correct results, or the display of the related post type is not working as expected with dynamic sources, I will be glad to take a closer look.
Thank you very much for your help and your clear descriptions and directions. I should've paid better attention the first time round.
The View is now for the Intermediate post and your code above installed and activated in Toolset Custom Code section. The first filter is for the 'Performing On' Relationship and the second filter tests the checkbox field in the intermediate post as you described above. This now works properly on the back end, displaying only the albums where the artist is collaborating and not their own albums (screenshot 1). However, on the front end the message "No items found" still shows up (screenshot 2) 🙁
Also, how do I display the 'Release Artist' Relationship for each of the Albums in this View? And more importantly, how do I add an 'Add to Cart' button for the Albums in this View?
New threads created by Christian Cox and linked to this one are listed below:
It might be easiest if I log in and take a look to see why this is working on the backend but not the frontend. Is it possible to provide login credentials in the private reply fields here? I can take a quick look and see what is happening here, and provide more information about your recent questions.
Okay I did some digging and it seems that the way I had set up the custom code filter was not working well with intermediary post queries, so I rewrote the filter to approach the query a bit differently:
add_filter( 'wpv_filter_query', 'unchecked_ints',99,3 );
function unchecked_ints( $query_args,$views_settings, $view_id) {
global $post;
$field_slug = 'release-artist-checked';
$views = array( 48324 ); /* "Collaborations" */
$ints_args = array(
'query_by_role' => 'child',
'role_to_return' => 'intermediary',
'limit' => 1000,
'offset' => 0,
'args' => [
'meta_key' => 'wpcf-' . $field_slug,
'meta_value' => 1,
'meta_compare' => '='
]
);
$ints = toolset_get_related_posts( $post->ID, 'supporting-artists', $ints_args);
// you should not need to edit below this line
if ( in_array( $view_id, $views ) ){
$query_args['post__not_in'] = $ints;
}
return $query_args;
}
In this case, I needed to remove the checkbox Query Filter from the View settings in the Content Template editor. Now it seems to be pulling the correct results on the front-end, but unfortunately the results in the back-end are not filtered by that checkbox state. I also noticed that the Intermediary Post Type option "publicly_queryable" was unchecked, so I had to check that in order to display these results on the front-end of the site.
Next, there is a bug in the Block Editor where it is not possible to select the featured image of a related post unless the current post type also supports featured images. So I have temporarily activated featured image support in the intermediary Performing On post type here: hidden link
Also, how do I display the 'Release Artist' Relationship for each of the Albums in this View?
This would require a nested View, which cannot be built in the Block Editor. I can split this off into a separate ticket to discuss in more detail, we will need to create this in the legacy Views experience and include this View using a shortcode.
And more importantly, how do I add an 'Add to Cart' button for the Albums in this View?
The Add to Cart button does not seem to be working correctly right now in a View of the intermediary post type. I've tried a couple of different methods, including creating a separate Content Template to include the Add-to-Cart button, but in both cases it never seems to show up on the front-end. Let me escalate this to the 2nd tier support team, since it appears to be a limitation of the Block Editor and Toolset's Add to Cart block. For now, you can find the embedded Content Template for editing in the Block Editor here: hidden link
Hi Christian - I guess I'm really pushing the envelope with this project... 😉
Thank you for resolving the front-end display; I can live with the back-end being incorrect if the user gets to see the right stuff.
For displaying the Release Artist, I'm fine with giving the legacy View approach a try and will open another ticket as you suggest if I run into difficulties with that.
I cannot stress enough how critical having the 'Add to Cart' button is; if you look around the site you'll see that purchase opportunities are placed absolutely everywhere where an album cover is displayed. Please do escalate this if you haven't already - and I'm totally happy to provide FTP access or a full backup to support resolving this particular issue.
That shows the "Buy CD" button like you have displayed in other Product Views on the same page, with a bit of styling difference. I added the CSS generated for the other add-to-cart buttons like this in the Content Template's CSS editor panel to match your block editor styles:
I've taken a look and this issue is fully resolved by the Custom HTML block, shortcake, and CSS as you've implemented - thank you! And I'm totally looking forward to your help with the nested View issue in the other active support ticket.
I have another issue that I started a few weeks ago - "I need to filter Views by alphabetic groups – A-L, M-R, and S-Z" that Jamal tried to help with but didn't satisfactorily resolve except to point me at a possibly related solution that you'd authored, so I'm hoping you can pick up and help with the new issue I'm about to open: "Why is is possible to have two filter conditions for Date fields (i.e. greater than date1 AND less than date2) but not for a string field?"
I'll be glad to help on that issue but I'm a bit behind from being out yesterday, and today is the end of my work week so it may be Sunday before I can realistically get to a new ticket that has not yet been submitted. It depends on the other tickets in my queue today. Feel free to request my assistance in the new ticket if that is acceptable.