Ако някога сте добавяли „Персонализиран тип публикация“ и след това сте откривали, че сте забравили постоянни връзки, възможности, защитени метабокси или правилна таксономия, знаете колко бързо това се изражда в мозайка.
Проблемът / Нуждата
Типична нужда: да се публикува съдържание, което не е нито статия, нито страница, със собствени полета (метаданни) и собствени категории/етикети. Конкретен пример: редакционен уебсайт, който иска да управлява „Казуси“ с клиент, дата на доставка, бюджет и „Сектори“ в таксономията.
Много уебсайтове започват с плъгин за персонализирани полета, след което набързо добавят плъгин за персонализирана обработка (CPT) и накрая сглобяват шаблон. Резултатът: лошо съхранени данни, несигурни полета, неработещи постоянни връзки и бек-офис, който е мъчителен за използване.
В крайна сметка ще разбереш КРЕЕ пълен CPT (WordPress 6.9.4, PHP 8.1+), с:
- персонализиран тип публикация „Казуси“ със собствени (етикети, медии, REST, архиви, възможности),
- две таксономии (една йерархична + една нейерархична),
- защитени метабокси (nonce, разрешения, санитизация),
- персонализирани колони в администраторския списък,
- „проливане“ на правила за пренаписване на правилното място (без нарушаване на производителността).
Бързо обобщение
- Създаваме мини-плъгин (препоръчително), вместо да поставяме код във functions.php.
- Записваме CPT
case_studyсshow_in_restактивирано (Gutenberg + REST API + конструктори). - Добавяме йерархична таксономия „Сектори“ (
sector) и таксономия „Етикети на проекти“ (project_tag). - Добавяме метабокс „Детайли“ (клиент, URL адрес на проект, бюджет, дата) със сигурно запазване.
- Подобряваме UX интерфейса на администратора: колони, сортиране, филтри.
- Тестваме правилно: постоянни връзки, възможности, автоматично запазване, ревизии, REST.
Кога да използвате това решение
- Имате повтарящ се тип съдържание (портфолио, събития, рецепти, препоръки, предложения за работа) със структурирани атрибути.
- Искате чист бек офис, без да отклонявате „Статиите“.
- Необходимо е това съдържание да е достъпно чрез блоковия редактор и REST API (Elementor/Divi/Avada често разчитат на него).
- Искате да контролирате сигурността и качеството на данните (санитизация/избягване), вместо да съхранявате суров JSON в поле.
- Планирате надеждно да изпълнявате заявки (WP_Query) и филтри (tax_query/meta_query).
Кога НЕ трябва да използвате това решение
- Достатъчен е обикновен шаблон на страница Ако имате 3 статични страници с „Казус“, CPT е допълнителен разход.
- Не са ви необходими никакви запитвания. Ако съдържанието никога не е изброено/филтрирано, страница + блокове могат да свършат работа.
- Вие сте зависими от бизнес плъгин : електронна търговия (WooCommerce), събития (Календарът на събитията), LMS… използвайте повторно техните типове публикации, в противен случай дублирате концепциите.
- Мислите си, че „CPT = автоматично SEO“ Не. SEO зависи предимно от шаблони, данни и индексиране. Лошо конфигурираната CPT оптимизация може дори да създаде дублирано съдържание (архиви, таксономии и др.).
- Искате сложна схема на данни (Силни връзки, ограничения, усъвършенствани SQL заявки): помислете за персонализирани таблици + специален потребителски интерфейс. Мета публикациите бързо се превръщат в пречка.
Предварителни изисквания / преди започване
Контекст: WordPress 6.9.4 (април 2026), PHP 8.1+. Кодът по-долу е насочен към тези версии.
- Работа в среда за подготовка и направете резервно копие (файлове + база данни).
- Активиране
WP_DEBUGetWP_DEBUG_LOGпри подготовката за изпълнение, за да се хванат PHP грешки (забравена скоба, неправилно изписана hook и т.н.). - Избягвайте да поставяте този код в родителска тема. Използвайте плъгин или дъщерна тема. Често съм виждал CPT-тата да „изчезват“, когато дадена тема се промени.
- Полезни инструменти:
- Монитор на заявки (за преглед на заявки и куки),
- WP-CLI (за промиване на постоянни връзки и тестване),
- плъгин за фрагменти, само ако знаете как да го деактивирате в случай на фатална грешка.
Полезни официални документи:
- register_post_type()
- register_taxonomy()
- Персонализирани мета кутии (плъгин за наръчник)
- add_meta_box()
- PHP филтри за санитарно третиране (filter_var)
Наивният подход (и защо да го избягваме)
Фрагментът, който виждам най-често: a register_post_type() минимално в functions.php, мета данни, запазени без еднократна проверка, и a flush_rewrite_rules() извиква се на всяка страница.
<?php
// ❌ Exemple volontairement mauvais : ne copiez pas.
add_action('init', function () {
register_post_type('case_study', [
'public' => true,
'label' => 'Études de cas',
]);
// ❌ Très mauvais : flush à chaque chargement => perf catastrophique.
flush_rewrite_rules();
});
add_action('save_post', function ($post_id) {
// ❌ Pas de nonce, pas de permission, pas de sanitization.
update_post_meta($post_id, 'client', $_POST['client']);
});
Защо това е проблем:
- Сигурност без nonce + проверка за възможности, всеки поток за редактиране (или компрометиран плъгин) може да инжектира данни.
- Автоматично запазване / редакции :
save_postЧесто се задейства. Без предпазни мерки, вие презаписвате данни. - Изпълнение :
flush_rewrite_rules()е скъпо. Правенето му наinitможе да забави целия сайт. - Поддръжка CPT (Съдържание на таблица) в рамките на тема = съдържание, съчетано с дизайн. В деня, в който смените темата, администраторският интерфейс става непоследователен.
Правилният подход — стъпка по стъпка урок
Стъпка 1 — Създайте мини-плъгин (препоръчително)
Създаване на папка wp-content/plugins/bpcab-case-studiesслед това файл bpcab-case-studies.phpАктивирайте го в администраторския панел.
Стъпка 2 — Запазете CPT и таксономиите за инициализация
Записваме всичко на init със стандартен приоритет. Активираме show_in_rest за Gutenberg, REST API и съвместимостта с конструктора.
Ние също така дефинираме a rewrite Експлицитно. Според моя опит, това е, което най-много намалява изненадите, когато даден сайт вече има страници с подобни дескриптори.
Стъпка 3 — Добавяне на защитена метабокс кутия
Ние използваме add_meta_box() от add_meta_boxesИзпращаме формуляр с еднократен номер (nonce), след което го запазваме на save_post_case_study (hook, специфичен за даден тип след изпълнението). Това избягва изпълнението на ненужен код за всички типове.
Стъпка 4 — Почистване и валидиране на записите
Санитизацията се извършва според вида:
- текст:
sanitize_text_field(), - URL адрес:
esc_url_raw(), - количество: стриктно преобразуване (число с плаваща запетая) + граници,
- дата: валидиране чрез
DateTimeImmutable(PHP 8.1).
Стъпка 5 — Подобрете администраторския интерфейс (колони, сортиране, филтри)
Това не е „бонус“. CPT без полезни колони бързо става досаден, след като имате 50 записа. Добавяме:
- колона „Клиент“,
- колона „Сектор“,
- колона „Бюджет“,
- сортиране по „Бюджет“.
Стъпка 6 — Изчистване на постоянните връзки в точното време
Изчистваме данните, когато плъгинът е активиран (и деактивиран), а не при всяка заявка. Това е разликата между „работи“ и „убива TTFB“.
Пълен код
Копирайте и поставете този файл такъв, какъвто е, в wp-content/plugins/bpcab-case-studies/bpcab-case-studies.phpСлед това активирайте плъгина. Кодът е завършен и функционален на WordPress 6.9.4+ и PHP 8.1+.
<?php
/**
* Plugin Name: BPCAB - Études de cas (CPT + Taxonomies + Métaboxes)
* Description: Ajoute un Custom Post Type "Études de cas" avec taxonomies et métaboxes sécurisées.
* Version: 1.0.0
* Requires at least: 6.9
* Requires PHP: 8.1
* Author: Votre Nom
* License: GPL-2.0-or-later
*/
declare(strict_types=1);
if (!defined('ABSPATH')) {
exit;
}
final class BPCAB_Case_Studies {
public const POST_TYPE = 'case_study';
public const TAX_SECTOR = 'sector';
public const TAX_PROJECT_TAG = 'project_tag';
// Clés meta (préfixées pour éviter les collisions).
public const META_CLIENT = '_bpcab_client';
public const META_PROJECT_URL = '_bpcab_project_url';
public const META_BUDGET = '_bpcab_budget';
public const META_DELIVERED_ON = '_bpcab_delivered_on'; // Format: YYYY-MM-DD
public static function init(): void {
add_action('init', [__CLASS__, 'register_cpt_and_taxonomies']);
add_action('add_meta_boxes', [__CLASS__, 'register_metaboxes']);
add_action('save_post_' . self::POST_TYPE, [__CLASS__, 'save_metaboxes'], 10, 2);
add_filter('manage_' . self::POST_TYPE . '_posts_columns', [__CLASS__, 'admin_columns']);
add_action('manage_' . self::POST_TYPE . '_posts_custom_column', [__CLASS__, 'admin_column_content'], 10, 2);
add_filter('manage_edit-' . self::POST_TYPE . '_sortable_columns', [__CLASS__, 'admin_sortable_columns']);
add_action('pre_get_posts', [__CLASS__, 'admin_sorting_query']);
register_activation_hook(__FILE__, [__CLASS__, 'activate']);
register_deactivation_hook(__FILE__, [__CLASS__, 'deactivate']);
}
public static function activate(): void {
// Enregistrer d'abord, puis flusher.
self::register_cpt_and_taxonomies();
flush_rewrite_rules();
}
public static function deactivate(): void {
// Flush pour retirer les règles de réécriture.
flush_rewrite_rules();
}
public static function register_cpt_and_taxonomies(): void {
$labels = [
'name' => 'Études de cas',
'singular_name' => 'Étude de cas',
'menu_name' => 'Études de cas',
'add_new' => 'Ajouter',
'add_new_item' => 'Ajouter une étude de cas',
'edit_item' => 'Modifier l’étude de cas',
'new_item' => 'Nouvelle étude de cas',
'view_item' => 'Voir l’étude de cas',
'view_items' => 'Voir les études de cas',
'search_items' => 'Rechercher des études de cas',
'not_found' => 'Aucune étude de cas trouvée',
'not_found_in_trash' => 'Aucune étude de cas dans la corbeille',
'all_items' => 'Toutes les études de cas',
'archives' => 'Archives des études de cas',
'attributes' => 'Attributs',
'insert_into_item' => 'Insérer dans l’étude de cas',
'uploaded_to_this_item' => 'Téléversé pour cette étude de cas',
'featured_image' => 'Image mise en avant',
'set_featured_image' => 'Définir l’image mise en avant',
'remove_featured_image' => 'Retirer l’image mise en avant',
'use_featured_image' => 'Utiliser comme image mise en avant',
'filter_items_list' => 'Filtrer la liste',
'items_list_navigation' => 'Navigation de liste',
'items_list' => 'Liste des études de cas',
];
$args = [
'labels' => $labels,
'public' => true,
'show_in_rest' => true, // Gutenberg + REST + builders
'menu_position' => 20,
'menu_icon' => 'dashicons-portfolio',
'supports' => ['title', 'editor', 'thumbnail', 'excerpt', 'revisions', 'author'],
'has_archive' => true,
'rewrite' => [
'slug' => 'etudes-de-cas',
'with_front' => false,
],
'query_var' => true,
'show_ui' => true,
'show_in_menu' => true,
'capability_type' => 'post',
'map_meta_cap' => true,
'hierarchical' => false,
];
register_post_type(self::POST_TYPE, $args);
// Taxonomie hiérarchique : Secteurs.
register_taxonomy(self::TAX_SECTOR, [self::POST_TYPE], [
'labels' => [
'name' => 'Secteurs',
'singular_name' => 'Secteur',
'search_items' => 'Rechercher des secteurs',
'all_items' => 'Tous les secteurs',
'parent_item' => 'Secteur parent',
'parent_item_colon' => 'Secteur parent :',
'edit_item' => 'Modifier le secteur',
'update_item' => 'Mettre à jour le secteur',
'add_new_item' => 'Ajouter un secteur',
'new_item_name' => 'Nom du nouveau secteur',
'menu_name' => 'Secteurs',
],
'public' => true,
'show_ui' => true,
'show_in_rest' => true,
'hierarchical' => true,
'rewrite' => [
'slug' => 'secteur',
'with_front' => false,
],
]);
// Taxonomie non hiérarchique : tags projet.
register_taxonomy(self::TAX_PROJECT_TAG, [self::POST_TYPE], [
'labels' => [
'name' => 'Tags projet',
'singular_name' => 'Tag projet',
'search_items' => 'Rechercher des tags',
'popular_items' => 'Tags populaires',
'all_items' => 'Tous les tags',
'edit_item' => 'Modifier le tag',
'update_item' => 'Mettre à jour le tag',
'add_new_item' => 'Ajouter un tag',
'new_item_name' => 'Nom du nouveau tag',
'separate_items_with_commas' => 'Séparez les tags par des virgules',
'add_or_remove_items' => 'Ajouter ou retirer des tags',
'choose_from_most_used' => 'Choisir parmi les plus utilisés',
'menu_name' => 'Tags projet',
],
'public' => true,
'show_ui' => true,
'show_in_rest' => true,
'hierarchical' => false,
'rewrite' => [
'slug' => 'tag-projet',
'with_front' => false,
],
]);
}
public static function register_metaboxes(): void {
add_meta_box(
'bpcab_case_study_details',
'Détails de l’étude de cas',
[__CLASS__, 'render_metabox_details'],
self::POST_TYPE,
'normal',
'default'
);
}
public static function render_metabox_details(WP_Post $post): void {
// Nonce pour sécuriser la sauvegarde.
wp_nonce_field('bpcab_case_study_save', 'bpcab_case_study_nonce');
$client = (string) get_post_meta($post->ID, self::META_CLIENT, true);
$project_url = (string) get_post_meta($post->ID, self::META_PROJECT_URL, true);
$budget = (string) get_post_meta($post->ID, self::META_BUDGET, true);
$delivered_on = (string) get_post_meta($post->ID, self::META_DELIVERED_ON, true);
?>
<div class="bpcab-metabox">
<p>
<label for="bpcab_client"><strong>Client</strong></label><br>
<input type="text" id="bpcab_client" name="bpcab_client" value="<?php echo esc_attr($client); ?>" class="regular-text">
</p>
<p>
<label for="bpcab_project_url"><strong>URL du projet</strong></label><br>
<input type="url" id="bpcab_project_url" name="bpcab_project_url" value="<?php echo esc_attr($project_url); ?>" class="regular-text" placeholder="https://exemple.com">
</p>
<p>
<label for="bpcab_budget"><strong>Budget (EUR)</strong></label><br>
<input type="number" step="0.01" min="0" id="bpcab_budget" name="bpcab_budget" value="<?php echo esc_attr($budget); ?>" class="small-text">
<span class="description">Stocké en nombre (décimal) dans la meta.</span>
</p>
<p>
<label for="bpcab_delivered_on"><strong>Date de livraison</strong></label><br>
<input type="date" id="bpcab_delivered_on" name="bpcab_delivered_on" value="<?php echo esc_attr($delivered_on); ?>">
<span class="description">Format YYYY-MM-DD.</span>
</p>
</div>
<?php
}
public static function save_metaboxes(int $post_id, WP_Post $post): void {
// 1) Nonce présent ?
if (!isset($_POST['bpcab_case_study_nonce'])) {
return;
}
// 2) Nonce valide ?
if (!wp_verify_nonce((string) $_POST['bpcab_case_study_nonce'], 'bpcab_case_study_save')) {
return;
}
// 3) Autosave / révision ?
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (wp_is_post_revision($post_id)) {
return;
}
// 4) Bon type de post ?
if ($post->post_type !== self::POST_TYPE) {
return;
}
// 5) Permissions : capacité d'éditer ce post.
if (!current_user_can('edit_post', $post_id)) {
return;
}
// Lecture + sanitization.
$client = isset($_POST['bpcab_client']) ? sanitize_text_field((string) $_POST['bpcab_client']) : '';
$project_url = isset($_POST['bpcab_project_url']) ? esc_url_raw((string) $_POST['bpcab_project_url']) : '';
$budget_raw = isset($_POST['bpcab_budget']) ? (string) $_POST['bpcab_budget'] : '';
$delivered_on_raw = isset($_POST['bpcab_delivered_on']) ? (string) $_POST['bpcab_delivered_on'] : '';
// Budget : normalisation stricte (point décimal).
$budget = '';
if ($budget_raw !== '') {
// Remplace la virgule par un point si l'utilisateur saisit "1200,50".
$budget_normalized = str_replace(',', '.', $budget_raw);
$budget_float = filter_var($budget_normalized, FILTER_VALIDATE_FLOAT);
if ($budget_float !== false && $budget_float >= 0) {
// Stockage en string formaté pour éviter les surprises d'affichage.
$budget = number_format((float) $budget_float, 2, '.', '');
}
}
// Date : validation YYYY-MM-DD via DateTimeImmutable.
$delivered_on = '';
if ($delivered_on_raw !== '') {
$dt = DateTimeImmutable::createFromFormat('Y-m-d', $delivered_on_raw);
$errors = DateTimeImmutable::getLastErrors();
if ($dt instanceof DateTimeImmutable && empty($errors['warning_count']) && empty($errors['error_count'])) {
$delivered_on = $dt->format('Y-m-d');
}
}
// Écriture meta : supprime si vide (base plus propre).
self::update_or_delete_meta($post_id, self::META_CLIENT, $client);
self::update_or_delete_meta($post_id, self::META_PROJECT_URL, $project_url);
self::update_or_delete_meta($post_id, self::META_BUDGET, $budget);
self::update_or_delete_meta($post_id, self::META_DELIVERED_ON, $delivered_on);
}
private static function update_or_delete_meta(int $post_id, string $meta_key, string $value): void {
if ($value === '') {
delete_post_meta($post_id, $meta_key);
return;
}
update_post_meta($post_id, $meta_key, $value);
}
public static function admin_columns(array $columns): array {
// On insère des colonnes après le titre.
$new = [];
foreach ($columns as $key => $label) {
$new[$key] = $label;
if ($key === 'title') {
$new['bpcab_client'] = 'Client';
$new['bpcab_sector'] = 'Secteur';
$new['bpcab_budget'] = 'Budget';
}
}
return $new;
}
public static function admin_column_content(string $column, int $post_id): void {
if ($column === 'bpcab_client') {
$client = (string) get_post_meta($post_id, self::META_CLIENT, true);
echo $client !== '' ? esc_html($client) : '—';
return;
}
if ($column === 'bpcab_budget') {
$budget = (string) get_post_meta($post_id, self::META_BUDGET, true);
echo $budget !== '' ? esc_html($budget . ' €') : '—';
return;
}
if ($column === 'bpcab_sector') {
$terms = get_the_terms($post_id, self::TAX_SECTOR);
if (is_wp_error($terms) || empty($terms)) {
echo '—';
return;
}
$names = array_map(static fn($t) => $t->name, $terms);
echo esc_html(implode(', ', $names));
return;
}
}
public static function admin_sortable_columns(array $columns): array {
// Tri sur budget (meta).
$columns['bpcab_budget'] = 'bpcab_budget';
return $columns;
}
public static function admin_sorting_query(WP_Query $query): void {
if (!is_admin() || !$query->is_main_query()) {
return;
}
$screen = function_exists('get_current_screen') ? get_current_screen() : null;
if (!$screen || $screen->post_type !== self::POST_TYPE) {
return;
}
$orderby = $query->get('orderby');
if ($orderby !== 'bpcab_budget') {
return;
}
// Tri numérique sur meta.
$query->set('meta_key', self::META_BUDGET);
$query->set('orderby', 'meta_value_num');
}
}
BPCAB_Case_Studies::init();
Обяснение на кода
Защо плъгин, а не functions.php?
Таблицата за производителност на съдържанието (CPT) представлява структурата на съдържанието. Темата представлява дисплея. Смесването на двете ви излага на класически сценарий: „Променихме темата, всичко изчезна.“ Съдържанието все още е в базата данни, но администраторският панел вече не показва менюто, таксономиите и метабоксовете. Виждал съм това в редизайните на Avada и Divi по-често, отколкото бих искал.
CPT регистрация + таксономии
register_post_type() et register_taxonomy() са призовани initСега е подходящият момент: WordPress вече знае основите, но е достатъчно рано, за да може пренаписването на URL адреси и REST да бъдат последователни.
- покажи_в_останката : от съществено значение през 2026 г., ако използвате Gutenberg, шаблони или конструктори, които консумират REST API.
- редактирам отново Експлицитният slug избягва колизии. Без него, ще получите нечетливи URL адреси от типа „case_study“ или, още по-лошо, конфликт със съществуваща страница.
- поддържа Вие избирате какво да показва редакторът. Поддържайте го минималистично: колкото повече активирате, толкова повече врати (и объркване) отваряте.
Metabox: рендиране + запазване
Рендирането избягва стойностите с esc_attr()Резервно копие:
- проверете еднократния номер (
wp_verify_nonce), - игнорирайте автоматичното запазване и редакциите,
- проверява капацитета
edit_post, - дезинфектирани по тип (текст, URL, число с плаваща запетая, дата),
- премахва празни метаданни (по-чиста база данни, по-прости заявки).
Куката
save_post_case_studyе детайл, който променя всичко: избягвате да изпълнявате този код на страници, статии, медии… В сайт с много редакции това е измеримо.
Административни и сортиращи колони
Колоните се добавят чрез manage_{post_type}_posts_columns и се попълва чрез manage_{post_type}_posts_custom_columnСортирането по бюджет включва:
- декларация на „сортиращата“ колона,
- промяна на администраторската заявка чрез
pre_get_posts, - дигитално сортиране с
meta_value_num.
Предупреждение: Мета-сортирането върху големи обеми може да бъде скъпо (липса на индексиране на postmetaАко имате хиляди записи и сортирате често, помислете за различно решение за съхранение (или поне ограничете това сортиране до ролите, които се нуждаят от него).
Изчистване на постоянните връзки
Промиването се извършва, когато плъгинът е активиран/деактивиран. Това е единственото приемливо място в повечето случаи. Ако промивате на init, вие принуждавате WordPress да преизчислява скъпите правила с всяка заявка.
Полезна справка: flush_rewrite_rules().
Варианти и случаи на употреба
Вариант 1 — Специализирани възможности (роли на редактор на портфолио)
Ако искате определени роли да управляват казуси, без да засягат статиите, създайте специални възможности чрез capability_type et capabilitiesПо-дълго е, но е чисто.
Подход: употреба capability_type => ['case_study','case_studies'] и картографирайте ограниченията. След това добавете ограниченията към ролите при активиране.
<?php
// Exemple partiel : à intégrer dans la classe si vous activez cette variante.
// Note : code volontairement court, il faut compléter la liste de capacités selon vos besoins.
$args['capability_type'] = ['case_study', 'case_studies'];
$args['map_meta_cap'] = true;
$args['capabilities'] = [
'edit_post' => 'edit_case_study',
'read_post' => 'read_case_study',
'delete_post' => 'delete_case_study',
'edit_posts' => 'edit_case_studies',
'edit_others_posts' => 'edit_others_case_studies',
'publish_posts' => 'publish_case_studies',
'read_private_posts' => 'read_private_case_studies',
];
Правя това главно в сайтове с множество автори. В противен случай се оказва, че „редактори“ публикуват статии, когато вие искате само казуси.
Вариант 2 — По-строга проверка и съобщения за грешки
WordPress не предоставя „нативна“ система за валидиране на метабоксове с потребителски интерфейс съобщения. Реалистична техника е да се съхраняват временни грешки и да се показват в admin_notices après Резервно копие. Полезно, ако датата е задължителна или ако URL адресът трябва да е на определен домейн.
Вариант 3 — REST експозиция на мета (за headless / builders)
Ако искате полетата ви да се показват правилно в REST API, регистрирайте ги с register_post_meta() (type, single, auth_callback, show_in_rest). Това избягва нетипизираните „сурови мета“.
Документи: register_post_meta().
<?php
add_action('init', function () {
register_post_meta('case_study', '_bpcab_budget', [
'type' => 'number',
'single' => true,
'show_in_rest' => true,
'auth_callback' => static function () {
return current_user_can('edit_posts');
},
]);
});
Важна забележка: ако предоставяте метаданни чрез REST, помислете за риска от изтичане на данни. Вашият бюджет може да е проблем. Обърнете внимание на това. auth_callback в зависимост от вашите нужди.
Съвместимост с Divi 5 / Elementor / Avada
с show_in_rest След като е активиран, вече сте на път към WordPress 6.9.4 и модерните конструктори.
Диви 5
- Divi 5 може да използва динамични шаблони. Вашият CPT ще се показва в списъците със съдържание, ако конструкторът разчита на публични типове.
- За показване на метаданните, най-надеждният метод остава:
- или модул, който чете полетата чрез заявка в WordPress,
- или обикновен шорткод (ако вашият Divi стек го позволява).
Ако създавате шорткод, винаги екранирайте изхода (XSS) и ограничете атрибутите.
Elementor
- Elementor обикновено открива публични CPT-и и може да създава шаблони „Single“ и „Archive“.
- Мета полета: В зависимост от вашата конфигурация, Elementor може да чете мета тагове чрез „Динамични тагове“. За чиста интеграция, запазете мета таговете с
register_post_meta()+show_in_rest(вариантът по-горе).
Авада (Fusion Builder)
- Avada обработва добре публичните CPT за архиви и сингли.
- Често съм виждал капан: охлюви, които влизат в конфликт с „синтез „слугове“ или съществуващи страници. Ако URL адрес на архив върне грешка 404, генерирайте отново постоянните връзки и проверете дали никоя страница не използва
/etudes-de-cas/.
Проверки след инсталацията
- В администраторския панел имате меню „Казуси“. Създайте запис и го публикувайте.
- Добавете „Сектор“ и „Етикет на проект“, след което ги свържете.
- Проверете URL адреса на архива:
/etudes-de-cas/(в зависимост от конфигурацията на вашата постоянна връзка). - Проверете един URL адрес:
/etudes-de-cas/mon-etude/. - В администраторския списък проверете:
- показване на колоните Клиент/Сектор/Бюджет,
- сортиране по бюджет (щракнете върху заглавието на колоната).
- Автоматично запазване на теста: Редактирайте проучването, изчакайте автоматичното запазване и след това го презаредете. Метаданните не трябва да се изчистват.
- REST тест (ако използвате API): отворен
/wp-json/wp/v2/case_study(Удостоверяване според вашия сайт). CPT трябва да бъде изложен.
Бърза диагностична диаграма
| симптом | Вероятна причина | проверка | Решение |
|---|---|---|---|
| Архив /казуси/ в 404 | Непрочистени правила за пренаписване, конфликт на пълния код | Настройки > Постоянни връзки, проверете страница със същия пълзящ код | Регенерирайте постоянните връзки, променете пълзящия код (slug) rewrite ако конфликт |
| Полетата на Metabox не се запазват | Липсващ/невалиден nonce, разрешения, неправилен hook | Мрежова конзола + проверка на HTML кода, лог файлове за отстраняване на грешки | Проверка wp_nonce_fieldкука save_post_case_studyспособност edit_post |
| Бюджетът е сортиран „като текст“ (100 преди 20) | Сортиране по meta_value вместо meta_value_num |
инспектира pre_get_posts |
употреба meta_value_num и съхранява нормализирано число |
| CPT не се показва в Elementor/Divi | public ou show_in_rest инвалиди |
Проверете аргументите на CPT | активирате show_in_restпроверете видимостта |
| Фатална грешка след поставяне на код | Липсваща скоба/точка и запетая, остарял PHP | Lit wp-content/debug.log |
Коригирайте синтаксиса, проверете PHP 8.1+ |
Ако това не проработи
- Проверете къде сте поставили кода ако го сложите вътре
functions.phpОт родителска тема, преместете я в плъгин или дъщерна тема. Много проблеми „не работи“ произтичат от това. - Вижте логовете активиране
WP_DEBUG_LOGи отворенwp-content/debug.logЗабравена скоба може да бъде забелязана за 10 секунди. - Временно деактивиране на кеширащите плъгини Виждал съм агресивни промени в презаписването на маски за кеширане. Изчистете и кеша на браузъра си.
- Регенериране на постоянни връзки Настройки > Постоянни връзки > Запазване. Или чрез WP-CLI:
wp rewrite flush --hard - Проверете охлювите : ако имате страница с „казуси“, тя може да е в конфликт с архива.
- Проверете резервната кука :
save_post_case_study(подходящиsave_postобщо, ако сте го модифицирали). - Проверете разрешенията Влезте като администратор, за да тествате. Персонализирана роля „редактор“ може да няма
edit_postспоред вашето картографиране. - Тествайте без строител : превключване към тема по подразбиране (на етапа на подготовка), за да се изолира конфликт Avada/Divi/Elementor.
Често срещани капани и грешки
| Грешка | Причина | Решение |
|---|---|---|
| CPT изчезва след промяната на темата | CPT е дефинирано в functions.php на темата |
Поставете CPT в плъгин (като тук) или поне в mu-плъгин |
| „404 Не е намерено“ в архива | Постоянните връзки не са изчистени, конфликт на пълния код | Запазете отново постоянните връзки, променете ги rewrite.slugпроверете съществуващите страници |
| Мета файловете се изпразват произволно. | Нефилтрирано автоматично запазване/редакция, прекалено агресивна логика на запазване | Поддържайте предпазните мерки на място DOING_AUTOSAVE + wp_is_post_revision |
| „За съжаление, нямате право да редактирате този елемент.“ | Неправилно конфигурирани възможности (непълен вариант на възможности) | Виж отново capability_type/capabilitiesДобавете ограниченията към ролите при активиране |
| Конфликт с плъгин за фрагменти | Фрагментът се зарежда твърде рано/твърде късно или е дублиран. | Деактивирайте фрагмента, преминете към плъгин с версии, избягвайте дублиращи се записи |
| „Не може да се декларира отново клас…“ | Поставили сте кода два пъти (плъгин + тема или два плъгина). | Запазете само един източник, преименувайте плъгина, ако е необходимо |
| Непоследователно сортиране на бюджета | Бюджет, съхранен със запетая или текстово сортиране | Нормализиране (десетична запетая), сортиране чрез meta_value_num |
| Полетата не се показват в REST | нерегистрирани мета данни чрез register_post_meta |
Използвайте REST варианта, ако е необходимо, с auth_callback |
| Грешка „Изчерпан е допустимият размер на паметта“ по време на многократно изчистване | flush_rewrite_rules() викани твърде често |
Пускане само при активиране/деактивиране, никога при init |
Съвети за безопасност, производителност и поддръжка
- Сигурност (XSS/CSRF) :
- Задължителен е nonce в метабоксовете, отметва се при запазване.
- Систематично бягство от показността:
esc_attrвъв входните данни,esc_htmlв колоните. - Ако изложите метаданни в REST, защитете ги с
auth_callbackБюджетът или клиентът могат да бъдат чувствителни.
- Изпълнение :
- Избягвайте ги
meta_queryСложна обработка на данни с големи обеми. Сортирането по метаданни в администраторския панел е удобно, но може да бъде скъпо. - Никога не изтривайте постоянните връзки по време на изпълнение.
- Предпочитайте конкретната кука
save_post_{post_type}да се ограничи изпълнението.
- Избягвайте ги
- поддръжка :
- Версия на този плъгин (Git). В деня, в който колега промени етикет или плъгин, искате история.
- Ако се промените
rewrite.slugВ производствена среда, планирайте 301 пренасочвания (SEO). - Дръжте мета ключовете си с префикс. Колизиите с плъгини за „поля“ се случват по-бързо, отколкото си мислите.
Полезна справка за метаданните: Метаданни (плъгин за наръчник)За постоянни връзки и правила за пренаписване: add_rewrite_rule() (полезно, ако продължите по-нататък).
ресурси
- register_post_type() — Справка
- register_taxonomy() — Справка
- add_meta_box() — Справка
- Персонализирани мета кутии — плъгин за наръчник
- register_post_meta() — Излагане на мета тагове в REST
- wordpress-develop (изходен код на WordPress)
- WordPress Core Trac (билети и история)
- PHP DateTimeImmutable
- PHP филтър_var()
Често задавани въпроси
Защо моят CPT архив връща грешка 404 веднага след активирането?
Защото правилата за пренаписване не са преизчислени (или кешът ви показва стар отговор). Запазете отново постоянните връзки, изчистете кеша и проверете дали никоя страница не използва същия slug.
Трябва ли да използвам плъгина ACF/SCF/Meta Box вместо да кодирам метабоксове?
Ако имате много полета и екипът не е удобен с кода, плъгин за полета може да е по-рентабилен. Когато се нуждаете от строг контрол (валидация, производителност, REST експозиция, сигурност), метабоксовете за кодиране остават много надеждно решение.
Защо да използвам save_post_case_study отколкото да save_post ?
За да се ограничи изпълнението до CPT и да се избегнат странични ефекти върху страници/статии. В сайтове с много редакции (или импортиране) това е от съществено значение.
Полетата ми не се запазват с Elementor/Divi, нормално ли е това?
Зависи какво редактирате. Метабоксовете са в екрана за редактиране на WordPress в CPT. Ако редактирате конструктор на шаблони, не сте в публикацията. case_studyПърво, тествайте го в оригиналния редактор на записа „Казус“.
Мога ли да направя определени полета задължителни?
Да, но WordPress не предоставя вградена валидация на потребителския интерфейс за метабоксове. Правилният метод е да се валидира при запазване, да се съхрани временно съобщение и да се покаже администраторско известие. Избягвайте внезапното им блокиране без обратна връзка, в противен случай потребителите ви ще си помислят, че WordPress е бъгав.
Защо да изтривате мета тага, когато полето е празно?
Това избягва появата на ненужни редове wp_postmetaПри заявките е по-лесно да се прави разлика между „липсва“ и „присъства, но е празно“.
Как да покажа тези полета във фронтенда (темата)?
употреба get_post_meta(get_the_ID(), '_bpcab_client', true) след това излезте от дисплея: esc_html() (текст) или esc_url() (URL). Не се доверявайте на базата данни: администраторът може да постави всичко.
Мога ли да сменя охлюва? /etudes-de-cas/ по-късно?
Да, но планирайте 301 пренасочвания, за да избегнете загуба на SEO и обратни връзки. Променете rewrite.slug, премахнете постоянните връзки, след което настройте пренасочванията (плъгин или сървър).
Защо сортирането на бюджета ми е бавно?
защото wp_postmeta не е оптимизиран за мащабно сортиране. Ограничете използването на сортиране, намалете обема или мигрирайте бюджета към специална таблица, ако данните са централни.
Какво трябва да направя, ако получа фатална грешка след активиране на плъгина?
Върнете се към FTP/SSH, преименувайте папката с плъгина, за да го деактивирате, след което прочетете debug.logНай-честите причини: липсваща точка и запетая, код, поставен два пъти, или PHP е твърде стар (проверете PHP 8.1+).