Building CRUD Plugin

<?php
/**
 * Plugin Name:     Student Database
 * Description:     A CRUD plugin for managing student records with create, read, update, and delete functionalities.
 * Version:         1.0
 * Author:          Shiara Bayonla
 */

if (!defined('ABSPATH')) {
    exit;
}

// 1. Create Database Table on Activation
function sdp_create_table() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'students';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table_name (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        name tinytext NOT NULL,
        email varchar(100) NOT NULL,
        course varchar(100) NOT NULL,
        PRIMARY KEY  (id)
    ) $charset_collate;";

    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);
}
register_activation_hook(__FILE__, 'sdp_create_table');

// 2. Enqueue Scripts (Optional, kept from your code)
function sdp_enqueue_scripts() {
    wp_enqueue_script(
        'sdp-script',
        plugin_dir_url(__FILE__) . 'js/script.js',
        array(),
        '1.0',
        true
    );
}
add_action('wp_enqueue_scripts', 'sdp_enqueue_scripts');

// 3. Main Shortcode Function
function sdp_crud_shortcode() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'students';
    $page_url = get_permalink();
    $message = '';

    // --- LOGIC HANDLERS (Moved to top for correct processing) ---

    // A. Handle Delete
    if (isset($_GET['action']) && $_GET['action'] == 'delete' && isset($_GET['id'])) {
        if (isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'sdp_delete_student_' . $_GET['id'])) {
            $id_to_delete = intval($_GET['id']);
            $wpdb->delete($table_name, array('id' => $id_to_delete));
            $message = '<div class="notice notice-success inline"><p>Student deleted successfully!</p></div>';
        } else {
            $message = '<div class="notice notice-error inline"><p>Security check failed.</p></div>';
        }
    }

    // B. Handle Update
    if (isset($_POST['update_student'])) {
        $id = intval($_POST['student_id']);
        $name = sanitize_text_field($_POST['student_name']);
        $email = sanitize_email($_POST['student_email']);
        $course = sanitize_text_field($_POST['student_course']);

        $wpdb->update(
            $table_name,
            ['name' => $name, 'email' => $email, 'course' => $course],
            ['id' => $id]
        );
        $message = '<div class="notice notice-success inline"><p>Student updated successfully!</p></div>';
    }

    // C. Handle Create (Add New)
    if (isset($_POST['submit_student'])) {
        $name = sanitize_text_field($_POST['student_name']);
        $email = sanitize_email($_POST['student_email']);
        $course = sanitize_text_field($_POST['student_course']);
        
        $wpdb->insert(
            $table_name,
            ['name' => $name, 'email' => $email, 'course' => $course]
        );
        $message = '<div class="notice notice-success inline"><p>Student added successfully!</p></div>';
    }

    // D. Check if Editing
    $student_to_edit = null;
    if (isset($_GET['action']) && $_GET['action'] == 'edit' && isset($_GET['id'])) {
        $id_to_edit = intval($_GET['id']);
        $student_to_edit = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $id_to_edit));
    }

    // --- DISPLAY OUTPUT ---
    ob_start();
    ?>
    
    <div class="wrap" style="background-color: #ffffff; padding: 20px; border-radius: 5px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
        
        <?php echo $message; ?>

        <h2 style="padding-top: 0;"><b><?php echo $student_to_edit ? 'Edit Student' : 'Add New Student'; ?></b></h2>
        
        <form method="post" action="<?php echo esc_url(remove_query_arg(['action', 'id'], $page_url)); ?>">
            <?php if ($student_to_edit) : ?>
                <input type="hidden" name="student_id" value="<?php echo esc_attr($student_to_edit->id); ?>">
            <?php endif; ?>

            <p>
                <label for="student_name">Name:</label><br>
                <input type="text" id="student_name" name="student_name" value="<?php echo $student_to_edit ? esc_attr($student_to_edit->name) : ''; ?>" required style="width: 100%; max-width: 400px;">
            </p>
            <p>
                <label for="student_email">Email:</label><br>
                <input type="email" id="student_email" name="student_email" value="<?php echo $student_to_edit ? esc_attr($student_to_edit->email) : ''; ?>" required style="width: 100%; max-width: 400px;">
            </p>
            <p>
                <label for="student_course">Course:</label><br>
                <input type="text" id="student_course" name="student_course" value="<?php echo $student_to_edit ? esc_attr($student_to_edit->course) : ''; ?>" required style="width: 100%; max-width: 400px;">
            </p>
            <p>
                <?php if ($student_to_edit) : ?>
                    <input type="submit" name="update_student" class="button button-primary" value="Update Student">
                    <a href="<?php echo esc_url($page_url); ?>" class="button">Cancel</a>
                <?php else : ?>
                    <input type="submit" name="submit_student" class="button button-primary" value="Add Student">
                <?php endif; ?>
            </p>
        </form>

        <hr style="margin: 30px 0;">

        <h2><b>Student List</b></h2>
        <form method="get" action="<?php echo esc_url($page_url); ?>" style="margin-bottom: 20px;">
            <p class="search-box" style="position: relative; float: none;">
                <label class="screen-reader-text" for="student-search-input">Search Students:</label>
                <input type="search" id="student-search-input" name="student_search" value="<?php echo isset($_GET['student_search']) ? esc_attr(sanitize_text_field($_GET['student_search'])) : ''; ?>">
                <input type="submit" id="search-submit" class="button" value="Search Students">
            </p>
        </form>

        <table class="wp-list-table widefat fixed striped" style="border: 1px solid #ddd; background-color: #ffffff;"> 
            <thead>
                <tr style="background-color: #ffffff;">
                    <th style="font-weight: bold;">ID</th>
                    <th style="font-weight: bold;">Name</th>
                    <th style="font-weight: bold;">Email</th>
                    <th style="font-weight: bold;">Course</th>
                    <th style="font-weight: bold;">Actions</th>
                </tr>
            </thead>
            <tbody>
                <?php
                $sql = "SELECT * FROM $table_name";
                $search_query = isset($_GET['student_search']) ? sanitize_text_field($_GET['student_search']) : '';

                if (!empty($search_query)) {
                    $sql .= $wpdb->prepare(" WHERE name LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
                }
                
                $students = $wpdb->get_results($sql);

                if (empty($students)) {
                    echo '<tr><td colspan="5" style="text-align: center; padding: 20px;">No students found.</td></tr>';
                } else {
                    foreach ($students as $student) {
                        $edit_url = add_query_arg(['action' => 'edit', 'id' => $student->id], $page_url);
                        $delete_nonce = wp_create_nonce('sdp_delete_student_' . $student->id);
                        $delete_url = add_query_arg([
                            'action' => 'delete',
                            'id' => $student->id,
                            '_wpnonce' => $delete_nonce
                        ], $page_url);

                        echo "<tr>";
                        echo "<td>" . esc_html($student->id) . "</td>";
                        echo "<td>" . esc_html($student->name) . "</td>";
                        echo "<td>" . esc_html($student->email) . "</td>";
                        echo "<td>" . esc_html($student->course) . "</td>";
                        echo '<td>
                                <a href="' . esc_url($edit_url) . '" class="button">Edit</a> 
                                <a href="' . esc_url($delete_url) . '" class="button sdp-delete-link" onclick="return confirm(\'Are you sure?\')">Delete</a>
                              </td>';
                        echo "</tr>";
                    }
                }
                ?>
            </tbody>
        </table>
    </div>
    <?php

    return ob_get_clean();
}
add_shortcode('student_database', 'sdp_crud_shortcode');

Learnings

Placing PHP form handling logic at the very top of the function is critical to ensure the page displays updated data immediately after a submission. I practiced overriding default WordPress styles by applying inline CSS, specifically setting the background color to white to create a cleaner, custom interface. Finally, I reinforced my ability to build a secure CRUD system by combining SQL queries with WordPress nonces to safely manage student records.