Cómo establecer una relación padre-hijo entre diferentes tipos de contenido personalizados
Acabo de configurar una relación post/padre entre un tipo de contenido "episodes" y un tipo de contenido "cartoon-series".
Utilicé este código para agregar el meta box que asigna el padre desde otro tipo de contenido:
add_action('admin_menu', function() {
remove_meta_box('pageparentdiv', 'episodes', 'normal');
});
add_action('add_meta_boxes', function() {
add_meta_box('episodes-parent', 'Series de Dibujos', 'episodes_attributes_meta_box', 'episodes', 'side', 'default');
});
function episodes_attributes_meta_box($post) {
$post_type_object = get_post_type_object($post->post_type);
if ( $post_type_object->hierarchical ) {
$pages = wp_dropdown_pages(array('post_type' => 'cartoon-series', 'selected' => $post->post_parent, 'name' => 'parent_id', 'show_option_none' => __('(sin padre)'), 'sort_column'=> 'menu_order, post_title', 'echo' => 0));
if ( ! empty($pages) ) {
echo $pages;
} // fin de verificación de páginas vacías
} // fin de verificación jerárquica
}
Eso funcionó en la pantalla de administración permitiéndome establecer la serie como padre del episodio, pero cuando intento ver la entrada, obtengo un 404. La estructura de URL es:
domain/episodes/series-name/episode-name
La URL para la serie es:
domain/cartoon-series/series-name
Me gustaría que la URL para el episodio fuera:
domain/cartoon-series/series-name/episode-name
¿Qué me estoy perdiendo? ¿Es posible hacer que un tipo de contenido completo sea hijo de otro tipo de contenido? De esta manera, incluso podría obtener la URL para la lista de episodios como:
domain/cartoon-series/series-name/episodes
¡Gracias! Matt
Como solicitado, aquí está el código para los dos tipos de contenido personalizados en cuestión:
$labels = array(
"name" => "Series de Dibujos",
"singular_name" => "Serie de Dibujos",
"menu_name" => "Series de Dibujos",
"all_items" => "Todas las Series de Dibujos",
"add_new" => "Añadir Nueva",
"add_new_item" => "Añadir Nueva Serie de Dibujos",
"edit" => "Editar",
"edit_item" => "Editar Serie de Dibujos",
"new_item" => "Nueva Serie de Dibujos",
"view" => "Ver",
"view_item" => "Ver Serie de Dibujos",
"search_items" => "Buscar Series de Dibujos",
"not_found" => "No se encontraron Series de Dibujos",
"not_found_in_trash" => "No se encontraron Series de Dibujos en la papelera",
"parent" => "Serie de Dibujos Padre",
);
$args = array(
"labels" => $labels,
"description" => "",
"public" => true,
"show_ui" => true,
"has_archive" => true,
"show_in_menu" => true,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => true,
"rewrite" => array( "slug" => "cartoon-series", "with_front" => true ),
"query_var" => true,
"supports" => array( "title", "revisions", "thumbnail" ), );
register_post_type( "cartoon-series", $args );
$labels = array(
"name" => "Episodios",
"singular_name" => "Episodio",
);
$args = array(
"labels" => $labels,
"description" => "",
"public" => true,
"show_ui" => true,
"has_archive" => true,
"show_in_menu" => true,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => true,
"rewrite" => array( "slug" => "episodes", "with_front" => true ),
"query_var" => true,
"supports" => array( "title", "revisions", "thumbnail" ), );
register_post_type( "episodes", $args );
Estoy usando el plugin CPT UI, así que no puedo editar ese código directamente. Ese es solo el código de exportación que proporciona CPT UI.
No tengo ningún otro código que vincule los dos CPTs. Tal vez eso es lo que me falta. Solo encontré ese código en línea que coloca el metabox en la página para hacer la vinculación. ¿No es suficiente para hacer el trabajo? Parece que establece el post_parent.
¡Gracias! Matt

Finalmente encontré una solución funcional. Las series de caricaturas pueden registrarse como lo hiciste, pero los tipos de contenido personalizados para episodios no pueden ser jerárquicos (creo que WordPress espera que el contenido padre sea del mismo tipo que el contenido hijo si la relación se establece usando post_parent
en la tabla de base de datos wp_posts
).
Al registrar los episodios, la regla de reescritura debe configurarse con el slug que deseas, es decir cartoon-series/%series_name%
. Luego podemos filtrar el enlace de los episodios para reemplazar %series_name%
con el nombre real del tipo de contenido padre cartoon-series
y una regla de reescritura para indicarle a WordPress cuándo se solicita un tipo de contenido de serie de caricaturas y cuándo es un episodio.
add_action('init', function(){
$labels = array(
"name" => "Series de Caricaturas",
"singular_name" => "Serie de Caricaturas",
"menu_name" => "Series de Caricaturas",
"all_items" => "Todas las Series",
"add_new" => "Añadir Nueva",
"add_new_item" => "Añadir Nueva Serie",
"edit" => "Editar",
"edit_item" => "Editar Serie",
"new_item" => "Nueva Serie",
"view" => "Ver",
"view_item" => "Ver Serie",
"search_items" => "Buscar Series",
"not_found" => "No se encontraron Series",
"not_found_in_trash" => "No hay Series en la Papelera",
"parent" => "Serie Padre",
);
$args = array(
"labels" => $labels,
"description" => "",
"public" => true,
"show_ui" => true,
"has_archive" => true,
"show_in_menu" => true,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => true,
"rewrite" => array( "slug" => "cartoon-series", "with_front" => true ),
"query_var" => true,
"supports" => array( "title", "revisions", "thumbnail" )
);
register_post_type( "cartoon-series", $args );
$labels = array(
"name" => "Episodios",
"singular_name" => "Episodio",
);
$args = array(
"labels" => $labels,
"description" => "",
"public" => true,
"show_ui" => true,
"has_archive" => true,
"show_in_menu" => true,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => false,
"rewrite" => array( "slug" => "cartoon-series/%series_name%", "with_front" => true ),
"query_var" => true,
"supports" => array( "title", "revisions", "thumbnail" )
);
register_post_type( "episodes", $args );
});
add_action('add_meta_boxes', function() {
add_meta_box('episodes-parent', 'Series de Caricaturas', 'episodes_attributes_meta_box', 'episodes', 'side', 'default');
});
function episodes_attributes_meta_box($post) {
$pages = wp_dropdown_pages(array('post_type' => 'cartoon-series', 'selected' => $post->post_parent, 'name' => 'parent_id', 'show_option_none' => __('(sin padre)'), 'sort_column'=> 'menu_order, post_title', 'echo' => 0));
if ( ! empty($pages) ) {
echo $pages;
} // fin de verificación de páginas vacías
}
add_action( 'init', function() {
add_rewrite_rule( '^cartoon-series/(.*)/([^/]+)/?$','index.php?episodes=$matches[2]','top' );
});
add_filter( 'post_type_link', function( $link, $post ) {
if ( 'episodes' == get_post_type( $post ) ) {
//Vamos a obtener el nombre de la serie padre
if( $post->post_parent ) {
$parent = get_post( $post->post_parent );
if( !empty($parent->post_name) ) {
return str_replace( '%series_name%', $parent->post_name, $link );
}
} else {
//Esto parece no funcionar. Está destinado a construir enlaces permanentes bonitos
//cuando los episodios no tienen padre, pero parece que necesitaría
//reglas de reescritura adicionales
//return str_replace( '/%series_name%', '', $link );
}
}
return $link;
}, 10, 2 );
NOTA: Recuerda limpiar las reglas de reescritura después de guardar el código anterior y antes de probarlo. Ve a wp-admin/options-permalink.php
y haz clic en guardar para regenerar las reglas de reescritura.
NOTA 2: Es probable que haya que añadir más reglas de reescritura, por ejemplo para que funcione con posts paginados. También puede necesitar más trabajo para tener una solución completa, por ejemplo, al eliminar una cartoon-series
, ¿eliminar también todos los episodios hijos? ¿Añadir un filtro en la pantalla de edición del admin para filtrar episodios por padre? ¿Modificar el título de los episodios en la pantalla de edición del admin para mostrar el nombre de la serie padre?

¡Gracias por revisar esto! Parece que el código que has publicado está eliminando el nombre de la serie de dibujos animados de la URL. En lugar de reemplazar %series_name% con el nombre del episodio, %series_name% debería ser el nombre del padre del episodio. El nombre del episodio iría después de eso. Por alguna razón, el cuadro de Cartoon Series no se está llenando para que pueda seleccionar un padre. Por eso pensé que los episodios necesitaban ser jerárquicos. Estoy tratando de averiguar por qué.

Sí, los episodios tienen que ser jerárquicos para que el cuadro de metadatos de Cartoon Series se pueda llenar.

Con los episodios jerárquicos para poder establecer el padre, la URL empeoró. Con el slug como sugieres, obtengo el nombre de la serie en la URL dos veces. Entonces, en lugar de domain/episodes/series-name/episode-name
como antes, obtuve domain/episodes/series-name/series-name/episode-name

Como dije, los episodios no pueden ser jerárquicos. Modifiqué el código del meta box para que se llene con tipos de posts no jerárquicos. Usa el código exacto que publiqué, lo probé y está funcionando. Si usas otro código no puedo saber qué está mal. Solo copia y pega el código de la respuesta y pruébalo. Puede que necesites desactivar el plugin CPT UI o, al menos, eliminar los tipos de posts personalizados del plugin ya que están registrados en el código.

Ah, mis disculpas, lo revisé rápidamente y pensé que esa parte era igual. Tienes razón, la página ahora carga y la URL se ve correcta.

Si esto responde a tu pregunta, por favor, marca la respuesta como aceptada marcando la palomita debajo de las flechas de votación en la parte superior izquierda de la respuesta.

No es necesario codificar manualmente en este caso, puedes simplemente usar este plugin:
https://wordpress.org/plugins/add-hierarchy-parent-to-post/
Incluso puedes tomar código de él. Sin embargo, puede que no sea una solución completa.

Necesitarás escribir tu propio código de análisis de URL para eso, ya que WordPress necesita saber el tipo de publicación que intenta recuperar de la base de datos según la estructura de la URL, y tu estructura de URL no proporciona ninguna pista sobre esto.
Esto no es algo muy fácil de hacer con la API de reglas de reescritura de WordPress, pero no hay nada que te impida omitir el mecanismo de reescritura y analizar las URLs por tu cuenta. Algo como:
- Ejecutar las reglas de reescritura de WordPress. Si se encuentra contenido, mostrarlo y salir.
- Obtener la primera parte de la URL, verificar si hay una publicación que coincida con ese slug y el tipo de publicación esperado.
- Iterar sobre el resto de las partes de la URL, verificando que las publicaciones existan y sean del tipo correcto.
- Si todo coincide, mostrar la última publicación encontrada; de lo contrario, mostrar una página 404.
