2

Got stuck trying to remove the post format slug, so i could have i.e. example.com/video for the video section instead of example.com/type/video.

Usually it's done like this:

add_action( 'init', 'custom_format_permalinks', 1 );
function custom_format_permalinks() {
   $taxonomy = 'post_format';
   $post_format_rewrite = array(      'slug' => NULL,      );
   add_permastruct( $taxonomy, "%$taxonomy%", $post_format_rewrite );
}

Or simply:

add_filter('post_format_rewrite_base', 'post_format_base');
function post_format_base($slug) {return '/';}

These are the typical answers given elsewhere for this question. And they both get the job done, except that... pagination and feeds are no longer working.

Then I tried this route...

add_filter('post_format_rewrite_base', 'no_format_base_rewrite_rules');
function no_format_base_rewrite_rules($format_rewrite) { 
$format_rewrite = array();    
$formats = get_post_format_slugs();    
foreach($formats as $format_nicename => $key) {
    $format_rewrite['('.$format_nicename.')/(?:feed/)?(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?post_format=$matches[1]&feed=$matches[2]';
    $format_rewrite['('.$format_nicename.')/?([0-9]{1,})/?$'] = 'index.php?post_format=$matches[1]&paged=$matches[2]';
    $format_rewrite['('.$format_nicename.')/?$'] = 'index.php?post_format=$matches[1]';
    }      
return $format_rewrite;
}

This sort of approach is proven to work perfectly with categories, tags or authors, but for some reason it doesn't do the job this time. What could be wrong?

  • Try to debug rewrite rules using global $wp, $wp->matched_rule and $wp->matched_query. Is your rules working? – Krzysztof Grabania Aug 11 '16 at 6:34
4
+50

In your last code example, you're returning a wrong type for the post_format_rewrite_base. It should be a string, not an array. However, your idea goes in the right direction.

You have to take care about all the «routes» that are implied by the default rewrite rules, WordPress creates for post formats:

type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$ => index.php?post_format=$matches[1]&feed=$matches[2]
type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$      => index.php?post_format=$matches[1]&feed=$matches[2]
type/([^/]+)/embed/?$                         => index.php?post_format=$matches[1]&embed=true
type/([^/]+)/page/?([0-9]{1,})/?$             => index.php?post_format=$matches[1]&paged=$matches[2]
type/([^/]+)/?$                               => index.php?post_format=$matches[1] 

How about using Cortex? It allows you to map routes directly to query parameters. Here's an example how to use it.

<?php

use
    Brain\Cortex,
    Brain\Cortex\Route;


add_action( 'wp_loaded', function() {

    Cortex::boot();

    $post_format_slugs = get_post_format_slugs();
    $post_format_slugs_pattern = implode( '|', $post_format_slugs );
    add_action(
        'cortex.routes',
        function ( Route\RouteCollectionInterface $collection ) use ( $post_format_slugs_pattern ) {
            /**
             * Default route for post format slugs
             *
             * /(standard|aside|chat|gallery|...)
             */
            $collection->addRoute(
                new Route\QueryRoute(
                    "/{slug:{$post_format_slugs_pattern}}",
                    function( $matches ) {

                        return [
                            'post_format' => $matches[ 'slug' ]
                        ];
                    }
                )
            );
            /**
             * Route for feed endpoints 1
             *
             * /(standard|aside|chat|gallery|...)/feed/(feed/rdf/rss/rss2/atom)
             */
            $collection->addRoute(
                new Route\QueryRoute(
                    "/{slug:{$post_format_slugs_pattern}}/feed/{feed:feed|rdf|rss|rss2|atom}",
                    function( $matches ) {

                        return [
                            'post_format' => $matches[ 'slug' ],
                            'feed'        => $matches[ 'feed' ]
                        ];
                    }
                )
            );
            /**
             * Route for feed endpoints 2
             *
             * /(standard|aside|chat|gallery|...)/(feed/rdf/rss/rss2/atom)
             */
            $collection->addRoute(
                new Route\QueryRoute(
                    "/{slug:{$post_format_slugs_pattern}}/{feed:feed|rdf|rss|rss2|atom}",
                    function( $matches ) {

                        return [
                            'post_format' => $matches[ 'slug' ],
                            'feed'        => $matches[ 'feed' ]
                        ];
                    }
                )
            );
            /**
             * Route for embed
             *
             * /(standard|aside|chat|gallery|...)/embed
             */
            $collection->addRoute(
                new Route\QueryRoute(
                    "/{slug:{$post_format_slugs_pattern}}/embed",
                    function( $matches ) {

                        return [
                            'post_format' => $matches[ 'slug' ],
                            'embed'       => TRUE
                        ];
                    }
                )
            );
            /**
             * Route for pagination
             *
             * ^/(standard|aside|chat|gallery|...)/page/(\d+)$
             */
            $collection->addRoute(
                new Route\QueryRoute(
                    "/{slug:{$post_format_slugs_pattern}}/page/{page:[0-9]+}",
                    function( $matches ) {

                        return [
                            'post_format' => $matches[ 'slug' ],
                            'paged'       => $matches[ 'page' ]
                        ];
                    }
                )
            );
        }
    );
} );

Cortex has the advantage that you don't have to flush rewrite rules. Internally it uses FastRoute to match the patterns, so you can use any of the pattern language, FastRoute provides. Cortext does not overwrite the internal routing process of WordPress, it just acts right before and will pass the request through to WordPress if no route matches.

The example shows just the essence of how to use Cortex. Some routes could possibly combined or even removed as they are only for backward compatibility (especially the redundant feed routes).

Remember that your concept requires overlapping routes, that means you won't be able to create pages with any of the slugs of the post format terms: standard, aside, chat, gallery, link, image, quote, status, video, audio.

You might want to implement an automated check that affects the unique post slug generation:

add_filter( 'wp_unique_post_slug_is_bad_hierarchical_slug', function( $is_bad, $slug, $post_type, $post_parent ) {
    if ( 'page' !== $post_type ) 
        return $is_bad;

    return in_array( $slug, get_post_format_slugs() );
} );

Another optimization would be to only use the post format slugs, the current theme actually supports. But this will invalidate existing URLs in case the theme changes.

If you don't want to use Cortex, the principle would be pretty much the same but you would have to manually remove the default post format rules and replace it with custom rewrite rules that follows the schema of the routes mentioned above. Remember to do this only on an activation hook and flush rewrite rules afterwards as they get cached in the database.

| improve this answer | |
  • Haha, pointing that post_format_rewrite_base requires string instead of array, solved it, I realised it may be the wrong function - and fortunately there is another one called post_format_rewrite_rules, apparently undocumented, but it gets the job done. (I took a wild guess and when it did work I googled it and found a few sparse examples. I just hope they don't pull the plug on it) – Lucian Davidescu Aug 11 '16 at 16:42
1

Excellent answer above, well worth the bounty. For the record, this is what it led to:

add_filter( 'post_format_rewrite_rules', 'no_format_base_rewrite_rules' );
function no_format_base_rewrite_rules( $format_rewrite ) {

    $format_rewrite = array(); 
    $formats = get_post_format_slugs();

    foreach($formats as $format_nicename) {
        $format_rewrite['(' . $format_nicename . ')/(?:feed/)?(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?post_format=$matches[1]&feed=$matches[2]';
        $format_rewrite['(' . $format_nicename . ')/?([0-9]{1,})/?$'] = 'index.php?post_format=$matches[1]&paged=$matches[2]';
        $format_rewrite['(' . $format_nicename . ')/?$'] = 'index.php?post_format=$matches[1]';
    }

    return $format_rewrite;
}
| improve this answer | |
  • A very good completing answer for the question. Just one thing: it might be better to combine all slugs in each of the threee regular expressions instead of creating three regexp for each post format slug. – David Aug 12 '16 at 14:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.