PHPackages                             spock/shadow-taxonomies - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. spock/shadow-taxonomies

ActiveLibrary

spock/shadow-taxonomies
=======================

A Composer library for creating relationships between custom post types using shadow taxonomies in WordPress.

0.5.0(3mo ago)146.8k↓13.6%1GPL-2.0+PHPPHP &gt;=8.2CI passing

Since Oct 13Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/PatelUtkarsh/shadow-taxonomy)[ Packagist](https://packagist.org/packages/spock/shadow-taxonomies)[ Docs](https://github.com/patelutkarsh/shadow-taxonomy)[ RSS](/packages/spock-shadow-taxonomies/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (5)Versions (10)Used By (0)

Shadow Taxonomy
===============

[](#shadow-taxonomy)

A WordPress Composer library for creating relationships between custom post types using shadow taxonomies.

Introduction
------------

[](#introduction)

One of the hardest things to do in WordPress is creating relationships between two different post types. Often this is accomplished by saving relationship data in post meta. However this leads to expensive meta queries, which are generally one of the poorest performing queries you can make in WordPress.

Metadata can also be a pain to keep synced. For example, when posts are deleted, what happens to the post meta saved on a separate post type?

Shadow Taxonomy solves this by using WordPress taxonomies as the relationship layer. Instead of meta queries, you get performant taxonomy queries and a built-in checkbox UI on the post edit screen for free.

What is a Shadow Taxonomy?
--------------------------

[](#what-is-a-shadow-taxonomy)

A shadow taxonomy is a custom WordPress taxonomy that automatically mirrors a specific post type. Anytime a post in that post type is created, updated, or deleted, the associated shadow taxonomy term is also created, updated, and deleted.

This library manages the entire lifecycle of the shadow terms, keeping your taxonomy in sync with its associated post type.

Installation
------------

[](#installation)

```
composer require spock/shadow-taxonomies

```

**Requirements:** PHP &gt;= 7.2, WordPress

Usage
-----

[](#usage)

### Step One: Create the Shadow Taxonomy

[](#step-one-create-the-shadow-taxonomy)

```
add_action( 'init', function() {
	register_taxonomy(
		'services-tax',
		'staff-cpt',
		array(
			'label'         => __( 'Services', 'text-domain' ),
			'rewrite'       => false,
			'show_tagcloud' => false,
			'hierarchical'  => true,
		)
	);
	// We will make our connection here in the next step.
});
```

Here we are creating a normal custom taxonomy. In this example we are creating a taxonomy to mirror a CPT called Services, so by convention the shadow taxonomy is named `services-tax`.

Because we want to link Services to another post type called Staff, this taxonomy is registered on the Staff CPT post edit screen.

The taxonomy is not made `public` so that nobody manually edits the terms. The library handles creating, updating, and deleting the shadow taxonomy terms to keep everything in sync.

### Step Two: Create the Association

[](#step-two-create-the-association)

```
\Shadow_Taxonomy\Core\create_relationship( 'service-cpt', 'service-tax' );
```

This one line creates the shadow taxonomy link. The first argument is the custom post type slug, and the second is the shadow taxonomy slug. Place this immediately after the `register_taxonomy` call.

### Combined Helper

[](#combined-helper)

Use `register_shadow_taxonomy` to register the taxonomy and establish the relationship in a single call:

```
\Shadow_Taxonomy\Core\register_shadow_taxonomy(
	[ 'movies' ],
	[ 'actor' ],
	'_actor',
	[
		'label'         => 'Actors',
		'rewrite'       => false,
		'show_tagcloud' => false,
		'show_ui'       => false,
		'hierarchical'  => false,
		'show_in_menu'  => false,
		'meta_box_cb'   => false,
		'show_in_rest'  => true,
	]
);
```

API
---

[](#api)

### get\_the\_posts

[](#get_the_posts)

```
\Shadow_Taxonomy\Core\get_the_posts( $post_id, $taxonomy, $cpt )
```

Fetch the associated posts for a given post ID. Returns an array of `WP_Post` objects or `false` if none are found.

- `$post_id` *(int)* **required** - The ID of the post whose associations you want to find.
- `$taxonomy` *(string)* **required** - The shadow taxonomy slug.
- `$cpt` *(string)* **required** - The associated custom post type slug.

### get\_associated\_term

[](#get_associated_term)

```
\Shadow_Taxonomy\Core\get_associated_term( $post, $taxonomy )
```

Get the shadow term for a given post. Accepts a `WP_Post` object or post ID. Returns a `WP_Term` object or `false`.

### get\_associated\_post

[](#get_associated_post)

```
\Shadow_Taxonomy\Core\get_associated_post( $term, $post_type )
```

Get the shadow post for a given term. Returns a `WP_Post` object or `false`.

### get\_meta\_key

[](#get_meta_key)

```
\Shadow_Taxonomy\Core\get_meta_key( $taxonomy, $type )
```

Build the meta key used to store shadow relationships. `$type` is either `'term_id'` or `'post_id'`.

Hooks
-----

[](#hooks)

The library fires the following actions:

- `shadow_taxonomy_term_created` - Fires after a shadow term is created. Parameters: `$new_term`, `$post_id`, `$taxonomy`.
- `shadow_taxonomy_term_updated` - Fires after a shadow term is updated. Parameters: `$term`, `$associated_post`, `$taxonomy`.
- `shadow_taxonomy_term_deleted` - Fires after a shadow term is deleted. Parameters: `$term`, `$post_id`, `$taxonomy`.

WP-CLI Commands
---------------

[](#wp-cli-commands)

The library includes WP-CLI commands for managing shadow taxonomies on existing sites with existing data.

### sync

[](#sync)

```
wp shadow sync --cpt= --tax= [--dry-run] [--verbose]
```

Syncs all posts in the given post type to shadow terms, and removes any orphan terms.

### sync-terms

[](#sync-terms)

```
wp shadow sync-terms --cpt= --tax= [--dry-run] [--verbose]
```

Like `sync`, but also repairs missing metadata on both the post and term side.

### deep-sync

[](#deep-sync)

```
wp shadow deep-sync --cpt= --tax= [--dry-run] [--verbose]
```

Creates shadow terms for posts missing both the shadow meta key and a matching term by slug.

### check

[](#check)

```
wp shadow check  --id= --tax=
```

Checks if a specific post or term has a valid shadow association.

### Options

[](#options)

- `--cpt` *(string)* **required** - The post type to shadow.
- `--tax` *(string)* **required** - The taxonomy to use as the shadow.
- `--dry-run` *(flag)* **optional** - Lists changes without making them.
- `--verbose` *(flag)* **optional** - Outputs additional logging during processing.

License
-------

[](#license)

GPL-2.0+

###  Health Score

49

—

FairBetter than 95% of packages

Maintenance82

Actively maintained with recent releases

Popularity31

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity58

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 67.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~316 days

Recently: every ~395 days

Total

6

Last Release

97d ago

PHP version history (2 changes)0.3.0PHP &gt;=7.2

0.5.0PHP &gt;=8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/cbe9ee9f714f7f781761025e4b9cb1e87955748e78d87c546a958e90a86b8f4b?d=identicon)[spock](/maintainers/spock)

---

Top Contributors

[![PatelUtkarsh](https://avatars.githubusercontent.com/u/5015489?v=4)](https://github.com/PatelUtkarsh "PatelUtkarsh (21 commits)")[![mrbobbybryant](https://avatars.githubusercontent.com/u/7875796?v=4)](https://github.com/mrbobbybryant "mrbobbybryant (7 commits)")[![rahulsprajapati](https://avatars.githubusercontent.com/u/10358350?v=4)](https://github.com/rahulsprajapati "rahulsprajapati (3 commits)")

---

Tags

wordpresscustom-post-typetaxonomyRelationships

###  Code Quality

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/spock-shadow-taxonomies/health.svg)

```
[![Health](https://phpackages.com/badges/spock-shadow-taxonomies/health.svg)](https://phpackages.com/packages/spock-shadow-taxonomies)
```

###  Alternatives

[wp-papi/papi

WordPress Page Type API with custom fields

26229.8k1](/packages/wp-papi-papi)[voceconnect/objects-to-objects

A WordPress plugin/module that provides the ability to map relationships between posts and other post types

347.1k](/packages/voceconnect-objects-to-objects)[wpbp/cpt_columns

Improve the CPT list in the backend for your CPTs

218.3k](/packages/wpbp-cpt-columns)[wpmetabox/mb-relationships

A lightweight solution for creating many-to-many posts to posts relationships in WordPress.

291.1k2](/packages/wpmetabox-mb-relationships)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
