fix: resolve container width issues, spa redirects, and appearance settings overwrite. feat: enhance order/sub details and newsletter layout
This commit is contained in:
366
includes/Database/SubscriberTable.php
Normal file
366
includes/Database/SubscriberTable.php
Normal file
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Subscriber Table - Custom database table for newsletter subscribers
|
||||
*
|
||||
* Provides scalable storage for subscribers instead of wp_options
|
||||
*
|
||||
* @package WooNooW\Database
|
||||
*/
|
||||
|
||||
namespace WooNooW\Database;
|
||||
|
||||
class SubscriberTable
|
||||
{
|
||||
|
||||
const TABLE_NAME = 'woonoow_subscribers';
|
||||
const DB_VERSION = '1.0';
|
||||
const DB_VERSION_OPTION = 'woonoow_subscribers_db_version';
|
||||
|
||||
/**
|
||||
* Get full table name with prefix
|
||||
*/
|
||||
public static function get_table_name()
|
||||
{
|
||||
global $wpdb;
|
||||
return $wpdb->prefix . self::TABLE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update the subscribers table
|
||||
*/
|
||||
public static function create_table()
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$table_name = self::get_table_name();
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
$sql = "CREATE TABLE $table_name (
|
||||
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
user_id BIGINT(20) UNSIGNED DEFAULT NULL,
|
||||
status ENUM('pending','active','unsubscribed') DEFAULT 'pending',
|
||||
consent TINYINT(1) DEFAULT 0,
|
||||
consent_text TEXT,
|
||||
source VARCHAR(50) DEFAULT 'form',
|
||||
subscribed_at DATETIME DEFAULT NULL,
|
||||
confirmed_at DATETIME DEFAULT NULL,
|
||||
unsubscribed_at DATETIME DEFAULT NULL,
|
||||
ip_address VARCHAR(45) DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY idx_email (email),
|
||||
KEY idx_status (status),
|
||||
KEY idx_user_id (user_id)
|
||||
) $charset_collate;";
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
dbDelta($sql);
|
||||
|
||||
update_option(self::DB_VERSION_OPTION, self::DB_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if table exists
|
||||
*/
|
||||
public static function table_exists()
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
return $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate existing subscribers from wp_options to custom table
|
||||
*
|
||||
* @return array Migration result with counts
|
||||
*/
|
||||
public static function migrate_from_options()
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
// Ensure table exists
|
||||
if (!self::table_exists()) {
|
||||
self::create_table();
|
||||
}
|
||||
|
||||
$table_name = self::get_table_name();
|
||||
$subscribers = get_option('woonoow_newsletter_subscribers', []);
|
||||
|
||||
if (empty($subscribers)) {
|
||||
return ['migrated' => 0, 'skipped' => 0, 'message' => 'No subscribers to migrate'];
|
||||
}
|
||||
|
||||
$migrated = 0;
|
||||
$skipped = 0;
|
||||
|
||||
foreach ($subscribers as $sub) {
|
||||
if (empty($sub['email'])) {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if already exists in table
|
||||
$exists = $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT id FROM $table_name WHERE email = %s",
|
||||
$sub['email']
|
||||
));
|
||||
|
||||
if ($exists) {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Insert into new table
|
||||
$result = $wpdb->insert($table_name, [
|
||||
'email' => $sub['email'],
|
||||
'user_id' => $sub['user_id'] ?? null,
|
||||
'status' => $sub['status'] ?? 'active',
|
||||
'consent' => !empty($sub['consent']) ? 1 : 0,
|
||||
'subscribed_at' => $sub['subscribed_at'] ?? current_time('mysql'),
|
||||
'confirmed_at' => $sub['confirmed_at'] ?? null,
|
||||
'ip_address' => $sub['ip_address'] ?? null,
|
||||
]);
|
||||
|
||||
if ($result) {
|
||||
$migrated++;
|
||||
} else {
|
||||
$skipped++;
|
||||
}
|
||||
}
|
||||
|
||||
// If all migrated successfully, remove the option
|
||||
if ($migrated > 0 && $skipped === 0) {
|
||||
delete_option('woonoow_newsletter_subscribers');
|
||||
}
|
||||
|
||||
return [
|
||||
'migrated' => $migrated,
|
||||
'skipped' => $skipped,
|
||||
'message' => "Migrated $migrated subscribers, skipped $skipped",
|
||||
];
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// CRUD OPERATIONS
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Add a new subscriber
|
||||
*/
|
||||
public static function add($data)
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
$result = $wpdb->insert($table_name, [
|
||||
'email' => $data['email'],
|
||||
'user_id' => $data['user_id'] ?? null,
|
||||
'status' => $data['status'] ?? 'pending',
|
||||
'consent' => !empty($data['consent']) ? 1 : 0,
|
||||
'consent_text' => $data['consent_text'] ?? null,
|
||||
'source' => $data['source'] ?? 'form',
|
||||
'subscribed_at' => $data['subscribed_at'] ?? current_time('mysql'),
|
||||
'ip_address' => $data['ip_address'] ?? null,
|
||||
]);
|
||||
|
||||
if ($result) {
|
||||
return $wpdb->insert_id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subscriber by email
|
||||
*/
|
||||
public static function get_by_email($email)
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
return $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT * FROM $table_name WHERE email = %s",
|
||||
$email
|
||||
), ARRAY_A);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subscriber by ID
|
||||
*/
|
||||
public static function get($id)
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
return $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT * FROM $table_name WHERE id = %d",
|
||||
$id
|
||||
), ARRAY_A);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a subscriber
|
||||
*/
|
||||
public static function update($id, $data)
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
return $wpdb->update($table_name, $data, ['id' => $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update subscriber by email
|
||||
*/
|
||||
public static function update_by_email($email, $data)
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
return $wpdb->update($table_name, $data, ['email' => $email]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a subscriber
|
||||
*/
|
||||
public static function delete($id)
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
return $wpdb->delete($table_name, ['id' => $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete subscriber by email
|
||||
*/
|
||||
public static function delete_by_email($email)
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
return $wpdb->delete($table_name, ['email' => $email]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active subscribers
|
||||
*
|
||||
* @param array $filters Optional filters
|
||||
* @return array List of subscribers
|
||||
*/
|
||||
public static function get_active($filters = [])
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
$where = ["status = 'active'"];
|
||||
$values = [];
|
||||
|
||||
// Filter: subscribed after date
|
||||
if (!empty($filters['subscribed_after'])) {
|
||||
$where[] = "subscribed_at >= %s";
|
||||
$values[] = $filters['subscribed_after'];
|
||||
}
|
||||
|
||||
// Filter: subscribed before date
|
||||
if (!empty($filters['subscribed_before'])) {
|
||||
$where[] = "subscribed_at <= %s";
|
||||
$values[] = $filters['subscribed_before'];
|
||||
}
|
||||
|
||||
// Filter: registered users only
|
||||
if (!empty($filters['registered_only'])) {
|
||||
$where[] = "user_id IS NOT NULL";
|
||||
}
|
||||
|
||||
// Filter: guests only
|
||||
if (!empty($filters['guests_only'])) {
|
||||
$where[] = "user_id IS NULL";
|
||||
}
|
||||
|
||||
$where_sql = implode(' AND ', $where);
|
||||
$sql = "SELECT * FROM $table_name WHERE $where_sql ORDER BY subscribed_at DESC";
|
||||
|
||||
if (!empty($values)) {
|
||||
$sql = $wpdb->prepare($sql, ...$values);
|
||||
}
|
||||
|
||||
return $wpdb->get_results($sql, ARRAY_A);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all subscribers with pagination
|
||||
*/
|
||||
public static function get_all($args = [])
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
$defaults = [
|
||||
'per_page' => 20,
|
||||
'page' => 1,
|
||||
'status' => '',
|
||||
'search' => '',
|
||||
'orderby' => 'subscribed_at',
|
||||
'order' => 'DESC',
|
||||
];
|
||||
|
||||
$args = wp_parse_args($args, $defaults);
|
||||
$where = [];
|
||||
$values = [];
|
||||
|
||||
if (!empty($args['status'])) {
|
||||
$where[] = "status = %s";
|
||||
$values[] = $args['status'];
|
||||
}
|
||||
|
||||
if (!empty($args['search'])) {
|
||||
$where[] = "email LIKE %s";
|
||||
$values[] = '%' . $wpdb->esc_like($args['search']) . '%';
|
||||
}
|
||||
|
||||
$where_sql = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
|
||||
|
||||
// Get total count
|
||||
$count_sql = "SELECT COUNT(*) FROM $table_name $where_sql";
|
||||
if (!empty($values)) {
|
||||
$count_sql = $wpdb->prepare($count_sql, ...$values);
|
||||
}
|
||||
$total = (int) $wpdb->get_var($count_sql);
|
||||
|
||||
// Get paginated results
|
||||
$offset = ($args['page'] - 1) * $args['per_page'];
|
||||
$orderby = sanitize_sql_orderby($args['orderby'] . ' ' . $args['order']) ?: 'subscribed_at DESC';
|
||||
|
||||
$sql = "SELECT * FROM $table_name $where_sql ORDER BY $orderby LIMIT %d OFFSET %d";
|
||||
$values[] = $args['per_page'];
|
||||
$values[] = $offset;
|
||||
|
||||
$items = $wpdb->get_results($wpdb->prepare($sql, ...$values), ARRAY_A);
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'pages' => ceil($total / $args['per_page']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Count subscribers by status
|
||||
*/
|
||||
public static function count_by_status($status = null)
|
||||
{
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
if ($status) {
|
||||
return (int) $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $table_name WHERE status = %s",
|
||||
$status
|
||||
));
|
||||
}
|
||||
|
||||
return (int) $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user