Automatically duplicate views on form duplicate

By: Ernesto Serrano | Asked: 10/25/2024
ForumsCategory: Code SnippetsAutomatically duplicate views on form duplicate
Ernesto Serrano asked 4 weeks ago

  Here’s a code snippet that duplicates associated views when you duplicate a form in Formidable Forms. This ensures that any views linked to the original form are also duplicated and correctly linked to the new form.

// Hook the function to frm_after_duplicate_form
add_action( 'frm_after_duplicate_form', 'duplicate_views_after_form', 10, 3 );

/**
* Duplicate the views associated with a form when it is duplicated
*
* @param int $new_form_id The ID of the duplicated form
* @param array $new_form The data of the duplicated form
* @param array $args Additional arguments, including the original form ID
*/
function duplicate_views_after_form( $new_form_id, $new_form, $args ) {
$old_form_id = $args['old_id'];


// Check if FrmViewsDisplay class and method exist to verify Pro license for views if ( ! class_exists( 'FrmViewsDisplay' ) || ! method_exists( 'FrmViewsDisplay', 'get_display_ids_by_form' ) ) { return; // Exit if views are not supported in this setup } // Get the views associated with the original form $views = FrmViewsDisplay::get_display_ids_by_form( $old_form_id ); // Exit if there are no associated views to duplicate if ( empty( $views ) ) { return; }
// Get the fields of the original and duplicated forms, indexed by 'field_name' $old_fields_by_name = array_column( FrmField::get_all_for_form( $old_form_id ), null, 'name' ); $new_fields_by_name = array_column( FrmField::get_all_for_form( $new_form_id ), null, 'name' ); foreach ( $views as $view_id ) { // Duplicate the view $new_view_id = FrmViewsDisplay::duplicate( $view_id ); // Update the title and slug of the duplicated view $new_view_slug = get_post( $view_id )->post_name . '-' . $new_form_id; wp_update_post( array( 'ID' => $new_view_id, 'post_name' => $new_view_slug, 'post_title' => $new_view_slug, )); // Update the form ID in the duplicated view update_post_meta( $new_view_id, 'frm_form_id', $new_form_id ); // Update fields in post_content, frm_options, and frm_dyncontent update_fields_in_post( $new_view_id, 'post_content', $old_fields_by_name, $new_fields_by_name ); update_fields_in_post( $new_view_id, 'frm_options', $old_fields_by_name, $new_fields_by_name, true ); update_fields_in_post( $new_view_id, 'frm_dyncontent', $old_fields_by_name, $new_fields_by_name, true ); } } /** * Update field IDs in post meta or post content * * @param int $view_id The ID of the post or view * @param string $field The field to update (post_content, frm_options, frm_dyncontent) * @param array $old_fields_by_name The fields of the original form, indexed by name * @param array $new_fields_by_name The fields of the duplicated form, indexed by name * @param bool $is_meta True if it's meta data, false if it's post_content */ function update_fields_in_post( $view_id, $field, $old_fields_by_name, $new_fields_by_name, $is_meta = false ) { // Get the current content $content = $is_meta ? get_post_meta( $view_id, $field, true ) : get_post_field( $field, $view_id ); if ( empty( $content ) ) { return; } // If we're in frm_options, specifically modify 'where' and 'order_by' fields if ( $field === 'frm_options' && is_array( $content ) ) { $content = replace_field_ids_in_options( $content, $old_fields_by_name, $new_fields_by_name ); } else { // For non-serialized content like `post_content` and `frm_dyncontent` $content = replace_field_ids_in_content( $content, $old_fields_by_name, $new_fields_by_name ); } // Save the updated content if ( $is_meta ) { update_post_meta( $view_id, $field, $content ); } else { wp_update_post( array( 'ID' => $view_id, $field => $content ) ); } } /** * Replace the IDs in 'where' and 'order_by' within frm_options * * @param array $options The options array from frm_options * @param array $old_fields_by_name The fields of the original form, indexed by name * @param array $new_fields_by_name The fields of the duplicated form, indexed by name * @return array The updated options array */ function replace_field_ids_in_options( $options, $old_fields_by_name, $new_fields_by_name ) { // Modify IDs in the 'where' field if it exists if ( isset( $options['where'] ) && is_array( $options['where'] ) ) { foreach ( $options['where'] as &$value ) { foreach ( $old_fields_by_name as $field_name => $old_field ) { if ( $value === (string) $old_field->id && isset( $new_fields_by_name[ $field_name ] ) ) { $value = (string) $new_fields_by_name[ $field_name ]->id; break; } } } } // Modify IDs in the 'order_by' field if it exists if ( isset( $options['order_by'] ) && is_array( $options['order_by'] ) ) { foreach ( $options['order_by'] as &$value ) { foreach ( $old_fields_by_name as $field_name => $old_field ) { if ( $value === (string) $old_field->id && isset( $new_fields_by_name[ $field_name ] ) ) { $value = (string) $new_fields_by_name[ $field_name ]->id; break; } } } } return $options; } /** * Replace the field IDs in the content * * @param string $content The content where IDs will be replaced * @param array $old_fields_by_name The fields of the original form, indexed by name * @param array $new_fields_by_name The fields of the duplicated form, indexed by name * @return string The content with the IDs replaced */ function replace_field_ids_in_content( $content, $old_fields_by_name, $new_fields_by_name ) { $pattern = '/\[(\/?)(if|foreach)?\s*(\d+)([^\]]*)\]/'; return preg_replace_callback( $pattern, function ( $matches ) use ( $old_fields_by_name, $new_fields_by_name ) { $old_field_id = $matches[3]; // The field ID $extra_params = $matches[4]; // Additional parameters // Find the old field by its ID and get the new ID foreach ( $old_fields_by_name as $field_name => $old_field ) { if ( $old_field->id == $old_field_id && isset( $new_fields_by_name[ $field_name ] ) ) { $new_field_id = $new_fields_by_name[ $field_name ]->id; return '[' . $matches[1] . $matches[2] . ($matches[2] ? ' ' : '') . $new_field_id . $extra_params . ']'; } } return $matches[0]; // If the field is not found, return the original match }, $content ); }

Explanation:

  • Purpose: This code ensures that when you duplicate a form using Formidable Forms, all associated views are also duplicated. It updates all necessary IDs and references so the new views work seamlessly with the duplicated form.
  • How It Works:
  • Hook into Duplication Action: The duplicate_views_after_form function is hooked to the frm_after_duplicate_form action, which is triggered after a form is duplicated.
  • Retrieve Original Views: It fetches all views associated with the original form.
  • Duplicate Views: Each view is duplicated using FrmViewsDisplay::duplicate.
  • Update View Details: The duplicated view’s title, slug, and associated form ID are updated to reflect the new form.
  • Update Field IDs: The code updates field IDs within the view’s content, options, and dynamic content to match the new form’s field IDs. This ensures all shortcodes and references point to the correct fields.
  • Functions Breakdown:
  • duplicate_views_after_form: Main function that handles the duplication process.
  • update_fields_in_post: Updates field IDs in the post content or meta fields.
  • replace_field_ids_in_options: Specifically updates field IDs in the frm_options array, handling where and order_by clauses.
  • replace_field_ids_in_content: Uses regex to find and replace field IDs within shortcodes in the content.

Usage Instructions:

  1. Add the Code: Place the provided code in your theme’s functions.php file or in a custom plugin.
  2. Test the Duplication: Duplicate a form in Formidable Forms as you normally would.
  3. Verify the Views: After duplication, check that the associated views have been duplicated and are correctly linked to the new form.

Notes:

  • Customization: If you have custom fields or additional data that need to be handled, you may need to extend the functions accordingly.
  • Backup: Always backup your site before adding new code to prevent any unexpected issues.
  • Compatibility: Ensure that your version of Formidable Forms supports the functions and hooks used in this code.

Conclusion: By adding this code to your WordPress site, you streamline the process of duplicating forms and their associated views, saving time and reducing the potential for errors in manually updating IDs and references. ​  

1 Answers
Victor Font answered 4 weeks ago
You did a nice job coding and documenting, but after reading through your code, I think it's based on assumptions that may not always be true. The code assumes two things. First, it assumes a Pro license that includes views. Basic Pro licenses do not include views. Second it assumes every form that is duplicated has views. You don't have any early exits in your function to bug out if the rest of the code isn't necessary to run or if running it will produce errors. Your $views array could be empty and you don't check for it. The code assumes an array of valid results.
Ernesto Serrano replied 4 weeks ago

Hello Victor,

Thank you for your feedback and for highlighting the potential issues in the code. I've updated it to check for a Formidable license and ensure views are present before running the duplication process. This should help prevent unnecessary executions and errors when duplicating forms without associated views or lacking the required license.

Your comments were very helpful in improving the code’s reliability—thank you once again!

Best regards,
Ernesto

Making the Best WordPress Plugin even better - Together

Take on bigger projects with confidence knowing you have access to an entire community of Formidable Experts and Professionals who have your back when the going gets tough. You got this!
Join the community
crossarrow-right