Получение постоянных иерархических пользовательских типов записей для работы точно так же, как страницы

16

Я перебрал каждый вопрос здесь о пользовательских постоянных типах записей, но в большинстве случаев это либо проблемы с пользовательскими изменениями таксономии, либо очевидное отсутствие flush_rewrite_rules (). Но в моем случае я использую только пользовательский тип записи (без таксономии), установленный как иерархический (чтобы я мог назначать отношения родитель-потомок), с надлежащей «поддержкой» для атрибута metabox и т. Д. И т. Д. Переписал правила тысячами разных способов. Я пробовал разные постоянные структуры. Но дочерние URL всегда приводят к 404!

Первоначально у меня были независимые пользовательские типы записей для «родительских» и «дочерних» элементов (с использованием p2p), и у меня, вероятно, не было бы проблем с использованием таксономии для «родительской» группировки - я знаю, что они были бы семантически более точными. Но для клиента им проще всего представить иерархию, когда «сообщения» отображаются в админке так же, как страницы: простое дерево, в котором дочерние элементы отображаются под родительским элементом с префиксом «-» и в правильный порядок. Также могут быть использованы различные методы для назначения порядка с помощью drag-n-drop. Группировка через таксономию (или p2p) приводит к простому списку «постов» в списках администратора, что просто не так визуально очевидно.

Так что я в буквальном смысле точно такое же поведение, как основные "страницы", но с моим пользовательским типом поста. Я зарегистрировал тип поста так, как ожидалось, и в админке он работает отлично - я могу назначить родителя и menu_order для каждого «поста» новостной рассылки, они правильно отображаются в списках редактирования:

Spring 2012
 First Article
 Second Article

И их постоянные ссылки выглядят правильно. Фактически, если я изменяю что-либо в структуре или даже изменяю слизь перезаписи при регистрации типа записи, они автоматически обновляются правильно, поэтому я знаю, что что-то работает:

http://mysite.com/parent-page/child-page/                  /* works for pages! */
http://mysite.com/post-type/parent-post/child-post/        /* should work? */
http://mysite.com/newsletter/spring-2012/                  /* works! */
http://mysite.com/newsletter/spring-2012/first-article/    /* 404 */
http://mysite.com/newsletter/spring-2012/second-article/   /* 404 */

У меня также есть стандартные базовые «страницы» с созданными иерархическими отношениями, и они выглядят одинаково в админке, но на самом деле они работают и во внешнем интерфейсе (родительские и дочерние URL-адреса работают нормально).

Моя структура постоянных ссылок настроена на:

http://mysite.com/%postname%/

Я также пытался это сделать (просто потому, что многие другие ответы указывали на то, что это необходимо, хотя в моем случае это не имело смысла):

http://mysite.com/%category%/%postname%/

Мой регистр CPT аргументы включают в себя:

$args = array(
    'public'                => true,
    'publicly_queryable'    => true,
    'show_ui'               => true,
    'has_archive'           => 'newsletter',
    'hierarchical'          => true,
    'query_var'             => true,
    'supports'              => array( 'title', 'editor', 'thumbnail', 'page-attributes' ),
    'rewrite'               => array( 'slug' => 'newsletter', 'with_front' => false ),

Единственное видимое различие между моими настраиваемыми дочерними типами записей и обычными дочерними страницами состоит в том, что мой CPT имеет слаг в начале структуры постоянной ссылки, за которым следуют слагаемые родительский / дочерний (где страницы только начинаются с слагаемых родительский / дочерний, нет "префикса"). Почему это все испортило, я не знаю. Множество статей, кажется, указывают, что именно так должны вести себя такие иерархические постоянные ссылки CPT - но мои, хотя и хорошо сформированные, не работают.

Что также сбивает с толку, так это когда я проверяю query_vars для этой страницы 404 - они, кажется, содержат правильные значения для WP, чтобы «найти» мои дочерние страницы, но что-то не работает.

$wp_query object WP_Query {46}
public query_vars -> array (58)
'page' => integer 0
'newsletter' => string(25) "spring-2012/first-article"
'post_type' => string(10) "newsletter"
'name' => string(13) "first-article"
'error' => string(0) ""
'm' => integer 0
'p' => integer 0
'post_parent' => string(0) ""
'subpost' => string(0) ""
'subpost_id' => string(0) ""
'attachment' => string(0) ""
'attachment_id' => integer 0
'static' => string(0) ""
'pagename' => string(13) "first-article"
'page_id' => integer 0
[...]

Я пробовал это с различными темами, включая двадцать двенадцать, просто чтобы убедиться, что это не какой-то пропущенный шаблон с моей стороны.

Используя Инспектор правил перезаписи, вот что отображается для URL: http://mysite.com/newsletter/spring-2012/first-article/

newsletter/(.+?)(/[0-9]+)?/?$   
       newsletter: spring-2012/first-article
           page: 
(.?.+?)(/[0-9]+)?/?$    
       pagename: newsletter/spring-2012/first-article
           page: 

как это отображается на другой странице инспектора:

RULE:
newsletter/(.+?)(/[0-9]+)?/?$
REWRITE:
index.php?newsletter=$matches[1]&page=$matches[2]
SOURCE:
newsletter

Этот результат перезаписи заставит меня поверить, что будет работать следующая «не красивая» постоянная ссылка:

http://mysite.com/?newsletter=spring-2012&page=first-article

Это не 404, но он показывает родительский пункт CPT "информационный бюллетень", а не дочерний элемент. Запрос выглядит так:

Array
(
    [page] => first-article
    [newsletter] => spring-2012
    [post_type] => newsletter
    [name] => spring-2012
)
соматический
источник

Ответы:

15

Я впервые участвую здесь на Stack Exchange, но я попробую и посмотрю, смогу ли я помочь вам указать верное направление.

По умолчанию иерархические CPT ведут себя точно так, как вы описали. Уникальный префикс slug, в данном случае «информационный бюллетень», позволяет механизму перезаписи знать, как различать запросы для разных типов записей.

Эти аргументы регистрации CPT выглядят хорошо, но когда запрашивается CPT, pagenameпеременная запроса не должна иметь значения, а nameпеременная запроса должна быть такой же, как newsletterздесь, поэтому кажется, что где-то в вашей настройке есть конфликт.

Чтобы помочь отладке, установите и активируйте плагин Rewrite Rules Inspector , затем перейдите на экран в « Tools -> Rewrite Rules ».

  1. Просматривая список, все ваши правила для CPT новостной рассылки должны быть перечислены до того, как правила перезаписи любой страницы. Убедитесь в этом, просканировав столбец « Источник ».
  2. Если это подтвердится, введите URL для вашей CPT «первая статья» в поле « Match URL » и нажмите кнопку «Фильтр», чтобы увидеть, какое правило соответствует. Он должен соответствовать правилу рассылки и правилу страницы, но правило рассылки должно быть первым.

Если это не выявит каких-либо проблем, выполните поиск в post_nameстолбце, wp_postsчтобы найти другие сообщения с слагом «первая статья», чтобы выяснить, не произошло ли столкновение. Ищите «новостную рассылку», чтобы быть уверенным.

Добавьте следующий фрагмент кода, чтобы проверить свои запросы в начале запроса, чтобы проверить и посмотреть, какие из них устанавливаются в соответствии с правилом перезаписи (посетите дочерний CPT во внешнем интерфейсе). Если pagenamevar здесь не появляется, тогда он устанавливается чем-то позже в запросе:

add_filter( 'request', 'se77513_display_query_vars', 1 );

function se77513_display_query_vars( $query_vars ) {
    echo '<pre>' . print_r( $query_vars, true ) . '</pre>';

    return $query_vars;
}

Любые плагины / функции, которые изменяют переменные запросов, могут вызывать конфликты, поэтому отключите их, если вы все еще видите проблемы. Также очищайте правила перезаписи после каждого шага, особенно если запрос соответствует неправильному правилу на втором шаге выше (оставьте экран «Постоянные ссылки» открытым на отдельной вкладке и просто обновите его).

Брейди Верчер
источник
Я отредактировал вопрос, чтобы показать вывод из инспектора правил перезаписи, так как эти комментарии не позволяют достаточно форматировать. Правила для CPT отображаются вверху списка, прежде чем что-либо еще, но не уверены, какие из приведенных ниже правил являются правилами «страницы» (не ясно). Также нет столкновений в post_nameколонке.
соматическое
Есть ли у вас какие-либо мысли о правильной структуре постоянных ссылок? Я вернулся к тому, что /%postname%/включение /%category%/инспектора переписывания, похоже, только сбивает с толку. Я даже видел, как люди утверждают, что /%category%/СРТ волшебным образом означает «родитель», хотя я не считаю, что это правда.
соматическое
Похоже, что этот вывод приходит для другого плагина, поэтому вы не видите «источник» правила. В любом случае, похоже, что ваше правило соответствует отлично. Я отредактировал свой ответ, включив в него фрагмент кода, чтобы проверить варианты запроса ранее в запросе, чтобы убедиться, что они установлены правильно.
Брэди Верчер
Что касается структуры постоянных ссылок, я не фанат включения категории. Это просто еще одна движущаяся часть, которая ничего не помогает, и сообщения могут принадлежать нескольким категориям. Если есть хоть какой-то шанс, что вы можете изменить структуру постоянных ссылок в будущем или захотите, чтобы другая CPT загружалась без префикса слагов, я бы порекомендовал «пространство имен» вашей структуры с чем-то фиксированным и уникальным, например /blog/%postname%/.
Брэди Верчер
это правильный плагин, но есть две страницы: «rewrite анализатор» и «rewrite rules», обе из которых предлагают тестовый URL. Я попробовал другую страницу, и она показала источник как «программу» для первой группы с отступом и «страницу» для второй.
соматическое
5

Постоянная parent/Childссылка работает из коробки, пока вы установите

'hierarchical'=> true,
'supports' => array('page-attributes' ....

Обновить:

Я только что проверил это снова, и он работает, как ожидалось, с этим тестовым примером:

add_action('init','test_post_type_wpa77513');
function test_post_type_wpa77513(){
    $args = array(
        'public' => true,
        'publicly_queryable' => true,
        'show_ui' => true, 
        'show_in_menu' => true, 
        'query_var' => true,
        'rewrite' => true,
        'capability_type' => 'post',
        'has_archive' => true, 
        'hierarchical' => true,
        'supports' => array( 'title', 'editor', 'thumbnail', 'page-attributes' )
    ); 

    register_post_type( 'newsletter', $args );
}

и постоянная ссылка установлена, чтобы /%postname%/я получил информационный бюллетень / родитель / ребенок, работающий просто отлично.

Bainternet
источник
мой код выше не показывал его, но у меня есть page-attributesподдержка (которая на самом деле только показывает метабокс для назначения происхождения, не должен влиять на постоянные ссылки) - но получение 404. Какую структуру постоянных ссылок нужно установить? или это имеет значение?
соматическое
@ Somatic Я только что проверил его снова и он работает нормально, что касается постоянной ссылки, я не уверен, если это имеет значение, но в любом случае я обновил свой ответ.
Bainternet
Я могу подтвердить, что с добавлением массива аргументов для «меток» (без которого CPT не будет отображаться в admin), этот базовый пример действительно работает на ванильной установке WP3.5 с twentlytwelve. Но я заметил несколько ключевых отличий в ваших register_post_type$ args: я использовал 'rewrite' => array( 'slug' => 'newsletter', 'with_front' => false ), где вы использовали только true. Кроме того, просто прошел trueдля has_archive, где я прошел слизняк. Однако, когда я совпал с твоими аргументами, у меня все равно было 404 ... Все еще пытаюсь отследить, где я иду не так.
соматический
3

Помимо вопроса: « Вы пытались выключить и снова включить его?» , Я также должен спросить: «Есть ли у вас какие-либо плагины, и ваша тема что-то делает с постоянными ссылками (например, для регистрации таксономии, типов записей, добавления правил перезаписи)? , и т.д.)?

Если так: происходит ли это после того, как вы отключили все плагины и переключились на TwentyEleven? введите описание изображения здесь

Для дальнейшей отладки перейдите на GitHub и возьмите плагин Toschos "Rewrite" . Затем переключитесь на официальный репозиторий плагинов на wp.org и установите плагин MonkeyManRewriteAnalyzer . Я даже написал небольшое расширение, чтобы склеить немного вместе. Это даст вам много деталей о вашей настройке.

кайзер
источник
хорошо, если я отключу все , у меня не будет пользовательского типа поста, который является пунктом этого вопроса ... но да, я пробовал даже с двадцатью двенадцатью темами. Я все еще копаюсь в нескольких необходимых заглушках, чтобы увидеть, что искажает запрос, поскольку, как я вижу, сами постоянные ссылки хорошо сформированы.
соматическое
@somatic Конечно, не отключаю ваш :)
Кайзер
@somatic Смотрите обновление.
Кайзер
1

Поэтому после подтверждения того, что я могу получить ожидаемое иерархическое поведение страницы с полностью чистой установкой и всего лишь одним объявлением CPT, я понял, что ошибка лежит где-то в моем собственном плагине, который я использую для обработки создания CPT (очень сложный, обрабатывает пользовательские метабоксы, таксономии , и т.д). Проблема заключалась в том, что, несмотря на все советы по проверке проблем с перезаписью или запросами, я не видел ничего явно неправильного. Я проверил каждый фильтр и хук, просматривая запрос в каждой точке и не видя ничего, что могло бы вызвать 404.

Поэтому мне пришлось вручную отключить / включить каждый из 9 больших классов, а затем найти, по крайней мере, 2 из них, вызывающих 404, пройти каждую функцию по одной и отключить / включить их, а затем проследить линию построчно в рамках этих функций - попытка грубой силы выяснить, что именно вызвало 404, даже если я не знаю, почему.

Вот тогда я обнаружил, что есть следствие использования $query->get_queried_object(). Казалось бы, использование этой функции-обертки фактически изменяет сам запрос $, который, как я думал, я возвращал неизменным в конце своей функции. Три фильтра через 2 класса были вовлечены , что модифицированный запрос: parse_query, posts_orderby, posts_join- и все функции обратного вызова были вызова $query->get_queried_object()на пройденный $queryарг , чтобы затем запустить несколько условных тестов , а иногда и изменить запрос вары (например , для особых случаев порядка сортировки). Как ни странно, эти функции действительно работали нормально, несмотря на то, что я использовал эту функцию. Я никогда не замечал ничего плохого в возвращенном $ запросе!

Каким-то образом, после более чем года разработки и использования на десятках живых производственных площадок, я никогда не испытывал никаких негативных последствий от этой ошибки. Только когда я решился на иерархические CPT, эта маленькая разница внезапно сломала вещи. Это часть того, что меня так сильно потрясло - насколько я мог судить, мои фильтры запросов были заслуживающими доверия!

Признаюсь, я до сих пор не знаю точно, почему вызов этой функции нарушает только этот маленький аспект дочерних страниц CPT - и все же никогда не проявлял никаких других проблем! Но было ясно, что использование его в обратном вызове фильтра $queryкаким-то образом исказило возвращаемое . При удалении этого звонка мои 404 ошибки исчезли.

Спасибо за все советы - хотелось бы разделить щедрость, поскольку я получил представление о каждом ответе, даже если окончательное решение было несколько не связано. Это был учебный урок о том, чтобы не слепо доверять вашему коду, даже если он долгое время работал на вас надежно или не выдает никаких очевидных ошибок.

Иногда, как наглядно воплощает график Кайзера, вам просто нужно начать переключаться, пока не загорятся огни. В моем случае мне пришлось использовать одну и ту же стратегию устранения неполадок вплоть до отдельных строк в функции, прежде чем я смог увидеть проблему.

соматический
источник
Вы также можете использовать функцию-оболочку, get_queried_object()не перехватывая $wpdbобъект.
Кайзер
но что $queryэто захватывает? Я использую это в фильтре для parse_query, и мне нужно специально получить запрашиваемый объект конкретного, $queryкоторый передается из фильтра ...
соматический
Я просто попытался использовать обертку внутри моего фильтра для parse_query. Это снова вызвало те же 404 ошибки. Я должен найти способ повторить то, что get_queried_object()делает, не загрязняя эти фильтры ...
соматический
-2

Вы используете самую последнюю версию WP? Я думаю, что это первый вопрос (например, когда вы звоните в службу технической поддержки, и они спрашивают вас, подключен ли ваш компьютер!), Так как я прочитал, что эта проблема была решена в самой последней версии обновления.

На digwp.com есть сообщение, посвященное этой проблеме (http://digwp.com/2011/06/dont-use-postname/). Очевидно, что WP не может легко определить разницу между сообщениями и страницами, и если вы начинаете свои URL-адреса с текстовой опцией, WP запускает флаг, который создает правило для каждой вашей страницы / публикации. Если у вас много контента на вашем сайте, это может привести к огромному количеству запросов, и ваш сервер, вероятно, истечет время ожидания, что приведет к куче 404, даже если страницы там есть.

Так. Если вы сначала используете числовую структуру, а затем текстовую структуру, я бы поспорил, что ваша проблема 404 будет решена. Теперь, учитывая проблемы SEO, я бы не стал использовать структуру дат, но принятие этого решения будет достаточно логичным для вас.

Мелани Самнер
источник
2
Я на 3.5. Начиная с WP3.3.1 эта проблема в digiwp исправлена.
соматическое