web-dev-qa-db-fra.com

Type de message personnalisé en tant que page parent

Il existe donc un million d'extraits expliquant comment obtenir des pages en tant que parents de types d'articles personnalisés.

Cependant, l'inverse semble discutable. On pourrait penser, puisque tout dans WordPress est techniquement un "post", ce serait trivial. Cependant, ce n'est pas le cas.

Jusqu'à présent, j'ai:

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

Et bien sûr, les pages me permettent maintenant de sélectionner les publications de type portal- en tant que parents. Les URLs semblent s’inscrire correctement dans Wordpress, bien qu’ils 404 tous.

Comment puis-je obtenir wordpress pour comprendre cette hiérarchie et charger les pages qui ont des publications en tant que parents?

5
Qix

WordPress utilise un ensemble de règles de réécriture pour pouvoir convertir une URL en requête sur une base de données.

La regex qui gère les URL pour les pages est très générale, le code IIRC est quelque chose comme (.+.?)/?, il écrit pratiquement tout n'a pas déjà été égalé par d'autres règles.

Pour cette raison, il est impossible d'écrire une règle de réécriture qui fonctionnera dans votre cas: parce que vous ne pouvez pas distinguer via regex que dans une URL telle que example.com/my-portal/sample-page, la partie 'mon-portail' est un CPT et sample-page est une page.

Les choses deviennent plus complexes si vous avez plus de niveaux d'imbrication: my-portal/my-portal-child/sample-page.

Pour gérer ce type d’URL, WordPress utilise get_page_by_path() function: il explose l’URL de la page par /, obtenant ainsi des slugs de page, puis interroge la base de données de tous les pages contenant ces slug.

Par exemple, si vous avez une page dont le slug est "sample-page" et que vous définissez comme parent le CPT "mon-portail" appels WordPress:

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

mais il ne ne renvoie aucun résultat car il recherche une page avec un slug 'page-échantillon' dont le parent est un autre page avec un slug 'mon-portail'. Cette page n'existe pas, vous obtenez donc l'erreur 404.

Cependant, get_page_by_path() accepte comme 3ème argument un tableau de types de publications: si vous le définissez sur array('page', 'portal'), la fonction pourra trouver correctement la page.

Vous pouvez donc résoudre le problème en définissant manuellement l'ID de la page (récupérée comme expliqué ci-dessus) dans WP vars de requête.

Le 'parse_request' hook est parfait pour la portée:

  • il court après que l'url a été analysé
  • il transmet aux callbacks l'instance d'objet $wp que vous pouvez utiliser pour définir les vars de requête

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
    }
  }
});

Ce code, associé au filtre dans OP, est tout ce dont vous avez besoin.

Notez que le code fonctionne même avec des portails hiérarchiques imbriqués.

7
gmazzap

Donc, réponse de @ gmazzap était un point dans la bonne direction. Cependant, pour une raison quelconque, var_dump($wp->query_vars) n'affichait qu'un tableau avec une clé, attachment, ce qui n'a pas aidé du tout.

Cependant, il a fait montre comment je pouvais y parvenir en utilisant un peu plus de ruse.

Le script ci-dessous obtient une page uniquement si la hiérarchie est correcte. c'est-à-dire que le parent de chaque slug met en correspondance une publication avec le slug approprié (0 pour le premier slug, bien sûr).

De plus, il saute les chemins avec 0 ou 1 slug simplement parce que ceux-ci désignent généralement des pages spéciales que WordPress devrait gérer (de plus, peu importe en termes de hiérarchie des pages ...)

Et non, la requête ne peut pas vraiment être trop optimisée. Cela est dû au fait que MySQL ne prend pas en charge les sélections/jointures récursives comme vous le souhaiteriez dans un modèle de données hiérarchique.

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