Custom Meta Fields WordPress
December 23, 2024 · AI Conversations
Highlight: Selection of yes/no, these projects will be listed on homepage and upper section of overview page
Intro Text: Simple Textarea
Kunde: Simple text field for customer name
Zusatz: Simple Text field for pretitle on highlighted projects
Inbetriebnahme: Simple Text field
Partner: Simple Text Field
Leistung der Anlage: Simple Text field
Projekttyp: Simple Text field
Galerie: Image gallery field
add above custom meta field to a custom post type wordpress
### Code Implementation:
1. **Register Meta Boxes**:
Add this code to your theme's `functions.php` file or a custom plugin file.
function custom_projects_meta_boxes() {
add_meta_box(
'project_meta_box', // ID
'Project Details', // Title
'render_project_meta_box', // Callback
'custom_post_type', // Post type (replace 'custom_post_type' with your custom post type slug)
'normal',
'high'
);
}
add_action('add_meta_boxes', 'custom_projects_meta_boxes');
2. **Render Meta Box**:
Define the fields in the meta box.
function render_project_meta_box($post) {
// Retrieve existing meta values
$highlight = get_post_meta($post->ID, '_highlight', true);
$intro_text = get_post_meta($post->ID, '_intro_text', true);
$kunde = get_post_meta($post->ID, '_kunde', true);
$zusatz = get_post_meta($post->ID, '_zusatz', true);
$inbetriebnahme = get_post_meta($post->ID, '_inbetriebnahme', true);
$partner = get_post_meta($post->ID, '_partner', true);
$leistung = get_post_meta($post->ID, '_leistung', true);
$projekttyp = get_post_meta($post->ID, '_projekttyp', true);
$galerie = get_post_meta($post->ID, '_galerie', true);
wp_nonce_field('save_project_meta', 'project_meta_nonce'); // Security nonce
?>
<p>
<label for="highlight">Highlight:</label>
<select name="highlight" id="highlight">
<option value="yes" <?php selected($highlight, 'yes'); ?>>Yes</option>
<option value="no" <?php selected($highlight, 'no'); ?>>No</option>
</select>
</p>
<p>
<label for="intro_text">Intro Text:</label>
<textarea name="intro_text" id="intro_text" rows="4" style="width:100%;"><?php echo esc_textarea($intro_text); ?></textarea>
</p>
<p>
<label for="kunde">Kunde:</label>
<input type="text" name="kunde" id="kunde" value="<?php echo esc_attr($kunde); ?>" style="width:100%;">
</p>
<p>
<label for="zusatz">Zusatz:</label>
<input type="text" name="zusatz" id="zusatz" value="<?php echo esc_attr($zusatz); ?>" style="width:100%;">
</p>
<p>
<label for="inbetriebnahme">Inbetriebnahme:</label>
<input type="text" name="inbetriebnahme" id="inbetriebnahme" value="<?php echo esc_attr($inbetriebnahme); ?>" style="width:100%;">
</p>
<p>
<label for="partner">Partner:</label>
<input type="text" name="partner" id="partner" value="<?php echo esc_attr($partner); ?>" style="width:100%;">
</p>
<p>
<label for="leistung">Leistung der Anlage:</label>
<input type="text" name="leistung" id="leistung" value="<?php echo esc_attr($leistung); ?>" style="width:100%;">
</p>
<p>
<label for="projekttyp">Projekttyp:</label>
<input type="text" name="projekttyp" id="projekttyp" value="<?php echo esc_attr($projekttyp); ?>" style="width:100%;">
</p>
<p>
<label for="galerie">Galerie (comma-separated image URLs):</label>
<textarea name="galerie" id="galerie" rows="4" style="width:100%;"><?php echo esc_textarea($galerie); ?></textarea>
</p>
<?php
}
3. **Save Meta Data**:
Save the input values when the post is saved.
function save_project_meta_data($post_id) {
if (!isset($_POST['project_meta_nonce']) || !wp_verify_nonce($_POST['project_meta_nonce'], 'save_project_meta')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
$fields = ['highlight', 'intro_text', 'kunde', 'zusatz', 'inbetriebnahme', 'partner', 'leistung', 'projekttyp', 'galerie'];
foreach ($fields as $field) {
if (isset($_POST[$field])) {
update_post_meta($post_id, '_' . $field, sanitize_text_field($_POST[$field]));
}
}
}
add_action('save_post', 'save_project_meta_data');
4. **Display Meta Data** (Optional):
Fetch and display the custom fields in your template file where the post type is displayed.
$highlight = get_post_meta(get_the_ID(), '_highlight', true);
// Repeat for other fields
Replace `custom_post_type` with the actual slug of your custom post type. You can customize this further as needed.
### Updated Code:
function save_project_meta_data($post_id) {
// Check if this is the correct custom post type
if (get_post_type($post_id) !== 'custom_post_type') { // Replace 'custom_post_type' with your actual custom post type slug
return;
}
// Verify nonce
if (!isset($_POST['project_meta_nonce']) || !wp_verify_nonce($_POST['project_meta_nonce'], 'save_project_meta')) {
return;
}
// Prevent auto-saves from triggering the meta update
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// Check if the user has the right capability
if (!current_user_can('edit_post', $post_id)) {
return;
}
// List of fields to save
$fields = ['highlight', 'intro_text', 'kunde', 'zusatz', 'inbetriebnahme', 'partner', 'leistung', 'projekttyp', 'galerie'];
// Save each field
foreach ($fields as $field) {
if (isset($_POST[$field])) {
update_post_meta($post_id, '_' . $field, sanitize_text_field($_POST[$field]));
} else {
delete_post_meta($post_id, '_' . $field); // Optional: Remove meta if not present in the form
}
}
}
add_action('save_post', 'save_project_meta_data');
### Explanation:
1. **Custom Post Type Check**:
if (get_post_type($post_id) !== 'custom_post_type') { ... }
This ensures that the code runs only for the specified custom post type.
2. **Security and Validations**:
– **Nonce Check**: Validates the form submission is legitimate.
– **Auto-Save Check**: Prevents overwriting during an auto-save.
– **Capability Check**: Ensures the user has permission to edit the post.
3. **Save or Delete Fields**:
– Updates the meta field if the data is present.
– Deletes the meta field if it's not included in the form submission (optional).
Replace `'custom_post_type'` with the slug of your custom post type. This ensures the `save_post` logic is scoped appropriately.
### Updated Function
“`php
function save_project_meta_data($post_id) {
// Check if this is the correct custom post type
if (get_post_type($post_id) !== 'custom_post_type') { // Replace 'custom_post_type' with your custom post type slug
return;
}
// Verify nonce
if (!isset($_POST['project_meta_nonce']) || !wp_verify_nonce($_POST['project_meta_nonce'], 'save_project_meta')) {
return;
}
// Prevent auto-saves from triggering the meta update
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// Check if the user has the right capability
if (!current_user_can('edit_post', $post_id)) {
return;
}
// List of fields to save
$fields = ['highlight', 'intro_text', 'kunde', 'zusatz', 'inbetriebnahme', 'partner', 'leistung', 'projekttyp', 'galerie'];
// Save each field
foreach ($fields as $field) {
if (isset($_POST[$field])) {
update
To fix this issue, ensure the function declaration includes `$post` as an argument. Here's the corrected code:
### Updated `render_project_meta_box` Function
function render_project_meta_box($post) {
// Ensure $post is used correctly
if (!$post instanceof WP_Post) {
return;
}
// Retrieve existing meta values
$highlight = get_post_meta($post->ID, '_highlight', true);
$intro_text = get_post_meta($post->ID, '_intro_text', true);
$kunde = get_post_meta($post->ID, '_kunde', true);
$zusatz = get_post_meta($post->ID, '_zusatz', true);
$inbetriebnahme = get_post_meta($post->ID, '_inbetriebnahme', true);
$partner = get_post_meta($post->ID, '_partner', true);
$leistung = get_post_meta($post->ID, '_leistung', true);
$projekttyp = get_post_meta($post->ID, '_projekttyp', true);
$galerie = get_post_meta($post->ID, '_galerie', true);
wp_nonce_field('save_project_meta', 'project_meta_nonce'); // Security nonce
?>
<p>
<label for="highlight">Highlight:</label>
<select name="highlight" id="highlight">
<option value="yes" <?php selected($highlight, 'yes'); ?>>Yes</option>
<option value="no" <?php selected($highlight, 'no'); ?>>No</option>
</select>
</p>
<p>
<label for="intro_text">Intro Text:</label>
<textarea name="intro_text" id="intro_text" rows="4" style="width:100%;"><?php echo esc_textarea($intro_text); ?></textarea>
</p>
<p>
<label for="kunde">Kunde:</label>
<input type="text" name="kunde" id="kunde" value="<?php echo esc_attr($kunde); ?>" style="width:100%;">
</p>
<p>
<label for="zusatz">Zusatz:</label>
<input type="text" name="zusatz" id="zusatz" value="<?php echo esc_attr($zusatz); ?>" style="width:100%;">
</p>
<p>
<label for="inbetriebnahme">Inbetriebnahme:</label>
<input type="text" name="inbetriebnahme" id="inbetriebnahme" value="<?php echo esc_attr($inbetriebnahme); ?>" style="width:100%;">
</p>
<p>
<label for="partner">Partner:</label>
<input type="text" name="partner" id="partner" value="<?php echo esc_attr($partner); ?>" style="width:100%;">
</p>
<p>
<label for="leistung">Leistung der Anlage:</label>
<input type="text" name="leistung" id="leistung" value="<?php echo esc_attr($leistung); ?>" style="width:100%;">
</p>
<p>
<label for="projekttyp">Projekttyp:</label>
<input type="text" name="projekttyp" id="projekttyp" value="<?php echo esc_attr($projekttyp); ?>" style="width:100%;">
</p>
<p>
<label for="galerie">Galerie (comma-separated image URLs):</label>
<textarea name="galerie" id="galerie" rows="4" style="width:100%;"><?php echo esc_textarea($galerie); ?></textarea>
</p>
<?php
}
### Explanation:
1. **Correct Argument**:
Ensure the `$post` argument is passed to the function and used correctly:
function render_project_meta_box($post) { ... }
2. **Post Type Validation**:
While usually not required for rendering, you can validate `$post` to ensure it is an instance of `WP_Post`.
3. **Fetching Meta Data**:
The `$post->ID` is used to fetch the meta data for the current post.
This updated function ensures that `$post` is defined and properly used. If you're still facing issues, double-check where the `render_project_meta_box` function is called to ensure `$post` is passed as expected.
### Updated `render_project_meta_box` Function
function render_project_meta_box($post) {
// Ensure $post is used correctly
if (!$post instanceof WP_Post) {
return;
}
// Retrieve existing gallery images
$gallery = get_post_meta($post->ID, '_galerie', true); // Stored as an array of image IDs
// Security nonce
wp_nonce_field('save_project_meta', 'project_meta_nonce');
?>
<p>
<label for="highlight">Highlight:</label>
<select name="highlight" id="highlight">
<option value="yes" <?php selected(get_post_meta($post->ID, '_highlight', true), 'yes'); ?>>Yes</option>
<option value="no" <?php selected(get_post_meta($post->ID, '_highlight', true), 'no'); ?>>No</option>
</select>
</p>
<!-- Other fields here -->
<!-- Image Gallery -->
<p>
<label for="galerie">Galerie:</label>
<div id="gallery-wrapper">
<button type="button" class="button gallery-upload-button">Upload Images</button>
<ul id="gallery-preview">
<?php
if (!empty($gallery)) {
foreach ($gallery as $image_id) {
$image_url = wp_get_attachment_image_src($image_id, 'thumbnail');
if ($image_url) {
echo '<li data-id="' . esc_attr($image_id) . '"><img src="' . esc_url($image_url[0]) . '" /><button type="button" class="remove-image-button">×</button></li>';
}
}
}
?>
</ul>
<input type="hidden" name="galerie" id="galerie" value="<?php echo esc_attr(implode(',', (array) $gallery)); ?>">
</div>
</p>
<script>
jQuery(document).ready(function($) {
var fileFrame;
$('.gallery-upload-button').on('click', function(e) {
e.preventDefault();
// If the media frame already exists, reopen it.
if (fileFrame) {
fileFrame.open();
return;
}
// Create a new media frame
fileFrame = wp.media({
title: 'Select Images',
button: {
text: 'Add to Gallery'
},
multiple: true
});
// When an image is selected, run a callback.
fileFrame.on('select', function() {
var selection = fileFrame.state().get('selection');
var galleryInput = $('#galerie');
var galleryPreview = $('#gallery-preview');
galleryInput.val(''); // Clear previous value
galleryPreview.empty(); // Clear preview
selection.each(function(attachment) {
var imageId = attachment.id;
var imageUrl = attachment.attributes.sizes.thumbnail.url;
galleryInput.val(function(i, val) {
return val ? val + ',' + imageId : imageId;
});
galleryPreview.append('<li data-id="' + imageId + '"><img src="' + imageUrl + '" /><button type="button" class="remove-image-button">×</button></li>');
});
});
// Open the media frame.
fileFrame.open();
});
// Remove an image from the gallery
$(document).on('click', '.remove-image-button', function() {
var imageItem = $(this).closest('li');
var imageId = imageItem.data('id');
var galleryInput = $('#galerie');
imageItem.remove();
var ids = galleryInput.val().split(',');
galleryInput.val(ids.filter(id => id != imageId).join(','));
});
});
</script>
<style>
#gallery-preview {
display: flex;
flex-wrap: wrap;
gap: 10px;
list-style: none;
padding: 0;
}
#gallery-preview li {
position: relative;
}
#gallery-preview li img {
max-width: 100px;
height: auto;
display: block;
}
.remove-image-button {
position: absolute;
top: 0;
right: 0;
background: red;
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
width: 20px;
height: 20px;
line-height: 18px;
text-align: center;
}
</style>
<?php
}
### Explanation:
1. **Gallery Input**:
– Stores image IDs as a comma-separated string in a hidden field (`galerie`).
2. **Media Library Integration**:
– Uses `wp.media` to open the WordPress Media Library.
– Allows selecting multiple images and retrieves their IDs and URLs.
3. **Image Preview**:
– Displays a preview of the selected images.
– Allows removing individual images, updating the hidden field accordingly.
4. **Saving the Data**:
In your `save_project_meta_data` function, ensure you save `$_POST['galerie']` properly:
if (isset($_POST['galerie'])) {
$gallery_ids = array_filter(array_map('intval', explode(',', $_POST['galerie'])));
update_post_meta($post_id, '_galerie', $gallery_ids);
} else {
delete_post_meta($post_id, '_galerie');
}
### Result:
You will now have an image gallery field in your custom post type meta box where users can upload and manage multiple images. The images are stored as an array of attachment IDs in the post meta field `_galerie`.
### Step-by-Step Implementation
1. **Register and Enqueue the Script**
Add the following to your theme's `functions.php` file or your custom plugin:
function enqueue_gallery_meta_box_scripts($hook) {
global $post;
// Check if on the edit screen for the correct post type
if (!isset($post) || $post->post_type !== 'custom_post_type') { // Replace 'custom_post_type' with your post type slug
return;
}
// Enqueue WordPress Media Library
wp_enqueue_media();
// Enqueue custom JavaScript for the meta box
wp_enqueue_script(
'gallery-meta-box-script',
get_template_directory_uri() . '/js/gallery-meta-box.js', // Update the path as needed
['jquery'], // Dependencies
null, // Version (null to disable versioning)
true // Load in footer
);
// Enqueue custom CSS for the meta box
wp_enqueue_style(
'gallery-meta-box-style',
get_template_directory_uri() . '/css/gallery-meta-box.css', // Update the path as needed
[],
null // Version
);
}
add_action('admin_enqueue_scripts', 'enqueue_gallery_meta_box_scripts');
2. **Create the JavaScript File**
Create the `gallery-meta-box.js` file in your theme or plugin folder, for example, in `js/gallery-meta-box.js`.
jQuery(document).ready(function($) {
var fileFrame;
$('.gallery-upload-button').on('click', function(e) {
e.preventDefault();
// If the media frame already exists, reopen it.
if (fileFrame) {
fileFrame.open();
return;
}
// Create a new media frame
fileFrame = wp.media({
title: 'Select Images',
button: {
text: 'Add to Gallery'
},
multiple: true
});
// When an image is selected, run a callback.
fileFrame.on('select', function() {
var selection = fileFrame.state().get('selection');
var galleryInput = $('#galerie');
var galleryPreview = $('#gallery-preview');
galleryInput.val(''); // Clear previous value
galleryPreview.empty(); // Clear preview
selection.each(function(attachment) {
var imageId = attachment.id;
var imageUrl = attachment.attributes.sizes.thumbnail.url;
galleryInput.val(function(i, val) {
return val ? val + ',' + imageId : imageId;
});
galleryPreview.append('<li data-id="' + imageId + '"><img src="' + imageUrl + '" /><button type="button" class="remove-image-button">×</button></li>');
});
});
// Open the media frame.
fileFrame.open();
});
// Remove an image from the gallery
$(document).on('click', '.remove-image-button', function() {
var imageItem = $(this).closest('li');
var imageId = imageItem.data('id');
var galleryInput = $('#galerie');
imageItem.remove();
var ids = galleryInput.val().split(',');
galleryInput.val(ids.filter(id => id != imageId).join(','));
});
});
3. **Create the CSS File**
Create the `gallery-meta-box.css` file in your theme or plugin folder, for example, in `css/gallery-meta-box.css`.
#gallery-preview {
display: flex;
flex-wrap: wrap;
gap: 10px;
list-style: none;
padding: 0;
}
#gallery-preview li {
position: relative;
}
#gallery-preview li img {
max-width: 100px;
height: auto;
display: block;
}
.remove-image-button {
position: absolute;
top: 0;
right: 0;
background: red;
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
width: 20px;
height: 20px;
line-height: 18px;
text-align: center;
}
4. **Verify the Setup**
– Go to the edit screen of your custom post type.
– Check that the JavaScript and CSS are correctly applied, allowing you to upload and manage images.
### Notes:
– Replace `get_template_directory_uri()` with `plugin_dir_url(__FILE__)` if you’re implementing this in a plugin.
– The `admin_enqueue_scripts` hook ensures the scripts only load in the WordPress admin area.
– Adjust paths (`/js/gallery-meta-box.js` and `/css/gallery-meta-box.css`) to match your actual file structure.
### Update the Field Code
<p>
<label for="inbetriebnahme">Commissioning:</label>
<input type="text" name="inbetriebnahme" id="inbetriebnahme"
value="<?php echo esc_attr(get_post_meta($post->ID, '_inbetriebnahme', true)); ?>" />
</p>
### Changes Made:
– **Label Text**: Changed from `"Inbetriebnahme"` to `"Commissioning"`.
– **Field Name and ID**: No need to change the `name` or `id` attributes (`inbetriebnahme`) if you want to keep the saved meta key consistent. If you prefer to rename it to `commissioning`, update it here and in the `save_post` logic.
### Ensure Consistency in Saving Logic:
If you rename the field name (e.g., to `commissioning`), make sure the saving logic in the `save_post` callback is updated accordingly:
if (isset($_POST['commissioning'])) {
update_post_meta($post_id, '_commissioning', sanitize_text_field($_POST['commissioning']));
}
This will save the field data under the `_commissioning` meta key instead of `_inbetriebnahme`. Otherwise, if you leave the field's `name` as `inbetriebnahme`, no changes to the saving logic are necessary.
### Update the Field Code
<p>
<label for="leistung_der_anlage">System Performance:</label>
<input type="text" name="leistung_der_anlage" id="leistung_der_anlage"
value="<?php echo esc_attr(get_post_meta($post->ID, '_leistung_der_anlage', true)); ?>" />
</p>
### Changes Made:
– **Label Text**: Changed from `"Leistung der Anlage"` to `"System Performance"`.
– **Field Name and ID**: If you want to keep the saved meta key consistent, you can maintain `leistung_der_anlage` for the `name` and `id` attributes. If you prefer to rename it to `system_performance`, you can do that as well.
### Ensure Consistency in Saving Logic:
If you rename the field name (e.g., to `system_performance`), ensure the saving logic in the `save_post` callback is updated accordingly:
if (isset($_POST['system_performance'])) {
update_post_meta($post_id, '_system_performance', sanitize_text_field($_POST['system_performance']));
}
This will save the field data under the `_system_performance` meta key instead of `_leistung_der_anlage`. Otherwise, if you leave the field's `name` as `leistung_der_anlage`, no changes to the saving logic are necessary.