Kann man mit ChatGPT et.al. Plugins entwickeln ohne Kenntnisse? Häufig wird im Netz angeboten, dass man eine App, Website oder ein Business ohne Programmierkenntnisse zu erstellen. Dies ist natürlich Bauernfängerei. Oder digitale Butterfahrt. Verkauft werden dann nur überteuerte Kurse die den Leuten nicht weiter helfen und dann doch wieder auf Profis zurückgreifen müssen.
Machen wir ein Experiment
Es soll ein einfaches Plugin für WordPress erstellt werden, dass unter einem Beitrag verwandte Artikel listet. Dazu benötigen wir einen Prompt. Dieser sollte alle wichtigen Zutaten enthalten und am Besten in englisch verfasst werden.
Disclaimer: Die Benutzung des Prompts und des generierten Codes erfolgt auf eigene Gefahr!
Der Prompt
Create a WordPress plugin that displays related posts at the bottom of the current post. The plugin should:
Post Type Support: Work with standard posts as well as custom post types.
Metadata Checking: Check the categories and tags assigned to the current post.
Exclusion: Exclude the current post from the related posts results.
Matching Logic: Search the site's database for other posts that share the same categories and tags (without weighting between them).
Display: Display links to related posts that include each post's featured image.
Default and Customizable Post Count: By default, show 4 related posts, with an admin dashboard setting that allows users to adjust the number of posts displayed.
Determination Criteria: Provide an admin setting to choose whether related posts are determined by matching categories only, tags only, or both.
Fallback Mechanism: If there aren’t enough posts matching the criteria, fill the remaining slots with the most recent posts (excluding the current one), ordered by publication date.
Custom Styling: Allow admin users to extend or customize the CSS classes applied to the related posts section for enhanced styling options.
Extensibility: Include hooks and filters to allow further customization or integration by developers.
Die Antwort
Below is a complete plugin that meets the requirements. Save it as a PHP file (for example, related-posts-plugin.php
) in your WordPress plugins folder, then activate it from your admin panel.
<?php
/*
Plugin Name: Related Posts Plugin
Plugin URI: https://example.com/related-posts-plugin
Description: Displays related posts at the bottom of single posts. Matches categories and/or tags (configurable) and fills missing slots with recent posts if needed. Supports standard and custom post types.
Version: 1.0
Author: Your Name
Author URI: https://example.com
License: GPL2
Text Domain: related-posts-plugin
*/
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
class RP_Related_Posts {
private static $instance;
private $settings_option = 'rp_related_posts_settings';
private $default_settings = array(
'post_count' => 4,
'matching_criteria' => 'both', // Options: categories, tags, both
'custom_css_class' => 'rp-related-posts'
);
public static function init() {
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof RP_Related_Posts ) ) {
self::$instance = new RP_Related_Posts;
self::$instance->setup_hooks();
}
return self::$instance;
}
public function setup_hooks() {
// Load plugin settings from options
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
// Append related posts to content
add_filter( 'the_content', array( $this, 'append_related_posts' ) );
}
/**
* Create the settings page
*/
public function add_admin_menu() {
add_options_page(
__( 'Related Posts Settings', 'related-posts-plugin' ),
__( 'Related Posts', 'related-posts-plugin' ),
'manage_options',
'rp_related_posts',
array( $this, 'create_settings_page' )
);
}
/**
* Register settings used by the plugin.
*/
public function register_settings() {
register_setting( 'rp_related_posts_settings_group', $this->settings_option, array( $this, 'sanitize_settings' ) );
add_settings_section(
'rp_related_posts_main_section',
__( 'Main Settings', 'related-posts-plugin' ),
null,
'rp_related_posts'
);
add_settings_field(
'post_count',
__( 'Number of Related Posts', 'related-posts-plugin' ),
array( $this, 'post_count_field_callback' ),
'rp_related_posts',
'rp_related_posts_main_section'
);
add_settings_field(
'matching_criteria',
__( 'Matching Criteria', 'related-posts-plugin' ),
array( $this, 'matching_criteria_field_callback' ),
'rp_related_posts',
'rp_related_posts_main_section'
);
add_settings_field(
'custom_css_class',
__( 'Custom CSS Class', 'related-posts-plugin' ),
array( $this, 'custom_css_class_field_callback' ),
'rp_related_posts',
'rp_related_posts_main_section'
);
}
public function sanitize_settings( $input ) {
$new_input = array();
if ( isset( $input['post_count'] ) ) {
$new_input['post_count'] = absint( $input['post_count'] );
}
if ( isset( $input['matching_criteria'] ) && in_array( $input['matching_criteria'], array( 'categories', 'tags', 'both' ) ) ) {
$new_input['matching_criteria'] = sanitize_text_field( $input['matching_criteria'] );
}
if ( isset( $input['custom_css_class'] ) ) {
$new_input['custom_css_class'] = sanitize_html_class( $input['custom_css_class'] );
}
return $new_input;
}
public function post_count_field_callback() {
$options = $this->get_settings();
?>
<input type="number" name="<?php echo $this->settings_option; ?>[post_count]" value="<?php echo esc_attr( $options['post_count'] ); ?>" min="1" />
<?php
}
public function matching_criteria_field_callback() {
$options = $this->get_settings();
?>
<select name="<?php echo $this->settings_option; ?>[matching_criteria]">
<option value="categories" <?php selected( $options['matching_criteria'], 'categories' ); ?>><?php _e( 'Categories Only', 'related-posts-plugin' ); ?></option>
<option value="tags" <?php selected( $options['matching_criteria'], 'tags' ); ?>><?php _e( 'Tags Only', 'related-posts-plugin' ); ?></option>
<option value="both" <?php selected( $options['matching_criteria'], 'both' ); ?>><?php _e( 'Categories & Tags', 'related-posts-plugin' ); ?></option>
</select>
<?php
}
public function custom_css_class_field_callback() {
$options = $this->get_settings();
?>
<input type="text" name="<?php echo $this->settings_option; ?>[custom_css_class]" value="<?php echo esc_attr( $options['custom_css_class'] ); ?>" />
<?php
}
public function create_settings_page() {
?>
<div class="wrap">
<h1><?php _e( 'Related Posts Settings', 'related-posts-plugin' ); ?></h1>
<form method="post" action="options.php">
<?php
settings_fields( 'rp_related_posts_settings_group' );
do_settings_sections( 'rp_related_posts' );
submit_button();
?>
</form>
</div>
<?php
}
/**
* Retrieve plugin settings with defaults
*/
public function get_settings() {
$options = get_option( $this->settings_option );
if ( ! is_array( $options ) ) {
$options = array();
}
return wp_parse_args( $options, $this->default_settings );
}
/**
* Append the related posts output to the single post content.
*/
public function append_related_posts( $content ) {
if ( ! is_singular() || ! in_the_loop() || ! is_main_query() ) {
return $content;
}
global $post;
if ( empty( $post ) ) {
return $content;
}
$related_posts = $this->get_related_posts( $post );
if ( empty( $related_posts ) ) {
return $content;
}
$settings = $this->get_settings();
$css_class = apply_filters( 'rp_related_posts_css_class', $settings['custom_css_class'] );
$output = '<div class="related-posts-container ' . esc_attr( $css_class ) . '">';
$output .= '<h3>' . __( 'Related Posts', 'related-posts-plugin' ) . '</h3>';
$output .= '<ul class="related-posts-list">';
foreach ( $related_posts as $related_post ) {
$post_title = get_the_title( $related_post );
$post_link = get_permalink( $related_post );
$thumbnail = get_the_post_thumbnail( $related_post, 'thumbnail' );
$output .= '<li class="related-post-item">';
$output .= '<a href="' . esc_url( $post_link ) . '">';
// Display featured image if present:
if ( $thumbnail ) {
$output .= $thumbnail;
}
$output .= '<span class="related-post-title">' . esc_html( $post_title ) . '</span>';
$output .= '</a>';
$output .= '</li>';
}
$output .= '</ul>';
$output .= '</div>';
// Allow developers to filter the output
$output = apply_filters( 'rp_related_posts_output', $output, $related_posts, $settings );
return $content . $output;
}
/**
* Get related posts based on categories and/or tags
*/
public function get_related_posts( $post ) {
$settings = $this->get_settings();
$post_count = intval( $settings['post_count'] );
$criteria = $settings['matching_criteria'];
$current_post_id = $post->ID;
$post_type = $post->post_type;
$tax_queries = array();
if ( $criteria === 'categories' || $criteria === 'both' ) {
// Only add taxonomy query if post type supports categories:
if ( taxonomy_exists( 'category' ) && has_category( '', $post ) ) {
$categories = wp_get_post_categories( $post->ID );
if ( ! empty( $categories ) ) {
$tax_queries[] = array(
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => $categories,
);
}
}
}
if ( $criteria === 'tags' || $criteria === 'both' ) {
// Only add taxonomy query if post type supports tags:
if ( taxonomy_exists( 'post_tag' ) && has_tag( '', $post ) ) {
$tags = wp_get_post_tags( $post->ID, array( 'fields' => 'ids' ) );
if ( ! empty( $tags ) ) {
$tax_queries[] = array(
'taxonomy' => 'post_tag',
'field' => 'term_id',
'terms' => $tags,
);
}
}
}
// Construct tax_query if available. Use OR relation when multiple taxonomies are set.
if ( ! empty( $tax_queries ) ) {
if ( count( $tax_queries ) > 1 ) {
$tax_queries['relation'] = 'OR';
}
}
// Allow developers to alter query args via filter.
$related_query_args = apply_filters( 'rp_related_posts_query_args', array(
'post_type' => $post_type,
'posts_per_page' => $post_count,
'post_status' => 'publish',
'post__not_in' => array( $current_post_id ),
'ignore_sticky_posts' => true,
'tax_query' => $tax_queries
), $post );
$related_posts = new WP_Query( $related_query_args );
$results = array();
if ( $related_posts->have_posts() ) {
while ( $related_posts->have_posts() ) {
$related_posts->the_post();
$results[] = get_the_ID();
}
}
wp_reset_postdata();
// If we don't have enough related posts, add fallback posts
if ( count( $results ) < $post_count ) {
$needed = $post_count - count( $results );
$fallback_args = apply_filters( 'rp_fallback_query_args', array(
'post_type' => $post_type,
'posts_per_page' => $needed,
'post_status' => 'publish',
'post__not_in' => array_merge( array( $current_post_id ), $results ),
'orderby' => 'date',
'order' => 'DESC',
'ignore_sticky_posts' => true,
), $post );
$fallback_posts = new WP_Query( $fallback_args );
if ( $fallback_posts->have_posts() ) {
while ( $fallback_posts->have_posts() ) {
$fallback_posts->the_post();
$results[] = get_the_ID();
}
}
wp_reset_postdata();
}
// Allow developers to filter final results.
return apply_filters( 'rp_related_posts_results', $results, $post );
}
}
RP_Related_Posts::init();
How the Plugin Works
- Admin Settings:
- A settings page is added under the Settings menu (titled Related Posts).
- Admins can change the number of related posts (default 4), matching criteria (categories only, tags only, or both), and the custom CSS class.
- Output on Frontend:
- The plugin hooks into the
the_content
filter to append a related posts section at the bottom of singular posts. - It checks the current post’s categories and tags (as per the admin’s selection) and uses a WP_Query to fetch posts sharing those taxonomies.
- It excludes the current post from the results and then fills any additional slots with the latest posts if needed.
- Related posts are displayed as list items with featured images (if available) and post titles wrapped in links.
- The plugin hooks into the
- Extensibility:
- Actions and filters such as
rp_related_posts_query_args
,rp_fallback_query_args
,rp_related_posts_css_class
,rp_related_posts_output
, andrp_related_posts_results
are provided for developers to customize the functionality further.
- Actions and filters such as
Feel free to extend or modify this plugin as needed for your project’s requirements. Enjoy!

Der Test
Ich teste den Prompt mit o3-mini. Das Plugin wird dann in einer WordPress-Testumgebung mit local by flywheel umgesetzt. Nach dem Anlegen der Datei related-posts-plugin.php im entsprechenden Plugin-Verzeichnis, wurde das Plugin aktiviert. Es traten keine Fehler auf. Dann vier Beitrage erstellt und einer Kategorie zugeordnet. Tatsächlich erschienen die Titel der Beiträge am Ende eine Beitrags. Allerdings erschien auch der obligatorische „Hello World“-Eintrag, der aber uncategorized ist. In den Einstellungen kann man tatsächlich auch „Category only“, „Tag only“ oder „both“ auswählen. Allerdings scheint es keine Auswirkung zu haben. Hier muss die Entwicklung dann weitergehen.
Fazit
Es ist durchaus möglich Erweiterungen mittels Chat-KI zu erstellen aber ohne Fachkenntnisse kommt man schnell an seine Grenzen. Über die Code-Qualität schweigen wir an dieser Stelle ;). Für Power-User und erfahrene Fachleute bietet sich durchaus eine Möglichkeit der Steigerung der Produktivität. Weitere Test mit direkter Einbindung von ChatGPT in WordPress werden folgen. Alternativ kann man auch gleich die diversen Plugin-Angebote einsetzten.
PS
Hier ein weiterer Versuch:
Below is an updated version of the plugin that ensures the fallback posts also meet the selected matching criteria (for example, when the admin setting is set to Categories Only, only posts in the same category are used even in fallback queries).
<?php
/*
Plugin Name: Related Posts Plugin
Plugin URI: https://example.com/related-posts-plugin
Description: Displays related posts at the bottom of single posts. Matches categories and/or tags (configurable) and fills missing slots with recent posts if needed. Supports standard and custom post types.
Version: 1.1
Author: Your Name
Author URI: https://example.com
License: GPL2
Text Domain: related-posts-plugin
*/
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
class RP_Related_Posts {
private static $instance;
private $settings_option = 'rp_related_posts_settings';
private $default_settings = array(
'post_count' => 4,
'matching_criteria' => 'both', // Options: categories, tags, both
'custom_css_class' => 'rp-related-posts'
);
public static function init() {
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof RP_Related_Posts ) ) {
self::$instance = new RP_Related_Posts;
self::$instance->setup_hooks();
}
return self::$instance;
}
public function setup_hooks() {
// Load plugin settings from options
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
// Append related posts to content
add_filter( 'the_content', array( $this, 'append_related_posts' ) );
}
/**
* Create the settings page
*/
public function add_admin_menu() {
add_options_page(
__( 'Related Posts Settings', 'related-posts-plugin' ),
__( 'Related Posts', 'related-posts-plugin' ),
'manage_options',
'rp_related_posts',
array( $this, 'create_settings_page' )
);
}
/**
* Register settings used by the plugin.
*/
public function register_settings() {
register_setting( 'rp_related_posts_settings_group', $this->settings_option, array( $this, 'sanitize_settings' ) );
add_settings_section(
'rp_related_posts_main_section',
__( 'Main Settings', 'related-posts-plugin' ),
null,
'rp_related_posts'
);
add_settings_field(
'post_count',
__( 'Number of Related Posts', 'related-posts-plugin' ),
array( $this, 'post_count_field_callback' ),
'rp_related_posts',
'rp_related_posts_main_section'
);
add_settings_field(
'matching_criteria',
__( 'Matching Criteria', 'related-posts-plugin' ),
array( $this, 'matching_criteria_field_callback' ),
'rp_related_posts',
'rp_related_posts_main_section'
);
add_settings_field(
'custom_css_class',
__( 'Custom CSS Class', 'related-posts-plugin' ),
array( $this, 'custom_css_class_field_callback' ),
'rp_related_posts',
'rp_related_posts_main_section'
);
}
public function sanitize_settings( $input ) {
$new_input = array();
if ( isset( $input['post_count'] ) ) {
$new_input['post_count'] = absint( $input['post_count'] );
}
if ( isset( $input['matching_criteria'] ) && in_array( $input['matching_criteria'], array( 'categories', 'tags', 'both' ) ) ) {
$new_input['matching_criteria'] = sanitize_text_field( $input['matching_criteria'] );
}
if ( isset( $input['custom_css_class'] ) ) {
$new_input['custom_css_class'] = sanitize_html_class( $input['custom_css_class'] );
}
return $new_input;
}
public function post_count_field_callback() {
$options = $this->get_settings();
?>
<input type="number" name="<?php echo $this->settings_option; ?>[post_count]" value="<?php echo esc_attr( $options['post_count'] ); ?>" min="1" />
<?php
}
public function matching_criteria_field_callback() {
$options = $this->get_settings();
?>
<select name="<?php echo $this->settings_option; ?>[matching_criteria]">
<option value="categories" <?php selected( $options['matching_criteria'], 'categories' ); ?>><?php _e( 'Categories Only', 'related-posts-plugin' ); ?></option>
<option value="tags" <?php selected( $options['matching_criteria'], 'tags' ); ?>><?php _e( 'Tags Only', 'related-posts-plugin' ); ?></option>
<option value="both" <?php selected( $options['matching_criteria'], 'both' ); ?>><?php _e( 'Categories & Tags', 'related-posts-plugin' ); ?></option>
</select>
<?php
}
public function custom_css_class_field_callback() {
$options = $this->get_settings();
?>
<input type="text" name="<?php echo $this->settings_option; ?>[custom_css_class]" value="<?php echo esc_attr( $options['custom_css_class'] ); ?>" />
<?php
}
public function create_settings_page() {
?>
<div class="wrap">
<h1><?php _e( 'Related Posts Settings', 'related-posts-plugin' ); ?></h1>
<form method="post" action="options.php">
<?php
settings_fields( 'rp_related_posts_settings_group' );
do_settings_sections( 'rp_related_posts' );
submit_button();
?>
</form>
</div>
<?php
}
/**
* Retrieve plugin settings with defaults
*/
public function get_settings() {
$options = get_option( $this->settings_option );
if ( ! is_array( $options ) ) {
$options = array();
}
return wp_parse_args( $options, $this->default_settings );
}
/**
* Append the related posts output to the single post content.
*/
public function append_related_posts( $content ) {
if ( ! is_singular() || ! in_the_loop() || ! is_main_query() ) {
return $content;
}
global $post;
if ( empty( $post ) ) {
return $content;
}
$related_posts = $this->get_related_posts( $post );
if ( empty( $related_posts ) ) {
return $content;
}
$settings = $this->get_settings();
$css_class = apply_filters( 'rp_related_posts_css_class', $settings['custom_css_class'] );
$output = '<div class="related-posts-container ' . esc_attr( $css_class ) . '">';
$output .= '<h3>' . __( 'Related Posts', 'related-posts-plugin' ) . '</h3>';
$output .= '<ul class="related-posts-list">';
foreach ( $related_posts as $related_post ) {
$post_title = get_the_title( $related_post );
$post_link = get_permalink( $related_post );
$thumbnail = get_the_post_thumbnail( $related_post, 'thumbnail' );
$output .= '<li class="related-post-item">';
$output .= '<a href="' . esc_url( $post_link ) . '">';
// Display featured image if present:
if ( $thumbnail ) {
$output .= $thumbnail;
}
$output .= '<span class="related-post-title">' . esc_html( $post_title ) . '</span>';
$output .= '</a>';
$output .= '</li>';
}
$output .= '</ul>';
$output .= '</div>';
// Allow developers to filter the output
$output = apply_filters( 'rp_related_posts_output', $output, $related_posts, $settings );
return $content . $output;
}
/**
* Get related posts based on categories and/or tags.
*
* If there aren’t enough posts matching the criteria, the fallback query will also
* restrict posts to the selected taxonomy terms. (If you wish to bypass this and use
* a taxonomy-unrestricted fallback, simply remove/comment the corresponding line.)
*/
public function get_related_posts( $post ) {
$settings = $this->get_settings();
$post_count = intval( $settings['post_count'] );
$criteria = $settings['matching_criteria'];
$current_post_id = $post->ID;
$post_type = $post->post_type;
$tax_queries = array();
if ( $criteria === 'categories' || $criteria === 'both' ) {
// Only add taxonomy query if post type supports categories:
if ( taxonomy_exists( 'category' ) && has_category( '', $post ) ) {
$categories = wp_get_post_categories( $post->ID );
if ( ! empty( $categories ) ) {
$tax_queries[] = array(
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => $categories,
);
}
}
}
if ( $criteria === 'tags' || $criteria === 'both' ) {
// Only add taxonomy query if post type supports tags:
if ( taxonomy_exists( 'post_tag' ) && has_tag( '', $post ) ) {
$tags = wp_get_post_tags( $post->ID, array( 'fields' => 'ids' ) );
if ( ! empty( $tags ) ) {
$tax_queries[] = array(
'taxonomy' => 'post_tag',
'field' => 'term_id',
'terms' => $tags,
);
}
}
}
// Construct tax_query if available. Use OR relation when multiple taxonomies are set.
if ( ! empty( $tax_queries ) ) {
if ( count( $tax_queries ) > 1 ) {
$tax_queries['relation'] = 'OR';
}
}
// Allow developers to alter query args via filter.
$related_query_args = apply_filters( 'rp_related_posts_query_args', array(
'post_type' => $post_type,
'posts_per_page' => $post_count,
'post_status' => 'publish',
'post__not_in' => array( $current_post_id ),
'ignore_sticky_posts' => true,
'tax_query' => $tax_queries
), $post );
$related_posts = new WP_Query( $related_query_args );
$results = array();
if ( $related_posts->have_posts() ) {
while ( $related_posts->have_posts() ) {
$related_posts->the_post();
$results[] = get_the_ID();
}
}
wp_reset_postdata();
// If we don't have enough related posts, add fallback posts.
// The fallback query below now also includes the same tax_query restrictions,
// so that fallback posts are also limited to the selected categories and/or tags.
if ( count( $results ) < $post_count ) {
$needed = $post_count - count( $results );
$fallback_args = array(
'post_type' => $post_type,
'posts_per_page' => $needed,
'post_status' => 'publish',
'post__not_in' => array_merge( array( $current_post_id ), $results ),
'orderby' => 'date',
'order' => 'DESC',
'ignore_sticky_posts' => true,
);
// Uncomment the following line if you want fallback posts to also be filtered by taxonomy.
if ( ! empty( $tax_queries ) ) {
$fallback_args['tax_query'] = $tax_queries;
}
$fallback_args = apply_filters( 'rp_fallback_query_args', $fallback_args, $post );
$fallback_posts = new WP_Query( $fallback_args );
if ( $fallback_posts->have_posts() ) {
while ( $fallback_posts->have_posts() ) {
$fallback_posts->the_post();
$results[] = get_the_ID();
}
}
wp_reset_postdata();
}
// Allow developers to filter final results.
return apply_filters( 'rp_related_posts_results', $results, $post );
}
}
RP_Related_Posts::init();