countries->get_address_fields('', 'billing_'); // Get shipping fields from WooCommerce $shipping_fields = WC()->countries->get_address_fields('', 'shipping_'); // Process billing fields foreach ($billing_fields as $key => $field) { $fields[] = $this->normalize_field($key, $field, 'billing'); } // Process shipping fields foreach ($shipping_fields as $key => $field) { $fields[] = $this->normalize_field($key, $field, 'shipping'); } // Add order notes field manually (not in address fields) $fields[] = array( 'field_key' => 'order_comments', 'label' => /* phpcs:ignore WordPress.WP.I18n.TextDomainMismatch */ __('Order notes', 'woocommerce'), 'type' => 'textarea', 'field_type' => 'textarea', 'section' => 'order', 'required' => 0, 'enabled' => 1, 'placeholder' => /* phpcs:ignore WordPress.WP.I18n.TextDomainMismatch */ __('Notes about your order, e.g. special notes for delivery.', 'woocommerce'), 'description' => '', 'is_default' => 1, 'can_delete' => 0, 'position' => 0, 'field_class' => 'form-row-wide', 'options' => array(), ); return $fields; } /** * Normalize WooCommerce field to our internal format * * @param string $key Field key (e.g. billing_first_name) * @param array $field WooCommerce field data * @param string $section Section (billing/shipping/order) * @return array Normalized field data */ private function normalize_field($key, $field, $section) { // HARDCODED POSITIONS for correct order (overrides WooCommerce priority) $position_map = array( 'billing_email' => 10, 'billing_first_name' => 20, 'billing_last_name' => 30, 'billing_company' => 40, 'billing_address_1' => 50, 'billing_address_2' => 60, 'billing_city' => 70, 'billing_state' => 80, 'billing_postcode' => 90, 'billing_country' => 100, 'billing_phone' => 110, 'shipping_first_name' => 20, 'shipping_last_name' => 30, 'shipping_company' => 40, 'shipping_address_1' => 50, 'shipping_address_2' => 60, 'shipping_city' => 70, 'shipping_state' => 80, 'shipping_postcode' => 90, 'shipping_country' => 100, ); // Use hardcoded position if available, otherwise use WC priority or 999 $position = isset($position_map[$key]) ? $position_map[$key] : (isset($field['priority']) ? $field['priority'] : 999); return array( 'field_key' => $key, 'label' => isset($field['label']) ? $field['label'] : '', 'type' => $this->map_field_type($field), 'field_type' => $this->map_field_type($field), 'section' => $section, 'required' => isset($field['required']) && $field['required'] ? 1 : 0, 'enabled' => 1, // Default fields are always enabled initially 'placeholder' => isset($field['placeholder']) ? $field['placeholder'] : '', 'description' => isset($field['description']) ? $field['description'] : '', 'is_default' => 1, // Mark as WooCommerce default field 'can_delete' => 0, // Cannot delete default fields 'position' => $position, // Use our hardcoded positions 'field_class' => isset($field['class']) ? implode(' ', (array)$field['class']) : 'form-row-wide', 'options' => isset($field['options']) ? $field['options'] : array(), ); } /** * Map WooCommerce field type to our field type system * * @param array $field WooCommerce field data * @return string Our field type */ private function map_field_type($field) { $type = isset($field['type']) ? $field['type'] : 'text'; // Map WooCommerce types to our types $type_map = array( 'text' => 'text', 'email' => 'email', 'tel' => 'phone', 'textarea' => 'textarea', 'select' => 'select', 'country' => 'select', 'state' => 'select', 'number' => 'number', 'password' => 'text', 'hidden' => 'text', ); return isset($type_map[$type]) ? $type_map[$type] : 'text'; } /** * Check if a field key is a WooCommerce default field * * @param string $field_key Field key to check * @return bool True if default field */ public function is_default_field($field_key) { $default_fields = $this->get_default_fields(); foreach ($default_fields as $field) { if ($field['field_key'] === $field_key) { return true; } } return false; } /** * Get a specific default field by key * * @param string $field_key Field key * @return array|null Field data or null if not found */ public function get_default_field($field_key) { $default_fields = $this->get_default_fields(); foreach ($default_fields as $field) { if ($field['field_key'] === $field_key) { return $field; } } return null; } /** * Get count of default fields * * @return int Number of default fields */ public function get_default_fields_count() { return count($this->get_default_fields()); } /** * Insert default WooCommerce fields into database * Called on plugin activation * * @param bool $force Force insertion even if fields exist * @return int Number of fields inserted */ public function insert_default_woocommerce_fields($force = false) { global $wpdb; $table = $wpdb->prefix . 'cfep_fields'; // Check if table exists // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter if ($wpdb->get_var("SHOW TABLES LIKE '$table'") != $table) { return 0; } // Check if already populated (don't re-insert on updates UNLESS forced) if (!$force) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter $existing_count = $wpdb->get_var("SELECT COUNT(*) FROM $table WHERE is_default = 1"); if ($existing_count > 0) { return 0; // Already populated } } $fields = $this->get_default_fields(); $inserted = 0; foreach ($fields as $field) { // Prepare data for insertion $data = array( 'field_key' => $field['field_key'], 'field_type' => $field['field_type'], 'section' => $field['section'], 'label' => $field['label'], 'placeholder' => $field['placeholder'], 'description' => $field['description'], 'required' => $field['required'], 'enabled' => $field['enabled'], 'position' => $field['position'], 'field_class' => $field['field_class'], 'is_default' => 1, 'can_delete' => 0, 'options' => is_array($field['options']) ? serialize($field['options']) : '' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter $result = $wpdb->insert($table, $data); if ($result) { $inserted++; } } return $inserted; } }