0

Based on the premise that one big database requests is better than many smaller ones, I've modified WP_Query for my custom post type 'word' and joined three tables to it. The resulting query looks more or less like this and works well.

SELECT wp_posts.*, verbs.*, nouns.*, definitions.*
FROM wp_posts  
LEFT JOIN verbs ON ( wp_posts.ID = verbs.word_id )  
LEFT JOIN nouns ON ( wp_posts.ID = nouns.word_id )  
LEFT JOIN definitions ON ( wp_posts.ID = definitions.word_id )  
WHERE 1=1  
AND wp_posts.post_type = 'word' 
AND (wp_posts.post_status = 'publish' OR [...] )  
ORDER BY wp_posts.post_title ASC 

I of course now have duplicate posts for each of the words' respective definitions and I am rolling them back up into one post per word with an array of definitions instead.

But because these results are used in different ways all around the website, I'm having to write similar logic time and time again. Here, for example, I am using the data in an AJAX response.

    $query = new WP_Query( $args );

    $posts = $query->get_posts();

    if ( $query->post_count ) > 0 )
    {
        $prev_word = null;
        foreach ( $posts as $key => &$row ) 
        {
            if ( !property_exists( $row, 'definitions' ) ) $row->{'definitions'} = array();

            $definition = array(
                'definition_id' => $row->definition_id,
                'definition' => $row->definition,
                'note' => $row->note,
            );

            if ( is_object( $prev_word ) )
            {
                if ( $prev_word->post_id == $row->post_id )
                {
                    // update the previously inserted row
                    $prev_word->definitions[] = $definition;

                    // remove the current row
                    unset( $row );

                    continue;
                }
            }

            $row->definitions[] = $definition;
            $prev_word = $row;
        }
    }

    echo json_encode( array_values( $posts ) );
    die();

What I would like to do now is extend the WP_Query class to have this done automatically on construction.

class Words_Query extends WP_Query
{
    function __construct( $args = array() )
    {
        $args = array_merge( $args, array(
            'post_type' => 'word'
        ) );

        parent::__construct( $args );

        [insert magic here?]
    }
}

I don't have any experience with OOP though so I am asking for some help/guidance. Is something like this possible?

Will I have to update a bunch of other properties to reflect the changes...? Like $post_count

What other things might I need to be aware of?

Thanks

1 Answer 1

0

I ended up using the the_post hook to add the definitions as the posts are setup. This of course costs another trip to the database for every post but dealing with the duplicate entries proved too difficult.

function add_definitions( $post ) 
{
    if ( 'word' != $post->post_type ) ) return;

    global $wpdb;

    $query  = "SELECT * FROM {$wpdb->prefix}definitions ";
    $query .= "WHERE word_id = " . $post->ID;

    $post->ideas = $wpdb->get_results( $query, ARRAY_A );
}
add_action( 'the_post', 'add_definitions' );

For those interested, however, here's how I extended the WP_Post Class to roll the duplicate posts up into one.

I suspect this approach is not recommended.

class Words_Query extends WP_Query
{
    function __construct( $args = array() )
    {
        $args = array_merge( $args, array(
            'post_type' => 'word'
        ) );

        parent::__construct( $args );

        $this->posts = parent::get_posts();

        if ( count( $this->posts ) > 0 )
        {
            $prev_word = null;
            foreach ( $this->posts as $key => &$post ) 
            {
                if ( !property_exists( $post, 'definitions' ) ) $post->{'definitions'} = array();

                $definition = array(
                    'definition_id' => $post->definition_id,
                    'definition' => $post->definition,
                    'note' => $post->note,
                );

                if ( is_object( $prev_word ) )
                {
                    if ( $prev_word->ID == $post->ID )
                    {
                        // update the previously inserted post
                        $prev_word->definitions[] = $definition;

                        // remove the current post
                        unset( $this->posts[$key] );

                        continue;
                    }
                }

                $post->definitions[] = $definition;
                $prev_word = $post;
            }
        }

        $this->posts = array_values( $this->posts );
    }

    var $posts = array();
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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