sam-r/fm-laravel
Laravel 对 FileMaker API 的友好封装
Requires
- php: >=5.4.0
- andrewmile/fm-api: 0.1.2
- andrewmile/fm-hash: 0.1.*
Requires (Dev)
- phpspec/phpspec: ~2.0
This package is not auto-updated.
Last update: 2024-09-21 08:09:59 UTC
README
请注意 这是对 FMLaravel 的开发分支,理想情况下,原始仓库将实现这些功能之一或全部。由于组织原因,尚未合并。因此,请也检查其状态,因为它现在可能更先进。
FMLaravel
此包添加了 Laravel 兼容的 FileMaker API 抽象,您可以使用以下功能使用
- 将 FileMaker 记录封装为 Eloquent 模型,并具有基本的读写操作
- 运行 FileMaker 脚本(在 FileMaker 服务器上)
- 对 FileMaker 服务器用户进行用户凭据验证
- 如果需要,使用 Laravel 提供的配置设置轻松访问原始 FileMaker PHP API
待办事项
- 相关记录:允许添加/删除
- 编写认证文档
- 编写单元测试
安装
安装 Laravel 框架
您需要 Composer PHP 包管理器来安装 Laravel 和 FMLaravel。您可以从 getcomposer.org 安装 Composer
如果您尚未安装 Laravel 框架,您需要通过在终端中运行以下命令来安装 Laravel
composer create-project laravel/laravel YourProjectName
Composer 安装完成后,您需要通过在终端中运行以下命令来给予 Laravel 对存储目录的写入权限
chmod -R 777 storage
安装 FMLaravel(此分支)
将以下行添加到您的 composer.json
"repositories": [
{
"type": "vcs",
"url": "https://github.com/tschiemer/FMLaravel"
}
],
运行以下终端命令来安装此 FMLaravel 分支
composer require tschiemer/fm-laravel
在您的文本编辑器中打开 config/app.php 并将以下行添加到 providers 数组中
'FMLaravel\Database\FileMakerServiceProvider',
在 config/database.php 中将以下内容添加到 connections 数组中
'filemaker' => [
'driver' => 'filemaker',
'host' => env('DB_HOST'),
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
'logLevel' => false, // possible values: false, 'error', 'info', 'debug'
// 'properties' => [], // override/feed any settings to the filemaker engine @see class FileMaker
// 'cacheStore' => 'file', // override default filemaker cache store settings
// 'read' => [], // override settings for read operations
// 'write' => [], // override settings for write operations
// 'script' => [] // override settings for script operations
],
注意,通过向 read
、write
或 script
数组中添加选项,您可以覆盖这些操作的设置。任何键值对都与默认配置相同。
在您的根目录中创建一个名为 .env 的新文件,并添加以下内容,包括您的数据库连接详细信息
DB_HOST=YourHost
DB_DATABASE=YourDatabase
DB_USERNAME=YourUsername
DB_PASSWORD=YourPassword
注意,如果您使用版本控制,您不希望 .env 文件成为您的存储库的一部分,因此它默认包含在 .gitignore 中。
如果您想将您的 FileMaker 服务器用作默认(或唯一)模型源,请将 config/database.php
中的默认连接类型更改为 filemaker
'default' => 'filemaker',
您可能会遇到错误消息,如 NYHTC#1(评论) 中所述,在那里您还可以找到快速修复。
使用方法
原始 FileMaker PHP API 访问
要访问原始 FileMaker PHP API(由 FileMaker Inc 正式提供),您首先必须检索连接抽象,然后获取 filemaker 实例,如下所示
// Load filemaker connection and retrieve engine for read configuration
$filemaker = DB::connection('filemaker')->filemaker('read');
// Load default filemaker engine from default connection
// NOTE _only_ works when you have setup 'filemaker' to be the default connection in `database.php` (see above)
$filemaker = DB::filemaker();
在 $filemaker
对象上,您现在可以执行 FileMaker 类提供的任何操作(另请参阅 composer 包 andrewmile/fm-api
)。以下是一个示例
$filemaker->getLayout('myLayout');
也可以通过以下方式简化 FileMaker API 提供的基本列表
DB::listDatabases();
DB::listScripts();
DB::listLayouts();
// or by specifying the connection to use
DB::connection('filemaker')->listDatabases();
..
执行 Filemaker 脚本
可以轻松地在您的 FileMaker 服务器上执行存储的 Filemaker 脚本。在您的控制器中使用以下示例代码
// somewhere at the top of your PHP file
use FMLaravel\Script\Script;
use FMLaravel\Database\RecordExtractor;
// Create an instance of the record extractor to use. This object postprocesses any Filemaker results to bring them into a nice format.
// In fact this is optional, if you do not want to use it the script execution step will return the raw filemaker result.
// RecordExtractor needs the filemaker meta key to use.
$recordExtractor = new RecordExtractor('myFileMakerMetaKey');
// In case you want results conforming to a specific FMLaravel Model, please use this way of instantiating the extractor.
$recordExtractor = RecordExtractor::forModel(Task::class);
// Optionally define a specific parameter formatter.
// By default parameter lists are joined with a newline character inbetween such as to create a value list for the filemaker script.
$paramPreprocessor = function($params){
if (is_array($params)) {
// do your custom processing here
}
return $params;
};
// Note that both arguments are optional
$script = new Script($recordExtractor, $paraPreprocessor);
// optionally set the connection to use
// if not set an instance of the default DB connection will be used (which hopefully is of the correct class)
$script->setConnection('filemaker');
$script->setConnection(DB::connection('filemaker'));
// Execute script on filemaker server
// Will throw an FileMakerException if an error occurs
$result = $script->execute('myLayout', 'myScript', $myParams);
如果没有发生错误,所有脚本都返回原始记录结果集(特别是没有模型实例)。如何处理数据取决于您。
注意:如果结果集为空,则不会抛出异常,但只是返回空结果集。
模型
基本用法
Laravel 包含一个名为 artisan 的命令行工具,可用于执行许多有用的任务,包括生成文件以避免重复输入模板代码。
您可能希望为项目中使用的每个表创建一个模型类。Laravel 使用约定来使用单数模型名称。要为任务表创建模型,请在终端中运行以下命令
php artisan make:model Task
为您生成的文件位于 app/Task.php。这个类扩展了 Laravel 的 Eloquent Model 类,但我们需要它扩展 FMLaravel Model 类。从新创建的 Task.php 文件中删除以下行
use Illuminate\Database\Eloquent\Model;
然后替换为以下行
use FMLaravel\Database\Model;
在您的模型类中,您需要指定在查询 FileMaker 数据库中的任务表时应使用的布局。为此,在 Task 类内部添加以下行
protected $layoutName = 'YourTaskLayoutName';
默认情况下,Laravel 将假设您的表的主键是 "id"。如果您有不同的主键,您需要在类内部添加以下内容
protected $primaryKey = 'YourTaskPrimaryKey';
查询表
在您将查询 FileMaker 任务数据的文件中,请在文件顶部添加以下内容
use App\Task;
现在您已经导入了 Task 模型,可以对任务表执行以下类型的查询
查找所有记录
$tasks = Task::all();
根据主键查找记录
$task = Task::find(3); //will find the task record with a primary key of 3
查找符合您查找条件的任务。您可以选择传递两个字段名和匹配值的参数,或者传递一个字段名和值的数组。
//will find tasks where the task_name is 'Go to the store'
$tasks = Task::where('task_name', 'Go to the store')->get();
//will find tasks where task_name is 'Go to the store' and priority is 1
$tasks = Task::where([
'task_name' => 'Go to the store',
'priority' => 1
])->get();
如果您想将查询限制为匹配您条件的第一个记录,可以使用 first() 而不是 get()
$tasks = Task::where('task_name', 'Go to the store')->first();
如果您想指定要限制查询的记录数,可以使用 limit() 方法。
//will find the first 10 records that match the find criteria
$tasks = Task::where('task_name', 'Go to the store')->limit(10)->get();
您还可以使用 skip() 方法指定要跳过的记录数。
//will find records that match the find criteria after skipping the first 10
$tasks = Task::where('task_name', 'Go to the store')->skip(10)->get();
这些查询方法可以链式使用,例如
//will find 10 records that match the find criteria after skipping the first 100
$tasks = Task::where('task_name', 'Go to the store')->skip(100)->limit(10)->get();
如果您在同一个查询中同时使用 skip() 和 limit(),并且希望将它们合并为一个方法,您也可以使用以下方法
//will find 10 records that match the find criteria after skipping the first 100
$tasks = Task::where('task_name', 'Go to the store')->setRange(100, 10)->get();
默认情况下,您在模型的 $layoutName 属性上设置的布局将用于查询数据。但是,如果您需要为特定查询指定不同的布局,可以使用 setLayout() 方法。
//will use the PastDue layout to perform the query
$tasks = Task::where('task_name', 'Go to the store')->setLayout('PastDue')->get();
重复字段
当模型布局上存在多个字段重复时,重复字段作为数值索引的数组返回。要正确使用重复字段,您必须在模型上定义所有这些字段,如下所示
// list your repetition fields in this array
protected $repetitionFields = ['categories'];
重复字段的值是数值索引的数组。想象一个例子,其中每个任务都有用于类别的重复字段
$task = Task::first();
dd($task->categories);
将输出
array:5 [
0 => "Urgent"
1 => "Support"
2 => ""
3 => ""
4 => ""
]
如果您要设置任何重复项,您有以下选项
// set a specific repetition
$task->setAttribute('categories', 'Not urgent anymore', 0);
// set a specific repetition
$task->categories = [0 => 'Not urgent anymore'];
请注意,在第二种情况下,只有指定的重复项被覆盖,其他重复项保持不变。
查询运算符
默认查询运算符是 FileMaker 字段匹配运算符 ==
。要使用不同的运算符,请按以下方式指定
$tasks = Task::where('priority', '>', 5)->get();
可能的运算符如下,语义由 FileMaker 定义
'=', '==', '<', '>', '<=', '>=', '<>', '!','~', '""', '*""', 'like'
传递给这些运算符的所有搜索参数(除了 like
)都自动使用 FileMaker 的特殊搜索字符串(#,@,*,, ,//,"")进行转义。如果您想使用任何这些运算符提供自定义/原始搜索,可以使用 like
查询运算符,它要求您自己转义搜索字符串。您将这样做如下
use FMLaravel\Database\Helpers;
$tasks = Task::where('task_name', 'like', Helpers::escape($search) . '-@#')->get();
相关记录/模型
也可以加载与模型布局中显示的任何相关记录,例如通过门户检索模型(例如)。以下是如何扩展您的模型示例:
//
protected $relatedRecordsInfo = [
'assignees' => [
'table' => 'FileMakerTableReference', // as set in your FM database's Relationship view
'class' => Assignees::class // model class used to represent the related records
],
'project' => [
'table' => 'task_projects',
'class' => Projects::class
]
];
public function assignees()
{
return $this->relatedRecords('assignees', 'many'); // the to many relationship is the default
}
public function project()
{
return $this->relatedRecords('project', 'one'); // a to-one relationship
}
您可以像平常一样检索相关记录,也可以实现预加载。
// eager loading of assignees
$task = Task::with('assignees')->first();
// eager loading of assignees and project
$task = Task::with(['assignees', 'project'])->first();
// Lazy
$task = Task::first();
// Accessing related models
do($task->assignees);
do($task->project);
请注意,FileMaker 结果集自动包含相关记录集,这导致检索相关记录时出现两种不同的行为:如果相关记录是预加载的,它们也将从原始模型查询中提取出来,而无需对服务器进行额外的查询;另一方面,如果相关记录是延迟加载的,则必须对服务器进行新的请求。
注意 对相关记录的任何保存操作都使用模型类中提供的配置(即使用其布局和主键)。
第二注意 目前不支持相关记录集操作(FileMaker 操作,添加/删除门户行)。这是未来的一个功能。
第三注意 在设置相关记录时,您必须确保使用正确的表名(这是相关表的名字)。如果您没有正确指定,您将不会收到错误,但只是一个空记录集。这是因为 FileMaker API 将对不存在集的请求与对空但有效的集的请求同等对待。
插入、更新和删除模型
基本模型创建、更新和删除方法也得到支持。因此,您可以像平常一样运行以下任何命令:
$task = new Task();
$task->myField = "Sheherazade";
$task->save(); // creates a new record in FileMaker DB
$task->myField = "I changed my mind";
$task->save(); // updates existing (in this case previously created) record in FileMaker DB
$task->delete(); // deletes record from FileMaker DB
扩展使用
FileMaker 元数据
对于内部处理,FileMaker 保留某些数据(如内部记录 ID),这些数据对于写入操作是必需的。默认情况下,任何此类元数据都存储在额外的属性 '_FileMaker_ ' 中。自行承担风险进行编辑。如果您确实需要此属性进行自己的流程,您可以通过在模型中设置键来轻松重命名该名称。
protected $fileMakerMetaKey = "__FileMaker_No_Collisions_Expected_Now_HAHAHAHA__";
可用的元数据(一旦对 FileMaker 服务器的请求已经发出)是记录 ID 和当前修改 ID。数据可以通过以下方式在模型实例中访问:
$meta = $task->getFileMakerMetaData();
$meta->recordId ..
$meta->modificationId ..
// or
$task->getFileMakerMetaData('recordId')
$task->getFileMakerMetaData('modificationId')
时间戳
默认情况下,由 Eloquent 生成的常规时间戳字段(如 'updated_at' 等)对所有 FMLaravel 模型都被禁用。要启用它们,请在您的模型中设置适当的选项。
public $timestamps = true;
请注意,启用时间戳后,您必须在 FileMaker 表/布局中提供相应的字段。
容器字段
容器字段不直接包含数据,而是包含两种形式的引用(也请参阅这篇 FM 帮助文章)。要访问容器字段数据,您可以通过调用 FileMaker API 或让您的模型为您处理,而引用通常包含文件名,从中可以猜测类型。
使用 API 调用的示例
// retrieve your model as you normally would
$task = Task::find(124);
// make API call on container field
// NOTE this assumed you've set up filemaker as your default database driver
$containerData = DB::connection('filemaker')->filemaker('read')->($task->myContainerField);
// example route response
return response($containerData)->header('Content-Type:','image/png');
使用隐式模型功能的示例
按照以下方式扩展您的模型
/***** required *****/
// list all your container fields in this array
protected $containerFields = ['myContainerField'];
/***** optional *****/
// When accessing container fields they are automatically mutated and in case you want the container data to be loaded
// automatically aswell set this to true.
// NOTE that retrieving container data from the server is a seperate query to the server.
// Default: false
protected $containerFieldsAutoload = true;
在您的控制器中
// retrieve your model as you normally would
$task = Task::find(124);
// original field is automatically mutated to an instance of class \FMLaravel\Database\ContainerField
$myContainerField = $task->myContainerField;
// checker wether it is empty
if ($myContainerField->isEmpty()) {
// ...
}
// now you can access the following attributes
$myContainerField->key == 'myContainerField'; // original attribute name
$myContainerField->filename == 'myImageFile.png';
$myContainerField->data == you-binary-image-data // NOTE if you have specified to NOT autoload container data a request to the server will be triggered before it is returned.
$myContainerField->url == '/fmi/xml/cnt/myImageFile.png? etc etc'; // original attribute value as returned from the
// example route response
return response($myContainerField->data)->header('Content-Type:',$myContainerField->mimeType);
(FileMaker 服务器) 容器字段数据的缓存
此外,您还可以启用从任何 FileMaker 服务器检索的容器字段数据的缓存。通过以下选项扩展您的模型
/***** required *****/
// file cache time in minutes
protected $containerFieldsCacheTime = 1;
/***** optional *****/
// set which laravel cache store is to be used
// Default: default system cache store
// Note: can also be configured in your database.php setup (see above)
protected $containerFieldsCacheStore = 'file';
// Override the default cache key format to use for container fields
// Five placeholders are available to be overridden automatically for each container field
// (each placeholder begins with a colon; for the available placeholders see this example)
// Default: ':url'
protected $containerFieldsCacheKeyFormat = ':field :filename :url :recordId :modificationId';
注意 FileMaker 提供的 URL 包含字段、文件名和记录 ID,但不包含(记录)修改 ID。这意味着,为了避免过时的缓存,对容器字段数据的任何更改都必须导致这些值中的任何一个发生变化。
设置和上传容器字段数据到 FileMaker 服务器
如果您的模型使用自动 ContainerField 功能,您还可以更新您的容器字段。
默认情况下,Filemaker PHP API 不提供直接将文件放入容器字段的方法 - 您可以直接写入,是的,但数据将被视为文本。
典型的解决方案依赖于额外的字段、Filemaker 脚本或机器人。因此没有默认的解决方案,但 FMLaravel 提供了一些处理此问题的工具。
当您尝试保存带有新数据的容器字段时,您将遇到一个异常,它会通知您模型的 updateContainerFields
方法尚未实现。实际上,在您的模型中覆盖此方法将是您的通用切入点,如下所示
/** Method called by container field update mechanism
* called on model saves (inserts & updates)
* @param array $values key-value list of dirty (ie changed ContainerFields)
* @throws Exception
* @see FMLaravel\Database\QueryBuilder
*/
public function updateContainerFields(array $values)
{
// do whatever you want
foreach ($values as $key => $val) {
// note that $val will be of type ContainerField
}
}
如果您的 Filemaker 服务器版本为 13.0 或更高(或者您已安装允许将 base64 数据写入容器字段作为文件的插件),您可以使用以下所示的解决方案。
首先扩展您的模型如下
// at the top of your PHP file
use FMLaravel\Database\ContainerField\RunBase64UploaderScriptOnSave;
// in your class model
use RunBase64UploaderScriptOnSave;
/***** optional *****/
// Set the script name
// Default (if not defined): "PHPAPI_YourModelName_RunBase64UploaderScriptOnSave"
protected $containerFieldUploaderScriptName = "PHPAPI_Task_RunBase64UploaderScriptOnSave";
// Override the layout to use
// Default: the model layout defined previously (see above)
protected $containerFieldUploaderScriptLayout = "myLayoutToUseForTheScript";
使用这种方式,每次保存操作都会触发一个自动执行指定布局上相应脚本的脚本执行,以下脚本参数(通过 Get(ScriptParameter)
可用)
<YourModelName>
<Primary key of model/record>
<number of container fields being updated>
<list of container data>
对于每个更新的容器字段,以下数据将给在占位符中
<name of field>
<filename>
<filedata base64 encoded>
因此,对于我们的示例 Task 模型,可能如下所示
Task
3
1
myContainerField
myNewImage.png
iVBORw0KGgoAAAANSUhEUgAAAHkAAABACAYAAAA+j9gs ... some long long sequence of characters ... AAAElFTkSuQmCC
在 Filemaker 服务器端,您自然需要一个脚本来处理传入的数据。以下是一个基本示例实现的示例。
最后,在您的控制器中,您有几种设置容器数据的方法
///// Setting the attribute itself
// empty the container field
$task->myContainerField = null;
// set a file as uploaded from any html form
$task->myContainerField = Request::file('myFileUploadField');
// or possibly
$task->myContainerField = $request->file('myFileUploadField');
// set a file you have instantiated differently and that fullfills the SplFileInfo interface
$task->myContainerField = new Symfony\Component\HttpFoundation\File\File('myImageFile.png');
///// Calling method on the ContainerField object
// optional filename (if not given will be deduced from realpath that might be messed up in case of temporary files)
$task->myContainerField->setFromRealpath($realpath, $filename);
// in case you are computing any data, use this
$task->myContainerField->setWithData($filename, $data);
// you have a file on laravel filesystem disk that you want to use?
// Passing the disk is optional, by default the default storage will be used.
$task->myContainerField->setFromStorage($filename, $disk);
通过 Filemaker 进行用户身份验证
待办 编写文档