it-swarm.com.de

Benutzerdefinierter Beitragstyp als übergeordnete Seite

Es gibt also eine Million Schnipsel, mit denen Sie Seiten als Eltern von benutzerdefinierten Beitragstypen abrufen können.

Das Gegenteil scheint jedoch fraglich zu sein. Man würde denken, da alles in WordPress technisch gesehen ein "Post" ist, wäre dies trivial. Ist es aber nicht.

Bisher habe ich:

add_filter( 'page_attributes_dropdown_pages_args',
  function( $dropdown_args, $post = null ) {
    $dropdown_args['post_type'] = 'portal';
    return $dropdown_args;
  } );

Auf den Seiten kann ich jetzt portal Posts als Eltern auswählen. Die URLs scheinen in WordPress korrekt zu sein, obwohl sie alle 404 sind.

Wie kann ich WordPress dazu bringen, diese Hierarchie zu verstehen und die Seiten mit Beiträgen als Eltern zu laden?

5
Qix

WordPress verwendet eine Reihe von Umschreiberegeln, um eine URL in eine Datenbankabfrage umzuwandeln.

Der reguläre Ausdruck, mit dem URLs für Seiten behandelt werden, ist sehr allgemein gehalten. IIRC ist so etwas wie (.+.?)/?, prinzipiell muss nicht alles bereits mit anderen Regeln übereinstimmen.

Aus diesem Grund ist es nicht möglich, eine Umschreiberegel zu schreiben, die in Ihrem Fall funktioniert: Da Sie über reguläre Ausdrücke nicht unterscheiden können, dass in einer URL wie example.com/my-portal/sample-page der Teil 'my-portal' ein CPT und sample-page eine Seite ist.

Die Dinge werden komplexer, wenn Sie mehr Verschachtelungsebenen haben: my-portal/my-portal-child/sample-page.

Um diese Art von URLs zu verarbeiten, verwendet WordPress die Funktion get_page_by_path() : Es explodiert die Seiten-URL nach /, ruft Seiten-Slugs ab und fragt dann die Datenbank nach allen pages ab, die diese Slugs enthalten.

Zum Beispiel, wenn Sie eine Seite haben, deren Slug "sample-page" ist, und Sie als übergeordnetes Element dafür das CPT "my-portal" festlegen, das WordPress aufruft:

get_page_by_path('my-portal/sample-page')

aber es nicht gibt kein Ergebnis zurück, weil es nach einer Seite mit Slug 'sample-page' sucht, deren Eltern eine andere Seite mit Slug 'my-portal' ist. Diese Seite existiert nicht, daher wird der Fehler 404 angezeigt.

get_page_by_path() akzeptiert jedoch als drittes Argument ein Array von Beitragstypen: Wenn Sie es auf array('page', 'portal') setzen, kann die Funktion die Seite korrekt finden.

Sie können das Problem also lösen, indem Sie die Seiten-ID (abgerufen wie oben beschrieben) manuell in WP Abfragevariablen setzen.

Der 'parse_request' Haken ist perfekt für den Umfang:

  • es wird ausgeführt, nachdem die URL analysiert wurde
  • er übergibt an Hooking Callbacks die Instanz des Objekts $wp, mit der Sie Abfragevariablen festlegen können

Code:

add_action('parse_request', function ($wp) {
  // only if WP found a page
  if (isset($wp->query_vars['pagename']) && ! empty($wp->query_vars['pagename'])) {
    $page = get_page_by_path( // let's find the page object
        $wp->query_vars['pagename'],
        OBJECT,
        array('page', 'portal') // we need to set both post types
    );
    if ($page instanceof WP_Post) { // if we find a page
        unset($wp->query_vars['pagename']); // remove pagename var
        $wp->query_vars['page_id'] = $page->ID; // replace with page_id query var
    }
  }
});

Dieser Code in Kombination mit dem Filter in OP ist alles, was Sie brauchen.

Beachten Sie, dass Code auch mit verschachtelten hierarchischen Portalen funktioniert.

7
gmazzap

Also war @ gmazzaps Antwort ein Punkt in die richtige Richtung. Aus irgendeinem Grund zeigte var_dump($wp->query_vars) jedoch nur ein Array mit einem Schlüssel, attachment, was überhaupt nicht half.

Es did zeigte jedoch, wie ich dies mit etwas mehr Trick erreichen konnte.

Das folgende Skript ruft nur dann eine Seite ab, wenn die Hierarchie korrekt ist. Das heißt, das übergeordnete Element jeder Schnecke vergleicht einen Beitrag mit der entsprechenden Schnecke (0 natürlich für die erste Schnecke).

Außerdem werden Pfade mit 0 oder 1 Slugs übersprungen, weil dies normalerweise spezielle Seiten bedeutet, mit denen WordPress umgehen sollte (auch in Bezug auf die Seitenhierarchie spielt das keine Rolle ...).

Und nein, die Abfrage kann nicht wirklich zu stark optimiert werden. Dies liegt daran, dass MySQL keine rekursiven Auswahlen/Verknüpfungen unterstützt, wie Sie sie in einem hierarchischen Datenmodell möchten.

function get_page_by_slug_path( $path ) {
  global $wpdb;
  global $table_prefix;

  $slugs = explode(
    '/',
    preg_replace(
      '~\/+~',
      '/',
      preg_replace(
        '~^\/*(.+?)\/*$~',
        '$1',
        $path
      )
    )
  );

  # Skip if slugs length is 1 or 0, of course.
  #   A single 'slug' URL may mean something completely different, so
  #   we'll bank on WordPress knowing what to do with it.
  if( count( $slugs ) < 2 ) {
    return;
  }

  $parents = [ 0 ];
  foreach( $slugs as $slug ) {
    $sql = $wpdb->prepare(
      "SELECT ID FROM ${table_prefix}posts WHERE post_name='%s' AND post_parent IN ("
      . implode( ',', $parents )
      . ")",
      $slug
    );

    $results = $wpdb->get_results( $sql );

    if( count( $results ) === 0 ) {
      return null;
    }

    $parents = array_map(
      function( $elem ) {
        return $elem->ID;
      },
      $results
    );

  }

  if( count( $parents ) > 1 ) {
    trigger_error( E_USER_WARNING, "Multiple IDs for this page slug: "
      . implode( ', ', $parents ) );
  }

  return intval( $parents[0] );
}

add_action('parse_request', function ($wp) {
  $path = parse_url(
    "http://" . $_SERVER['HTTP_Host'] . $_SERVER['REQUEST_URI'],
    PHP_URL_PATH
  );

  $pageID = get_page_by_slug_path( $path );

  if( $pageID ) {
    $wp->query_vars = [ 'page_id' => $pageID ];
  }
});
0
Qix