Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add post type and taxonomy filters in the script data #21814

Open
wants to merge 12 commits into
base: feature/dash-phase-1
Choose a base branch
from

Conversation

leonidasmi
Copy link
Contributor

@leonidasmi leonidasmi commented Nov 11, 2024

Context

Summary

This PR can be summarized in the following changelog entry:

  • Adds post type and taxonomy filters in the script data for the frontend to use.

Relevant technical choices:

  • We have created a new wpseo_{$content_type_name}_filtering_taxonomy filter, that allows users to set the filtering taxonomy for each post type
    • In order for taxonomies to be able to be filters, they need to be:
      • public
      • available in the REST API
      • associated with the post type of the filter
    • If a taxonomy is added via that filter but is not all three above, a doing_it_wrong notice will be displayed
  • For retrieving indexable post types, the Post_Type_Helper::get_indexable_post_types() is used. Unfortunately, it's configured to retrieve only post type names and not object, which makes our code a bit more complex than needed. A @todo was added along with a subtask, to take care of that outside of this PR.

Test instructions

Test instructions for the acceptance test before the PR gets merged

This PR can be acceptance tested by following these steps:

Default post content type:

  • In a fresh site, go to Yoast SEO->Dashboard and type wpseoScriptData.contentTypes in the console.
  • Confirm that you get an object with two post types (posts, pages) and a category taxonomy inside the post object:
{
name:  “post”,
label: “Posts”,
taxonomy: 
    { 
    name: “category”,
    label: “Categories”,
    "links": {
        "search": "http://basic.wordpress.test/wp-json/wp/v2/categories”
    "},
    }
},
{
name:  “page”,
label: “Pages”,
taxonomy: null,
},
  • Also confirm that the links/search link is actually pointing to the correct REST API URL that lists all product categories

Default post content type with categories de-registered:

  • After the above test, de-register the category taxonomy from the post content type:
function unregister_category_taxonomy() {
    unregister_taxonomy_for_object_type('category', 'post');
}
add_action('init', 'unregister_category_taxonomy');
  • Repeat the test and confirm that the post object became:
{
name:  “post”,
label: “Posts”,
taxonomy: null
},

Added filter for products:

  • Install and activate Woocommerce, repeat the above and confirm that you now get an extra post type in the above object:
{
name:  “product”,
label: “Products”,
taxonomy: 
    { 
    name: “product_cat”,
    label: “Product categories”,
    "links": {
        "search": "http://basic.wordpress.test/wp-json/wp/v2/product_cat”
    "},
    }
},
  • Also confirm that the links/search link is actually pointing to the correct REST API URL that lists all product categories.

Added filter for products, but product category is de-registered:

  • After the above test, de-register the product_cat taxonomy from the product content type:
function unregister_product_category_taxonomy() {
    unregister_taxonomy_for_object_type('product_cat', 'product');
}
add_action('init', 'unregister_product_category_taxonomy');
  • Repeat the test and confirm that the product object became:
{
name:  “product”,
label: “Products”,
taxonomy: null
},

Added filter via hook:

  • We can also add a hook to enable a filter for a post type: wpseo_{$content_type}_filtering_taxonomy
  • Use that filter by replacing the $content_type with the post type you want to enable the filter for, for example for enabling the movie-genre taxonomy for the movie CPT:
add_filter( 'wpseo_movie_filtering_taxonomy', 'movie_filtering_taxonomy' );
function movie_filtering_taxonomy ( ) {
	return 'movie-genre';
}
  • After adding that, install the yoast test helper and enable custom post types
  • Confirm that you get the following object in wpseoScriptData.contentTypes:
{
name:  “movie”,
label: “Movies”,
taxonomy: 
    { 
    name: “movie-genre”,
    label: “Genres”,
    "links": {
        "search": "http://basic.wordpress.test/wp-json/wp/v2/movie-genre”
    "},
    }
},
  • Also confirm that the links/search link is actually pointing to the correct REST API URL that lists all movie genres.
  • If in that filter, instead of movie-genre, you
    • add a taxonomy that doesn't belong to the movies CPT (eg. category) or
    • a string that's not even a taxonomy (e.g not-taxonomy) or
    • a taxonomy that's not public or
    • a taxonomy that's not available in the REST API

, you'll get a null instead in the taxonomy attribute and a

PHP Notice: Function Filter: 'wpseo_{$content_type}_filtering_taxonomy' was called incorrectly. The wpseo_{$content_type}_filtering_taxonomy filter should return a registered taxonomy that is associated with that content type

PHP notice:

{
name:  “movies”,
label: “Movies”,
taxonomy: null
},

Added filter via hook, for a taxonomy with custom REST API URL:

  • We can also use that hook to enable a filter for the book post type, but this time we're going to use a custom taxonomy instead.
  • We will create a custom taxonomy, with custom REST API URL, using the below code:
function register_genre_taxonomy() {
    $args = array(
        'label'        => __('Alternative Genres'),
        'public'       => true,
        'hierarchical' => true, // true if it's like categories, false for tags
        'show_in_rest' => true, // Enable REST API
        'rest_base'    => 'alternative_genres', // Custom REST base
        'rest_namespace' => 'my_custom_namespace/v1', // Custom REST namespace
        'rewrite'      => array('slug' => 'alternative-genre'), // Custom slug
    );

    register_taxonomy('alternative-genre', array('book'), $args);
}
add_action('init', 'register_genre_taxonomy');
  • Now, use the wpseo_{$content_type}_filtering_taxonomy again by replacing the $content_type with the post type you want to enable the filter for, in this example:
add_filter( 'wpseo_book_filtering_taxonomy', 'book_filtering_taxonomy' );
function book_filtering_taxonomy ( ) {
	return 'alternative-genre';
}
  • After adding that, confirm that you get the following object in wpseoScriptData.contentTypes:
{
name:  “book”,
label: “Books”,
taxonomy: 
    { 
    name: “alternative-genre”,
    label: “Alternative genres”,
    "links": {
        "search": "http://basic.wordpress.test/wp-json/my_custom_namespace/v1/alternative_genres”
    "},
    }
},
  • Also confirm that the links/search link is actually pointing to the correct REST API URL that lists all movie genres.

Added category filter for custom post type:
Like described in the technical choices, if a CPT has the default WP category taxonomy associated with it, and there's no other hook involved, we use category for filtering that CPT. To test that:

  • Create a Blog posts CPT with the category taxonomy associated with it with this code:
function register_blog_post_type() {
    $args = [
		'label'              => \__( 'Blog posts', 'yoast-test-helper' ),
		'labels'             => [
			'name'          => \__( 'Blog posts', 'yoast-test-helper' ),
			'singular_name' => \__( 'Blog post', 'yoast-test-helper' ),
		],
        'public'             => true,
        'has_archive'        => true,
        'show_in_rest'       => true, // Enable Gutenberg editor support
        'taxonomies'         => ['category', 'alternative-genre'], // Associate with default 'category' and custom 'bar'
    ];

    register_post_type('blog-post', $args);
}
add_action('init', 'register_blog_post_type');
  • After adding that, confirm that you get the following object in wpseoScriptData.contentTypes:
{
name:  “blog-post”,
label: “Blog posts”,
taxonomy: 
    { 
    name: “category”,
    label: “Categories”,
    "links": {
        "search": "http://ai.local/wp-json/wp/v2/categories”
    "},
    }
},
  • Also confirm that the links/search link is actually pointing to the correct REST API URL that lists all categories.
  • Now, if a hook is added for that CPT, it overrides the default category taxonomy as the filter. So, add
add_filter( 'wpseo_blog-post_filtering_taxonomy', 'blog_post_filtering_taxonomy' );
function blog_post_filtering_taxonomy ( ) {
	return 'alternative-genre';
}
  • that will make Alternative genre the filtering taxonomy for Blog Posts
  • So, after adding that, confirm that you get the following object in wpseoScriptData.contentTypes:
{
name:  “blog-post”,
label: “Blog posts”,
taxonomy: 
    { 
    name: “alternative-genre”,
    label: “Alternative genres”,
    "links": {
        "search": "http://ai.local/wp-json/my_custom_namespace/v1/alternative_genres”
    "},
    }
},

No public (aka, indexable) post types:

  • Add the following filter, to disable indexables for all your site's post types:
add_filter( 'wpseo_indexable_excluded_post_types', 'exclude_page' );
function exclude_page( ) {
	return ['movie','book','product','post','page','blog-post',];
}
  • Make sure that return ['movie','book','product','post','page','blog-post',] covers ALL content types
  • Check the wpseoScriptData.contentTypes and confirm that it returns an empty array

Relevant test scenarios

  • Changes should be tested with the browser console open
  • Changes should be tested on different posts/pages/taxonomies/custom post types/custom taxonomies
  • Changes should be tested on different editors (Default Block/Gutenberg/Classic/Elementor/other)
  • Changes should be tested on different browsers
  • Changes should be tested on multisite

Test instructions for QA when the code is in the RC

  • QA should use the same steps as above.

QA can test this PR by following these steps:

Impact check

This PR affects the following parts of the plugin, which may require extra testing:

UI changes

  • This PR changes the UI in the plugin. I have added the 'UI change' label to this PR.

Other environments

  • This PR also affects Shopify. I have added a changelog entry starting with [shopify-seo], added test instructions for Shopify and attached the Shopify label to this PR.

Documentation

  • I have written documentation for this change. For example, comments in the Relevant technical choices, comments in the code, documentation on Confluence / shared Google Drive / Yoast developer portal, or other.

Quality assurance

  • I have tested this code to the best of my abilities.
  • During testing, I had activated all plugins that Yoast SEO provides integrations for.
  • I have added unit tests to verify the code works as intended.
  • If any part of the code is behind a feature flag, my test instructions also cover cases where the feature flag is switched off.
  • I have written this PR in accordance with my team's definition of done.
  • I have checked that the base branch is correctly set.

Innovation

  • No innovation project is applicable for this PR.
  • This PR falls under an innovation project. I have attached the innovation label.
  • I have added my hours to the WBSO document.

Fixes https://github.com/Yoast/reserved-tasks/issues/320

@leonidasmi leonidasmi added the changelog: non-user-facing Needs to be included in the 'Non-userfacing' category in the changelog label Nov 12, 2024
@leonidasmi leonidasmi added this to the feature/dash-phase-1 milestone Nov 12, 2024
@leonidasmi leonidasmi marked this pull request as ready for review November 12, 2024 10:51
Copy link
Contributor

@thijsoo thijsoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Just some minor 🧅 suggestions and some WP decouple suggestions. Also missing any intergration/unit tests. But lets talk about the need for those at this stage.

@leonidasmi
Copy link
Contributor Author

Also missing any intergration/unit tests

I was hoping you wouldnt notice that 😄

In all seriousness though, I wanted to nail the layout first, and after this round of CR, I think I can now add those (or add them in a separate subtask).

Copy link
Contributor

@thijsoo thijsoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some tiny style thingies. The rest looks great!

/**
* Returns the content types array.
*
* @return array<array<string,array<string, array<string, array<string, string|null>>>>> The content types array.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if @return array<Content_Types> makes more sense here 🤔 It is an array representation due to the ->to_array call. But for developer comprehension knowing that its a list of content types might make more sense.

*
* @param string $name The name of the content type.
* @param string $label The label of the content type.
* @param Taxonomy $taxonomy The taxonomy that filters the content type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param Taxonomy $taxonomy The taxonomy that filters the content type.
* @param Taxonomy|null $taxonomy The taxonomy that filters the content type.

/**
* Gets the taxonomy that filters the content type.
*
* @return string The taxonomy that filters the content type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return string The taxonomy that filters the content type.
* @return Taxonomy|null The taxonomy that filters the content type.

*
* @return bool Whether the taxonomy in question is valid.
*/
public function is_valid_taxonomy( $taxonomy, $content_type ): bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public function is_valid_taxonomy( $taxonomy, $content_type ): bool {
public function is_valid_taxonomy( $taxonomy, string $content_type ): bool {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changelog: non-user-facing Needs to be included in the 'Non-userfacing' category in the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants