Использование произвольного поля в качестве заголовка записи
Я создал пользовательский тип записи с рядом произвольных полей, используя класс WPAlchemy. Я пытаюсь взять значение одного из этих полей и использовать его в качестве заголовка записи. Однако пока без успеха. Я изучил разные варианты и пробовал следующие два блока кода:
function custom_post_type_title ( $post_id ) {
global $wpdb;
if ( get_post_type( $post_id ) == 'listing' ) {
$title = get_post_meta($post_id, 'listing_name', true);
$where = array( 'ID' => $post_id );
$wpdb->update( $wpdb->posts, array( 'post_title' => $title ), $where );
}
}
add_action('init', 'listing_save_post');
function listing_save_post( $post_id ) {
if ( ! defined( 'DOING_AUTOSAVE' ) && ! DOING_AUTOSAVE ) return;
add_action('save_post', 'custom_post_type_title', 100);
add_action('publish_post', 'custom_post_type_title', 100);
}
И также (временно закомментировал nonce-поля, пока не разберусь, как они работают):
add_filter('wp_insert_post_data', 'change_title', 99, 2);
function change_title($data, $postarr)
{
// Если форма не отправлена, ничего не делаем
if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
// Проверяем, что данные пришли с нужной страницы и с правильными правами,
// так как save_post может вызываться в других случаях
// if(!isset($_POST['my_nonce_field'])) return;
// Если nonce установлен, проверяем его
// if(isset($_POST['my_nonce_field']) && !wp_verify_nonce($_POST['my_nonce_field'], plugins_url(__FILE__))) return;
// Объединяем адрес с термином
$title = $_POST['listing_name'];
$data['post_title'] = $title;
return $data;
}
В обоих случаях результат одинаков:
При сохранении новой записи страница перезагружается, показывает сообщение об успешном сохранении, но все произвольные поля оказываются пустыми. (Без этого кода пользовательский тип записи работает нормально, и поля сохраняются).
При просмотре списка записей пользовательского типа, записи не отображаются.
Есть идеи?
ОБНОВЛЕНИЕ
Для уточнения: я использую пользовательский тип записи без поддержки заголовка. Тем не менее я пытаюсь заполнить поле заголовка значением из произвольного поля.

Итак, каждый раз при сохранении записи вы хотите заменить значение заголовка, который только что был сохранён, на другое значение из произвольного поля...
Кажется, что проще сразу вносить нужный заголовок в соответствующее поле.
НО я предполагаю, что это делается для целей отображения: вы хотите, чтобы этот тип записи показывал другой заголовок на фронтенде, основанный на значении произвольного поля. Фильтры — это простой способ реализовать такое поведение.
the_title
, стандартный тег шаблона, является тонкой обёрткой вокруг get_the_title
, которая содержит фильтр под названием the_title
. Он получает два аргумента: сам заголовок и ID записи. Подключитесь к этому фильтру и измените заголовок на основе вашего произвольного поля.
<?php
add_filter( 'the_title', 'wpse33385_filter_title', 10, 2 );
function wpse33385_filter_title( $title, $post_id )
{
if( $new_title = get_post_meta( $post_id, 'custom_field_name', true ) )
{
return $new_title;
}
return $title;
}
Несколько важных моментов о вашем коде:
Действия не получают аргументы произвольно. Например, функции, подключённые к init
, не получают никаких аргументов. Когда вызывается do_action
, первый аргумент — это название хука. Последующие аргументы передаются подключённым функциям, если это указано (через четвёртый, опциональный аргумент add_action
).
Вызов do_action( 'init' );
находится в wp-settings.php. Посмотрите, аргументов нет.
Так что вот такой код:
<?php
add_action('init', 'listing_save_post');
function listing_save_post( $post_id ) {
if ( ! defined( 'DOING_AUTOSAVE' ) && ! DOING_AUTOSAVE ) return;
add_action('save_post', 'custom_post_type_title', 100);
add_action('publish_post', 'custom_post_type_title', 100);
}
Не будет работать так, как вы ожидаете. Более того, вызовы add_action
внутри функции можно просто вынести наружу... Вот так будет работать:
add_action('save_post', 'custom_post_type_title', 100);
function custom_post_type_title( $post_id ) {
// здесь действия
}
Нужно использовать только save_post
, а не его вместе с publish_post
.
Я всегда стараюсь избегать прямого обращения к $wpdb
, если это возможно, потому что часто есть более удобные API. То, что вы пытались сделать — это обновление записи. Используйте wp_update_post
. В вашем случае это не подходящий вариант (как указано в кодексе, это может вызвать бесконечный цикл).
Это получилось довольно многословно, извините. Надеюсь, это прояснило некоторые моменты о системе хуков WordPress!
Сделайте эти ресурсы своими лучшими друзьями:
- База данных хуков WP: http://adambrown.info/p/wp_hooks/hook
- WordPress X ref http://xref.yoast.com/

Кристофер, спасибо за развернутые мысли! Я внимательно их изучу. Чтобы уточнить, я на самом деле пытаюсь создать пользовательский тип записи без использования поля заголовка, а затем отображать заголовок (составленный из произвольного поля) на фронтенде и в бэкенде (в списке пользовательских записей). Это проясняет ситуацию?

Фильтр, который я вам показал, сработает для этого. Если вы хотите полностью убрать поле заголовка, в массиве $args
для register_post_type
есть аргумент под названием post_type_supports
, установите его так: 'post_type_supports' => array( 'content' )
, и поле заголовка исчезнет с экрана редактирования.

Это сработало! Итак, я использую фильтр для достижения нужного эффекта. Если бы я попытался получить значение, скажем, через WPDB, поля post_title, я бы получил значение "auto draft" - верно? "Перфекционист" во мне хочет это исправить, хотя я понимаю, что "фильтр" работает. Есть мысли? ОГРОМНОЕ спасибо за помощь!

Спасибо, ваш фильтр отлично сработал.
К вашему сведению:
Я добавил проверку на админку, чтобы избежать изменения entry-title на мой кастомный "longtitle" (по умолчанию = menu-item-title) во время редактирования страницы.
function pcdg_filter_title( $title, $post_id )
{
if(!is_admin())
{
if( $new_title = get_post_meta( $post_id, 'longtitle', true ) )
{
return $new_title;
}
}
return $title;
}
add_filter( 'the_title','pcdg_filter_title',10,2 );
