scribu / wp-posts-to-posts

Efficient many-to-many connections between posts, pages, custom post types, users.
http://wordpress.org/plugins/posts-to-posts/
975 stars 260 forks source link

Permalinks support #122

Open beeksiwaais opened 12 years ago

beeksiwaais commented 12 years ago

How to support permalinks with posts-to-posts and custom post type to have something like '/clients/%client%/works/%work%' where 'clients' and 'works' are two custom post type with 'many-to-many' association (with custom post type) ?

scribu commented 12 years ago

You just have to create some rewrite rules that map to the P2P query vars:

'connected_type', 'connected_items' and maybe 'connected_direction'.

Here's just one of many tutorials: http://matty.co.za/2009/11/custom-url-rewrites-in-wordpress/

ghost commented 12 years ago

I have a very similar question. It's a different scenario but it's also concerning the "many-to-many"-association.

Custom rewrite and many-to-many relations

One CPT for products and another CPT for spare parts.

CPT 1: products Permalink: example.com/products/productName

CPT 2: spare_parts Permalink: example.com/spare-parts/%productName%

The desired end-result is if one would browse /spare-parts/%productName% it would display every spare part this specific product is related to, so basically one would be able to view all spare parts for a specific product since many products can have many spare parts (many-to-many).

When creating a spare part one would relate this to a product

<?php
p2p_register_connection_type( array(
    'name' => 'posts_to_posts',
    'from' => 'spare_parts',
    'to' => 'products'
) );
?>

Question is

How would one be able to pull all related posts made with "Posts 2 Posts" belonging to a specific post from CPT1 and being able to display them under CPT2?

So imagine if "product1" from CPT1 (products) is related to "part1, part2, part3" from _CPT2 (spareparts) - viewing these through /spare-parts/product1 would show every spare part for product1.

ghost commented 12 years ago

Sorry about the double post. I was typing the comment as the issue got closed. Once it was closed and I had posted it after closure I thought it wouldn't be visible so I created a new issue only to see you'd already answered the first one.

WordPress Custom Rewrite Attempt

I have been fiddeling with some rewrites to make this "many-to-many" work using the guide you provided. Although the parts I'm having problems with is setting a specific template to use for these "special" singles and also where you meant us to map into the P2P query vars;

_You just have to create some rewrite rules that map to the P2P query vars: 'connected_type', 'connected_items' and maybe 'connecteddirection'.

Basically, I'm not sure how P2P works; are these vars made publicly available in the public query vars?

As is now, the code forces the use of "single-products.php" where I'd like it to use "single-spare_parts.php" having P2P-code to retrieve the related parts etc. As well as I'm not sure how to map to the P2P query vars as you've mentioned.

The code I've got so far
<?php
// P2P connection
function my_connection_types() {
    // Make sure the Posts 2 Posts plugin is active.
    if ( !function_exists( 'p2p_register_connection_type' ) )
        return;

    p2p_register_connection_type( array(
        'name' => 'posts_to_posts',
        'from' => 'spare_parts',
        'to' => 'products',
        'cardinality' => 'many-to-many'
    ) );
}
add_action( 'wp_loaded', 'my_connection_types' );

// the rules
function custom_p2p_rewrites() {
    global $wp_rewrite;

    // Define custom rewrite tokens
    $rewrite_tag = '%productName%';

    // Add the rewrite tokens
    $wp_rewrite->add_rewrite_tag( $rewrite_tag, '(.+?)', 'products=' );

    // Define the custom permalink structure
    $rewrite_keywords_structure = $wp_rewrite->root . "/spare-parts/$rewrite_tag/";

    // Generate the rewrite rules
    $new_rule = $wp_rewrite->generate_rewrite_rules( $rewrite_keywords_structure );

    // Add the new rewrite rule into the global rules array
    $wp_rewrite->rules = $new_rule + $wp_rewrite->rules;

    return $wp_rewrite->rules;
}

// add to query vars
function add_custom_p2p_rewrites( $public_query_vars ) {
    $public_query_vars[] = 'products';
    return $public_query_vars;
}

// flush
function flush_custom_rules() {
    global $wp_rewrite;
    $wp_rewrite->flush_rules();
}

add_action( 'init', 'flush_custom_rules' );
add_action( 'generate_rewrite_rules', 'custom_p2p_rewrites' );
add_filter( 'query_vars', 'add_custom_p2p_rewrites' );  

/* 

This is what happens with the above

Request: spare-parts/the_tester
Matched Rewrite Rule: spare-parts/(.+?)/?$
Matched Rewrite Query: products=the_tester
Loaded Template: single-products.php

Request: products/the_tester
Matched Rewrite Rule: products/(.+?)(/[0-9]+)?/?$
Matched Rewrite Query: products=the_tester&page=
Loaded Template: single-products.php

*/
?>
Any suggestions?

So the above "could" work if it could use another template file, or some sort of condition to run P2P stuff. So if this could be fixed it could quite possibly work. But since you recommended to add to the P2P query vars I'm not entirely sure what you mean. Perhaps you can give an example? Sounded so easy when you put it like that!

EDIT

Since I don't know how to tell WP to use a specific template for the spare-part single, this hacky condition, inside single-products.php, is what I've got instead of a specific template to run:

<?php 
    global $wp; 
    if( ( stripos( $wp->request, 'products' ) !== FALSE ) ) {
        echo 'P2P specific stuff here';
    }
?>

So inside that condition I could loop the related posts etc, but it doesn't feel like this is what you meant at all.

scribu commented 12 years ago

Basically, I'm not sure how P2P works; are these vars made publicly available in the public query vars?

Yes, they are: https://github.com/scribu/wp-posts-to-posts/blob/df2a1ffb2f0ae5f6a74e58369cc8d5bfe09fd920/core/url-query.php

The thing is that you will probably need to use add_rewrite_rule() directly, instead of $wp_rewrite->generate_rewrite_rules() so the guide I linked to isn't much help.

As for the template, you can use either the 'template_redirect' action or the 'template_include' filter.

ghost commented 12 years ago

Thank you for clarifying. I'll edit this comment when I have studied the P2P url-query.php more thoroughly and read more about using add_rewrite_rule() vs. $wp_rewrite->generate_rewrite_rules(). And for the template I'll look into the action or if I should use a filter, whichever works better.

jessor commented 12 years ago

On this current project I have an extensive page hierarchy and use p2p to connect products (cpt) to specific pages in different levels. Because of the complexity and contentual requirements of this structure I can't use taxonomies and archive pages.

An easier way to do this would be awesome.

So this

register_post_type(
        'product',
        ...
);

p2p_register_connection_type(array(
    'name' => 'product to pages',
    'from' => 'product',
    'to' => 'page'
));

would result in /whichever/page/i/am/connecting/to/connected_product

Rahe commented 12 years ago

Hi, you can use the endpoint API for you url too, like : website.com/any_post_type_single_or_archive/related_to/relation Where the "related_to" is the endpoint name and the "relation" is the name of the relation ( if you need but this can be an product name or everything else to use to get the relations ). I have written an tutorial but it's in french (sorry) :http://blog.nicolas-juen.fr/2011/10/27/utiliser-lapi-endpoint-pour-creer-une-regle-de-reecriture-durl-simple/

Codex link http://codex.wordpress.org/Rewrite_API/add_rewrite_endpoint

scribu commented 12 years ago

Here's a tutorial in English: http://make.wordpress.org/plugins/2012/06/07/rewrite-endpoints-api/

jessor commented 12 years ago

Yes, custom endpoints was what I ended up using, too. Thanks!

callmeska commented 11 years ago

Hello everybody

First i want to apologise for my english which can be no perfect.

I give you my experience because my goal was similar that is to say i wanted to have a permalink with this structure :

"domain.com/moviename/scenename"

where' moviename' and 'scenenam'e are custom post type connected with p2p.

'moviename' in the url has no role in the template, it's only for having a clever and readable url.

After a lot of tests and bugs I finaly I finally adopted a simple solution which is not very heavy in term of request.

1 :add a rewrite attribute in your custom post type 'scenename'

 'rewrite' => array('slug' => '%movieslugtags%'), 

2 : declare a custom taxonomy for custom post 'scenename'

register_taxonomy( 'movieslugtags', 'scene', array( 'hierarchical' => FALSE, 'label' => 'Movie slug', 'query_var' => true, 'rewrite' => true ) ); 

3 : add a function in order to auto fill this custom taxonomy with the slug of the 'moviename' linked.

add_action( 'save_post', 'save_scene_meta_movie', 10, 2 );
function save_scene_meta_movie( $post_ID, $post ) {
     if ( 'scene' != $post->post_type || wp_is_post_revision( $post_ID ) ){
        return;
    }

    $connected = new WP_Query( array(
      'connected_type' => 'film2scene',
      'connected_items' => $post_ID
    ) );

    if ( $connected->have_posts() ) {
        while ( $connected->have_posts() ) { 

            $connected->the_post(); 
            $title = $connected->post->post_name;
             wp_reset_postdata();
        } 
         wp_reset_postdata();
        $taxonomy_slug = $title;

    }else{
        $taxonomy_slug = 'inconnu';
    }
     wp_set_object_terms( $post_ID, $taxonomy_slug, 'movieslugtags' );
}

4 : then add a filter in order to replace the permalink with the specific slug registered before

add_filter('post_type_link', 'scene_permalink', 10, 3);
function scene_permalink($permalink, $post_id, $leavename) {
    if ( (is_home || is_singular( 'scene' )) && ! is_admin() ) {

        if (strpos($permalink, '%movieslugtags%') === FALSE) return $permalink;

        // Get post
        $post = get_post($post_id);
        if (!$post) return $permalink;

        // Get taxonomy terms
        /**/

        $terms = wp_get_object_terms($post->ID, 'movieslugtags');   
        if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])){
            $taxonomy_slug = $terms[0]->slug;
        }else{
            $taxonomy_slug = 'inconnu';
        }   

        return str_replace('%movieslugtags%', $taxonomy_slug, $permalink);

    } 
}  

This solution works well in my case. I hope It will help people who want an url containing linked post type.