Skip to content

Commit 419cd68

Browse files
committed
added config options to cache session token to prevent the need to log in on every request
1 parent da2b352 commit 419cd68

File tree

4 files changed

+57
-20
lines changed

4 files changed

+57
-20
lines changed

README.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ We support the [currently supported versions of Laravel](https://laravel.com/doc
2929

3030
## What's new in 2.0
3131
* Added support for Laravel 11
32-
* Empty fields in FileMaker are returned as null instead of an empty string
33-
* FileMaker Sessions only last for the duration of a single request to Laravel instead of being reused for 15 minutes - Cache is no longer required
32+
* Empty fields in FileMaker are returned as null instead of an empty string by default
33+
* FileMaker Sessions only last for the duration of a single request to Laravel instead of being reused for 15 minutes by default - this can be changed in the config
3434
* Improvements to whereNot logic and implementation to make it behave more closely to what it should be
3535

3636
### Upgrading from 1.x to 2.x
@@ -46,11 +46,11 @@ In version 1.0, empty fields in FileMaker were returned as an empty string. In v
4646
If you'd like to continue with the old behavior your can change the `empty_strings_to_null` config value to false to keep with the empty strings. Otherwise, if you have any code which relies on empty fields being returned as an empty string, you may need to refactor your code to work with the new behavior.
4747

4848
##### Minor - Changes to session management - Minor Change
49-
In version 1.0 the same FileMaker Data API session was used for all requests for about 15 minutes at a time, until the token expired. This token was stored in the Laravel Cache between requests. This behavior has been changed in version 2.0.
49+
In version 1.0 the same FileMaker Data API session was used for all requests for about 15 minutes at a time, until the token expired. This token was stored in the Laravel Cache between requests. This behavior has been changed in version 2.0 to work when cache is not configured.
5050

51-
In this version 2.0, a Data API session lasts for only the duration of one request to your Laravel app. Login is performed the first time you use request data from the Data API. The session is ended and a logout is performed after the response has been sent to the browser through the use of [terminable middleware](https://laravel.com/docs/11.x/middleware#terminable-middleware).
51+
By default, in version 2.0 a Data API session lasts for only the duration of one request to your Laravel app. Login is performed the first time you use request data from the Data API. The session is ended and a logout is performed after the response has been sent to the browser through the use of [terminable middleware](https://laravel.com/docs/11.x/middleware#terminable-middleware).
5252

53-
If you have any code which relies on the Data API session being reused between requests to your Laravel app, such as setting a global field once and then reading it across multiple page loads of your Laravel app, you will need to refactor your code to work with the new behavior.
53+
If you have any code which relies on the Data API session being reused between requests to your Laravel app, such as setting a global field once and then reading it across multiple page loads of your Laravel app, you will need to either change this behavior by setting ` 'cache_session_token' => true` in your FileMaker connection config or refactor your code to work with the new behavior.
5454

5555
##### Minor - Improvements to whereNot logic
5656
There were some cases where whereNot may return results that were probably not correct or expected. This has been fixed in version 2.0. If you have any code which relies on the old, incorrect behavior of whereNot, you may need to refactor your code to work with the new corrected behavior.
@@ -67,7 +67,7 @@ With the package installed you can now have access to all the features of this p
6767
## Database configuration
6868
The first thing to do is to add a new data connection in your `database.php` config file. The connections you specify here will be used in your FMModel classes to configure which databases each model will connect to.
6969

70-
You may use the following code block below as a template.
70+
You may use the following code block below as a template, which has some good defaults.
7171
```php
7272
'filemaker' => [
7373
'driver' => 'filemaker',
@@ -78,11 +78,13 @@ You may use the following code block below as a template.
7878
'prefix' => env('DB_PREFIX', ''),
7979
'version' => env('DB_VERSION', 'vLatest'),
8080
'protocol' => env('DB_PROTOCOL', 'https'),
81+
'cache_session_token' => env('DB_CACHE_SESSION_TOKEN', true), // set to true to cache the session token between requests and prevent the need to re-login each time. This can be a significant performance improvement!
82+
'empty_strings_to_null' => env('DB_EMPTY_STRINGS_TO_NULL', true), // set to false to return empty strings instead of null values when fields are empty in FileMaker
8183
]
8284
```
8385
You should add one database connection configuration for each FileMaker database you will be connecting to. Each file can have completely different configurations, and can even be on different servers.
8486

85-
Sessions will be maintained on a per-connection basis and tokens will automatically be cached using whatever cache configuration you have set up for your Laravel app.
87+
If `cache_session_token` is true, login sessions will be maintained on a per-connection basis and tokens will automatically be cached using whatever cache configuration you have set up for your Laravel app. This prevents the need to re-login to the Data API for each request, which can be a significant performance improvement. If you have a cache configured for your Laravel app, you should generally this to true.
8688

8789
#### Prefix
8890
The prefix configuration option adds a prefix to each of the layout/table names which you specify. You don't need to specify a prefix, but it can be very convenient to do so.
@@ -119,7 +121,7 @@ protected $layout = 'MyLayout';
119121
### Null values and empty strings
120122
Null is an important, expected possible value for developers when working with databases. FileMaker as a platform, unfortunately, does not have the concept of a null value. A field which has not had a value written to it instead contains an empty string. In order to make this behavior more web-developer-friendly, Eloquent FileMaker automatically converts the value of `''` in a FileMaker field to `null` when reading data from the Data API.
121123

122-
If you would like to have empty FileMaker fields returned as empty strings you can set the `empty_strings_to_null` config value to false in your `config/eloquent-filemaker.php` file. The config file can be published to your config folder by running `artisan vendor:publish --tag=eloquent-filemaker-config`.
124+
If you would like to have empty FileMaker fields returned as empty strings you can set the `empty_strings_to_null` config value to false in your connection configuration.
123125

124126
Eloquent FileMaker will always automatically convert `null` values to `''` when writing data back to your FileMaker database to prevent errors.
125127

src/Middleware/EndSession.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,16 @@ public function handle(Request $request, Closure $next): Response
2222

2323
/**
2424
* Handle tasks after the response has been sent to the browser.
25+
* Terminate the FileMaker Session after the response has been sent to the browser.
26+
* Leave it active if the user has chosen to cache the session token.
2527
*/
2628
public function terminate(Request $request, Response $response): void
2729
{
30+
$shouldCacheSessionToken = FM::connection()->getConfig()['cache_session_token'];
31+
if ($shouldCacheSessionToken) {
32+
return;
33+
}
34+
2835
// disconnect the FileMaker session
2936
FM::disconnect();
3037
}

src/Providers/FileMakerConnectionServiceProvider.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,5 @@ public function boot(Kernel $kernel)
4949
{
5050
// add the middleware to the global middleware so that we always end the FileMaker session
5151
$kernel->pushMiddleware(EndSession::class);
52-
53-
$this->publishes([
54-
__DIR__ . '/../config/eloquent-filemaker.php' => config_path('eloquent-filemaker.php'),
55-
], 'eloquent-filemaker-config');
5652
}
5753
}

src/Services/FileMakerConnection.php

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Illuminate\Http\UploadedFile;
1515
use Illuminate\Support\Arr;
1616
use Illuminate\Support\Collection;
17+
use Illuminate\Support\Facades\Cache;
1718
use Illuminate\Support\Facades\Http;
1819
use Psr\Http\Message\RequestInterface;
1920
use Psr\Http\Message\ResponseInterface;
@@ -31,10 +32,28 @@ class FileMakerConnection extends Connection
3132

3233
protected $password;
3334

34-
protected static $sessionToken;
35+
protected $sessionToken;
3536

3637
protected $retries = 1;
3738

39+
protected $sessionTokenCacheKey;
40+
41+
protected $emptyStringToNull = true;
42+
43+
public const CONFIG_KEY_CACHE_SESSION_TOKEN = 'eloquent-filemaker.cache_session_token';
44+
45+
public function __construct($pdo, $database = '', $tablePrefix = '', array $config = [])
46+
{
47+
48+
$this->emptyStringToNull = $config['empty_strings_to_null'] ?? true;
49+
$this->shouldCacheSessionToken = $config['cache_session_token'] ?? false;
50+
51+
// set the session cache key with the name of the connection to support multiple connections
52+
$this->sessionTokenCacheKey = 'eloquent-filemaker-session-token-' . $config['name'];
53+
54+
parent::__construct($pdo, $database, $tablePrefix, $config);
55+
}
56+
3857
/**
3958
* Crazy high number of records to return.
4059
* Used to get an empty set when using a whereIn with no values.
@@ -63,12 +82,20 @@ public function getLayout()
6382
public function login()
6483
{
6584
// return early if we're already logged in
66-
if (self::$sessionToken) {
85+
if ($this->sessionToken) {
6786
return;
6887
}
6988

7089
// retrieve and store the session token
71-
self::$sessionToken = $this->fetchNewSessionToken();
90+
// Store it in the cache if the connection is configured to do so
91+
if ($this->shouldCacheSessionToken) {
92+
$this->sessionToken = Cache::remember($this->sessionTokenCacheKey, 890, function () {
93+
return $this->fetchNewSessionToken();
94+
});
95+
} else {
96+
// we're not going to reuse it, so just get a new one
97+
$this->sessionToken = $this->fetchNewSessionToken();
98+
}
7299
}
73100

74101
/**
@@ -604,11 +631,11 @@ public function performScript(FMBaseBuilder $query)
604631
*/
605632
public function disconnect()
606633
{
607-
if (! self::$sessionToken) {
634+
if (! $this->sessionToken) {
608635
return;
609636
}
610637

611-
$url = $this->getDatabaseUrl() . '/sessions/' . self::$sessionToken;
638+
$url = $this->getDatabaseUrl() . '/sessions/' . $this->sessionToken;
612639

613640
// make an http delete request to the data api to end the session
614641
$response = Http::delete($url);
@@ -624,7 +651,12 @@ public function disconnect()
624651
*/
625652
public function forgetSessionToken()
626653
{
627-
self::$sessionToken = null;
654+
$this->sessionToken = null;
655+
656+
// clear the token from the cache if we're configured to store it
657+
if ($this->shouldCacheSessionToken) {
658+
Cache::forget($this->sessionTokenCacheKey);
659+
}
628660
}
629661

630662
public function layout($layoutName)
@@ -649,9 +681,9 @@ public function setGlobalFields(array $globalFields)
649681
protected function prepareRequestForSending($request)
650682
{
651683
if ($request instanceof PendingRequest) {
652-
$request->withToken(self::$sessionToken);
684+
$request->withToken($this->sessionToken);
653685
} else {
654-
$request = Http::withToken(self::$sessionToken);
686+
$request = Http::withToken($this->sessionToken);
655687
}
656688

657689
$request->withMiddleware($this->retryMiddleware());

0 commit comments

Comments
 (0)