FileMaker https://publish.mediacurrent.com/ en 9 Steps to Sync your FileMaker data with Drupal https://publish.mediacurrent.com/blog/9-steps-sync-your-filemaker-data-drupal <span class="field field--name-title field--type-string field--label-hidden">9 Steps to Sync your FileMaker data with Drupal</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>If you read <a href="http://www.mediacurrent.com/blog/featured-recipe-filemaker-and-drupal">my first blogpost</a> on using WSClient and Rules to push your Drupal data to FileMaker, then you're ready for the rest of the story: How to get your <em>FileMaker</em> data into <em>Drupal</em>. Well, sir or madam, there are two ways: write FileMaker scripts to <em>push</em> data to Drupal, or use Drupal to <em>pull</em> data from FileMaker. We suggest the latter, and here’s why.</p> <h3>FileMaker scripting is a major <a href="http://www.urbandictionary.com/define.php?term=PITA">PITA</a></h3> <p>Pushing field-level data to Drupal with the <a href="http://www.filemaker.com/11help/html/script_trigg.38.6.html#1028606">OnObjectSave trigger</a> sounds like <em>such</em> a great idea at first blush, but major blockers surface pretty quick. Guess what happens every time you modify any FileMaker field: OnObjectSave calls your custom script to push a REST request to Drupal services, and the FileMaker client interface locks up for the duration of the request! That's about a three second "freeze" every time your client edits a field. “Well, let’s simply log the change for later processing by a server-side FileMaker script.” Great idea. But there still remains the impossibility of maintaining object-level triggers on each your 50 fields × 10 layouts = 500 point-and-click-operations = “Wait, did I miss one? How the heck do I track that down.”</p> <p>For the FileMaker scripting gurus who hope to avoid the pain of PHP coding, there is still hope. You might try pushing record-level data to Drupal with the <a href="http://www.filemaker.com/11help/html/script_trigg.38.9.html">OnRecordCommit trigger</a>. Since this is a layout-level trigger, maintenance is feasible, and your Drupal configuration is all point-and-click: set up the <a href="http://drupal.org/project/services">Drupal Services module</a> to receive your REST requests. To send those REST requests in FileMaker scripting, you’ll need a plugin that provides HTTP request functions. The <a href="http://www.troi.com/software/urlplugin.html">Troi URL Plugin</a> costs $300 but provides everything you need out of the box. The <a href="http://www.360works.com/scriptmaster/">360works ScriptMaster plugin</a> is free but only provides HTTP GET and POST; you can write the necessary HTTP PUT and DELETE functions via the plugin interface in a language called Groovy (=Java).</p> <h3>The best approach: skip Drupal Services entirely.</h3> <p>I know, it’s a bold idea. And it was also rejected by our lead developer when I first proposed it. But here’s where we ended up: skip the FileMaker “push” entirely and pull all FileMaker changes via Drupal Web Services Client! This approach has the added benefit of reducing our dependency on FileMaker scripts that reside outside our project codebase. Here’s the basic idea:</p> <p><strong>FileMaker work</strong></p> <ul> <li>Add a “Deletions Log” table to keep track of any records that were deleted in FileMaker.</li> <li>Create a custom script “Log Deletion” that will add a row to the “Deletions Log” table when records are deleted.</li> <li>Use FileMaker Pro Advanced (yes, you need the “Advanced” version) to modify the default “Menu Actions” for your FileMaker file. Have the “Delete” menu action call a custom script to add the ID of the deleted row to your new “Deletions Log”.</li> <li>Add a “modification timestamp” field to all your FileMaker tables.</li> </ul> <p><strong>Drupal work</strong></p> <ul> <li>Modify your WSClient LIST operations to filter on that “modification timestamp” field.</li> <li>Add new LIST and DELETE operations to act on your new “Deletions Log” layout.</li> <li>Add a “Skip FileMaker CRUD” field to all your to-be-sync’d entities.</li> <li>Create a new function in your existing custom module to perform the sync operations.</li> <li>In this sync function, call each of your WSClient LIST operations, and pull all the FileMaker records where “modification_timestamp &gt; $timestamp_of_last_sync”.</li> <li>Process the records returned by those LIST operations into their corresponding Drupal entities.</li> </ul> <p><strong>System work</strong></p> <ul> <li>Use cron (whether Drupal or system) to hit your custom module’s “sync” function on a regular basis.</li> </ul> <p>Admittedly, there are more than a few gotchas for each of the steps in this recommended approach. But fortunately, yours truly has tripped over all of them and found solutions to each. Here’s the walk-through.</p> <h3>Step 1. Add a Deletions Log to your FileMaker file</h3> <p>This log table needs two fields: the Drupal Entity ID of the record which was deleted, and the Drupal Entity Type to which it belongs.</p> <p style="width:100%; text-align:center"><img alt="" src="https://publish.mediacurrent.com/sites/default/files/step_1.png" style="max-width: 590px; border: 1px grey solid;" /></p> <h3>Step 2. Write the Log Deletion custom FileMaker script.</h3> <p>This custom FileMaker script will create a new row in the Deletions Log table and set it with the Drupal Entity Type and Drupal Entity ID of the record being deleted.</p> <p>To get the Drupal Entity Type, you might use the TableName of the deleted record to lookup the corresponding type. Or you might use a global variable of the current table to store the Drupal Entity Type. The Drupal Entity ID is available as a field of the record being deleted. Next, add script steps to switch to your “Deletions Log” layout and create a “New Record/Request”. After setting the Drupal Entity Type and Drupal Entity ID into the new record, you’re all done!</p> <p>Note that if the Drupal Entity ID of the deleted record is not set, we may assume that our Drupal Sync Script has not yet pulled the new record into Drupal, and the deletion need not be logged.</p> <p style="width:100%; text-align:center"><img alt="" src="https://publish.mediacurrent.com/sites/default/files/step_2.png" style="max-width: 590px; border: 1px grey solid;" /></p> <h3>Step 3. Modify your FileMaker file’s Menu Actions.</h3> <p>Tools → Custom Menus → “Custom Menus” Tab → Create → and choose “Records”. From there you can edit the “Delete Record” Menu Item and add an action to perform the “Log Deletion” script outlined above. Don’t forget to create a “Custom Menu Set” and mark it as the default for your FileMaker file.</p> <p style="width:100%; text-align:center"><img alt="" src="https://publish.mediacurrent.com/sites/default/files/step_3a_0.png" style="max-width: 590px; border: 1px grey solid;" /></p> <p style="width:100%; text-align:center"><img alt="" src="https://publish.mediacurrent.com/sites/default/files/step_3b_0.png" style="max-width: 590px; border: 1px grey solid;" /></p> <p style="width:100%; text-align:center"><img alt="" src="https://publish.mediacurrent.com/sites/default/files/step_3c_0.png" style="max-width: 590px; border: 1px grey solid;" /></p> <h3>Step 4. Add a “modification timestamp” field to all your FileMaker tables.</h3> <p>Add this “calculation” field to each of the FileMaker tables you want to sync:</p> <pre> Let ( ~trigger = GetField ( "" ) ; Round ( Get ( UTCmSecs ) / 1000 ; 0 ) - Timestamp ("01/01/1970" ; "00:00:00") ) </pre><p> </p> <p>The first line triggers our field to update every time the record is modified. The second line gets the current UTC (~GMT) time in seconds since 00:00 on 1/1/0000. The third line converts that to epoch/unix time (seconds since 00:00 on 1/1/1970).</p> <p style="width:100%; text-align:center"><img alt="" src="https://publish.mediacurrent.com/sites/default/files/step_4.png" style="max-width: 590px; border: 1px grey solid;" /></p> <h3>Step 5. Add a filter to your WSClient LIST operations</h3> <p>If you haven’t yet created your WSClient LIST operations, see <a href="http://www.mediacurrent.com/blog/featured-recipe-filemaker-and-drupal">part one of this blogpost</a>. Also, make sure you’ve reviewed <a href="http://www.restfm.com/manual_v1/uri-restfmdatabaselayoutlayout">the RESTfm manual</a>which describes how to add filters to your LIST requests. Filters can be added to any LIST request with a combination of two URL parameters: one to specify the field, and one to specify the value. For example, if your field were named “modification_timestamp”, your WSClient LIST operation URL might look like:</p> <pre> http://your-server/RESTfm/your-database/layout/your-layout? RFMsF1=modification_timestamp&amp;RFMsV1=&gt;@timestamp_of_last_sync </pre><p> </p> <p>Note the greater-than sign before @timestamp_of_last_sync. This tells RESTfm that we want any record whose field value is greater than the value we pass to it. Also note that I suggest using the <a href="http://www.restfm.com/manual_v1/uri-restfmdatabaselayoutlayout">RFMmax URL parameter</a> to explicitly define how many records your server can realistically handle (probably a few hundred). Without specifying a value, RESTfm defaults to only 25, which may cause you to miss some records.</p> <h3>Step 6. Write LIST and DELETE operations for your “Deletions Log”</h3> <p>Follow the steps outlined in <a href="http://www.mediacurrent.com/blog/featured-recipe-filemaker-and-drupal">part one</a> to write additional LIST and DELETE operations for WSClient. These will act on your “Deletions Log” layout.</p> <h3>Step 7. Add a “Skip FileMaker CRUD” field to all your entities</h3> <p>Choose "boolean" for the field type.</p> <p style="width:100%; text-align:center"><img alt="" src="https://publish.mediacurrent.com/sites/default/files/step_7.png" style="max-width: 590px; border: 1px grey solid;" /></p> <h3>Step 8. Update your Rules to respect the "Skip FileMaker CRUD" field</h3> <p>To each UPDATE Rule created in <a href="http://www.mediacurrent.com/blog/featured-recipe-filemaker-and-drupal">part one</a>, add two "Data Comparison" conditions. The first condition acts on "your-entity:field_skip_filemaker_crud" and the other acts on "your-entity-unchanged:field_skip_filemaker_crud" (the entity data before it was modified). Also add the first condition to each of your CREATE and DELETE rules. Be sure to check the "Negate" option, because we only want our rules to execute when this field is FALSE or missing.</p> <p style="width:100%; text-align:center"><img alt="" src="https://publish.mediacurrent.com/sites/default/files/step_8a.png" style="max-width: 590px; border: 1px grey solid;" /></p> <p style="width:100%; text-align:center"><img alt="" src="https://publish.mediacurrent.com/sites/default/files/step_8b.png" style="max-width: 590px; border: 1px grey solid;" /></p> <h3>Step 9. Pull and process FileMaker data!</h3> <p>You might keep this sync code in a custom script that <a href="http://www.interworks.com/blogs/jkhalaj/2012/05/01/how-bootstrap-drupal">manually bootstraps a Drupal environment</a>, or you might store it in an include file of the custom module you already created (in <a href="http://www.mediacurrent.com/blog/featured-recipe-filemaker-and-drupal">part one of this tutorial</a>). Note, the code that follows is just a starting point and, with the exception of Deletion Log processing, syncs only one entity type.</p> <p><strong>Setup.</strong></p> <pre> // Set the new timestamp before sync to err on the side of overlap. $lastsync = variable_get('_your_module_last_sync_timestamp', 0); variable_set('_your_module_last_sync_timestamp', time()); // Load the WSClient service. In part one, we named it “restfmclient”. $service = wsclient_service_load('restfmclient'); // At minimum, we need to know the entity type and bundle. $basetype = ‘your_entity_type’; $bundle = ‘your_bundle’; // Now use entity_get_info to set the other parameters we’ll need later on. $entityinfo = entity_get_info($basetype); $entityid_key = $entityinfo['entity keys']['id']; $typefield = $entityinfo['entity keys']['bundle']; </pre><p> </p> <p><strong>Pull FileMaker records that were modified since last sync.</strong> If you’ve followed along faithfully to this point, you should be able to call your WSClient operation like so.</p> <pre> // Get all records modified in FileMaker since this script last ran. // In part one of the post, we used “list” as the operation name. $response = $service-&gt;your-wsclient-list-operation($lastsync); </pre><p> </p> <p><strong>But! RESTfm likes to return a 500 error when no records are found. So let’s catch it.</strong></p> <pre> try { // Get all records modified in FileMaker since this script last ran. $response = $service-&gt;your-wsclient-list-operation($lastsync); } catch (WSClientException $e) { $last_response = $service-&gt;endpoint()-&gt;client()-&gt;lastResponse; $body = $last_response-&gt;body; if (500 == $last_response-&gt;responseCode &amp;&amp; FALSE !== strpos($body, '401: No records match the request')) { // OK to ignore error - RESTfm returns 500 error on no records found. } else { // Continue error propogation. throw $e; } } </pre><p> </p> <p><strong>Loop through the returned records</strong> Get the corresponding Drupal entity. Set the new values. Save.</p> <pre> foreach($response['data'] as $fmrecord) { // Check whether FileMaker already had an entity id for this record. if (empty($fmrecord[$entityid_key])) { // Since FileMaker did not send the entity id, this entity does not // yet exist in Drupal and must be created (specifying the bundle property). $entity = new Entity(array($typefield =&gt; $bundle), $basetype); } else { // FileMaker had entity id, so the entity already exists in Drupal. $entities = entity_load($basetype, array($fmrecord[$entityid_key])); $entity = array_pop($entities); } // We must get our field list from someplace other than the data sent to us by // RESTfm, because RESTfm leaves empty fields out of the response. $fields = array_keys(field_info_instances($basetype, $bundle)); // Update entity data with values from FileMaker. foreach ($fields as $field) { // Check whether RESTfm sent us data for this field. if(empty($fmrecord[$field])) { // FileMaker sent no data. If the field previously had data, it was // deleted in FileMaker, so delete it from Drupal, too. unset($entity-&gt;$field); // Move on to next field. continue; } // Get the value sent by RESTfm for this record. $value = $fmrecord[$field]; // Non-property fields use a multi-lingual and multi-value array structure. if(substr($field, 0, 6) == 'field_') { // Set the field's value. $entity-&gt;{$field}[LANGUAGE_NONE][0]['value'] = $value; } else { // Assume no value array structure to deal with. $entity-&gt;$field = $value; } } // We use a flag to keep Rules from executing WSClient calls to RESTfm on // save, which would cause an infinite loop of sync updates. $entity-&gt;field_skip_filemaker_crud[LANGUAGE_NONE][0]['value'] = 1; $saveresult = entity_save($basetype, $entity); // Unset the flag and re-save the entity. For this to work, our Rules // conditions must check for the flag in both the "unchanged" (i.e., // pre-update) entity data and the new entity data. unset($entity-&gt;field_skip_filemaker_crud); entity_save($basetype, $entity); // If the entity was just created in Drupal, we want to send the entity id // back to FileMaker, so we'll call entity_save to trigger an update operation. if (SAVED_NEW == $saveresult) { entity_save($basetype, $entity); } } </pre><p> </p> <p><strong>Phew.</strong> If you made it through that, you’re doin' swell! Now, let’s process our Deletions Log. Start by calling the WSClient LIST operation we created for our Deletions Log. Remember to catch the 500 error returned by RESTfm on no-records-found! <strong>LIST records in the FileMaker Deletions Log</strong></p> <pre> try { $response = $service-&gt;your-wsclient-deletion-log-list-operation(); } catch (WSClientException $e) { // @see Above. } </pre><p> </p> <p><strong>Loop and Delete!</strong></p> <pre> foreach ($response['data'] as $index =&gt; $deletionlog_entry) { // Get the entity type. $entity_type = $deletionlog_entry['entity type']; // Get the entity id. $entity_id = $deletionlog_entry['entity id']; // Delete! $deleteresult = entity_delete($entity_type, $entity_id); if (FALSE !== $deleteresult) { // Success, so clear the entry from the FileMaker Deletions Log. $recordID = $response['meta'][$index]['recordID']; $service-&gt;your_wsclient_deletion_log_delete_operation($recordID); } } </pre><p style="border-top: 1px grey solid; width: 80%; margin: auto; margin-top: 30px;"> </p> <p>To learn about syncing FileMaker join tables with multi-value entity reference fields, <a href="http://drupal.org/user/1571646/contact">contact me on d.o</a>.</p> <p style="font-style: italic;">This post was made possible with support and guidance of lead project developer James Rutherford, trusty project manager Donna Merit, and tireless FileMaker guru Dale Long. I also thank the Mediacurrent partners and marketing team for making it a priority to promote community contributions like this one.</p> <p> </p> <p><strong>Additional Resources</strong></p> <p><a href="http://www.mediacurrent.com/blog/inline-wysiwyg-editing-drupal">Inline WYSIWYG Editing in Drupal</a></p> <p><a href="http://www.mediacurrent.com/blog/responsive-design-mobile-menu-options">Responsive Design: Mobile Menu Options</a></p> <p><a href="http://www.mediacurrent.com/blog/webinar-display-suite-themers-perspective">Display Suite - A Themers Perspective</a></p> <div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/about/our-team/mediacurrent-team" lang="" about="/about/our-team/mediacurrent-team" typeof="schema:Person" property="schema:name" datatype="" class="username">Mediacurrent Team</a></span> <span class="field field--name-created field--type-created field--label-hidden">Mon, 03/18/2013 - 09:27</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/tags/development" hreflang="en">Development</a></div> <div class="field__item"><a href="/tags/drupal" hreflang="en">Drupal</a></div> <div class="field__item"><a href="/tags/filemaker" hreflang="en">FileMaker</a></div> <div class="field__item"><a href="/tags/rest" hreflang="en">REST</a></div> <div class="field__item"><a href="/tags/rest-server" hreflang="en">REST Server</a></div> <div class="field__item"><a href="/tags/restfm" hreflang="en">RESTfm</a></div> <div class="field__item"><a href="/tags/services" hreflang="en">Services</a></div> <div class="field__item"><a href="/tags/troi" hreflang="en">Troi</a></div> <div class="field__item"><a href="/tags/troi-url-plugin" hreflang="en">Troi URL Plugin</a></div> <div class="field__item"><a href="/tags/web-services" hreflang="en">Web Services</a></div> <div class="field__item"><a href="/tags/wsclient" hreflang="en">WSClient</a></div> </div> </div> <div class="gatsby-iframe-container"><iframe class="gatsby-iframe" src="https://preview-misriptide.gtsb.io/blog/9-steps-sync-your-filemaker-data-drupal"></iframe></div> Mon, 18 Mar 2013 13:27:13 +0000 Mediacurrent Team 810 at https://publish.mediacurrent.com Featured Recipe: FileMaker and Drupal https://publish.mediacurrent.com/blog/featured-recipe-filemaker-and-drupal <span class="field field--name-title field--type-string field--label-hidden">Featured Recipe: FileMaker and Drupal</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Do you love <a href="http://www.filemaker.com">FileMaker</a>? Do you work with (or for!) someone who does? I’m now a Drupal Developer at Mediacurrent, but my last position (at <a href="http://www.gatech.edu">Georgia Tech</a>’s <a href="http://oie.gatech.edu">Office of International Education</a>) involved a lot of FileMaker work, and this FileMaker integration would have been a lifesaver, so here goes! First, a bit of background on FileMaker for the unindoctrinated: “Tackle any business task... Powerful, easy-to-use database software for yourself or a team. Create forms, reports and labels. Share on a network or over the web.” <em>Lots</em> of non-technical folks love to use FileMaker; it's a cheap and intuitive alternative to more expensive visual database solutions, like Microsoft Access. It's the #1 best-selling standalone database for both Macintosh and Windows and has won dozens of awards in the industry. (Source: <a href="http://www.scottworld.com/filemaker/faq.html#290b">ScottWorld.com</a>). At Mediacurrent, we recently integrated a client's existing FileMaker database with a freshly-launched Drupal 7 website. Here’s an overview of the workable solutions we explored:</p> <ul> <li><strong>Drupal → Filemaker</strong><br /> push data to RESTfm (a REST server for FileMaker) via the WSClient and Rules modules.</li> <li><strong>Filemaker → Drupal</strong><br /> send REST requests from FileMaker to Drupal Services via the Troi URL plugin.</li> <li><strong>Drupal ← Filemaker</strong><br /> pull data into Drupal via a LIST response from RESTfm.</li> </ul> <h3>Ingredients</h3> <p> </p> <ul> <li>FileMaker Server ($3k)</li> <li>FileMaker Pro Advanced (yes, you need the “Advanced” version) ($500)</li> <li>RESTfm ($400)</li> <li>Troi URL plugin (optional, for pushing directly Filemaker to Drupal) (web server license - $300).</li> <li>Drupal ($Priceless.00)</li> </ul> <p> </p> <h3>Baking Instructions, Drupal → Filemaker</h3> <p> </p> <ul> <li>Enable PHP Web Publishing (<a href="http://www.filemaker.com/support/technologies/php.html">details</a>) on your FileMaker server. (Documentation <a href="http://www.filemaker.com/downloads/pdf/fms10_cwp_php_en.pdf">v10</a> and <a href="http://www.filemaker.com/support/product/docs/12/fms/fms12_cwp_php_en.pdf">v12</a>)</li> <li>On your FileMaker database (aka your FileMaker “file”), go to the Privileges settings and enable “[Full Access]” for PHP Web Publishing. (<a href="http://www.filemaker.com/11help/html/passwords.13.24.html">details</a>, and <a href="http://www.filemaker.com/support/product/docs/12/fms/fms12_help.pdf">documentation</a>)</li> <li>Install and configure RESTfm on the Web Server of your choice. Check out <a href="http://www.restfm.com/manual">the RESTfm manual</a>for instructions. I chose to follow the manual's suggestion and keep-it-simple by installing RESTfm on IIS (Windows web server) on the same server hosting FileMaker Server.</li> <li>Test your RESTfm configuration by browsing the RESTfm HTML interface: "<a href="http://your-server/RESTfm">http://your-server/RESTfm</a>".</li> <li>Test creation, deletion, and modification of records via the RESTfm demo page, "<a href="http://your-server/RESTfm/demo.html">http://your-server/RESTfm/demo.html</a>". <strong><em>Note that RESTfm returns a 500 error if any fields on your layout are set to both (1) Auto-Enter: Prohibit modification of value during data entry, and (2) Validation: Not empty.</em></strong> The assumption here is that FileMaker processes the validators before calculating the Auto-Enter fields. You will also see this error if you leave empty any field set to Validation: Not empty.</li> <li>Test CRUDL (CREATE, RETRIEVE, UPDATE, DELETE, LIST) access to RESTfm from your browser with the Postman extension for Chrome or Poster add-on for FireFox. If none of that made sense, you may want to do some reading up on <a href="http://en.wikipedia.org/wiki/Representational_state_transfer">REST servers</a>.</li> </ul> <p>If you’ve made it this far, congratulations! Those sweat and tears were worthwhile. Let’s look at Drupal configuration now. Start by installing the <a href="http://drupal.org/project/wsclient">WSClient</a> and <a href="http://drupal.org/project/rules">Rules</a> modules. (<a href="http://drupal.org/documentation/install/modules-themes">How to install modules</a>). WSClient doesn’t support configuration of a full REST client via its user interface. In order to configure a WSClient service to consume RESTfm, you’ll have to write it in code with a custom module (for the unfamiliar, review <a href="http://drupal.org/node/1074360">this module creation tutorial (general)</a> and <a href="http://drupal.org/developing/modules">this module developer's guide (technical)</a>). All the code that follows was adapted from the "WSClient Examples" module (included with <a href="http://drupal.org/project/wsclient">the WSClient module</a>), and I suggest reviewing it. Note that the useful example will not appear unless you have the <a href="http://drupal.org/project/restws">RESTful Web Services</a> module enabled. Your code might look like this inside <strong>hook_default_wsclient_service</strong>in your custom module:</p> <pre> <code class="language-php">service = new WSClientServiceDescription(); $service-&gt;name = 'restfmclient'; $service-&gt;label = 'RESTfm Client'; // Replace "server" with the hostname of your RESTfm web server. $service-&gt;url = url('http://server/RESTfm', array('absolute' =&gt; TRUE)); $service-&gt;type = 'rest'; // Add HTTP Basic Authorization headers. // Or you can look into using RESTfm's support for authentication via API key. // @see http://www.restfm.com/manual_v1/authentication $curl_options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; // Replace "filemakeruser" and "password" with your FileMaker file login. $curl_options[CURLOPT_USERPWD] = ‘filemakeruser:password'; $service-&gt;settings['curl options'] = $curl_options;</code></pre><p><strong>But we’re not done yet! RESTfm is persnickety. It wants you to send data to it in just the right structure</strong>: an array called 'data', pointing to another array called '0' (zero), pointing to a third array of fields and values. In PHP code, it looks like "array('data' =&gt; array(0 =&gt; array('field_name' =&gt; $value)))". In order to formulate our data into this structure, we must create three separate data structures for WSClient to pass on to Rules. (<a href="http://drupal.org/node/298639">some info on Rules data types</a>)</p> <pre> <code class="language-php">// Data structure to send on UPDATE or CREATE: // array('data' =&gt; array(0 =&gt; array('field_name' =&gt; $value))) $service-&gt;datatypes = array( 'request_data_wrapper_outer' =&gt; array( 'label' =&gt; 'request data - outer wrapper', 'property info' =&gt; array( 'data' =&gt; array( 'type' =&gt; 'request_data_wrapper_inner', 'label' =&gt; 'request data - inner wrapper', ), ), ), 'request_data_wrapper_inner' =&gt; array( 'label' =&gt; 'request data - inner wrapper', 'property info' =&gt; array( '0' =&gt; array( 'type' =&gt; 'request_data', 'label' =&gt; 'request data', ), ), ), 'request_data' =&gt; array( 'label' =&gt; 'request data', // HERE: LIST THE FIELDS YOU WANT PUSHED TO FILEMAKER 'property info' =&gt; array( 'title' =&gt; 'text', 'nid' =&gt; 'integer', // 'field_your_field' =&gt; 'text', // ... ), ), );</code></pre><p><strong>Phew. Glad that’s out of the way. Now, let’s setup the data structures to <em>receive</em> data back from a RESTfm CREATE operation.</strong>The data we receive looks like "array('meta' =&gt; array(0 =&gt; array('recordID' =&gt; $fm_id, 'href' =&gt; $uri)))".</p> <pre> <code class="language-php">// Create the data structures needed to receive responses from RESTfm CREATE. // array('meta' =&gt; array(0 =&gt; array('recordID' =&gt; $fm_id, 'href' =&gt; $uri))) $service-&gt;datatypes = array_merge($service-&gt;datatypes, array( 'create_result_wrapper_outer' =&gt; array( 'label' =&gt; 'response from CREATE operation - outer wrapper', 'property info' =&gt; array( 'meta' =&gt; array( 'type' =&gt; 'create_result_wrapper_inner', 'label' =&gt; 'response from CREATE operation - inner wrapper', ), ), ), 'create_result_wrapper_inner' =&gt; array( 'label' =&gt; 'response from CREATE operation - inner wrapper', 'property info' =&gt; array( '0' =&gt; array( 'type' =&gt; 'create_result_data', 'label' =&gt; 'response from CREATE operation - data', ), ), ), 'create_result_data' =&gt; array( 'label' =&gt; 'response from CREATE operation - data', 'property info' =&gt; array( 'recordID' =&gt; array( 'type' =&gt; 'integer', 'label' =&gt; 'FileMaker Record ID', ), 'href' =&gt; array( 'type' =&gt; 'text', 'label' =&gt; 'Resource request URI', ), ), ), );</code></pre><p><strong>Now we’re ready to create our CRUDL operations! It is important to remember that RESTfm performs these operations against FileMaker <em>layouts</em> and not against the actual underlying tables.</strong>Note that RESTfm needs a FileMaker Record ID for RETRIEVE, UPDATE, and DELETE operations. This Record ID is an internal FileMaker identifier and can be found by creating a calculated field in your FileMaker table set to "Get ( RecordID )". Also note that there is no way to set or modify a record's Record ID (FileMaker assigns it automatically).</p> <pre> <code class="language-php">// Replace "your-FileMaker-dabase" and "your-FileMaker-layout" here: $url = 'your-FileMaker-database/layout/your-FileMaker-layout’; $targeted_url = $url . ‘/@recordID’; // CREATE operation. $operation = array(); $operation['label'] = 'CREATE'; $operation['url'] = $url; $operation['type'] = 'POST'; $operation['data'] = 'restfm_data'; $operation['parameter']['restfm_data'] = array( 'type' =&gt; 'request_data_wrapper_outer', 'label' =&gt; 'request data - outer wrapper'); $operation['result'] = array('type' =&gt; 'create_result_wrapper_outer', 'label' =&gt; 'response'); $service-&gt;operations['create'] = $operation; // RETRIEVE operation. $operation = array(); $operation['label'] = 'RETRIEVE'; $operation['url'] = $targeted_url; $operation['parameter'][‘recordID’] = array( 'type' =&gt; 'integer', 'label' =&gt; 'FileMaker Record ID'); $operation['result'] = array('type' =&gt; 'text', 'label' =&gt; 'response'); $service-&gt;operations['retrieve'] = $operation; // UPDATE operation. $operation = array(); $operation['label'] = 'UPDATE'; $operation['url'] = $targeted_url; $operation['type'] = 'PUT'; $operation['data'] = 'restfm_data'; $operation['parameter']['restfm_data'] = array( 'type' =&gt; 'request_data_wrapper_outer', 'label' =&gt; 'request data - outer wrapper'); $operation['parameter'][‘recordID’] = array( 'type' =&gt; 'integer', 'label' =&gt; 'FileMaker Record ID'); $operation['result'] = array('type' =&gt; 'text', 'label' =&gt; 'response'); $service-&gt;operations['update'] = $operation; // DELETE operation. $operation = array(); $operation['label'] = 'DELETE'; $operation['url'] = $targeted_url; $operation['type'] = 'DELETE'; $operation['parameter'][‘recordID’] = array( 'type' =&gt; 'integer', 'label' =&gt; 'FileMaker Record ID'); $operation['result'] = array('type' =&gt; 'text', 'label' =&gt; 'response'); $service-&gt;operations['delete'] = $operation; // LIST operation. // You may add parameters to the URL to filter the result set. // @see http://www.restfm.com/manual_v1/uri-restfmdatabaselayoutlayout $operation = array(); $operation['label'] = 'LIST'; $operation['url'] = $url; $operation['result'] = array('type' =&gt; 'text', 'label' =&gt; 'response'); $service-&gt;operations['list'] = $operation;</code></pre><p><strong>And don’t forget to return the service when you’re done.</strong></p> <pre> <code class="language-php">// All done. $services[$service-&gt;name] = $service; return $services;</code></pre><p>Okay, folks, the hard part is out of the way; pat yourselves on the back! All that’s left is Rules configuration. Head to the <strong>Rules Configuration</strong> page and set up</p> <ul> <li>a “RESTfm Client: CREATE” action on event “After saving new content” - Include three "Create data structure" actions to set up the RESTfm request data: - (1) request data, (2) inner wrapper, (3) outer wrapper - Include a "Set a data value" action to save the returned FM recordID into your entity. - The "Set a data value" value would look like "response:meta:0:recordID".</li> <li>a “RESTfm Client: UPDATE” action on event “After updating existing content” - Again, include three "Create data structure" actions to set up the RESTfm request data.</li> <li>a “RESTfm Client: DELETE” action on event “After deleting content”</li> </ul> <p>That takes care pushing Drupal data to FileMaker. <strong>Stay tuned for part deux of this blog post for a walk-through of FileMaker → Drupal solutions.</strong> <em>Would you like to see this topic presented at DrupalCon Portland? +1 <a href="http://portland2013.drupal.org/session/integrating-filemaker-drupal-case-study">the session proposal</a> and leave a comment for the selection committee! <a href="http://portland2013.drupal.org/session/integrating-filemaker-drupal-case-study">Link to DrupalCon Session Proposal</a>.</em></p> <p> </p> <p><strong>Additional Resources</strong></p> <p><a href="http://www.mediacurrent.com/blog/inline-wysiwyg-editing-drupal">Inline WYSIWYG Editing in Drupal</a></p> <p><a href="http://www.mediacurrent.com/blog/responsive-design-mobile-menu-options">Responsive Design: Mobile Menu Options</a></p> <p><a href="http://www.mediacurrent.com/blog/webinar-display-suite-themers-perspective">Display Suite - A Themers Perspective</a></p> <div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/about/our-team/mediacurrent-team" lang="" about="/about/our-team/mediacurrent-team" typeof="schema:Person" property="schema:name" datatype="" class="username">Mediacurrent Team</a></span> <span class="field field--name-created field--type-created field--label-hidden">Thu, 02/21/2013 - 11:52</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/tags/development" hreflang="en">Development</a></div> <div class="field__item"><a href="/tags/drupal" hreflang="en">Drupal</a></div> <div class="field__item"><a href="/tags/filemaker" hreflang="en">FileMaker</a></div> <div class="field__item"><a href="/tags/rest" hreflang="en">REST</a></div> <div class="field__item"><a href="/tags/rest-server" hreflang="en">REST Server</a></div> <div class="field__item"><a href="/tags/restfm" hreflang="en">RESTfm</a></div> <div class="field__item"><a href="/tags/services" hreflang="en">Services</a></div> <div class="field__item"><a href="/tags/troi" hreflang="en">Troi</a></div> <div class="field__item"><a href="/tags/troi-url-plugin" hreflang="en">Troi URL Plugin</a></div> <div class="field__item"><a href="/tags/web-services" hreflang="en">Web Services</a></div> <div class="field__item"><a href="/tags/wsclient" hreflang="en">WSClient</a></div> </div> </div> <div class="gatsby-iframe-container"><iframe class="gatsby-iframe" src="https://preview-misriptide.gtsb.io/blog/featured-recipe-filemaker-and-drupal"></iframe></div> Thu, 21 Feb 2013 16:52:30 +0000 Mediacurrent Team 790 at https://publish.mediacurrent.com