Skip Navigation

[Resolved] Need a more stable way of hiding CPTs that have no child relations…

This thread is resolved. Here is a description of the problem and solution.

Problem: I have a nested View structure that shows parent posts in the outer View and child posts in the nested View. In some cases the parent post has no child posts, and I would like to hide those parent posts in the results. The problem is that the nested child post View is filtered. Is there a conditional that tests whether or not a parent post contains these filtered child posts?

Solution: You can solve this without custom code by restructuring the Views. Instead of displaying the parent post information in the outer View, display the parent post information in the nested child View, inside wpv-items-found but outside wpv-loop. Use the item attribute to display parent post information with any shortcodes in the nested View, since the post context in the nested View is the context of the child posts. The syntax for the item attribute is item="@relationship-slug.parent" in a typical one-to-many (O2M) relationship.

Relevant Documentation:
https://toolset.com/documentation/programmer-reference/views/views-shortcodes/item-attribute/

This support ticket is created 2 years, 11 months ago. There's a good chance that you are reading advice that it now obsolete.

This is the technical support forum for Toolset - a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients can post in it. Toolset support works 6 days per week, 19 hours per day.

Sun Mon Tue Wed Thu Fri Sat
8:00 – 12:00 8:00 – 12:00 8:00 – 12:00 8:00 – 12:00 8:00 – 12:00 - -
13:00 – 17:00 13:00 – 17:00 13:00 – 17:00 13:00 – 17:00 13:00 – 17:00 - -

Supporter timezone: America/New_York (GMT-04:00)

Author
Posts
#2132899

Tell us what you are trying to do?
I'm looking for (possibly) a server side solution for hiding empty parents in a view...

Background: some pages use a network of nested views to output items.

Page "A" shows each Series (CPT) with all the Models (CPT) related to each Series (CPT).
Page "B" shows each Product Type (CPT) with all Series (CPT) related to each Product Type (CPT)

For Page "A"... Sometimes a Series has no active Models, which means the Series needs to be hidden
For Page "B"... Sometimes a Series has no active Models, which means the Series needs to be hidden AND if all Series in a Product Type have no active models, then the Product Type needs to be hidden.

Currently I have JS looking for an element in the loop, and if it's not there, it hides the output.

Here's an excerpt for example...

	// Look at Each element with parent-series-cpt class
	$('.parent-series-cpt').each( function() {
		// Inside this element find modelsetchildchk class, take the length and set it equal to variable existingchild
		// in other words, give the element a value if the element exist
		var existingchild = $(this).find('.modelsetchildchk').length;
		// If the variable existingchild is equal to 0 then hide the element AND add the class canned-class
		if(existingchild == 0) {
			$(this).hide();
            		$(this).addClass('canned-class');
    		}
	});

I actually got this recommendation from the Toolset forums and at first I was satisfied with it as a solution. The issue with this approach though is that JS isn't consistent for all users. Maybe a user in China has to access the site on a slow VPN. Maybe another user must disable JS for some reason. Either way it creates a jarring result.

If there isn't something I can do with these views using the standard settings, then surely there's something I can do with view settings or query args in php?

I'm open to any ideas.

Is there any documentation that you are following?
Documentation I found previously directed me to a browser side solution.

Is there a similar example that we can see?
Sure...
hidden link
Run this with JS and you'll only see M Series and D Series
Run this without JS and you'll see SS Series (empty), M Series, D Series, C Series (empty), A Series (empty)

hidden link
Run this with JS and you'll see (to sum up) three different Series under the first Product Type, and ten different Series under the second Product Type.
Run this without JS and you'll see (again, to sum up) eight different Series under the first Product Type and twelve different Series under the second Product Type.

The series are presented differently on different pages. Sorry if that throws you, but in both cases the JS is doing the same thing... looking for models belonging to the series and hiding the series when there are no models present.

As stated, I need to accomplish this with a more stable (likely server side) solution.

What is the link to your site?
hidden link

#2133517

Nigel
Supporter

Languages: English (English ) Spanish (Español )

Timezone: Europe/London (GMT+01:00)

Where you have a top-level View, e.g. to display Series (which then has nested Views for Models, etc.), it sounds like you should use a conditional shortcode for all of the output inside the loop (I guess you maybe output the Series title as a heading, then the nested Models View), where the condition checks whether the current Series has any child Models.

You could crafy quite specific custom shortcodes for that, but you could use the generic "connections" shortcode I've shared previously, details of which are here: hidden link

Register the custom shortcode, then add connections to Toolset > Settings > Frontend Content > Third-party shortcode arguments.

The wrap the output of your loop inside a conditional shortcode, using the connections shortcode as the test (the result should be > 0 to be displayed).

It should look something like this:

[wpv-conditional if="( '[connections relationship='series-model']' gt '0' )"]content goes here[/wpv-conditional]
#2133867

That makes sense and I was able to add it, but I have one snag. I should have been more clear about the 'empty' parents thing. They're not technically empty. They have relationships, but those children are filtered from the view.

For instance, connections shortcode returns a "1" for the amount of series-models relationships for that series in the loop. But in this view, we're wanting to filter out any archived and global only models from the SS Series. So SS Series on the product page has "0" models showing as they're filtered...

Select items with field:
Does this product appear on the Global Page? is a number equal to VIEW_PARAM(place)
AND
Is this product Global Page Only? is a number equal to VIEW_PARAM(global)
AND
Availability Status of Contents is a number different from VIEW_PARAM(contents)

...with shortcode attributes using the results of custom fields.

Maybe I could build all new views to output a number value for relationships filtered by this, but before I try that, is there a way to modify the connections shortcode to consider these filters?

#2133887

...I considered using the conditional with the nested view in place of the shortcode, comparing the view (which with product some result) as not equal to '' (ne '')... it didn't work. Even changed the output of the nested view to a numeric result to compare as lt / gt. Didn't work. I think maybe all the html that gets added interferes... so I definitely need a php solution.

#2134085

I can think of two ways to do this, and both are effectively done before page load so you won't need JavaScript to show and hide posts. One is a View-only solution and the other is a PHP filter.

The View-only solution only works if the nested child View is filtered so that only those children posts you want to display are included in the results. It does not work if you use conditional logic in the nested View to determine which children should be displayed. If the former is true, the nested View is truly filtered and not using conditional logic to suppress results, then you can simply display information about the parent post in the wpv-items-found block of the nested View instead of displaying it in the loop of the outer parent View. No additional PHP needed.

For example, you might have something like this in the outer View's loop:

<wpv-loop>
   [wpv-post-link] <!-- this shows information from the parent post -->
   [wpv-view name="your nested view of child posts"] <!-- this is the nested view of child posts -->
</wpv-loop>

In this case, you would remove the parent post link shortcode from the outer View loop and place it in the wpv-items-found section of the inner View instead, so that it is only displayed if the child post View returns results. Do not place it in the wpv-loop area, or it will be repeated once for each child post. Use the item attribute in the Types and Views shortcodes to point to the parent post. The syntax for the item attribute is @relationship-slug.parent for a one-to-many relationship. So in the loop editor of the nested child View, you'll have something like this:

...
[wpv-items-found]
[wpv-post-link item="@relationship-slug.parent"] <!-- this will display a link to the parent post -->
<!-- wpv-loop-start -->
<wpv-loop>
... your child view loop contents continue here...

You can review the use of the item attribute here:
https://toolset.com/documentation/programmer-reference/views/views-shortcodes/item-attribute/

The PHP filter approach uses the Views filter API wpv_filter_query_post_process to process each found parent post and determine if it has child posts that match your filtering criteria. If not, remove that parent post from the set of results. It seems like the simplest way to determine whether or not each parent post has child posts that fit the filter criteria is to call the Views API get_view_query_results and pass in the parent post ID as an argument, as well as any other shortcode attributes necessary to fetch the filtered child posts. Count the number of results to determine whether or not each parent post should be displayed. If no results are found, drop that parent from the parent query results.
Details about these PHP APIs:
https://toolset.com/documentation/programmer-reference/views-filters/#wpv_filter_query_post_process
https://toolset.com/documentation/programmer-reference/views-api/#get_view_query_results

This solution requires moderate-level PHP.

#2134125

Maybe I can do that first - views method, but I'm not sure I get it.

Obviously the example was meant to be simple, but I'm having an issue applying it to the highly complex set up. I'd like to share it with you for some more clarity and you can tell me if it's possible.

Here are the most basic views - if I can make sense of these I can do the others. Skip to the end for my questions...

1. VIEW - CPT Series - REL - CPT Product --- Loops Each Series Related to Product (product CPT in the example is Amplifiers)
2. VIEW - CPT Models -AND- CPT Sets - REL - CPT Series --- Loops Each Model and Set Related Series (Nested in loop template of View "1").

View "1" - Query Filter

Filter by post relationship or repeatable fields group owner...
Select posts in a Products Series relationship that are a related to the current post in the loop.

Custom field filter...
Select items with field:
Does this product appear on the Global Page? is a number equal to VIEW_PARAM(place)
AND
Is this product Global Page Only? is a number equal to VIEW_PARAM(global)
AND
Availability Status of Contents is a number different from VIEW_PARAM(contents)

View "1" - Loop Editor (Search and Pagination was Default - skipped)

[wpv-layout-start]
	[wpv-items-found]
	<!-- wpv-loop-start -->
	<wpv-loop>
		[wpv-post-body view_template="loop-item-in-view-cpt-series-rel-cpt-product"]
	</wpv-loop>
	<!-- wpv-loop-end -->
	[/wpv-items-found]
	[wpv-no-items-found]
		[wpml-string context="wpv-views"]
		
		[/wpml-string]
	[/wpv-no-items-found]
[wpv-layout-end]

View "1" - Loop item in VIEW - CPT Series - REL - CPT Product

[relconnections relationship='series-model'] ##this is left over from the first attempt and shows how many connections there are in total

## This is all Series information...
<div class="uk-background-default uk-width-5-6 uk-padding uk-margin uk-align-center parent-series-cpt">
    [wpv-conditional if="( '[wpv-attribute name="gbl"]' ne '1' )"]
        <a href="[wpv-post-url]?status=[wpv-attribute name="sts"]&global=[wpv-attribute name="gbl"]" class="prodfocus" style="text-decoration: none; color: #000;" aria-label="Link to [wpv-post-title] product page">
    [/wpv-conditional]
    [wpv-conditional if="( '[wpv-attribute name="gbl"]' eq '1' )"]
        <a href="[wpv-post-url]?status=[wpv-attribute name="sts"]&global=1" class="prodfocus" style="text-decoration: none; color: #000;" aria-label="Link to [wpv-post-title] product page">
    [/wpv-conditional]
    [wpv-conditional if="( '[wpv-attribute name="gbl"]' eq '' )"]
        <a href="[wpv-post-url]?status=[wpv-attribute name="sts"]" class="prodfocus" style="text-decoration: none; color: #000;" aria-label="Link to [wpv-post-title] product page">
    [/wpv-conditional]          
    [wpv-conditional if="( '[wpv-attribute name="sts"]' eq '1' )"]
        <a href="[wpv-post-url]?status=[wpv-attribute name="sts"]" class="prodfocus" style="text-decoration: none; color: #000;" aria-label="Link to [wpv-post-title] product page">
    [/wpv-conditional]
  			<div>
  				<h2 class="uk-h1 uk-text-uppercase uk-text-normal">[wpv-post-title]</h2>
				<p class="uk-width-3-4" style="font-size: 1.125rem!important;">[types field='description'][/types]</p>
  				<hr>
    		</div>
    	</a>

## And this is the nested view for Models and Sets related to the Series
		<div class="parent-prod-type-tax">
      		[wpv-view name="VIEW - CPT Models -AND- CPT Sets - REL - CPT Series"
          		wpvmodeltype="[wpv-taxonomy-title]"
          		market="[wpv-attribute name="mkt"]"
          		mkt="[wpv-attribute name="mkt"]"
          		proaudio="[wpv-attribute name="pro"]"
          		pro="[wpv-attribute name="pro"]"
          		global="[wpv-attribute name="gbl"]"
          		gbl="[wpv-attribute name="gbl"]"
          		place="[wpv-attribute name="plc"]"
          		plc="[wpv-attribute name="plc"]"
          		status="[wpv-attribute name="sts"]"
          		sts="[wpv-attribute name="sts"]"
          		contents="[wpv-attribute name="cts"]"
          		cts="[wpv-attribute name="cts"]"
          		series="[wpv-post-slug]"
  				hideit="[wpv-attribute name="hideit"]"
          	]
		</div>

## The attributes in that nested view and elsewhere get passed down from the Content Template or landing page

</div>

(Output Editor is Default - Skipped)

So for view one, I'm not seeing what you mean by post-link

View "2" - Query Filter

Filter by post relationship or repeatable fields group owner...
Select posts in Any relationship that are a related to the current post in the loop.

Custom field filter...
Select items with field:
Is this product only available in a set? is a string different from 1
AND
Availability Status is a number different from VIEW_PARAM(status)
AND
Is this product Pro Audio? is a string different from VIEW_PARAM(proaudio)
AND
Does this product appear on the Global Page? is a number equal to VIEW_PARAM(place)
AND
Is this product Global Page Only? is a number equal to VIEW_PARAM(global)

View "2" - Loop Editor (Search and Pagination was Default - skipped)

[wpv-layout-start]
	[wpv-items-found]

	<!-- wpv-loop-start -->
<div class="uk-text-left" uk-grid [wpv-attribute name="hideit"]>
	<wpv-loop>
		[wpv-post-body view_template="loop-item-in-view-cpt-models-and-cpt-sets-rel-cpt-series"]
	</wpv-loop>
</div>
	<!-- wpv-loop-end -->
	[/wpv-items-found]
	[wpv-no-items-found]
		[wpml-string context="wpv-views"][/wpml-string]
	[/wpv-no-items-found]
[wpv-layout-end]

View "2" - Loop item in VIEW - CPT Models -AND- CPT Sets - REL - CPT Series

<div class="uk-text-center modelsetchildchk uk-margin-small-top uk-padding-remove-vertical uk-padding-remove-right">
	<a href="[wpv-post-url]" class="prodfocus" style="text-decoration: none; color: #000;" aria-label="Link to [wpv-post-title] product page">
    	<div class="uk-padding-small">
      		[types field='nav-img' title='%%TITLE%%' alt='%%ALT%%' size='full' style='max-width: 100px' class='uk-padding-remove-bottom'][/types]
			<h3 class="uk-h5 uk-margin-small-top" id="heading-for-[wpv-post-slug]">[types field='model-number'][/types]</h3>
		</div>
	</a>
</div>

(Output Editor is Default - Skipped)
I hope that makes some sense.

If I understood correctly, I'm to take all View "1" content pertaining to the Series CPT (everything but the shortcode for View "2") and moving it to View "2" outside the loop (adding the item attribute to all custom fields so it knows where to pull from), but I can't see how to do that and preserve my overall structure.

Can it be done?

#2134861

Okay post-link was just an example. It looks like you build a custom link to the parent post using

<a href="[wpv-post-url]..."> ...</a>

other shortcodes instead. To implement my suggestions in this structure, you must do the following:

In the template View "1" - Loop item in VIEW - CPT Series - REL - CPT Product:
Remove everything that is currently outside the VIEW - CPT Models -AND- CPT Sets - REL - CPT Series shortcode. All that information should be moved inside the nested View, in the items found section, but outside the loop. You should be left with only the nested view shortcode now in this template:

            [wpv-view name="VIEW - CPT Models -AND- CPT Sets - REL - CPT Series"
                wpvmodeltype="[wpv-taxonomy-title]"
                market="[wpv-attribute name="mkt"]"
                mkt="[wpv-attribute name="mkt"]"
                proaudio="[wpv-attribute name="pro"]"
                pro="[wpv-attribute name="pro"]"
                global="[wpv-attribute name="gbl"]"
                gbl="[wpv-attribute name="gbl"]"
                place="[wpv-attribute name="plc"]"
                plc="[wpv-attribute name="plc"]"
                status="[wpv-attribute name="sts"]"
                sts="[wpv-attribute name="sts"]"
                contents="[wpv-attribute name="cts"]"
                cts="[wpv-attribute name="cts"]"
                series="[wpv-post-slug]"
                hideit="[wpv-attribute name="hideit"]"
            ]

In View "2" - Loop Editor, you must place everything you removed from the previous loop template. Anything that was before the nested View gets moved before the loop, and anything that was after the nested View gets moved after the loop. This maintains the nested loop integrity. Note that any wpv-attributes shortcodes from the outer loop template must now be updated if necessary to get their attribute values from the attributes of the inner nested View shortcode - they no longer have direct access to the outer View's attributes. It looks like the attribute names here are all okay based on the nested View's attributes above. It seems you took care to pass the outer attributes into matching inner View attributes, so they should all be available inside the inner View. Next, any other shortcodes like wpv-post-url, wpv-post-title, or types field shortcodes that referred to information in the parent post should now include the item attribute with the @relationship-slug.parent syntax, because they are now in the context of the nested View instead of the parent post type. I have added those item attributes in the next code block, and you should update relationship-slug in each one to reflect the actual relationship slug. I assume it's series-model but I'm not completely sure. I'm also not sure about how the relconnections shortcode works, that may need to be adjusted to work in the new nested context.

[wpv-layout-start]
    [wpv-items-found]
#### this part is pasted from previous loop template
#### You should ensure all wpv-attributes point to inner View attributes. pass any outer view attributes into matching inner view attributes.
#### You should add the item attribute @relationship-slug.parent to all wpv-post-url, wpv-post-title, type field shortcodes, etc. here when they refer to the parent post

#### I'm not sure how the relconnections custom shortcode works so I can't say if it needs to be adjusted in the new structure. Now the shortcode is executed in the context of the nested View, so you may need to adjust it to work in this new context.
 [relconnections relationship='series-model'] 
 

<div class="uk-background-default uk-width-5-6 uk-padding uk-margin uk-align-center parent-series-cpt">
    [wpv-conditional if="( '[wpv-attribute name="gbl"]' ne '1' )"]
        <a href="[wpv-post-url item="@relationship-slug.parent"]?status=[wpv-attribute name="sts"]&global=[wpv-attribute name="gbl"]" class="prodfocus" style="text-decoration: none; color: #000;" aria-label="Link to [wpv-post-title item="@relationship-slug.parent"] product page">
    [/wpv-conditional]
    [wpv-conditional if="( '[wpv-attribute name="gbl"]' eq '1' )"]
        <a href="[wpv-post-url item="@relationship-slug.parent"]?status=[wpv-attribute name="sts"]&global=1" class="prodfocus" style="text-decoration: none; color: #000;" aria-label="Link to [wpv-post-title item="@relationship-slug.parent"] product page">
    [/wpv-conditional]
    [wpv-conditional if="( '[wpv-attribute name="gbl"]' eq '' )"]
        <a href="[wpv-post-url item="@relationship-slug.parent"]?status=[wpv-attribute name="sts"]" class="prodfocus" style="text-decoration: none; color: #000;" aria-label="Link to [wpv-post-title item="@relationship-slug.parent"] product page">
    [/wpv-conditional]          
    [wpv-conditional if="( '[wpv-attribute name="sts"]' eq '1' )"]
        <a href="[wpv-post-url item="@relationship-slug.parent"]?status=[wpv-attribute name="sts"]" class="prodfocus" style="text-decoration: none; color: #000;" aria-label="Link to [wpv-post-title item="@relationship-slug.parent"] product page">
    [/wpv-conditional]
            <div>
                <h2 class="uk-h1 uk-text-uppercase uk-text-normal">[wpv-post-title item="@relationship-slug.parent"]</h2>
                <p class="uk-width-3-4" style="font-size: 1.125rem!important;">[types field='description' item="@relationship-slug.parent"][/types]</p>
                <hr>
            </div>
        </a>
        <div class="parent-prod-type-tax">
#### end first paste from previous loop template

    <!-- wpv-loop-start -->
<div class="uk-text-left" uk-grid [wpv-attribute name="hideit"]>
    <wpv-loop>
        [wpv-post-body view_template="loop-item-in-view-cpt-models-and-cpt-sets-rel-cpt-series"]
    </wpv-loop>
</div>
    <!-- wpv-loop-end -->
#### this part is pasted from the previous loop template:
        </div>
</div>
#### end second paste from previous loop template
    [/wpv-items-found]
    [wpv-no-items-found]
        [wpml-string context="wpv-views"][/wpml-string]
    [/wpv-no-items-found]
[wpv-layout-end]

No changes are needed in View "2" - Loop item in VIEW - CPT Models -AND- CPT Sets - REL - CPT Series

#2135041

Very nice. I'm going to close this ticket. I might run into issues as I apply this solution to all my views (not all products are sorted series -> model, but they share some views)... but I'm feeling good about this and can probably figure it out. If not, I'll reference this ticket. My issue is resolved now. Thank you!

#2135045

My issue is resolved now. Thank you!

This ticket is now closed. If you're a Toolset client and need related help, please open a new support ticket.