Parallel Practice: Gravity Forms Pattern

A screenshot of stacked bar graphs with accordion web elements below it.

Round of the parallel practice tool was done in Google Forms/Sheets. Things went pretty well and an internal award was given to help take the project to the next level. I did a bit of early sketching with the form a little while ago. This week, I’ve been working on it more diligently and I thought it’d be interesting to talk through one path for storing, viewing, and updating data straight from the Gravity Form entries. It’s not entirely standard and so might be worth checking out just to see some odd things you can do.

I could have made each form submission create a post or some custom post type1 but I thought it might be a bit cleaner just to have one place where the data lives (Gravity Forms entries) and reference that data elsewhere. It’s just a decision. I could argue either side of this.

You can see a video tour below if you like that kind of thing.

Getting Gravity Form Entries

The GFAPI provides a solid, supported path for getting form entries.

First, I have a custom post type for the student data. Each student gets one and it’s where they enter and see their data. So we’ve got to show just their data.

The function below will search for Gravity Form entries where the entries match the author of the student page. So if my student page is authored by fake_person, then it uses that variable as part of our search pattern looking for a match with the invisible field with ID 14. That field is automatically filled in as part of the post creation process.

//Form entry display for student page
function pp_practice_log(){
    $user_login = get_the_author_meta('user_login');
    $form_id = 1;//FORM ID

    $search_criteria['field_filters'][] = array( 'key' => '14', 'value' => $user_login);//search for entries where the post author's login matches field #14

    $sorting         = array();//leaving it in order of entry
    $paging          = array( 'offset' => 0, 'page_size' => 500);//first 500 
    $entries = GFAPI::get_entries($form_id, $search_criteria, $sorting, $paging, $total_count );
    echo "<div class='accordion' id='practice-data'>";
    foreach ($entries as $key => $value) {  
        pp_table_maker($value, $key);
    }
    echo "</div>";
}

Now the pp_table_makerpp stands for parallel practice.

Now I’ve got the right data showing up on each page.2

Updating Gravity Form Entries

Now people might make typos or want to change these entries so we need a nice path to that. We can do that. gform_after_submission is our path to this. What I did was set a hidden field (ID 15). If you click on the edit button from any of the previous entries, this field gets populated via javascript. The function looks to see if there is any number in that field. If there is, that’s the Gravity Forms entry that will be edited. If not, it’ll create a new practice entry (like normal). I use GFAPI::update_entry_field to update the existing data and then GFAPI::delete_entry to delete the extra entry that would have been created.

add_action( 'gform_after_submission_1', 'pp_update_gfentry', 10, 2 );//only run this after form 1

function pp_update_gfentry($entry, $form){
    if($entry[15] > 0){
        $entry_id = $entry[15];
        $lang_practice = $entry[1];
        $lang_focus = $entry[3];
        $lang_yea = $entry[4];
        $lang_hmm = $entry[5];
        $lang_strat = $entry[6];
        $alt_practice = $entry[7];
        $alt_focus = $entry[8];
        $alt_yea = $entry[9];
        $alt_hmm = $entry[10];
        $alt_strat = $entry[11];
        GFAPI::update_entry_field( $entry_id, 1, $lang_practice );
        GFAPI::update_entry_field( $entry_id, 3, $lang_focus );
        GFAPI::update_entry_field( $entry_id, 4, $lang_yea );
        GFAPI::update_entry_field( $entry_id, 5, $lang_hmm );
        GFAPI::update_entry_field( $entry_id, 6, $lang_strat );
        GFAPI::update_entry_field( $entry_id, 7, $alt_practice );
        GFAPI::update_entry_field( $entry_id, 8, $alt_focus );
        GFAPI::update_entry_field( $entry_id, 9, $alt_yea );
        GFAPI::update_entry_field( $entry_id, 10, $alt_hmm );
        GFAPI::update_entry_field( $entry_id, 11, $alt_strat ); 
        GFAPI::delete_entry($entry['id']);//auto delete so we don't end up with duplicates
    }   

}

Now to make it easier to edit these things, I use some javascript to populate the fields with the existing data when the modal pops up. That makes it easy to modify the data. It’s another long one you can check out here. I’ll give a small example of it below.

Each entry gets a button. That button has a number of data attributes as a way to pass the data. You can see some more about PHP and variables in curly braces here. Then you can take a deeper dive in data attributes and javascript here.

" <button type='button' data-bs-toggle='modal' data-bs-target='#logData' class='btn btn-primary edit-entry' data-entryid='{$entry_id}' data-practice='{$lang_practice}' data-focus ='{$lang_focus}' data-yea='{$lang_yea}' data-hmm='{$lang_hmm}' data-strat='{$lang_strat}' data-altpractice='{$alt_practice}' data-altfocus ='{$alt_focus}' data-altyea='{$alt_yea}' data-althmm='{$alt_hmm}' data-altstrat='{$alt_strat}'>Edit</button>"
const editButtons = document.querySelectorAll('.edit-entry');
editButtons.forEach((button) => {
	button.addEventListener('click', function (event) {
	  let practice = button.dataset.practice;//get the button data attribute 
      	  let formPractice = document.querySelector('#input_1_1');//get the gravity form field
	  formPractice.value = practice;//set the value of the form field with the value from the data attribute
          })
})

Updating a form entry with another form

Students log their entries and we wanted an easy way for the instructor to add a comment. There are a lots of ways I might have done this. What I opted to do was add a hidden field to the main student logging form for comment.

I then made a second form for commenting. It has two fields. One field for the comment and one hidden field which holds the ID associated with the student entry. You can see that the pattern is pretty similar to what we just did. It updates the hidden comment field on the entry from the practice form and deletes the entry that would be generated by the comment form (ID 4).

//comment adder
add_action( 'gform_after_submission_4', 'pp_practice_comment', 10, 2 );

function pp_practice_comment($entry, $form){
    $comment = $entry[1];
    $entry_id = $entry[2];
    GFAPI::update_entry_field( $entry_id, 16, $comment );
    GFAPI::delete_entry($entry['id']);//auto delete so we don't end up with entries because we don't need them
}


1 I also considered custom fields, ACF repeater field, etc. There are a million ways to do most things.

2 I’ll go through how the chart works at another time.