{"id":4954,"date":"2026-08-30T00:20:41","date_gmt":"2026-08-29T15:20:41","guid":{"rendered":"https:\/\/donguri3.net\/server-tech\/modify-cf7-flamingo-db-table-2\/"},"modified":"2026-08-30T00:20:42","modified_gmt":"2026-08-29T15:20:42","slug":"modify-cf7-flamingo-db-table","status":"publish","type":"post","link":"https:\/\/donguri3.net\/en\/server-tech\/wordpress-blog-seo\/modify-cf7-flamingo-db-table\/","title":{"rendered":"When I Tried Changing the Storage Destination for Contact Form 7 + Flamingo Submission Data"},"content":{"rendered":"<p>When using contact forms in WordPress, combining Contact Form 7 (CF7) and Flamingo is a classic choice. By default, however, submitted data is stored in the <code>wp_posts<\/code> and <code>wp_postmeta<\/code> tables within the WordPress database.<\/p>\n<p>In this article, I will summarize my attempt to change these storage locations to custom tables named <code>wp_custom_flamingo_posts<\/code> and <code>wp_custom_flamingo_meta<\/code>.<\/p>\n<h2><strong>Creating a Custom Plugin: Custom Flamingo Mod<\/strong><\/h2>\n<p>To modify Flamingo&#8217;s data storage process, I created a custom WordPress plugin called &#8220;Custom Flamingo Mod&#8221;.<\/p>\n<h3>Basic Plugin Structure<\/h3>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\/wp-content\/plugins\/custom-flamingo-mod\/\n    \u2514\u2500\u2500 custom-flamingo-mod.php<\/pre>\n<h3><strong>What Was Attempted<\/strong><\/h3>\n<h4><strong>Changing the Storage Table for Submitted Data<\/strong><\/h4>\n<p>Changed the storage location from <code>wp_posts<\/code> and <code>wp_postmeta<\/code> to <code>wp_custom_flamingo_posts<\/code> and <code>wp_custom_flamingo_meta<\/code>.<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\/\/ Create custom tables upon plugin activation\nfunction create_custom_flamingo_table() {\n        global $wpdb;\n        $table_name = $wpdb-&amp;amp;amp;amp;amp;amp;gt;prefix . &#039;custom_flamingo_messages&#039;;\n\n        $charset_collate = $wpdb-&amp;amp;amp;amp;amp;amp;gt;get_charset_collate();\n\n        $sql = &quot;CREATE TABLE IF NOT EXISTS $table_name (\n                ID bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n                post_id bigint(20) unsigned NOT NULL DEFAULT 0,\n                post_author bigint(20) unsigned NOT NULL DEFAULT 0,\n                post_date datetime NOT NULL DEFAULT &#039;0000-00-00 00:00:00&#039;,\n                post_date_gmt datetime NOT NULL DEFAULT &#039;0000-00-00 00:00:00&#039;,\n                post_content longtext COLLATE utf8mb4_unicode_ci NOT NULL,\n                post_title text COLLATE utf8mb4_unicode_ci NOT NULL,\n                post_excerpt text COLLATE utf8mb4_unicode_ci NOT NULL,\n                post_status varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT &#039;publish&#039;,\n                comment_status varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT &#039;open&#039;,\n                ping_status varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT &#039;open&#039;,\n                post_password varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT &#039;&#039;,\n                post_name varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT &#039;&#039;,\n                to_ping text COLLATE utf8mb4_unicode_ci NOT NULL,\n                pinged text COLLATE utf8mb4_unicode_ci NOT NULL,\n                post_modified datetime NOT NULL DEFAULT &#039;0000-00-00 00:00:00&#039;,\n                post_modified_gmt datetime NOT NULL DEFAULT &#039;0000-00-00 00:00:00&#039;,\n                post_content_filtered longtext COLLATE utf8mb4_unicode_ci NOT NULL,\n                post_parent bigint(20) unsigned NOT NULL DEFAULT 0,\n                guid varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT &#039;&#039;,\n                menu_order int(11) NOT NULL DEFAULT 0,\n                post_type varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT &#039;flamingo_message&#039;,\n                post_mime_type varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT &#039;&#039;,\n                comment_count bigint(20) NOT NULL DEFAULT 0,\n                PRIMARY KEY  (ID),\n                KEY post_name (post_name),\n                KEY post_parent (post_parent),\n                KEY post_author (post_author),\n                KEY post_type (post_type)\n        ) $charset_collate;&quot;;\n\n        $table_name = $wpdb-&amp;amp;amp;amp;amp;amp;gt;prefix . &#039;custom_flamingo_meta&#039;;\n\n        $charset_collate = $wpdb-&amp;amp;amp;amp;amp;amp;gt;get_charset_collate();\n\n        $sql_meta = &quot;CREATE TABLE IF NOT EXISTS $table_name (\n                meta_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n                post_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,\n                meta_key VARCHAR(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n                meta_value LONGTEXT COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n                PRIMARY KEY (meta_id),\n                KEY post_id (post_id),\n\t            KEY meta_key (meta_key)\n        ) ENGINE=InnoDB $charset_collate;&quot;;\n\n        require_once(ABSPATH . &#039;wp-admin\/includes\/upgrade.php&#039;);\n        dbDelta($sql);\n        dbDelta($sql_meta);\n}\nregister_activation_hook(__FILE__, &#039;create_custom_flamingo_table&#039;);<\/pre>\n<h4><strong>How to Change the Post Data Storage Table<\/strong><\/h4>\n<p>By utilizing the <code>wp_insert_post<\/code> hook and validating the <code>post_type<\/code>, I successfully saved the data into <code>wp_custom_flamingo_posts<\/code>.<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\/\/ Change the storage table to a custom table\nfunction copy_flamingo_data_to_custom_table($post_id, $post, $update) {\n        error_log(&quot;post_id: &quot; . $post_id);\n        \/\/ Process only `flamingo_inbound` data\n        if ($post-&amp;amp;amp;amp;amp;gt;post_type === &#039;flamingo_inbound&#039;) {\n                $existing_value = get_option(&#039;flamingo_custom_id&#039;, &#039;&#039;);\n\n                \/\/ Concatenate new data (e.g., append &quot;_new_data&quot;)\n                $new_value = $existing_value . &#039;,&#039; . $post_id;\n\n                \/\/ Update\n                update_option(&#039;flamingo_custom_id&#039;, $new_value, &#039;yes&#039;);\n\n                \/\/ Output log (for verification)\n                error_log(&quot;Updated flamingo_custom_id: &quot; . $new_value);\n\n                global $wpdb;\n                $table_name = $wpdb-&amp;amp;amp;amp;amp;gt;prefix . &#039;custom_flamingo_messages&#039;;\n\n                $data = array(\n                        &#039;ID&#039;                =&amp;amp;amp;amp;amp;gt; NULL, \/\/ AUTO_INCREMENT\n                        &#039;post_id&#039;           =&amp;amp;amp;amp;amp;gt; $post_id,\n                        &#039;post_author&#039;       =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_author,\n                        &#039;post_date&#039;         =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_date,\n                        &#039;post_date_gmt&#039;     =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_date_gmt,\n                        &#039;post_content&#039;      =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_content,\n                        &#039;post_title&#039;        =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_title,\n                        &#039;post_excerpt&#039;      =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_excerpt,\n                        &#039;post_status&#039;       =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_status,\n                        &#039;comment_status&#039;    =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;comment_status,\n                        &#039;ping_status&#039;       =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;ping_status,\n                        &#039;post_password&#039;     =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_password,\n                        &#039;post_name&#039;         =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_name,\n                        &#039;to_ping&#039;           =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;to_ping,\n                        &#039;pinged&#039;            =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;pinged,\n                        &#039;post_modified&#039;     =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_modified,\n                        &#039;post_modified_gmt&#039; =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_modified_gmt,\n                        &#039;post_content_filtered&#039; =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_content_filtered,\n                        &#039;post_parent&#039;       =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_parent,\n                        &#039;guid&#039;              =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;guid,\n                        &#039;menu_order&#039;        =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;menu_order,\n                        &#039;post_type&#039;         =&amp;amp;amp;amp;amp;gt; &#039;flamingo_inbound&#039;, \/\/ Type for custom table\n                        &#039;post_mime_type&#039;    =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_mime_type,\n                        &#039;comment_count&#039;     =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;comment_count,\n                );\n\n                \/\/ Insert data\n                $result = $wpdb-&amp;amp;amp;amp;amp;gt;insert($table_name, $data);\n\n                \/\/ Delete from `wp_posts` (if necessary)\n                wp_delete_post($post_id, true);\n\n                return $post_id;\n        } elseif ($post-&amp;amp;amp;amp;amp;gt;post_type === &#039;flamingo_contact&#039;) {\n                $existing_value = get_option(&#039;flamingo_custom_id&#039;, &#039;&#039;);\n\n                \/\/ Concatenate new data (e.g., append &quot;_new_data&quot;)\n                $new_value = $existing_value . &#039;,&#039; . $post_id;\n\n                \/\/ Update\n                update_option(&#039;flamingo_custom_id&#039;, $new_value, &#039;yes&#039;);\n\n                \/\/ Output log (for verification)\n                error_log(&quot;Updated flamingo_custom_id: &quot; . $new_value);\n\n                global $wpdb;\n                $table_name = $wpdb-&amp;amp;amp;amp;amp;gt;prefix . &#039;custom_flamingo_messages&#039;;\n                $data = array(\n                        &#039;ID&#039; =&amp;amp;amp;amp;amp;gt; NULL,\n                        &#039;post_id&#039;       =&amp;amp;amp;amp;amp;gt; $post_id,\n                        &#039;post_type&#039;     =&amp;amp;amp;amp;amp;gt; &#039;flamingo_contact&#039;,\n                        &#039;post_status&#039;   =&amp;amp;amp;amp;amp;gt; &#039;publish&#039;,\n                        &#039;post_title&#039;    =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_title,\n                        &#039;post_name&#039;     =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_name,\n                        &#039;post_content&#039;  =&amp;amp;amp;amp;amp;gt; $post-&amp;amp;amp;amp;amp;gt;post_content,\n                );\n                \/\/ Insert data\n                $result = $wpdb-&amp;amp;amp;amp;amp;gt;insert($table_name, $data);\n\n                \/\/ Delete from `wp_posts` (if necessary)\n                wp_delete_post($post_id, true);\n\n                return $post_id;\n        } else {\n                return;\n        }\n}\nadd_action(&#039;wp_insert_post&#039;, &#039;copy_flamingo_data_to_custom_table&#039;, 10, 3);<\/pre>\n<h4><strong>Changing Metadata Storage<\/strong><\/h4>\n<p>I attempted to use the <code>update_post_meta<\/code> hook to determine whether it was a Flamingo post based on the <code>post_id<\/code> stored in <code>wp_options<\/code>.<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\/\/ Change metadata storage destination to the custom metadata table\nfunction redirect_flamingo_meta_update( $null, $object_id, $meta_key, $meta_value ) {\n        error_log(&quot;redirect_flamingo_meta_update&quot;);\n        $post_type = &#039;post&#039;;\n        if( &#039;post&#039; === $post_type){\n                $flamingo_custom_id = get_option(&#039;flamingo_custom_id&#039;, &#039;&#039;);\n\n                error_log(&quot;Hook triggered - flamingo_custom_id: {$flamingo_custom_id}, object_id: {$object_id}, meta_key: {$meta_key}, meta_value: {$meta_value}&quot;);\n\n                if (strpos($flamingo_custom_id, $object_id) !== false) {\n                        global $wpdb;\n                        $custom_table = $wpdb-&amp;amp;gt;prefix . &#039;custom_flamingo_meta&#039;;\n\n                        error_log(&quot;Saving to custom table - post_id: {$object_id}, meta_key: {$meta_key}, meta_value: {$meta_value}&quot;);\n\n                        \/\/ Save data to custom table\n                        $wpdb-&amp;amp;gt;replace(\n                                $custom_table,\n                                &#x5B;\n                                        &#039;post_id&#039; =&amp;amp;gt; $object_id,\n                                        &#039;meta_key&#039; =&amp;amp;gt; $meta_key,\n                                        &#039;meta_value&#039; =&amp;amp;gt; $meta_value\n                                ]\n                        );\n\n                        error_log(&quot;Data saved successfully!&quot;);\n\n                        \/\/ Returning `false` skips saving via `update_post_meta()`\n                        return false;\n                }\n        }\n        return $null;\n}\nadd_action( &#039;update_post_meta&#039;, &#039;redirect_flamingo_meta_update&#039;, 10, 4 );<\/pre>\n<h3><strong>Issue Encountered: The <code>update_post_meta<\/code> Hook Isn&#8217;t Triggering?<\/strong><\/h3>\n<p>When I first started building this, the <code>update_post_meta<\/code> hook should have been working normally, but for some reason, it stopped responding halfway through.<\/p>\n<p>Wondering &#8220;Why?&#8221;, I continued debugging\u2014such as switching to <code>update_metadata<\/code>\u2014but the <code>update_post_meta<\/code> hook simply refused to fire, and I haven&#8217;t been able to identify the cause so far.<\/p>\n<h2><strong>Discussion: Does Flamingo Follow WordPress Best Practices for Data Storage?<\/strong><\/h2>\n<p>Looking back at Flamingo&#8217;s data storage method, it seems to align with standard WordPress data management workflows.<\/p>\n<p>Initially, I thought that &#8220;separating tables would make it easier to check when looking directly at the DB,&#8221; but when checking the database directly, filtering the <code>wp_posts<\/code> table with <code>SELECT * FROM `wp_posts` WHERE `post_type` LIKE 'flamingo%'<\/code> is probably sufficient.<\/p>\n<p>Furthermore, by leveraging the standard WordPress data management workflow, you benefit from features like database optimization tools (e.g., LiteSpeed Cache) that can automatically clean up unnecessary (orphaned) postmeta information. Managing data in custom tables means you lose out on these standard WordPress features. Therefore, going out of your way to separate tables might not even be necessary.<\/p>\n<p>However, because <code>wp_postmeta<\/code> stores individual data points\u2014such as names, email addresses, and message contents\u2014separately for each inquiry, it is designed so that the number of records increases rapidly. On sites receiving a massive volume of inquiries, this could potentially become a bottleneck.<\/p>\n<h2><strong>Conclusion: Is Table Partitioning Necessary?<\/strong><\/h2>\n<p>Although I tried creating custom tables, data management that leverages standard WordPress features might actually be more practical.<\/p>\n<p>That said, since the <code>wp_postmeta<\/code> approach can impact performance as data grows, sites receiving a heavy volume of inquiries will likely need to carefully consider their optimal storage method.<\/p>\n<p>If I feel up to it, I plan to continue testing this in the future.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When using contact forms in WordPress, combining Contact Form 7 (CF7) and Flamingo is a classic choice. By def [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":1106,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_locale":"en_US","_original_post":"https:\/\/donguri3.net\/?p=717","footnotes":""},"categories":[1166],"tags":[15,117,61,179,228],"class_list":["post-4954","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-wordpress-blog-seo","tag-litespeed","tag-litespeed-cache","tag-wordpress","tag-179","tag-228","en-US"],"_links":{"self":[{"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/posts\/4954","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/comments?post=4954"}],"version-history":[{"count":1,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/posts\/4954\/revisions"}],"predecessor-version":[{"id":4957,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/posts\/4954\/revisions\/4957"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/media\/1106"}],"wp:attachment":[{"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/media?parent=4954"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/categories?post=4954"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/tags?post=4954"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}