it-swarm.com.de

Posts mit mehreren Post-Meta-Datumsfeldern bestellen

Hallo WordPress-Entwickler. Im Laufe der Jahre habe ich Antworten auf viele Fragen gefunden, die ich mit WordPress hatte. Ich möchte mich für Ihre Hilfe bedanken. Heute habe ich selbst eine Frage und suche kompetenten Rat.

Ich baue eine Seite für ein Kino. Ich habe einen benutzerdefinierten Beitragstyp movies . Ich verwende Types plugin, um die Showdaten und -zeiten für jeden Film als sich wiederholendes Datums-/Zeitfeld zu speichern. Es speichert das Datum und die Uhrzeit als Unix-Zeitstempel für die Postmeta-Tabelle mit dem meta_key "wpcf-showtime". Jeder Film hat viele Spielzeiten, daher gibt es eine Anzahl von meta_key/meta_value-Paaren mit demselben Schlüssel, z.

post_id: 70, meta_key: wpcf-showtime, meta_value 1417548600
post_id: 70, meta_key: wpcf-showtime, meta_value 1417813200
post_id: 70, meta_key: wpcf-showtime, meta_value 1417896000

Für die Startseite der Website möchte ich alle laufenden Filme bis Mittwoch der nächsten Woche zeigen, d. H. Filme, die von nun an Vorführzeiten haben, ausgenommen Filme, die in der Vergangenheit aufgetreten sind oder als zukünftig gekennzeichnet sind (benutzerdefinierte Taxonomie).

Mein Problem ist die Bestellung der Filme; Ich möchte sie nach Showtime geordnet zeigen, damit der Film, der der gegenwärtigen Zeit am nächsten kommt, an erster Stelle steht.

Dies ist die Abfrage, die ich ausführe:

$startdate = time();
$enddate = new DateTime('thursday next week');

$ongoing_movies = new WP_Query( array( 
    'post_type' => 'movies',
    'meta_key'  => 'wpcf-showtime',
    'orderby'   => 'meta_value_num', 
    'order'     => 'ASC',
    'tax_query' => array(
        array(
            'taxonomy' => 'genre',
            'field'    => 'slug',
            'terms'    => 'future', 
            'operator' => 'NOT IN'
        )
    ),
    'meta_query' => array(
        array(
            'key'      => 'wpcf-showtime',
            'value'    => array ( $startdate, $enddate->format('U')),
            'compare'  => 'BETWEEN', 
        ),
    )
));

Dies gibt die richtigen Filme zurück, sortiert nach Showtime. Die für die Sortierung verwendete Showtime ist jedoch der erste Wert, der für den für diesen bestimmten Film gespeicherten meta_key "wpcf-showtime" auftritt. Das heißt Der erste angezeigte Film ist möglicherweise nicht derjenige, der die nächste Show hat, aber derjenige, der die früheste Showzeit in db gespeichert hat.

Ich würde mich über jede Hilfe bei der Suche nach einer Lösung freuen. pre_get_posts action, posts_orderby filter?

4

1) Verwenden Sie nur den Filter posts_orderby:

Es sieht so aus, als ob Sie die Filme nach dem Minimum der wpcf-showtime-Metawerte ordnen möchten.

Mit dem Filter ORDER BY können Sie den Teil WP_Query des vom posts_orderby generierten SQL ändern.

Hier ist ein Beispiel:

add_filter( 'posts_orderby', 'wpse_posts_orderby' );
$ongoing_movies = new WP_Query( $args );

wobei der Filterrückruf definiert ist als:

/**
 *  Use MIN() on the meta value part ordering.
 */
function wpse_posts_orderby( $orderby )
{      
    global $wpdb;

    // Only run this callback once:
    remove_filter( current_filter(), __FUNCTION__ );

    // Replacement part:  
    $find    = "{$wpdb->postmeta}.meta_value+0 ";
    $replace = "mt1.meta_value+0 ";

    // Make sure we add MIN() to the correct part:    
    if( $find == str_ireplace( array( 'ASC', 'DESC' ), '', $orderby ) )
    {
        // Preserve the order direction:
        $orderby = str_ireplace(
            $find,
            "MIN( $replace) ",
            $orderby
        );
    }
    return $orderby;
}

Hier verwenden wir mt1.meta_value, wenn die Meta-Wert-Einschränkung das erste Argument-Array des meta_query ist.

Dies wird sich ändern:

ORDER BY wp_postmeta.meta_value+0 {ASC|DESC}

zu

ORDER BY MIN( mt1.meta_value+0 ) {ASC|DESC}

2) Einführung eines benutzerdefinierten Bestellparameters in WP_Query

Sie können versuchen, dieses Setup zu verwenden:

$startdate = time();
$enddate = new DateTime('thursday next week');

$ongoing_movies = new WP_Query( array( 
    'post_type'    => 'movies',
    'meta_key'     => 'wpcf-showtime',
    'wpse_orderby' => 'meta_value_num_min',      // <-- New parameter to order by MIN!
    'orderby'      => 'meta_value_num', 
    'order'        => 'ASC',
    'tax_query'    => array(
        array(
            'taxonomy' => 'genre',
            'field'    => 'slug',
            'terms'    => 'future', 
            'operator' => 'NOT IN'
        )
    ),
    'meta_query' => array(
        array(
            'key'      => 'wpcf-showtime',
            'value'    => array ( $startdate, $enddate->format('U')),
            'compare'  => 'BETWEEN', 
            'type'     => 'UNSIGNED'     // <-- Let's override the default 'CHAR'
        ),
    )
));

dabei verwenden wir das folgende Plugin, um den Parameter wpse_orderby zu unterstützen:

<?php
/**
 * Plugin Name: Modify the WP_Query ordering to support MAX and MIN
 * Description: Possible values of 'wpse_orderby' are  'meta_value_num_{min,max}'.
 * Plugin URI:  http://wordpress.stackexchange.com/a/173496/26350
 * Author:      Birgir Erlendsson (birgire)
 * Version:     0.0.2
 */ 

add_action( 'init', function(){
    if( class_exists( 'WPSE_Modify_Ordering' ) )
    {
        $o = new WPSE_Modify_Ordering;
        $o->init();
    }
});

class WPSE_Modify_Ordering
{
    private $type    = '';
    private $order   = '';
    private $orderby = '';

    public function init()
    {
        add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
    }

    public function pre_get_posts( WP_Query $q )
    {
        if( 
            in_array( 
                $q->get( 'wpse_orderby' ), 
                array( 'meta_value_num_min', 'meta_value_num_max' ) 
            ) 
        )
        {
            $this->type = strtoupper( substr( $q->get( 'wpse_orderby' ), -3 ) );
            $this->order = ( '' !== $q->get( 'order' ) ) ? $q->get( 'order' ) : 'ASC';
            add_filter( 'posts_orderby', array( $this, 'posts_orderby' ) );
        }
    }

    public function posts_orderby( $orderby )
    {
        remove_filter( current_filter(), array( $this, __FUNCTION__ ) );
        return " {$this->type}( mt1.meta_value+0 ) " . $this->order;
    }

} // end class

Der Parameter wpse_orderby überschreibt den nativen Parameter orderby. Es unterstützt die Werte meta_value_num_min und meta_value_num_max.

Hier ordnen wir nach dem mt1.meta_value, da die Meta-Wert-Einschränkung das erste Argument-Array des meta_query ist.

Der Grund, warum wir den Parameter orderby beibehalten, besteht darin, ihn als Fallback zu verwenden, wenn das obige Plugin deaktiviert ist.

Hoffentlich können Sie dies weiter an Ihre Bedürfnisse anpassen.

4
birgire

Dies ist eine täuschend komplexe Frage; Es geht speziell darum, nach Kontext Datum innerhalb des Bereichs einer Abfrage zu sortieren, wenn ein Objekt mehrere Daten haben kann. Beim Sortieren und Sortieren wird normalerweise der niedrigste Wert im Array von Datumsangaben verwendet, was häufig zu einer falschen Reihenfolge führt, wenn ein bestimmter Datumsbereich abgefragt wird.

Als Referenz hier das abgespeckte Fleisch des Codes, den ich in meinem eigenen Ansatz verwendet habe:

// Wrap query in filters to modify behaviour
// ==============  
add_filter('posts_join_paged','doty_custom_posts_join');
add_filter('posts_orderby','doty_custom_posts_orderby');
$new_query = new WP_Query( $args );
remove_filter('posts_orderby','doty_custom_posts_orderby');
remove_filter('posts_join_paged','doty_custom_posts_join');

// Hooks into the 'WHERE' component of the SQL query
// ==============
function doty_custom_posts_join($where) {
  global $wpdb,$query_dates;
  $start_date = (int)$query_dates[0]-86400;
  $where .= "LEFT JOIN (
        SELECT post_id,($start_date - MIN(meta_value)) as orderdate
        FROM $wpdb->postmeta
        WHERE meta_key = 'dates'
        AND meta_value > $start_date 
        GROUP BY post_id) AS date_orders
        ON $wpdb->posts.ID = date_orders.post_id";
  return $where;
}

// Hooks into the 'ORDER BY' component of the SQL query
// ==============
function doty_custom_posts_orderby($orderby) {
  $orderby = " date_orders.orderdate DESC";
  return $orderby;
}

Mit posts_join_pages und posts_orderby wird die Art und Weise geändert, in der WP die SQL-Abfrage generiert, die sich aus der normalen WP_Query-Funktion ergibt. Im Einzelnen ist es:

  • Ermittelt das Startdatum des Bereichs aus einer globalen Variablen
  • Erstellt einen neuen LEFT JOIN, um eine orderdate-Variable zu erstellen, die aus dem nächstgelegenen Datum im Array ausgewählt wird, und prüft anschließend, ob dies in Zukunft der Fall ist.
  • Verwendet die Variable orderdate als Sortierschlüssel

Mir ist bewusst, dass ich ungefähr 6 Stunden damit verbracht habe, dies zusammen zu hacken, und dass es nicht das sauberste ist, aber hoffentlich hat es irgendwo einen Wert.

1
Jono Alderson