Skip Navigation

[Resolved] Filtering backend post listings by relationship

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

Problem:

The client needed to restrict backend visibility and editing of “Provider” posts so that “Manager” users could only view or edit Providers linked to the same Zip Codes via Toolset relationships. WordPress doesn’t natively filter post listings by relationships.

Solution:

Two many-to-many relationships were created: provider-zip and manager-zip.
A “Manager” custom role was added in Toolset Access, with each Manager tied 1:1 to a “Manager” CPT post.
Custom PHP was then added (via Toolset’s “Custom Code” feature) to:

Filter the Providers admin list using pre_get_posts, showing only Providers sharing a Zip Code with the logged-in Manager.

Enforce edit/read/delete capabilities using user_has_cap so Managers can only manage Providers they are linked to via shared Zip Codes.

Relevant Documentation:

https://toolset.com/documentation/programmer-reference/adding-custom-code/using-toolset-to-add-custom-code/

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.

This topic contains 3 replies, has 1 voice.

Last updated by Christopher Amirian 3 weeks, 6 days ago.

Assisted by: Christopher Amirian.

Author
Posts
#2831037

Tell us what you are trying to do?

I would like to create a role, Manager, that can only view posts from the Providers post type when both the Manager and the Provider are associated with a third post type called Zip Code. The goal is to assign Providers to Zip Codes, and to assign Managers of those Provider posts to overlapping Zip Codes and let the Managers view / edit those providers who are in Zip Codes that the Manager has been assigned to.

Is there any documentation that you are following?

We did explore this a bit: https://toolset.com/forums/topic/display-related-posts-chosen-in-the-backend/

Is there a similar example that we can see?

What is the link to your site?

hidden link ... providers can be seen by clicking on a county map on the homepage, taking you to a location like hidden link where they are listed below.

#2831151

Christopher Amirian
Supporter

Languages: English (English )

Hi,

Welcome to Toolset support. I have an idea that is considered a custom development. So I will be able to give you a starting point, but a complete implementation will be on your shoulders.

The WordPress admin list can't be filtered by relationships out of the box, but you can achieve exactly what you want with a tiny bit of code + Toolset Access.

Overall Idea:

- Keep two many-to-many relationships:

provider-zip: Providers <> Zip Codes

manager-zip: Managers <> Zip Codes

- Create a Manager user role (Toolset > Access).

- Represent each manager as a "Manager" CPT post authored by that user (1:1). This lets us use Toolset's relationship API with users indirectly.

- In wp-admin, filter the Providers list so a logged-in Manager only sees Providers sharing at least one Zip Code with their Manager post.

- Also, hard-enforce edit permission for single posts.

You would add the implementation code using the method below:
https://toolset.com/documentation/programmer-reference/adding-custom-code/using-toolset-to-add-custom-code/

1- Filter the Providers list in wp-admin

Add the code below:

<?php
// --- Helpers ---------------------------------------------------------------

function dsp_get_manager_zip_ids( $user_id ) {
    // Find the Manager CPT authored by this user.
    $mgr = get_posts([
        'post_type'      => 'manager',      // <-- your Manager CPT slug
        'author'         => $user_id,
        'posts_per_page' => 1,
        'fields'         => 'ids',
    ]);
    if (empty($mgr)) return [];

    $manager_post_id = $mgr[0];

    // Get Zip Codes related to the Manager via the manager-zip relationship.
    // Adjust query_by_role to match how you defined the sides.
    $zip_ids = toolset_get_related_posts( $manager_post_id, 'manager-zip', [
        'query_by_role' => 'parent',        // or 'child' as per your setup
        'return'        => 'ids',
        'args'          => ['posts_per_page' => -1],
    ]);

    return array_map('intval', (array) $zip_ids);
}

function dsp_get_provider_ids_for_zip_ids( array $zip_ids ) {
    $out = [];
    foreach ($zip_ids as $zip_id) {
        // Get Providers related to each Zip Code.
        $prov = toolset_get_related_posts( $zip_id, 'provider-zip', [
            'query_by_role' => 'parent',    // or 'child' as per your setup
            'return'        => 'ids',
            'args'          => ['posts_per_page' => -1],
        ]);
        $out = array_merge($out, (array) $prov);
    }
    return array_values(array_unique(array_map('intval', $out)));
}

function dsp_manager_can_access_provider( $user_id, $provider_id ) {
    $mgr_zips  = dsp_get_manager_zip_ids($user_id);
    if (empty($mgr_zips)) return false;

    // Zip codes linked to this Provider.
    $prov_zips = toolset_get_related_posts( $provider_id, 'provider-zip', [
        'query_by_role' => 'child',         // opposite of what you used above
        'return'        => 'ids',
        'args'          => ['posts_per_page' => -1],
    ]);

    return (bool) array_intersect( $mgr_zips, (array) $prov_zips );
}

// --- 1) Limit Providers list in admin -------------------------------------

add_action('pre_get_posts', function( $q ){
    if ( ! is_admin() || ! $q->is_main_query() ) return;
    if ( $q->get('post_type') !== 'provider' )   return;

    $user = wp_get_current_user();
    if ( ! in_array( 'manager', (array) $user->roles, true ) ) return;

    $zip_ids       = dsp_get_manager_zip_ids( $user->ID );
    $provider_ids  = dsp_get_provider_ids_for_zip_ids( $zip_ids );

    // If none match, show empty list.
    $q->set( 'post__in', $provider_ids ?: [0] );
});

// --- 2) Enforce edit/read/delete on single posts ---------------------------

add_filter('user_has_cap', function( $all_caps, $caps, $args, $user ){
    $requested = $caps[0] ?? '';
    if ( ! in_array( $requested, ['edit_post','read_post','delete_post'], true ) ) return $all_caps;

    $post_id = $args[2] ?? 0;
    $post    = get_post( $post_id );
    if ( ! $post || $post->post_type !== 'provider' ) return $all_caps;
    if ( ! in_array( 'manager', (array) $user->roles, true ) ) return $all_caps;

    if ( ! dsp_manager_can_access_provider( $user->ID, $post_id ) ) {
        $all_caps[ $requested ] = false;
    }
    return $all_caps;
}, 10, 4);


Replace slugs to match your site:

- CPT slugs: provider, zip-code, manager
- Relationship slugs: provider-zip, manager-zip
- Relationship roles (parent/child) as created in Toolset.

The code above will achieve the points below:

In Posts > Providers, Managers only see Providers that share at least one Zip with them.
If a Manager tries to access a Provider directly (URL), permission is denied.

2- Toolset Access

- Create Manager role.
- Grant it the minimal caps for the Provider CPT (e.g., read, edit).
- The code above will further limit which Providers they can see/edit.
- Leave admin/editor with full access.

I think this will be a good starting point and a solid foundation for what you want to achieve.

Needless to say, I did not personally test the steps, and it is just an idea for further implementation by you.

Thanks.

#2834110
Screenshot 2025-11-09 153647.png

I wanted to say thank you - this was a great starting point. We are continuing to work on this issue. We had to modify a few things to match our custom post types, and also update 'return' => 'ids' to 'return' => 'post_id' in a few places. Now we are able to load the page again, and are working on checking our parent / child values. If you'd like to close this thread to keep your support queue tidy, that's fine, we can open another one if we need further assistance.

#2834301

Christopher Amirian
Supporter

Languages: English (English )

Sure thanks. Feel free to open a new ticket if you need additional help.