L5SimpleFM v 1.0 – A Composer and Laravel 5 friendly PHP package for interacting with hosted FileMaker data

 TLDR Overview

L5SimpleFM is a PHP package used to provide a simple, readable, declarative syntax for accessing data hosted on a FileMaker Server from a composer-friendly PHP project. The package also has a ServiceProvider class which allows it to be easily pulled into a Laravel 5 project. The L5SimpleFM package is now available as a 1.0 release on github. It can also be installed from packagist via composer.

A brief example:

Selecting the first 10 records from a layout Inventory with the status “active” would look like:

 use L5SimpleFMFileMakerModelsBaseModel;  // The model class class Inventory extends BaseModel {     protected $layoutName = 'Inventory'; }  // The controller use AppFileMakerModelsInventory;  class InventoryController extends Controller  {      protected $inventory;      public function __construct(Inventory $inventory)     {         $this->inventory = $inventory;     }      public function getActiveInventoryItems(Request $request)     {         $returnCount = $request->input('count');         $result = $this->inventory->findByFields(['status' => 'active'])->max($returnCount)->executeCommand();         $records = $result->getRows();         return compact('records');     }  }  

The tool is a wrapper around the Soliant Consulting tool SimpleFM. SimpleFM handles the actual sending and receiving of data to and from FileMaker server, L5SimpleFM creates a human readable syntax and allows for the creation of Model classes for an MVC project pattern. A simple example Task project I created using L5SimpleFM can be found here on github.


 

The need

Recently I started working on a web project for Skeleton Key that used FileMaker as a back end database. I had worked with the FileMaker PHP API in the past and while it was useful, it had a couple of drawbacks that made me want to explore other tools. Namely:

  • It contains code that has been deprecated by PHP which causes errors when running from a modern version of PHP.
  • It is not PSR friendly which makes it harder to work with a modern PHP framework (e.g. Laravel which is my favorite framework).

Ultimately, I tried to include the FileMaker PHP API into a Laravel project via composer classmap and ran into so many roadblocks for the simplest database call that I scrapped the idea of trying to fit it in. From there I explored a couple of other tools as far as accessing FileMaker from a PHP framework. I explored Goya’s RestFM and Soliant Consulting’s SimpleFM. Both are amazing tools and I can see using both of them in various kinds of applications. In the end, I chose SimpleFM for a number of reasons, but the most important reason was that I wanted to be able to point my project to any FileMaker Server, not just the one I configured RestFM for.

Wrapping SimpleFM

I pulled SimpleFM into my project and started using it directly. It’s fairly straight forward to use if you’re familiar and comfortable with FileMaker’s XML web publishing syntax, but the look and feel of the code I was writing wasn’t as readable as I would like:

         // A contrived example:     public function index(Request $request)     {         $returnCount = $request->input('count');          // This would normally be included in some global file accessible by any php page that will use the adapter         // I'm showing this here with the credentials inline to show the full picture.         $hostConnection = new HostConnection('127.0.0.1', 'L5SimpleFMExample', 'web_user', 'webdemo!');         $this->adapter = new Adapter($hostConnection);          // within an InventoryController         $this->adapter->setLayoutName('Inventory');         $commandArray = [             'status' => 'active',             '-max' => $returnCount,             '-find' => null,         ];          $result = $this->adapter->setCommandArray($commandArray)->execute();         $records = $result->getRows();          return compact("records");     } 

The action being performed is defined by the data rather than a class. There isn’t an issue with this, it’s a very transparent way of interfacing with FileMaker XML web publishing, but keeping it this way makes the code less portable. Rather than coding common commands into data each time I want to interact with FileMaker in my application I would prefer to structure those common commands into a class and inject that class into my code as needed. This is how L5SimpleFM started. The construction and instantiation of the host connection and adapter were moved out to a ServiceProvider for easier Laravel integration. Note: This is the standard ServiceProvider for L5SimpleFM. You do not have to code this, you only need to add the service provider reference to your laravel `config/app.php` file.

 use IlluminateSupportServiceProvider; use L5SimpleFML5SimpleFM; use SoliantSimpleFMAdapter; use SoliantSimpleFMHostConnection;  class L5SimpleFMServiceProvider extends ServiceProvider {     public function register()     {         $this->constructAndBindL5SimpleFM();     }      protected function constructAndBindL5SimpleFM()     {         $this->app->bind('L5SimpleFMContractsFileMakerInterface', function ($app) {             // credentials are pulled from the laravel `.env` file.             $username = env('FM_USERNAME');             $password = env('FM_PASSWORD');             $host = env('FM_HOST);             $database = env('FM_DATABASE');              $hostConnection = new HostConnection($host, $database, $username, $password);             return new L5SimpleFM(new Adapter($hostConnection));         });     } }  

Common commands could be fired from a L5SimpleFM class method:

 use L5SimpleFMContractsFileMakerInterface; use IlluminateHttpRequest;  class InventoryController extends Controller {      protected $fm;      public function __construct(FileMakerInterface $fm)     {         $this->fm = $fm;     }      public function index(Request $request)     {         $max = $request->input('max');         $skip = $request->input('skip');         $result = $this->fm->setLayout('Inventory')->findAll()->max($max)->skip($skip)->executeCommand();         $records = $result->getRows();         return compact('records');     } }  

FileMaker Models

This worked but seemed a bit disorganized within an MVC framework. Any time I built up and fired a command I was dealing with one context/entity within my database at a time. The next logical step in organizing the tool was to use it to create FileMaker Model classes. I created a BaseModel abstract class that acts as a map between the L5SimpleFM service and whatever new model you extend from the BaseModel. Now a FileMaker model class can be created that clearly defines an entity within the Laravel project:

 use L5SimpleFMFileMakerModelsBaseModel;  class Inventory extends BaseModel {     protected $layoutName = 'Inventory'; }  

The layout name specified sets the context for all commands fired from the model. Now I can inject the new model into my controller and clean up my command just a bit:

 use AppFileMakerModelsInventory; use IlluminateHttpRequest;  class InventoryController extends Controller {     protected $inventory;      public function __construct(Inventory $inventory)     {         $this->inventory = $inventory;     }      public function index(Request $request)     {         $max = $request->input('max');         $skip = $request->input('skip');         $result = $this->inventory->findAll()->max($max)->skip($skip)->executeCommand();         $records = $result->getRows();         return compact('records');     } } 

The L5SimpleFM service class can still be used directly via the FileMakerInterface which is ideal for building small services that interact with FileMaker but don’t necessarily need a model class, but for cases where creating a dedicated model makes better sense the BaseModel abstract can be used.

Using the package

The L5SimpleFM package is now available as a 1.0 release on github. It can also be installed from packagist via composer. The tool is open source so feel free to explore. If you have any feedback you can post questions to github, shoot me an email at chris.schmitz@skeletonkey.com, or post to me on twitter @cschmitz81. Christopher Schmitz is a Certified Developer at Skeleton Key. About Skeleton Key Skeleton Key helps turn complex, complicated, and outdated systems into true information platforms. Our team of consultants and developers do this by developing custom-fit software tools and reporting dashboards that help businesses find, use, and understand their data, freeing them to focus on and grow their core business. In addition to custom databases and applications, we also provide training and coaching for getting the most out of your existing systems and understanding your unruly data. Skeleton Key is an open-book management company and active player of the Great Game of Business.

Help iOS users save their data

I ran into a situation recently where a user would not or could not remember to commit their changes before exiting their FileMaker Go solution on their iPad.

The truth is it’s easy to complain that the user isn’t using the system as it was intended. Complaining about the user doesn’t really solve the problem, and we are problem solvers. This is custom software; it should work the way the user works, not the other way around.

clock In this case, the user was using an iPhone or iPad Mini. He pulls it from his pocket, opens a work order, adds some photos and notes, and then sticks the device back in his pocket. Then he does some work (like replacing a window in a building) and then takes some more photos of the completed repair. The way FileMaker Go reconnects to the server (and now in version 14 FileMaker Pro works this way also) makes this work seamlessly for the user. The problem in this case is that the user is sleeping his device without committing changes so when another user modifies that record while FileMaker Go is “sleeping”, the iPad user’s changes are lost.

When FileMaker Go is in a “sleeping/disconnected” state, uncommitted changes don’t get explicitly committed…and other users can modify the record.

Here at Skeleton Key we keep a “Developer’s Chat room” open on Slack throughout the day. The team kicked this issue around for a bit trying to come up with a good solution. We came up with several ideas:

  • Show some sort of alert to the user when there were uncommitted changes
  • Use a wizard style interface for making changes
  • Use a web page approach (edit & submit) buttons
  • Capture changes via global fields then write them with a transaction style script

All of these items came with their own set of issues and drawbacks. And we felt like they were really just treating the symptom rather than solving the problem. What was the problem? The problem was that the user wasn’t “saving” their changes. Or to say that differently, the data wasn’t being saved (again, not really the user’s fault). So we decided to take a different approach and see if we could save the changes without changing how the user was working. The solution was found by utilizing the way OnTimer script triggers work. To be honest, I usually don’t have much use for OnTimer scripts. Maybe it is because one of the first examples I saw of how they could be used was to show a dialog once an hour reminding users to submit their time sheets. In general, they have always been kind of a turn off for me. Despite its drawbacks the OnTimer trigger has some specific characteristics that play a big role in our solution.

  • Any window can only have one OnTimer trigger at any moment
    • Calling the Install OnTimer Script step will cancel any currently running trigger
    • Calling the Install OnTimer Script step without a script specified will cancel all timer scripts
    • Calling the Install OnTimer Script step with the interval set to 0 or blank will cancel all timer scripts
  • Time-triggered events run during idle time

The solution for us was to use an OnObject Modify script trigger to call a script that invoked an OnTimer trigger that would commit the current record and restore our cursor to its original position. In its most basic form, that solution looked like this: Save It Screen Shot 2015-07-31 at 12.51.07 AM Commit & Restore Cursor (simple) Screen Shot 2015-07-31 at 12.50.24 AM

You’ll notice we are waiting 1 second in the script above. In the picker example referenced at the end of this article a .25 second wait time works best. We found that on an iPad the longer wait time was needed.

The basic idea is that each time a field is modified it initiates the “Save It” script. Subsequent calls cancel the previous calls and the timer script won’t start until there is idle time (the user stops typing). Once there is a lull in the action the timer will fire the “Commit & Restore Cursor” script. There are a few things you need to put in place to make this work:

  • First, you’ll need to give each field on the layout a name because after we commit the record we use the Go To Object script step to get the cursor back to its original location.
  • Next, you’ll need to attach an OnObject Modify trigger to each field on the layout.
    • Don’t worry, they all call the same script and there are no parameters to pass, so it’s still pretty easy to implement

And there are a few “gotcha” items as well:

  • Portals
    • When the user makes a change to a field in a portal the Go to Object step will put the cursor in the first portal row regardless of which row the user was originally in.
    • We can overcome this issue by capturing the portal row before the commit, and then using a looping script to get back to the correct row.
  • Containers
    • Container fields in FileMaker Go will show a dialog pop over whenever the cursor is put into the field. Therefore the “restore cursor” portion of our script causes an unwanted dialog.
    • We can overcome this issue by using the “FieldType” function to capture whether the current field is a container field or not and if it is, exit the script before the “restore cursor” steps.
  • Styles
    • Similar to the issue with container fields, fields that have any control style applied (drop-down list, pop-up menu, checkbox, radio button, drop-down calendar) will behave poorly when we go back to the field after the commit step.
    • We can overcome this issue by using the “FieldStyle” function to capture whether the current field is using a control style or not and if it is, exit the script before the “restore cursor” steps.

When you put it all together the result looks like this: Commit & Restore Cursor Screen Shot 2015-07-31 at 12.54.47 AM I first came across a similar technique over at www.modularfilemaker.org. The issue there was running a picker over a WAN and trying to make it behave quickly. The solution was an OnTimer triggered script used in a similar way to what we have done here. Let me know in the comments if you’ve found this technique useful. Feel free to leave your own stories of users who don’t “use the system the way it was designed.” 😉   About Skeleton Key Skeleton Key helps turn complex, complicated, and outdated systems into true information platforms. Our team of consultants and developers do this by developing custom-fit software tools and reporting dashboards that help businesses find, use, and understand their data, freeing them to focus on and grow their core business. In addition to custom databases and applications, we also provide training and coaching for getting the most out of your existing systems and understanding your unruly data. Skeleton Key is an open-book management company and active player of the Great Game of Business.