Last active
January 12, 2024 04:07
-
-
Save dskurth/d56744de4ae076c417f59b2a459eaf4b to your computer and use it in GitHub Desktop.
CBM2 - USING CUSTOM TABLES TO READ AND STORE DATA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
//////////////////////////////////////////////////////////////////////////////////////// | |
// | |
// This a demo version of how to use CMB2 with WordPress Table of your own design | |
// It assumes you have put together a custom post and are integrating this development tool | |
// for form management. | |
// | |
// PROS | |
// All the tools for easy data managment for forms with all the data input tools, such as required fields, repeatable fields and all data is stored within the | |
// cache of the system, rather than being stored to disk. It is quick and easy to bring up basic forms. | |
// | |
// CONS | |
// A high level documentation exists and the support group makes great efforts to help, but there are lots of assumptions you have to figure out | |
// for yourself. It is not always simple straight forward coding. Especially when it comes to standard HTML type controls such as checkboxes and HTML5 dates. | |
// If you like fancy written code that is cool, because you can do it...this sample will not be for you. | |
// | |
// | |
// Hopefully this code set examplewill help that process. | |
// I used the following snippet heavily, for figuring out what the parameters did and did not contain. The DebugLog | |
// call is a custom function that I wrote for this process. You should replace it with any print utitily you perfer to use. | |
// | |
// $results = print_r($object_id, true); | |
// DebugLog('DATA DUMP $object_id: ' . $results); | |
// | |
// DISCOVERIES | |
// 1. The call back function 'cmb2_override_meta_remove', does not work. | |
// 2. All the control is done through the use of add_filter('filter type', 'your function', 10, 2); Where the last number '2' is the number of parameters passed to the function. | |
// 3. The cmb2_override_meta_save is called only once on the clicking of the Update button. | |
// 4. The cmb2_override_meta_value is called repeatly. One time for every field you are using, plus one. If accessing your database and you have 30 fields, this will hit your | |
// database 31 times. There for I have written some code to use past database reads and use that UNTIL the last read (fieldcnt +1). You will need to write this code in the | |
// The cmb2_override_meta_value function call or let it hit your database. | |
// | |
// ASSUMPTIONS | |
// This piece of code assumes the following: | |
// | |
// 1. you are only using this work tool for ONE | |
// 2. repeatable records | |
// 3. Independent table | |
// 4. This code "builds" the form based on a static array of fields. | |
// 5. The database is designed to be uses a data storage AND logical tool for knowing what records to delete and add | |
// | |
// If you are using this tool, within a theme or plugin | |
// for more than one type of metabox, then the callbacks shown here will have | |
// to be unique and probably should follow the designers thinking for inserting an identifcation name | |
// within the call back function for clarity. | |
// | |
// This code demonstrates 2 filters: | |
// cmb2_override_meta_value : A filter to overide the codes default desire to store all inputed data to the postmeta table in serialized form. | |
// cmb2_override_meta_save | |
// | |
// | |
// | |
/////////////////////////////////////////////////////////////////////////////////////// | |
// Return if this code has been called directly | |
if ( ! defined( 'ABSPATH' ) ) {return; } | |
//----------------------------------------------------------------- | |
// ENTRIES OF CUSTOM POST | |
// | |
//----------------------------------------------------------------- | |
function mmd_form() | |
{ | |
$prefix = 'mmd'; // Custom Post Name | |
$cmb = new_cmb2_box( array( | |
'id' => 'mmd_records_test', | |
'title' => __( 'Test Form', 'mmd' ), | |
'object_types' => $prefix, // Post type | |
'context' => 'normal', | |
'priority' => 'high', | |
'show_names' => true, // Show field names on the left | |
) ); | |
$ListId = get_the_ID(); // ListId is the same as the PostId | |
// Get the data to output | |
$input_fields = GetTemplateFields(); | |
$StateArray = GetStateNames(); | |
$CountryArray = GetSupportedCountries(); | |
//------------------------------------------------------------------- | |
// Group Asssignment | |
$group_field_id = $cmb->add_field( array( | |
'id' => 'MMDListsRecord', | |
'type' => 'group', | |
'description' => __( 'Individual Directory Listings', 'mmd' ), | |
'options' => array( | |
'group_title' => __( 'Record {#}', 'mmd' ), // since version 1.1.4, {#} gets replaced by row number | |
'add_button' => __( 'Add Another Record', 'mmd' ), | |
'remove_button' => __( 'Remove Record', 'mmd' ), | |
'sortable' => true, | |
), | |
) ); | |
//--------------------- Build the template | |
for($i=0; $i<sizeof($input_fields); $i++) | |
{ | |
$Field = $input_fields[$i]; | |
$FieldDesc = $Field['desc']; // For defintions of array field types, the the input_fields Table | |
$FieldId = $Field['id']; | |
$FieldType = $Field['type']; | |
// Using a method that builds a form, based on a table. As such, you will not see hard code attributes. The reasoning behind this is | |
// that each field may have unique requriements | |
// if even their type is the same. Therefore we "build" the attributes based on what the table settings indicate are necessary | |
// for the particular field. In general, this work package (CMB2) uses the assumption of if it is required the string settings | |
// is "there", if not, then the string does not exist, even if it is set to blank, some fields, like readonly, will make the field readonly. | |
// It is simular to the old HTML input scripting. | |
$Attributes=array(); | |
if($Field['required'] == 1 && $Field['readonly'] == 1) | |
$Attributes = array('required' => 'required', 'readonly' => 'readonly'); | |
if($Field['required'] == 0 && $Field['readonly'] == 1) | |
$Attributes = array('readonly' => 'readonly'); | |
if($Field['required'] == 1 && $Field['readonly'] == 0) | |
$Attributes = array('required' => 'required'); | |
switch($Field['type']) | |
{ | |
case 'text': | |
$cmb->add_group_field( $group_field_id, array( | |
'name' => $FieldDesc, | |
'id' => $FieldId, | |
'type' => 'text', | |
'on_front' => false, | |
'attributes' => $Attributes, | |
'default_cb' => 'SetTextBox', | |
) ); | |
break; | |
case 'text_small': | |
$cmb->add_group_field( $group_field_id, array( | |
'name' => $FieldDesc, | |
'id' => $FieldId, | |
'type' => 'text_small', | |
'on_front' => false, | |
'attributes' => $Attributes | |
) ); | |
break; | |
case 'textarea': | |
$cmb->add_group_field( $group_field_id, array( | |
'name' => $FieldDesc, | |
'id' => $FieldId, | |
'type' => 'textarea_small', | |
'on_front' => false, | |
'attributes' => $Attributes | |
) ); | |
break; | |
case 'select_cnt': | |
$SupportedCountries = mmd_lists_GetSupportedCountries(); | |
$cmb->add_group_field( $group_field_id, array( | |
'name' => $FieldDesc, | |
'id' => $FieldId, | |
'type' => 'select', | |
'on_front' => false, | |
'attributes' => $Attributes, | |
'options' => $SupportedCountries, | |
) ); | |
break; | |
case 'select_st': | |
$StateArray = mmd_lists_GetStateNames(); | |
$cmb->add_group_field( $group_field_id, array( | |
'name' => $FieldDesc, | |
'id' => $FieldId, | |
'type' => 'select', | |
'on_front' => false, | |
'attributes' => $Attributes, | |
'options' => $StateArray, | |
) ); | |
break; | |
case 'date': | |
$cmb->add_group_field( $group_field_id, array( | |
'name' => $FieldDesc, | |
'id' => $FieldId, | |
'on_front' => false, | |
'type' => 'text_date', | |
'attributes' =>$Attributes, | |
//'default_cb' => 'time' // Only works for a complete blank time. | |
) ); | |
break; | |
case 'checkbox': | |
$cmb->add_group_field( $group_field_id, array( | |
'name' => $FieldDesc, | |
'id' => $FieldId, | |
'on_front' => false, | |
'type' => 'checkbox', | |
'attributes' =>$Attributes, | |
'default' => '', | |
) ); | |
break; | |
} | |
unset ($Attributes); // Clear out the data for the next round | |
} //for($i=0; $i<sizeof($input_fields); $i++) | |
} // function mmd_listings_add_manual_form() | |
add_action( 'cmb2_admin_init', 'mmd_form' ); | |
/////////////////////////////////////////////////////////////////////////////////// | |
// Draw Data | |
/////////////////////////////////////////////////////////////////////////////////// | |
add_filter('cmb2_override_MMDListsRecord_meta_value', 'mmd_get_methods_custom_data', 10, 2); | |
function mmd_get_methods_custom_data( $dont_override, $PostId ) | |
{ | |
$DatabaseData = array(); | |
$DatabaseData = GetDataBaseRecords($PostId); // YOu will have to write your own version of this function | |
$TotalRecords = $DatabaseData[0]; | |
$Records = array(); // Data to be returned back to the form | |
$DBIndex = 1; // The database record starts with record 0 as the total number of records. So the data starts at record 1. | |
for($RecordIndex=0; $RecordIndex < $TotalRecords; $RecordIndex++) | |
{ | |
$RowData = $DatabaseData[$DBIndex]; | |
// CBM2 assumes that checkboxes values are on/off. In particular only "on" or nothing there. To fix the checkbox situation | |
// from a classic indicator of 1 or 0, this code checks for the database state and outputs the string information based on CBM2 requirement. | |
// Fix the checkboxes from a 0/1 to on or off | |
$FeaturedCkBox = ''; | |
if($RowData['Featured'] == 1) | |
$FeaturedCkBox = 'on'; | |
//CBM2 has a ton of trouble with default date/time indicators as used as default in a MySql table. it displays 0000-00-00 00:00:00 with slashes | |
// To fix the problem, this example sets the date to a "forever" state of a 1,000 years in the future. | |
if(strcmp($RowData['PaymentDate'], '0000-00-00 00:00:00')==0) | |
$RowData['PaymentDate'] = '2025-01-01 00:00:00'; // Can not go above about 5 years or the scripts do not know what to do. | |
// This is where you actually update the CMB2 fiels. | |
// Each array, within the master array, is a repeatable record with the form. | |
$Record[$RecordIndex] = array( | |
'title' => $RowData['BusinessName'], | |
'address' => $RowData['BusinessAddress'], | |
'city' => $RowData['BusinessCity'], | |
'state' => $RowData['BusinessState'], | |
'postcode' => $RowData['BusinessZip'], | |
'country' => $RowData['BusinessCountry'], | |
'phone' => $RowData['BusinessPhone'], | |
'email' => $RowData['Email'], | |
'paymentdate' => $RowData['PaymentDate'], | |
'featured' => $FeaturedCkBox, | |
'dbid' => $RowData['id'], | |
); | |
$Records[] = $Record[$RecordIndex]; // CBM2 works off of a zero based record set. | |
$DBIndex +=1; | |
} | |
return $Records; | |
} | |
/////////////////////////////////////////////////////////////// | |
// Save the data back to the database | |
// | |
// A crazy thing with the CMB2 system is that, when it deletes | |
// a record in it's cache, it does not send a message to the | |
// delete call back. Instead, it just delete the information array | |
// for the cache. SO, a safe is really a two operation function. | |
// First you have to find the records from the fields that are missing | |
// This is where a single table design works nicely. | |
// During the draw process, you place the database record id number in the form, either by hidden value | |
// or for display in the form, in a readonly state. Then when you save the data, you have a map | |
// of all the records that are still being stored and which records needs to be deleted from the table. | |
// containing the unqiue database id. | |
// | |
////////////////////////////////////////////////////////////// | |
add_filter('cmb2_override_MMDListsRecord_meta_save', 'save_custom_data', 10, 2); | |
function save_custom_data($data, $values ) | |
{ | |
$PostId = get_the_ID(); | |
$DatabaseData = GetDataBaseRecords($PostId); // Array of records | |
$FieldData = $values['value']; // Access to the data contained within the fields. | |
//----------------------------------------------- | |
// First check if any record(s) were deleted | |
//array_diff ($DatabaseData, | |
$FieldBaseAvaiableRecords = array(); | |
$DBBaseAvaiableRecords = array(); | |
$DBDeleteRecords = array(); | |
$FieldData = $values['value']; | |
// Store all records by db id into an array | |
for($i=0; $i<$DatabaseData[0]; $i++) | |
$DBBaseAvaiableRecords[$i] = $DatabaseData[$i+1]['id']; // Database records start at 0 | |
// STore all records by db id that remain | |
$RecordId=0; | |
while(1==1) | |
{ | |
$Record = $FieldData[$RecordId]; | |
if (empty($Record)) | |
break; | |
if(strlen( $Record['dbid'])>0) // No id means new entry | |
$FieldBaseAvaiableRecords[$RecordId] = $Record['dbid']; | |
$RecordId ++; | |
} //while(1==1) | |
// Are there any records to be deleted? There will be more in the database, than in the fields | |
// Yes, find out what | |
$DeletedRecords=array_diff($DBBaseAvaiableRecords, $FieldBaseAvaiableRecords); | |
if(sizeof($DeletedRecords)>0) | |
{ | |
$DeleteRecordKeys = array_keys($DeletedRecords); | |
for($i=0; $i<sizeof($DeletedRecords); $i++) | |
{ | |
$Key = $DeleteRecordKeys[$i]; | |
$FinalDeleteList[$i] = $DeletedRecords[$Key]; | |
} | |
delete_records($PostId, $FinalDeleteList); | |
} | |
//--------------------------------------- | |
// Save all the field arra | |
$RecordId=0; | |
while(1==1) | |
{ | |
$Record = $FieldData[$RecordId]; | |
if (empty($Record)) | |
break; // Stop when there are no more records in the cache | |
$DBRecordSet['ListId'] = $PostId; | |
// These are th eonly records save by manual entry | |
$DBRecordSet['Email'] = $Record['email']; | |
$DBRecordSet['BusinessName'] = $Record['title']; | |
$DBRecordSet['BusinessAddress'] = $Record['address']; | |
$DBRecordSet['BusinessCity'] = $Record['city']; | |
$DBRecordSet['BusinessState'] = $Record['state']; | |
$DBRecordSet['BusinessZip'] = $Record['postcode']; | |
$DBRecordSet['BusinessCountry'] = $Record['country']; | |
$DBRecordSet['BusinessPhone'] = $Record['phone']; | |
$DBRecordSet['PaymentDate'] = $Record['paymentdate']; | |
$DBRecordSet['Featured'] = 0; | |
if( array_key_exists( 'featured', $Record ) ) | |
{ | |
if(strlen($Record['featured']) > 0) | |
$DBRecordSet['Featured'] = 1; | |
} | |
if(strlen( $Record['dbid'])>0) | |
UpdateRecord($PostId, $Record['dbid'], $DBRecordSet); // This record already exists, update it. | |
else | |
NewRecord($PostId, $DBRecordSet); // New record, insert it. | |
$RecordId ++; | |
} //while(1==1) | |
return true; | |
} | |
// THIS FUNCTION NEVER APPEARS TO BE CALLED, EVEN WITH A REMOVE | |
add_filter('cmb2_override_MMDListsRecord_meta_remove', 'remove_custom_data', 10, 2); | |
function remove_custom_data($data, $values) | |
{ | |
return true; | |
} | |
/////////////////////////////////////////////////////////////////////////////////////////////////// | |
// TEMPLATE CARD DATA | |
///////////////////////////////////////////////////////////////////////////////////////////////// | |
function GetTemplateFields() | |
{ | |
$input_fields = array( | |
array( 'id'=>'title', 'type'=>'text', 'desc'=>'Title: ', 'required'=>1, 'readonly'=> 0, 'PH'=>'Business Name' ), | |
array( 'id'=>'address', 'type'=>'text', 'desc'=>'Address: ', 'required'=>1, 'readonly'=> 0, 'PH'=>'Business Address' ), | |
array( 'id'=>'city', 'type'=>'text', 'desc'=>'City: ', 'required'=>1, 'readonly'=> 0, 'PH'=>'Business City' ), | |
array( 'id'=>'state', 'type'=>'select_st', 'desc'=>'State: ', 'required'=>1, 'readonly'=> 0, 'PH'=>'' ), | |
array( 'id'=>'country', 'type'=>'select_cnt', 'desc'=>'Country: ', 'required'=>1, 'readonly'=> 0, 'PH'=>'' ), | |
array( 'id'=>'postcode', 'type'=>'text_small', 'desc'=>'Post/Zip Code: ','required'=>0, 'readonly'=> 0, 'PH'=>'Post Code' ), | |
array( 'id'=>'phone', 'type'=>'text', 'desc'=>'Phone: ', 'required'=>0, 'readonly'=> 0, 'PH'=>'Business Phone' ), | |
array( 'id'=>'email', 'type'=>'text', 'desc'=>'Email: ', 'required'=>1, 'readonly'=> 0, 'PH'=>'Business Email' ), | |
array( 'id'=>'paymentdate', 'type'=>'date', 'desc'=>'Expire Date: ', 'required'=>0, 'readonly'=> 0, 'PH'=>'' ), | |
array( 'id'=>'featured', 'type'=>'checkbox', 'desc'=>'Featured', 'required'=>0, 'readonly'=> 0, 'PH'=>'' ), | |
array( 'id'=>'dbid', 'type'=>'text_small', 'desc'=>'DBId: ', 'required'=>0, 'readonly'=> 1, 'PH'=>'' ), | |
); | |
return $input_fields; | |
} | |
function GetStateNames() | |
{ | |
$StateArray = array('PICK A STATE'=> __( 'PICK A STATE', 'mmd' ), | |
'Alabama'=> __( 'Alabama', 'mmd' ), | |
'Alaska'=> __( 'Alaska', 'mmd' ), | |
'Arizona'=> __( 'Arizona', 'mmd' ), | |
'Arkansas'=> __( 'Arkansas', 'mmd' ), | |
'California'=> __( 'California', 'mmd' ), | |
'Colorado'=> __( 'Colorado', 'mmd' ), | |
'Connecticut'=> __( 'Connecticut', 'mmd' ), | |
'Delaware'=> __( 'Delaware', 'mmd' ), | |
'District of Columbia'=> __( 'District of Columbia', 'mmd' ), | |
'Florida'=> __( 'Florida', 'mmd' ), | |
'Georgia'=> __( 'Georgia', 'mmd' ), | |
'Hawaii'=> __( 'Hawaii', 'mmd' ), | |
'Idaho'=> __( 'Idaho', 'mmd' ), | |
'Illinois'=> __( 'Illinois', 'mmd' ), | |
'Indiana'=> __( 'Indiana', 'mmd' ), | |
'Iowa'=> __( 'Iowa', 'mmd' ), | |
'Kansas'=> __( 'Kansas', 'mmd' ), | |
'Kentucky'=> __( 'Kentucky', 'mmd' ), | |
'Louisiana'=> __( 'Louisiana', 'mmd' ), | |
'Maine'=> __( 'Maine', 'mmd' ), | |
'Maryland'=> __( 'Maryland', 'mmd' ), | |
'Massachusetts'=> __( 'Massachusetts', 'mmd' ), | |
'Michigan' => __( 'Michigan', 'mmd' ), | |
'Minnesota'=> __( 'Minnesota', 'mmd' ), | |
'Mississippi'=> __( 'Mississippi', 'mmd' ), | |
'Missouri'=> __( 'Missouri', 'mmd' ), | |
'Montana'=> __( 'Montana', 'mmd' ), | |
'Nebraska'=> __( 'Nebraska', 'mmd' ), | |
'Nevada'=> __( 'Nevada', 'mmd' ), | |
'New Hampshire'=> __( 'New Hampshire', 'mmd' ), | |
'New Jersey'=> __( 'New Jersey', 'mmd' ), | |
'New Mexico'=> __( 'New Mexico', 'mmd' ), | |
'New York'=> __( 'New York', 'cmb2' ), | |
'North Carolina'=> __( 'North Carolina', 'mmd' ), | |
'North Dakota'=> __( 'North Dakota', 'mmd' ), | |
'Ohio'=> __( 'Ohio', 'mmd' ), | |
'Oklahoma'=> __( 'Oklahoma', 'mmd' ), | |
'Oregon'=> __( 'Oregon', 'mmd' ), | |
'Pennsylvania'=> __( 'Pennsylvania', 'mmd' ), | |
'Rhode Island'=> __( 'Rhode Island', 'mmd' ), | |
'South Carolina'=> __( 'South Carolina', 'mmd' ), | |
'South Dakota'=> __( 'South Dakota', 'mmd' ), | |
'Tennessee'=> __( 'Tennessee', 'mmd' ), | |
'Texas'=> __( 'Texas', 'mmd' ), | |
'Utah'=> __( 'Utah', 'mmd' ), | |
'Vermont'=> __( 'Vermont', 'mmd' ), | |
'Virginia'=> __( 'Virginia', 'mmd' ), | |
'Washington'=> __( 'Washington', 'mmd' ), | |
'West Virginia'=> __( 'West Virginia', 'mmd' ), | |
'Wisconsin'=> __( 'Wisconsin', 'mmd' ), | |
'Wyoming'=> __( 'Wyoming', 'mmd' ), | |
'--'=> __( '--', 'mmd' ), | |
'Nunavut'=> __( 'Nunavut', 'mmd' ), | |
'Quebec'=> __( 'Quebec', 'mmd' ), | |
'Northwest Territories'=> __( 'Northwest Territories', 'mmd' ), | |
'Ontario'=> __( 'Ontario', 'mmd' ), | |
'British Columbia'=> __( 'British Columbia', 'mmd' ), | |
'Alberta'=> __( 'Alberta', 'mmd' ), | |
'Saskatchewan'=> __( 'Saskatchewan', 'mmd' ), | |
'Manitoba'=> __( 'Manitoba', 'mmd' ), | |
'Yukon'=> __( 'Yukon', 'mmd' ), | |
'Newfoundland and Labrador'=> __( 'Newfoundland and Labrador', 'mmd' ), | |
'New Brunswick'=> __( 'New Brunswick', 'mmd' ), | |
'Nova Scotia'=> __( 'Nova Scotia', 'mmd' ), | |
'Prince Edward Island'=> __( 'Prince Edward Island', 'mmd' ), ); | |
return $StateArray; | |
} | |
function GetSupportedCountries() | |
{ | |
$CountryArray = array('United States' => __( 'United States', 'mmd' ), | |
'Canada' => __( 'MONTHLY DIRECTORY LISTING', 'mmd' )); | |
return $CountryArray; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment