├── .gitignore ├── LICENSE.md ├── README.md ├── autoloader.php ├── composer.json ├── demo └── sample.php ├── phpunit.sample.xml ├── psalm.xml ├── src ├── Command │ ├── Add.php │ ├── Command.php │ ├── CommandTrait.php │ ├── CompoundFind.php │ ├── Delete.php │ ├── Duplicate.php │ ├── Edit.php │ ├── Find.php │ ├── FindAll.php │ ├── FindAny.php │ ├── FindRequest.php │ ├── PerformScript.php │ └── RequestTrait.php ├── Error │ ├── de.php │ ├── en.php │ ├── fr.php │ ├── it.php │ ├── ja.php │ └── sv.php ├── FileMaker.php ├── FileMakerException.php ├── FileMakerValidationException.php ├── Helpers │ └── DateFormat.php ├── Object │ ├── Field.php │ ├── Layout.php │ ├── Record.php │ ├── RelatedSet.php │ └── Result.php └── Parser │ ├── FMPXMLLAYOUT.php │ └── FMResultSet.php └── tests ├── bootstrap.php ├── filemaker-test.fmp12 ├── functional └── DateFormatTest.php └── unit └── src ├── Command ├── AddTest.php ├── CommandTest.php ├── CompoundFindTest.php ├── DeleteTest.php ├── DuplicateTest.php ├── EditTest.php ├── FindAllTest.php ├── FindAnyTest.php ├── FindRequestTest.php ├── FindTest.php └── PerformScriptTest.php ├── FileMakerExceptionTest.php ├── FileMakerTest.php ├── FileMakerValidationExceptionTest.php ├── Helpers └── DateFormatTest.php ├── Object ├── FieldTest.php ├── LayoutTest.php ├── RecordTest.php ├── RelatedSetTest.php └── ResultTest.php └── Parser ├── FMPXMLLAYOUTTest.php └── FMResultSetTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | # phpstorm project files 2 | /.idea 3 | 4 | # netbeans project files 5 | /nbproject 6 | 7 | # zend studio for eclipse project files 8 | /.buildpath 9 | /.project 10 | /.settings 11 | 12 | # windows thumbnail cache 13 | Thumbs.db 14 | 15 | # composer vendor dir 16 | /vendor 17 | 18 | # composer itself is not needed 19 | composer.phar 20 | 21 | # composer.lock is not needed for libraries 22 | /composer.lock 23 | 24 | # Mac DS_Store Files 25 | .DS_Store 26 | 27 | # phpunit itself is not needed 28 | phpunit.phar 29 | # local phpunit config 30 | /phpunit.xml 31 | /tests/bootstrap_originalapi.php 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The FileMaker PHP-API is free software. It is released under the terms of 2 | the following BSD License. 3 | 4 | Copyright © 2016 by 1-more-thing group (http://www.1-more-thing.com) 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | * Neither the name of 1-more-thing nor the names of its 18 | contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FileMaker® PHP-API 2 | FileMaker® PHP API rewritten for PHP 5.5+. 3 | It is compatible with PHP 7.0+ and uses PSR-4 autoloading specifications. 4 | 5 | ## Features 6 | This version of the PHP-API add the following feature to the offical API : 7 | * Error handling using Exception (you can restore the original behavior using option 'errorHandling' => 'default') 8 | * PSR-4 autoloading and installation using composer 9 | * PHP 7.0+ compatibility 10 | * 'dateFormat' option to select the input/output date format 11 | * 'emptyAsNull' option to return empty value as null 12 | * Support setRange() method with PerformScript command (as supported by CWP) 13 | * A method to get the url of your last CWP call: `$fm->getLastRequestedUrl()` 14 | * A method to check if a findRequest is empty: `$request->isEmpty()` 15 | * A method to get the value list associated to a field from a Record: `$record->getValueListTwoField('my_field')` 16 | * EXPERIMENTAL 'useDateFormatInRequests' allow you to use defined 'dateFormat' in request (support wildcards and range) 17 | 18 | ## Requirements 19 | 20 | * PHP >= 5.5 21 | * (optional) PHPUnit to run tests. 22 | 23 | ## Installation 24 | 25 | ### Using Composer 26 | You can use the `composer` package manager to install. Either run: 27 | 28 | $ php composer.phar require airmoi/filemaker "*" 29 | 30 | or add: 31 | 32 | "airmoi/filemaker": "^2.2" 33 | 34 | to your composer.json file 35 | 36 | ### Manual Install 37 | 38 | You can also manually install the API easily to your project. Just download the source [ZIP](https://github.com/airmoi/FileMaker/archive/master.zip) and extract its content into your project. 39 | 40 | ## Usage 41 | 42 | STEP 1 : Read the 'Important Notice' below 43 | 44 | STEP 2 : include the API autoload 45 | 46 | ```php 47 | require '/path/to/autoloader.php'; 48 | ``` 49 | *This step is facultative if you are using composer* 50 | 51 | STEP 3 : Create a FileMaker instance 52 | ```php 53 | use airmoi\FileMaker\FileMaker; 54 | 55 | $fm = new FileMaker($database, $host, $username, $password, $options); 56 | ``` 57 | 58 | STEP 4 : use it quite the same way you would use the offical API... 59 | 60 | ...And enjoy code completion using your favorite IDE and php 7 support without notice/warnings. 61 | 62 | You may also find sample usage by reading the `sample.php` file located in the "demo" folder 63 | 64 | ### Sample demo code 65 | 66 | ```php 67 | use airmoi\FileMaker\FileMaker; 68 | use airmoi\FileMaker\FileMakerException; 69 | 70 | require('/path/to/autoloader.php'); 71 | 72 | $fm = new FileMaker('database', 'localhost', 'filemaker', 'filemaker', ['prevalidate' => true]); 73 | 74 | try { 75 | $command = $fm->newFindCommand('layout_name'); 76 | $records = $command->execute()->getRecords(); 77 | 78 | foreach($records as $record) { 79 | echo $record->getField('fieldname'); 80 | ... 81 | } 82 | } 83 | catch (FileMakerException $e) { 84 | echo 'An error occured ' . $e->getMessage() . ' - Code : ' . $e->getCode(); 85 | } 86 | ``` 87 | 88 | ## Important notice 89 | 90 | The 2.1 release aims to improve compatibility with the original FileMaker PHP-API. 91 | However, you will need to changes few things in your code in order to use it 92 | 93 | The major changes compared to the official package are : 94 | 95 | * Call autoloader.php instead of FileMaker.php to load the API 96 | * API now support Exceptions error handling, you may switch between those behaviors by changing property 'errorHandling' to 'default' or 'exception' (default value is 'exception') 97 | * There is no more 'conf.php' use "setProperty" to define specifics API's settings. You may also use an array of properties on FileMaker instanciation, ie : new FileMaker( $db, $host, $user, $pass, ['property' => 'value']) 98 | * All constants are now part of the FileMaker class, use FileMaker:: instead of 99 | * Also notice that FILEMAKER_SORT_ASCEND/DESCEND have been renamed to FileMaker::SORT_ASCEND/FileMaker::SORT_DESCEND 100 | 101 | You can use the offical [PHP-API guide](https://fmhelp.filemaker.com/docs/14/fr/fms14_cwp_guide.pdf) provided by FileMaker® for everything else. 102 | 103 | ## TODO 104 | * Finish PHPunit test 105 | * Add functionnal tests 106 | * Improve parsers 107 | * Add new parsers 108 | * Documentation 109 | 110 | ## License 111 | FileMaker PHP API is licensed under the BSD License - see the LICENSE file for detail 112 | 113 | ## Credits 114 | 115 | ### Contributors 116 | 117 | - Thanks to [Matthias Kühne](https://github.com/MatthiasKuehneEllerhold) for PSR-4 implementation and code doc fixes. 118 | - Thanks to [jeremiahsmall](https://github.com/jeremiahsmall) for improving error handling. 119 | -------------------------------------------------------------------------------- /autoloader.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | tests/src/FileMakerTest.php 11 | 12 | 13 | tests/src/FileMakerExceptionTest.php 14 | 15 | 16 | tests/src/FileMakerValidationExceptionTest.php 17 | 18 | 19 | 20 | 21 | src/ 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Command/Add.php: -------------------------------------------------------------------------------- 1 | value pairs. To set field repetitions, 29 | * use a numerically indexed array for the value of a field, with the numeric keys 30 | * corresponding to the repetition number to set. 31 | * @param bool $useRawData Prevent data conversion on setField 32 | */ 33 | public function __construct(FileMaker $fm, $layout, $values = [], $useRawData = false) 34 | { 35 | parent::__construct($fm, $layout); 36 | $this->useRawData = $useRawData; 37 | foreach ($values as $fieldname => $value) { 38 | if (!is_array($value)) { 39 | $this->setField($fieldname, $value, 0); 40 | } else { 41 | foreach ($value as $repetition => $repetitionValue) { 42 | $this->setField($fieldname, $repetitionValue, $repetition) ; 43 | } 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * 50 | * @return \airmoi\FileMaker\Object\Result|FileMakerException|FileMakerValidationException 51 | * @throws FileMakerException 52 | * @throws FileMakerValidationException 53 | */ 54 | public function execute() 55 | { 56 | if ($this->fm->getProperty('prevalidate')) { 57 | $validation = $this->validate(); 58 | if (FileMaker::isError($validation)) { 59 | return $validation; 60 | } 61 | } 62 | $layout = $this->fm->getLayout($this->layout); 63 | if (FileMaker::isError($layout)) { 64 | return $layout; 65 | } 66 | 67 | $params = $this->getCommandParams(); 68 | $params['-new'] = true; 69 | foreach ($this->fields as $field => $values) { 70 | if (strpos($field, '.') !== false) { 71 | list($fieldname, $fieldType) = explode('.', $field, 2); 72 | $fieldType = '.' . $fieldType; 73 | } else { 74 | $fieldname = $field; 75 | $fieldInfos = $layout->getField($field); 76 | if (FileMaker::isError($fieldInfos)) { 77 | return $fieldInfos; 78 | } 79 | if ($fieldInfos->isGlobal()) { 80 | $fieldType = '.global'; 81 | } else { 82 | $fieldType = ''; 83 | } 84 | } 85 | foreach ($values as $repetition => $value) { 86 | $params[$fieldname . '(' . ($repetition + 1) . ')' . $fieldType] = $value; 87 | } 88 | } 89 | $result = $this->fm->execute($params); 90 | return $this->getResult($result); 91 | } 92 | 93 | /** 94 | * Sets the new value for a field. 95 | * 96 | * @param string $field Name of field to set. 97 | * @param string $value Value to set for this field. 98 | * @param integer $repetition Field repetition number to set, 99 | * Defaults to the first repetition. 100 | * 101 | * @return string 102 | * @throws FileMakerException 103 | */ 104 | public function setField($field, $value, $repetition = 0) 105 | { 106 | $layout = $this->fm->getLayout($this->layout); 107 | if (FileMaker::isError($layout)) { 108 | return $layout; 109 | } 110 | $fieldInfos = $layout->getField($field); 111 | if(FileMaker::isError($fieldInfos)){ 112 | return $fieldInfos; 113 | } 114 | 115 | $format = FileMaker::isError($fieldInfos) ? null : $fieldInfos->result; 116 | $dateFormat = $this->fm->getProperty('dateFormat'); 117 | if (!$this->useRawData && $dateFormat !== null && ($format === 'date' || $format === 'timestamp')) { 118 | try { 119 | if ($format === 'date') { 120 | $value = DateFormat::convert($value, $dateFormat, 'm/d/Y'); 121 | } else { 122 | $value = DateFormat::convert($value, $dateFormat . ' H:i:s', 'm/d/Y H:i:s'); 123 | } 124 | } catch (\Exception $e) { 125 | return $this->fm->returnOrThrowException( 126 | $value . ' could not be converted to a valid timestamp for field ' 127 | . $field . ' (expected format '. $dateFormat .')' 128 | ); 129 | } 130 | } 131 | 132 | $this->fields[$field][$repetition] = $value; 133 | return $value; 134 | } 135 | 136 | /** 137 | * Sets the new value for a date, time, or timestamp field from a 138 | * UNIX timestamp value. 139 | * 140 | * If the field is not a date or time field, then this method returns 141 | * an Error object. Otherwise, returns TRUE. 142 | * 143 | * If layout data for the target of this command has not already 144 | * been loaded, calling this method loads layout data so that 145 | * the type of the field can be checked. 146 | * 147 | * @param string $field Name of the field to set. 148 | * @param string $timestamp Timestamp value. 149 | * @param integer $repetition Field repetition number to set. 150 | * Defaults to the first repetition. 151 | * 152 | * @return string|FileMakerException 153 | * @throws FileMakerException 154 | */ 155 | public function setFieldFromTimestamp($field, $timestamp, $repetition = 0) 156 | { 157 | $layout = $this->fm->getLayout($this->layout); 158 | if (FileMaker::isError($layout)) { 159 | return $layout; 160 | } 161 | 162 | $fieldInfos = $layout->getField($field); 163 | if (FileMaker::isError($fieldInfos)) { 164 | return $fieldInfos; 165 | } 166 | 167 | switch ($fieldInfos->getResult()) { 168 | case 'date': 169 | return $this->setField($field, date('m/d/Y', $timestamp), $repetition); 170 | case 'time': 171 | return $this->setField($field, date('H:i:s', $timestamp), $repetition); 172 | case 'timestamp': 173 | return $this->setField($field, date('m/d/Y H:i:s', $timestamp), $repetition); 174 | } 175 | 176 | return $this->fm->returnOrThrowException( 177 | 'Only time, date, and timestamp fields can be set to the value of a timestamp.' 178 | ); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/Command/Command.php: -------------------------------------------------------------------------------- 1 | fm = $fm; 47 | $this->layout = $layout; 48 | $this->recordClass = $fm->getProperty('recordClass'); 49 | } 50 | /** 51 | * Requests that the command's result be returned in a layout different 52 | * from the current layout. 53 | * 54 | * @param string $layout Layout to return results in. 55 | * @return self 56 | */ 57 | public function setResultLayout($layout) 58 | { 59 | $this->resultLayout = $layout; 60 | return $this; 61 | } 62 | 63 | /** 64 | * Sets a ScriptMaker script to be run after the Find result set is 65 | * generated and sorted. 66 | * 67 | * @param string $scriptName Name of the ScriptMaker script to run. 68 | * @param string $scriptParameters Any parameters to pass to the script. 69 | * @return self 70 | */ 71 | public function setScript($scriptName, $scriptParameters = null) 72 | { 73 | $this->script = $scriptName; 74 | $this->scriptParams = $scriptParameters; 75 | return $this; 76 | } 77 | 78 | /** 79 | * Sets a ScriptMaker script to be run before performing a command. 80 | * 81 | * @param string $scriptName Name of the ScriptMaker script to run. 82 | * @param string $scriptParameters Any parameters to pass to the script. 83 | * @return self 84 | */ 85 | public function setPreCommandScript($scriptName, $scriptParameters = null) 86 | { 87 | $this->preReqScript = $scriptName; 88 | $this->preReqScriptParams = $scriptParameters; 89 | return $this; 90 | } 91 | 92 | /** 93 | * Sets a ScriptMaker script to be run after performing a Find command, 94 | * but before sorting the result set. 95 | * 96 | * @param string $scriptName Name of the ScriptMaker script to run. 97 | * @param string $scriptParameters Any parameters to pass to the script. 98 | * @return self 99 | */ 100 | public function setPreSortScript($scriptName, $scriptParameters = null) 101 | { 102 | $this->preSortScript = $scriptName; 103 | $this->preSortScriptParams = $scriptParameters; 104 | return $this; 105 | } 106 | 107 | /** 108 | * Sets the PHP class that the API instantiates to represent records 109 | * returned in any result set. 110 | * 111 | * The default is to use the provided \airmoi\FileMaker\Object\Record class. Any 112 | * substitute classes must provide the same API that \airmoi\FileMaker\Object\Record does, 113 | * either by extending it or re-implementing the necessary methods. The 114 | * user is responsible for defining any custom class before the API 115 | * needs to instantiate it. 116 | * 117 | * @param string $className Name of the class to represent records. 118 | * @return self 119 | */ 120 | public function setRecordClass($className) 121 | { 122 | $this->recordClass = $className; 123 | return $this; 124 | } 125 | 126 | /** 127 | * Pre-validates either a single field or the entire command. 128 | * 129 | * This method uses the pre-validation rules that are enforceable by the 130 | * PHP engine -- for example, type rules, ranges, and four-digit dates. 131 | * Rules such as "unique" or "existing," or validation by calculation 132 | * field, cannot be pre-validated. 133 | * 134 | * If you pass the optional $fieldName argument, only that field is 135 | * pre-validated. Otherwise, the command is pre-validated as if execute() 136 | * were called with "Enable record data pre-validation" selected in 137 | * FileMaker Server Admin Console. If pre-validation passes, validate() 138 | * returns TRUE. If pre-validation fails, then validate() throws a 139 | * \airmoi\FileMaker\FileMakerValidationException object containing details about what failed 140 | * to pre-validate. 141 | * 142 | * @param string $fieldName Name of field to pre-validate. If empty, 143 | * pre-validates the entire command. 144 | * 145 | * @return bool|FileMakerValidationException TRUE, if pre-validation passes. 146 | * @throws FileMakerException 147 | * @throws FileMakerValidationException 148 | */ 149 | public function validate($fieldName = null) 150 | { 151 | if (!is_a($this, Add::class) && !is_a($this, Edit::class)) { 152 | return true; 153 | } 154 | $layout = $this->fm->getLayout($this->layout); 155 | if (FileMaker::isError($layout)) { 156 | return $layout; 157 | } 158 | 159 | $validationErrors = new FileMakerValidationException($this->fm); 160 | if ($fieldName === null) { 161 | foreach ($layout->getFields() as $fieldName => $field) { 162 | if (!isset($this->fields[$fieldName]) || !count($this->fields[$fieldName])) { 163 | $values = [0 => null]; 164 | } else { 165 | $values = $this->fields[$fieldName]; 166 | } 167 | foreach ($values as $value) { 168 | try { 169 | $field->validate($value); 170 | } catch (FileMakerValidationException $e) { 171 | foreach ($e->getErrors() as $error) { 172 | $validationErrors->addError($error[0], $error[1], $error[2]); 173 | } 174 | } 175 | } 176 | } 177 | } else { 178 | $field = $layout->getField($fieldName); 179 | if (FileMaker::isError($field)) { 180 | return $field; 181 | } 182 | 183 | if (!isset($this->fields[$fieldName]) || !count($this->fields[$fieldName])) { 184 | $values = [0 => null]; 185 | } else { 186 | $values = $this->fields[$fieldName]; 187 | } 188 | foreach ($values as $value) { 189 | try { 190 | $field->validate($value); 191 | } catch (FileMakerValidationException $e) { 192 | foreach ($e->getErrors() as $error) { 193 | $validationErrors->addError($error[0], $error[1], $error[2]); 194 | } 195 | } 196 | } 197 | } 198 | if ($validationErrors->numErrors()) { 199 | if ($this->fm->getProperty('errorHandling') === 'default') { 200 | return $validationErrors; 201 | } 202 | throw $validationErrors; 203 | } 204 | return true; 205 | } 206 | 207 | /** 208 | * Executes the command. 209 | * 210 | * @return Result Result object. 211 | */ 212 | public function execute() 213 | { 214 | } 215 | 216 | /** 217 | * Sets the record ID for this command. 218 | * 219 | * For Edit, Delete, and Duplicate commands, a record ID must be specified. 220 | * It is also possible to find a single record by specifying its record 221 | * ID. This method is ignored by Add and FindAny commands. 222 | * 223 | * @param string $recordId ID of record this command acts upon. 224 | * @return self 225 | */ 226 | public function setRecordId($recordId) 227 | { 228 | $this->recordId = $recordId; 229 | return $this; 230 | } 231 | 232 | /** 233 | * Set a global field to be define before perfoming the command. 234 | * 235 | * 236 | * @param string $fieldName the global field name. 237 | * @param string $fieldValue value to be set. 238 | * @return self 239 | */ 240 | public function setGlobal($fieldName, $fieldValue) 241 | { 242 | $this->globals[$fieldName] = $fieldValue; 243 | return $this; 244 | } 245 | 246 | /** 247 | * Return defined globals 248 | * 249 | * @return array 250 | */ 251 | public function getGlobals() 252 | { 253 | return $this->globals; 254 | } 255 | 256 | /** 257 | * 258 | * @param string $xml 259 | * @return Result|FileMakerException 260 | * @throws FileMakerException 261 | */ 262 | protected function getResult($xml) 263 | { 264 | $parser = new FMResultSet($this->fm); 265 | $parseResult = $parser->parse($xml); 266 | if (FileMaker::isError($parseResult)) { 267 | return $parseResult; 268 | } 269 | 270 | $result = new Result($this->fm); 271 | $parseResult = $parser->setResult($result, $this->recordClass); 272 | if (FileMaker::isError($parseResult)) { 273 | return $parseResult; 274 | } 275 | 276 | return $result; 277 | } 278 | 279 | /** 280 | * Build command params 281 | * @return array 282 | */ 283 | protected function getCommandParams() 284 | { 285 | $queryParams = [ 286 | '-db' => $this->fm->getProperty('database'), 287 | '-lay' => $this->layout 288 | ]; 289 | 290 | foreach ([ 291 | 'script' => '-script', 292 | 'preReqScript' => '-script.prefind', 293 | 'preSortScript' => '-script.presort' 294 | ] as $varName => $paramName) { 295 | if ($this->$varName) { 296 | $queryParams[$paramName] = $this->$varName; 297 | $varName .= 'Params'; 298 | if ($this->$varName !== null) { 299 | $queryParams[$paramName . '.param'] = $this->$varName; 300 | } 301 | } 302 | } 303 | if ($this->resultLayout) { 304 | $queryParams['-lay.response'] = $this->resultLayout; 305 | } 306 | 307 | foreach ($this->globals as $fieldName => $fieldValue) { 308 | $queryParams[$fieldName.'.global'] = $fieldValue; 309 | } 310 | return $queryParams; 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/Command/CommandTrait.php: -------------------------------------------------------------------------------- 1 | fm->getLayout($this->layout); 39 | } 40 | 41 | /** 42 | * Get the field "type" (date/text/number...) 43 | * @param $fieldName 44 | * 45 | * @return null|Field|FileMakerException Field object, if successful. 46 | */ 47 | public function getFieldResult($fieldName) 48 | { 49 | try { 50 | $layout = $this->fm->getLayout($this->layout); 51 | if (FileMaker::isError($layout)) { 52 | /** @psalm-suppress InvalidThrow */ 53 | throw $layout; 54 | } 55 | $field = $layout->getField($fieldName); 56 | if(FileMaker::isError($field)){ 57 | /** @psalm-suppress InvalidThrow */ 58 | throw $field; 59 | } 60 | } catch (FileMakerException $e) { 61 | return null; 62 | } 63 | return $field->result; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Command/CompoundFind.php: -------------------------------------------------------------------------------- 1 | requests[$precedence] = $findrequest; 43 | } 44 | 45 | /** 46 | * Adds a sorting rule to this Compound Find command. 47 | * 48 | * @param string $fieldname Name of the field to sort by. 49 | * @param integer $precedence Integer from 1 to 9, inclusive. A value 50 | * of 1 sorts records based on this sorting rule first, a value of 51 | * 2 sorts records based on this sorting rule only when two or more 52 | * records have the same value after the first sorting rule is 53 | * applied, and so on. 54 | * @param mixed $order Direction of the sort. Specify the 55 | * FileMaker::SORT_ASCEND constant, the FileMaker::SORT_DESCEND 56 | * constant, or the name of a value list specified as a string. 57 | */ 58 | public function addSortRule($fieldname, $precedence, $order = null) 59 | { 60 | $this->sortFields[$precedence] = $fieldname; 61 | if ($order !== null) { 62 | $this->sortOrders[$precedence] = $order; 63 | } 64 | } 65 | 66 | /** 67 | * Clears all existing sorting rules from this Compound Find command. 68 | */ 69 | public function clearSortRules() 70 | { 71 | $this->sortFields = []; 72 | $this->sortOrders = []; 73 | } 74 | 75 | /** 76 | * 77 | * @return \airmoi\FileMaker\Object\Result|\airmoi\FileMaker\FileMakerException 78 | * @throws \airmoi\FileMaker\FileMakerException 79 | */ 80 | public function execute() 81 | { 82 | $query = null; 83 | $requestCount = 1; 84 | $totalCritCount = 1; 85 | 86 | $params = $this->getCommandParams(); 87 | $this->setSortParams($params); 88 | $this->setRangeParams($params); 89 | $this->setRelatedSetsFiltersParams($params); 90 | 91 | ksort($this->requests); 92 | $totalRequestCount = count($this->requests); 93 | 94 | foreach ($this->requests as $precedence => $request) { 95 | $findCriterias = $request->findCriteria; 96 | $critCount = count($findCriterias); 97 | 98 | //Handle first request omit flag 99 | if ($request->omit) { 100 | $query .= '!'; 101 | } 102 | 103 | $query .= '('; 104 | 105 | $i = 0; 106 | foreach ($findCriterias as $fieldname => $testvalue) { 107 | $params['-q' . $totalCritCount] = $fieldname; 108 | $params['-q' . $totalCritCount . '.' . "value"] = $testvalue; 109 | $query = $query . 'q' . $totalCritCount; 110 | $totalCritCount++; 111 | $i++; 112 | 113 | if ($i < $critCount) { 114 | $query .= ','; 115 | } 116 | } 117 | $query .= ')'; 118 | $requestCount++; 119 | if ($requestCount <= $totalRequestCount) { 120 | $query .= ';'; 121 | } 122 | } 123 | $params['-query'] = $query; 124 | $params['-findquery'] = true; 125 | $result = $this->fm->execute($params); 126 | return $this->getResult($result); 127 | } 128 | 129 | /** 130 | * Sets a range to request only part of the result set. 131 | * 132 | * @param integer $skip Number of records to skip past. Default is 0. 133 | * @param integer $max Maximum number of records to return. 134 | * Default is all. 135 | */ 136 | public function setRange($skip = 0, $max = null) 137 | { 138 | $this->skip = $skip; 139 | $this->max = $max; 140 | } 141 | 142 | /** 143 | * Returns the current range settings. 144 | * 145 | * @return array Associative array with two keys: 'skip' for 146 | * the current skip setting, and 'max' for the current maximum 147 | * number of records. If either key does not have a value, the 148 | * returned value for that key is NULL. 149 | */ 150 | public function getRange() 151 | { 152 | return [ 153 | 'skip' => $this->skip, 154 | 'max' => $this->max 155 | ]; 156 | } 157 | 158 | /** 159 | * Sets a filter to restrict the number of related records to return from 160 | * a portal. 161 | * 162 | * For more information, see the description for the 163 | * {@link Find::setRelatedSetsFilters()} method. 164 | * 165 | * @param string $relatedsetsfilter Specify either 'layout' or 'none' to 166 | * control filtering. 167 | * @param string $relatedsetsmax Maximum number of portal records 168 | * to return. 169 | */ 170 | public function setRelatedSetsFilters($relatedsetsfilter, $relatedsetsmax = null) 171 | { 172 | $this->relatedsetsfilter = $relatedsetsfilter; 173 | $this->relatedsetsmax = $relatedsetsmax; 174 | } 175 | 176 | /** 177 | * Returns the current settings for the related records filter and 178 | * the maximum number of related records to return. 179 | * 180 | * @return array Associative array with two keys: 'relatedsetsfilter' for 181 | * the portal filter setting, and 'relatedsetsmax' for the maximum 182 | * number of records. If either key does not have a value, the returned 183 | * for that key is NULL. 184 | */ 185 | public function getRelatedSetsFilters() 186 | { 187 | return [ 188 | 'relatedsetsfilter' => $this->relatedsetsfilter, 189 | 'relatedsetsmax' => $this->relatedsetsmax 190 | ]; 191 | } 192 | 193 | /** 194 | * Build relatedSets Filter params 195 | * @param $params 196 | */ 197 | public function setRelatedSetsFiltersParams(&$params) 198 | { 199 | if ($this->relatedsetsfilter) { 200 | $params['-relatedsets.filter'] = $this->relatedsetsfilter; 201 | } 202 | if ($this->relatedsetsmax) { 203 | $params['-relatedsets.max'] = $this->relatedsetsmax; 204 | } 205 | } 206 | 207 | /** 208 | * Build sort params 209 | * @param $params 210 | */ 211 | public function setSortParams(&$params) 212 | { 213 | foreach ($this->sortFields as $precedence => $fieldname) { 214 | $params['-sortfield.' . $precedence] = $fieldname; 215 | } 216 | foreach ($this->sortOrders as $precedence => $order) { 217 | $params['-sortorder.' . $precedence] = $order; 218 | } 219 | } 220 | 221 | /** 222 | * Build range params 223 | * @param $params 224 | */ 225 | public function setRangeParams(&$params) 226 | { 227 | if ($this->skip) { 228 | $params['-skip'] = $this->skip; 229 | } 230 | if ($this->max !== null) { 231 | $params['-max'] = $this->max; 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/Command/Delete.php: -------------------------------------------------------------------------------- 1 | recordId = $recordId; 33 | } 34 | 35 | /** 36 | * 37 | * @return \airmoi\FileMaker\Object\Result|FileMakerException 38 | * @throws FileMakerException 39 | */ 40 | public function execute() 41 | { 42 | if (empty($this->recordId)) { 43 | return $this->fm->returnOrThrowException('Delete commands require a record id.'); 44 | } 45 | $params = $this->getCommandParams(); 46 | $params['-delete'] = true; 47 | $params['-recid'] = $this->recordId; 48 | $result = $this->fm->execute($params); 49 | return $this->getResult($result); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Command/Duplicate.php: -------------------------------------------------------------------------------- 1 | recordId = $recordId; 33 | } 34 | 35 | /** 36 | * Return a Result object with the duplicated record 37 | * use Result->getFirstRecord() to get the record 38 | * 39 | * @return Result|FileMakerException 40 | * @throws FileMakerException 41 | */ 42 | public function execute() 43 | { 44 | if (empty($this->recordId)) { 45 | return $this->fm->returnOrThrowException('Duplicate commands require a record id.'); 46 | } 47 | $params = $this->getCommandParams(); 48 | $params['-dup'] = true; 49 | $params['-recid'] = $this->recordId; 50 | $result = $this->fm->execute($params); 51 | return $this->getResult($result); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Command/Edit.php: -------------------------------------------------------------------------------- 1 | value pairs. 35 | * To set field repetitions, use a numerically indexed array for 36 | * the value of a field, with the numeric keys corresponding to the 37 | * repetition number to set. 38 | * @param bool $useRawData Prevent data conversion on setField 39 | */ 40 | public function __construct(FileMaker $fm, $layout, $recordId, $updatedValues = [], $useRawData = false) 41 | { 42 | parent::__construct($fm, $layout); 43 | $this->recordId = $recordId; 44 | $this->deleteRelated = null; 45 | $this->useRawData = $useRawData; 46 | foreach ($updatedValues as $fieldname => $value) { 47 | if (!is_array($value)) { 48 | $this->setField($fieldname, $value, 0); 49 | } else { 50 | foreach ($value as $repetition => $repetitionValue) { 51 | $this->setField($fieldname, $repetitionValue, $repetition) ; 52 | } 53 | } 54 | } 55 | } 56 | 57 | /** 58 | * 59 | * @return \airmoi\FileMaker\Object\Result|FileMakerException|FileMakerValidationException 60 | * @throws FileMakerException 61 | * @throws FileMakerValidationException 62 | */ 63 | public function execute() 64 | { 65 | $params = $this->getCommandParams(); 66 | if (empty($this->recordId)) { 67 | return $this->fm->returnOrThrowException('Edit commands require a record id.'); 68 | } 69 | if (!count($this->fields)) { 70 | if ($this->deleteRelated === null) { 71 | return $this->fm->returnOrThrowException('There are no changes to make.'); 72 | } 73 | } 74 | 75 | if ($this->fm->getProperty('prevalidate')) { 76 | $validation = $this->validate(); 77 | if (FileMaker::isError($validation)) { 78 | return $validation; 79 | } 80 | } 81 | 82 | $layout = $this->fm->getLayout($this->layout); 83 | if (FileMaker::isError($layout)) { 84 | return $layout; 85 | } 86 | 87 | $params['-edit'] = true; 88 | if ($this->deleteRelated === null) { 89 | foreach ($this->fields as $fieldname => $values) { 90 | $suffix = ''; 91 | if (strpos($fieldname, '.') !== false) { 92 | list ($fieldname, $infos) = explode('.', $fieldname, 2); 93 | $suffix = '.' . $infos; 94 | } else { 95 | $field = $layout->getField($fieldname); 96 | if (FileMaker::isError($field)) { 97 | return $field; 98 | } 99 | if ($field->isGlobal()) { 100 | $suffix = '.global'; 101 | } 102 | } 103 | foreach ($values as $repetition => $value) { 104 | $params[$fieldname . '(' . ($repetition + 1) . ')' . $suffix] = $value; 105 | } 106 | } 107 | } 108 | if ($this->deleteRelated !== null) { 109 | $params['-delete.related'] = $this->deleteRelated; 110 | } 111 | $params['-recid'] = $this->recordId; 112 | if ($this->modificationId) { 113 | $params['-modid'] = $this->modificationId; 114 | } 115 | $result = $this->fm->execute($params); 116 | return $this->getResult($result); 117 | } 118 | 119 | /** 120 | * Sets the new value for a field. 121 | * 122 | * @param string $field Name of the field to set. 123 | * @param string $value Value for the field. 124 | * @param integer $repetition Field repetition number to set, 125 | * Defaults to the first repetition. 126 | * 127 | * @return string|FileMakerException 128 | * @throws FileMakerException 129 | */ 130 | public function setField($field, $value, $repetition = 0) 131 | { 132 | if(preg_match('/(.*)(\.\d+)$/', $field, $matches)){ 133 | $fieldname = $matches[1]; 134 | } else { 135 | $fieldname = $field; 136 | } 137 | 138 | $layout = $this->fm->getLayout($this->layout); 139 | if (FileMaker::isError($layout)) { 140 | return $layout; 141 | } 142 | $fieldInfos = $layout->getField($fieldname); 143 | if(FileMaker::isError($fieldInfos)){ 144 | return $fieldInfos; 145 | } 146 | 147 | $format = FileMaker::isError($fieldInfos) ? null : $fieldInfos->result; 148 | 149 | $dateFormat = $this->fm->getProperty('dateFormat'); 150 | if (!$this->useRawData && $dateFormat !== null && ($format === 'date' || $format === 'timestamp')) { 151 | try { 152 | if ($format === 'date') { 153 | $value = DateFormat::convert($value, $dateFormat, 'm/d/Y'); 154 | } else { 155 | $value = DateFormat::convert($value, $dateFormat . ' H:i:s', 'm/d/Y H:i:s'); 156 | } 157 | } catch (\Exception $e) { 158 | return $this->fm->returnOrThrowException( 159 | $value . ' could not be converted to a valid timestamp for field ' 160 | . $field . ' (expected format '. $dateFormat .')' 161 | ); 162 | } 163 | } 164 | 165 | $this->fields[$field][$repetition] = $value; 166 | return $value; 167 | } 168 | 169 | /** 170 | * Sets the new value for a date, time, or timestamp field from a 171 | * UNIX timestamp value. 172 | * 173 | * If the field is not a date or time field, then this method returns 174 | * an Error object. Otherwise, returns TRUE. 175 | * 176 | * If layout data for the target of this command has not already 177 | * been loaded, calling this method loads layout data so that 178 | * the type of the field can be checked. 179 | * 180 | * @param string $field Name of the field to set. 181 | * @param string $timestamp Timestamp value. 182 | * @param integer $repetition Field repetition number to set. 183 | * Defaults to the first repetition. 184 | * 185 | * @return string|FileMakerException 186 | * @throws FileMakerException 187 | */ 188 | public function setFieldFromTimestamp($field, $timestamp, $repetition = 0) 189 | { 190 | $layout = $this->fm->getLayout($this->layout); 191 | if (FileMaker::isError($layout)) { 192 | return $layout; 193 | } 194 | $fieldInfos = $layout->getField($field); 195 | if (FileMaker::isError($fieldInfos)) { 196 | return $fieldInfos; 197 | } 198 | switch ($fieldInfos->getResult()) { 199 | case 'date': 200 | return $this->setField($field, date('m/d/Y', $timestamp), $repetition); 201 | case 'time': 202 | return $this->setField($field, date('H:i:s', $timestamp), $repetition); 203 | case 'timestamp': 204 | return $this->setField($field, date('m/d/Y H:i:s', $timestamp), $repetition); 205 | } 206 | return $this->fm->returnOrThrowException( 207 | 'Only time, date, and timestamp fields can be set to the value of a timestamp.' 208 | ); 209 | } 210 | 211 | /** 212 | * Sets the modification ID for this command. 213 | * 214 | * Before you edit a record, you can use the 215 | * {@link Record::getModificationId()} method to get the record's 216 | * modification ID. By specifying a modification ID when you execute an 217 | * Edit command, you can make sure that you are editing the current version 218 | * of a record. If the modification ID value you specify does not match the 219 | * current modification ID value in the database, the Edit command is not 220 | * allowed and an error code is returned. 221 | * 222 | * @param integer $modificationId Modification ID. 223 | */ 224 | public function setModificationId($modificationId) 225 | { 226 | $this->modificationId = $modificationId; 227 | } 228 | 229 | /** 230 | * Set the related record ID to delete 231 | * @param int $relatedRecordId 232 | */ 233 | public function setDeleteRelated($relatedRecordId) 234 | { 235 | $this->deleteRelated = $relatedRecordId; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/Command/Find.php: -------------------------------------------------------------------------------- 1 | sortRules[$precedence] = $fieldName; 46 | if ($order !== null) { 47 | $this->sortOrders[$precedence] = $order; 48 | } 49 | return $this; 50 | } 51 | 52 | /** 53 | * Clears all existing sorting rules from this Find command. 54 | * 55 | * @return self 56 | */ 57 | public function clearSortRules() 58 | { 59 | $this->sortRules = []; 60 | $this->sortOrders = []; 61 | return $this; 62 | } 63 | 64 | /** 65 | * Execute the command 66 | * @return \airmoi\FileMaker\FileMakerException|\airmoi\FileMaker\Object\Result|string 67 | * @throws \airmoi\FileMaker\FileMakerException 68 | */ 69 | public function execute() 70 | { 71 | $params = $this->getCommandParams(); 72 | $this->setSortParams($params); 73 | $this->setRangeParams($params); 74 | $this->setRelatedSetsFiltersParams($params); 75 | if (count($this->findCriteria) || $this->recordId) { 76 | $params['-find'] = true; 77 | } else { 78 | $params['-findall'] = true; 79 | } 80 | if ($this->recordId) { 81 | $params['-recid'] = $this->recordId; 82 | } 83 | if ($this->operator) { 84 | $params['-lop'] = $this->operator; 85 | } 86 | foreach ($this->findCriteria as $field => $value) { 87 | $params[$field] = $value; 88 | } 89 | $result = $this->fm->execute($params); 90 | if (FileMaker::isError($result)) { 91 | return $result; 92 | } 93 | return $this->getResult($result); 94 | } 95 | 96 | /** 97 | * Specifies how the find criteria in this Find command are combined 98 | * as either a logical AND or OR search. 99 | * 100 | * If not specified, the default is a logical AND. 101 | * 102 | * @param integer $operator Specify the FileMaker::FIND_AND or 103 | * FileMaker::FIND_OR constant. 104 | * 105 | * @return self 106 | */ 107 | public function setLogicalOperator($operator) 108 | { 109 | switch ($operator) { 110 | case FileMaker::FIND_AND: 111 | case FileMaker::FIND_OR: 112 | $this->operator = $operator; 113 | break; 114 | } 115 | return $this; 116 | } 117 | 118 | /** 119 | * Sets a range to request only part of the result set. 120 | * 121 | * @param integer $skip Number of records to skip past. Default is 0. 122 | * @param integer $max Maximum number of records to return. 123 | * Default is all. 124 | * 125 | * @return self 126 | */ 127 | public function setRange($skip = 0, $max = null) 128 | { 129 | $this->skip = $skip; 130 | $this->max = $max; 131 | return $this; 132 | } 133 | 134 | /** 135 | * Returns the current range settings. 136 | * 137 | * @return array Associative array with two keys: 'skip' for 138 | * the current skip setting, and 'max' for the current maximum 139 | * number of records. If either key does not have a value, the 140 | * returned value for that key is NULL. 141 | */ 142 | public function getRange() 143 | { 144 | return [ 145 | 'skip' => $this->skip, 146 | 'max' => $this->max 147 | ]; 148 | } 149 | 150 | /** 151 | * Sets a filter to restrict the number of related records to return from 152 | * a portal. 153 | * 154 | * The filter limits the number of related records returned by respecting 155 | * the settings specified in the FileMaker Pro Portal Setup dialog box. 156 | * 157 | * @param string $relatedsetsfilter Specify one of these values to 158 | * control filtering: 159 | * - 'layout': Apply the settings specified in the FileMaker Pro 160 | * Portal Setup dialog box. The records are sorted based 161 | * on the sort defined in the Portal Setup dialog box, 162 | * with the record set filtered to start with the 163 | * specified "Initial row." 164 | * - 'none': Return all related records in the portal without 165 | * filtering or presorting them. 166 | * 167 | * @param string $relatedsetsmax If the "Show vertical scroll bar" setting 168 | * is enabled in the Portal Setup dialog box, specify one of these 169 | * values: 170 | * - an integer value: Return this maximum number of related records 171 | * after the initial record. 172 | * - 'all': Return all of the related records in the portal. 173 | * If "Show vertical scroll bar" is disabled, the Portal 174 | * Setup dialog box's "Number of rows" setting determines 175 | * the maximum number of related records to return. 176 | * 177 | * @return self 178 | */ 179 | public function setRelatedSetsFilters($relatedsetsfilter, $relatedsetsmax = null) 180 | { 181 | $this->relatedSetsFilter = $relatedsetsfilter; 182 | $this->relatedSetsMax = $relatedsetsmax; 183 | return $this; 184 | } 185 | 186 | /** 187 | * Returns the current settings for the related records filter and 188 | * the maximum number of related records to return. 189 | * 190 | * @return array Associative array with two keys: 'relatedsetsfilter' for 191 | * the portal filter setting, and 'relatedsetsmax' for the maximum 192 | * number of records. If either key does not have a value, the returned 193 | * for that key is NULL. 194 | */ 195 | public function getRelatedSetsFilters() 196 | { 197 | return [ 198 | 'relatedsetsfilter' => $this->relatedSetsFilter, 199 | 'relatedsetsmax' => $this->relatedSetsMax 200 | ]; 201 | } 202 | 203 | /** 204 | * Set relatedSet Filters params 205 | * @param array $params 206 | */ 207 | protected function setRelatedSetsFiltersParams(&$params) 208 | { 209 | if ($this->relatedSetsFilter) { 210 | $params['-relatedsets.filter'] = $this->relatedSetsFilter; 211 | } 212 | if ($this->relatedSetsMax) { 213 | $params['-relatedsets.max'] = $this->relatedSetsMax; 214 | } 215 | } 216 | 217 | /** 218 | * Set sort Params 219 | * @param array $params 220 | */ 221 | protected function setSortParams(&$params) 222 | { 223 | foreach ($this->sortRules as $precedence => $fieldname) { 224 | $params['-sortfield.' . $precedence] = $fieldname; 225 | } 226 | foreach ($this->sortOrders as $precedence => $order) { 227 | $params['-sortorder.' . $precedence] = $order; 228 | } 229 | } 230 | 231 | /** 232 | * Set Range params 233 | * @param array $params 234 | */ 235 | protected function setRangeParams(&$params) 236 | { 237 | if ($this->skip) { 238 | $params['-skip'] = $this->skip; 239 | } 240 | if ($this->max !== null) { 241 | $params['-max'] = $this->max; 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/Command/FindAll.php: -------------------------------------------------------------------------------- 1 | getCommandParams(); 26 | $params['-findall'] = true; 27 | $this->setSortParams($params); 28 | $this->setRangeParams($params); 29 | return $this->getResult($this->fm->execute($params)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Command/FindAny.php: -------------------------------------------------------------------------------- 1 | getCommandParams(); 25 | $params['-findany'] = true; 26 | return $this->getResult($this->fm->execute($params)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Command/FindRequest.php: -------------------------------------------------------------------------------- 1 | fm = $fm; 32 | $this->layout = $layout; 33 | } 34 | 35 | /** 36 | * Sets whether this request is an omit request. 37 | * 38 | * An omit request removes the matching records from the final result set. 39 | * 40 | * @param boolean $value TRUE if this is an omit request. Otherwise, FALSE. 41 | * 42 | * @return self 43 | */ 44 | public function setOmit($value) 45 | { 46 | $this->omit = $value; 47 | return $this; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Command/PerformScript.php: -------------------------------------------------------------------------------- 1 | script = $scriptName; 38 | $this->scriptParams = $scriptParameters; 39 | } 40 | 41 | /** 42 | * Sets a range to request only part of the result set. 43 | * 44 | * @param integer $skip Number of records to skip past. Default is 0. 45 | * @param integer $max Maximum number of records to return. 46 | * Default is all. 47 | * 48 | * @return self 49 | */ 50 | public function setRange($skip = 0, $max = null) 51 | { 52 | $this->skip = $skip; 53 | $this->max = $max; 54 | return $this; 55 | } 56 | 57 | /** 58 | * Returns the current range settings. 59 | * 60 | * @return array Associative array with two keys: 'skip' for 61 | * the current skip setting, and 'max' for the current maximum 62 | * number of records. If either key does not have a value, the 63 | * returned value for that key is NULL. 64 | */ 65 | public function getRange() 66 | { 67 | return [ 68 | 'skip' => $this->skip, 69 | 'max' => $this->max 70 | ]; 71 | } 72 | 73 | /** 74 | * 75 | * @return Result 76 | */ 77 | public function execute() 78 | { 79 | $params = $this->getCommandParams(); 80 | $params['-findany'] = true; 81 | $this->setRangeParams($params); 82 | return $this->getResult($this->fm->execute($params)); 83 | } 84 | 85 | /** 86 | * Set Range params 87 | * @param array $params 88 | */ 89 | protected function setRangeParams(&$params) 90 | { 91 | if ($this->skip) { 92 | $params['-skip'] = $this->skip; 93 | } 94 | if ($this->max) { 95 | $params['-max'] = $this->max; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Command/RequestTrait.php: -------------------------------------------------------------------------------- 1 | getFieldResult($fieldName); 26 | if ($this->fm->useDateFormatInRequests 27 | && $this->fm->dateFormat !== null 28 | && ($fieldType == "date" || $fieldType == "datetime") 29 | ) { 30 | $value = DateFormat::convertSearchCriteria($value, $this->fm->dateFormat, 'm/d/Y'); 31 | } 32 | $this->findCriteria[$fieldName] = $value; 33 | return $this; 34 | } 35 | 36 | /** 37 | * Clears all existing criteria from this find request. 38 | * 39 | * @return self 40 | */ 41 | public function clearFindCriteria() 42 | { 43 | $this->findCriteria = []; 44 | return $this; 45 | } 46 | 47 | /** 48 | * 49 | * @return bool true if the request as no criterion set 50 | */ 51 | public function isEmpty() 52 | { 53 | return empty($this->findCriteria); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Error/en.php: -------------------------------------------------------------------------------- 1 | 'Unknown error', 18 | 0 => 'No error', 19 | 1 => 'User canceled action', 20 | 2 => 'Memory error', 21 | 3 => 'Command is unavailable (for example, wrong operating system, wrong mode, etc.)', 22 | 4 => 'Command is unknown', 23 | 5 => 'Command is invalid (for example, a Set Field script step does not have a calculation specified)', 24 | 6 => 'File is read-only', 25 | 7 => 'Running out of memory', 26 | 8 => 'Empty result', 27 | 9 => 'Insufficient privileges', 28 | 10 => 'Requested data is missing', 29 | 11 => 'Name is not valid', 30 | 12 => 'Name already exists', 31 | 13 => 'File or object is in use', 32 | 14 => 'Out of range', 33 | 15 => 'Can\'t divide by zero', 34 | 16 => 'Operation failed, request retry (for example, a user query) ', 35 | 17 => 'Attempt to convert foreign character set to UTF-16 failed', 36 | 18 => 'Client must provide account information to proceed', 37 | 19 => 'String contains characters other than A-Z, a-z, 0-9 (ASCII)', 38 | 20 => 'Command or operation cancelled by triggered script', 39 | 100 => 'File is missing', 40 | 101 => 'Record is missing', 41 | 102 => 'Field is missing', 42 | 103 => 'Relationship is missing', 43 | 104 => 'Script is missing', 44 | 105 => 'Layout is missing', 45 | 106 => 'Table is missing', 46 | 107 => 'Index is missing', 47 | 108 => 'Value list is missing', 48 | 109 => 'Privilege set is missing', 49 | 110 => 'Related tables are missing', 50 | 111 => 'Field repetition is invalid', 51 | 112 => 'Window is missing', 52 | 113 => 'Function is missing', 53 | 114 => 'File reference is missing', 54 | 130 => 'Files are damaged or missing and must be reinstalled', 55 | 131 => 'Language pack files are missing (such as template files)', 56 | 200 => 'Record access is denied', 57 | 201 => 'Field cannot be modified', 58 | 202 => 'Field access is denied', 59 | 203 => 'No records in file to print, or password doesn\'t allow print access', 60 | 204 => 'No access to field(s) in sort order', 61 | 205 => 'User does not have access privileges to create new records; import will overwrite existing data', 62 | 206 => 'User does not have password change privileges, or file is not modifiable', 63 | 207 => 'User does not have sufficient privileges to change database schema, or file is not modifiable', 64 | 208 => 'Password does not contain enough characters', 65 | 209 => 'New password must be different from existing one', 66 | 210 => 'User account is inactive', 67 | 211 => 'Password has expired ', 68 | 212 => 'Invalid user account and/or password. Please try again', 69 | 213 => 'User account and/or password does not exist', 70 | 214 => 'Too many login attempts', 71 | 215 => 'Administrator privileges cannot be duplicated', 72 | 216 => 'Guest account cannot be duplicated', 73 | 217 => 'User does not have sufficient privileges to modify administrator account', 74 | 300 => 'File is locked or in use', 75 | 301 => 'Record is in use by another user', 76 | 302 => 'Table is in use by another user', 77 | 303 => 'Database schema is in use by another user', 78 | 304 => 'Layout is in use by another user', 79 | 306 => 'Record modification ID does not match', 80 | 400 => 'Find criteria are empty', 81 | 401 => 'No records match the request', 82 | 402 => 'Selected field is not a match field for a lookup', 83 | 403 => 'Exceeding maximum record limit for trial version of FileMaker Pro', 84 | 404 => 'Sort order is invalid', 85 | 405 => 'Number of records specified exceeds number of records that can be omitted', 86 | 406 => 'Replace/Reserialize criteria are invalid', 87 | 407 => 'One or both match fields are missing (invalid relationship)', 88 | 408 => 'Specified field has inappropriate data type for this operation', 89 | 409 => 'Import order is invalid ', 90 | 410 => 'Export order is invalid', 91 | 412 => 'Wrong version of FileMaker Pro used to recover file', 92 | 413 => 'Specified field has inappropriate field type', 93 | 414 => 'Layout cannot display the result', 94 | 415 => 'One or more required related records are not available', 95 | 500 => 'Date value does not meet validation entry options', 96 | 501 => 'Time value does not meet validation entry options', 97 | 502 => 'Number value does not meet validation entry options', 98 | 503 => 'Value in field is not within the range specified in validation entry options', 99 | 504 => 'Value in field is not unique as required in validation entry options ', 100 | 505 => 'Value in field is not an existing value in the database file as required in validation entry options', 101 | 506 => 'Value in field is not listed on the value list specified in validation entry option', 102 | 507 => 'Value in field failed calculation test of validation entry option', 103 | 508 => 'Invalid value entered in Find mode', 104 | 509 => 'Field requires a valid value ', 105 | 510 => 'Related value is empty or unavailable ', 106 | 511 => 'Value in field exceeds maximum number of allowed characters', 107 | 600 => 'Print error has occurred', 108 | 601 => 'Combined header and footer exceed one page ', 109 | 602 => 'Body doesn\'t fit on a page for current column setup', 110 | 603 => 'Print connection lost', 111 | 700 => 'File is of the wrong file type for import', 112 | 706 => 'EPSF file has no preview image ', 113 | 707 => 'Graphic translator cannot be found ', 114 | 708 => 'Can\'t import the file or need color monitor support to import file', 115 | 709 => 'QuickTime movie import failed ', 116 | 710 => 'Unable to update QuickTime file reference because the database file is read-only', 117 | 711 => 'Import translator cannot be found ', 118 | 714 => 'Password privileges do not allow the operation', 119 | 715 => 'Specified Excel worksheet or named range is missing', 120 | 716 => 'A SQL query using DELETE, INSERT, or UPDATE is not allowed for ODBC import', 121 | 717 => 'There is not enough XML/XSL information to proceed with the import or export', 122 | 718 => 'Error in parsing XML file (from Xerces)', 123 | 719 => 'Error in transforming XML using XSL (from Xalan)', 124 | 720 => 'Error when exporting; intended format does not support repeating fields', 125 | 721 => 'Unknown error occurred in the parser or the transformer', 126 | 722 => 'Cannot import data into a file that has no fields', 127 | 723 => 'You do not have permission to add records to or modify records in the target table', 128 | 724 => 'You do not have permission to add records to the target table', 129 | 725 => 'You do not have permission to modify records in the target table', 130 | 726 => 'There are more records in the import file than in the target table. Not all records were imported', 131 | 727 => 'There are more records in the target table than in the import file. Not all records were updated', 132 | 729 => 'Errors occurred during import. Records could not be imported', 133 | 730 => 'Unsupported Excel version. Convert file to Excel 7.0 (Excel 95), 97, 2000, XP, or 2007 format and try again.', 134 | 731 => 'The file you are importing from contains no data', 135 | 732 => 'This file cannot be inserted because it contains other files', 136 | 733 => 'A table cannot be imported into itself', 137 | 734 => 'This file type cannot be displayed as a picture', 138 | 735 => 'This file type cannot be displayed as a picture. It will be inserted and displayed as a file', 139 | 800 => 'Unable to create file on disk', 140 | 801 => 'Unable to create temporary file on System disk', 141 | 802 => 'Unable to open file', 142 | 803 => 'File is single user or host cannot be found', 143 | 804 => 'File cannot be opened as read-only in its current state', 144 | 805 => 'File is damaged; use Recover command', 145 | 806 => 'File cannot be opened with this version of FileMaker Pro', 146 | 807 => 'File is not a FileMaker Pro file or is severely damaged', 147 | 808 => 'Cannot open file because access privileges are damaged', 148 | 809 => 'Disk/volume is full', 149 | 810 => 'Disk/volume is locked', 150 | 811 => 'Temporary file cannot be opened as FileMaker Pro file', 151 | 813 => 'Record Synchronization error on network', 152 | 814 => 'File(s) cannot be opened because maximum number is open', 153 | 815 => 'Couldn\'t open lookup file ', 154 | 816 => 'Unable to convert file', 155 | 817 => 'Unable to open file because it does not belong to this solution', 156 | 819 => 'Cannot save a local copy of a remote file', 157 | 820 => 'File is in the process of being closed', 158 | 821 => 'Host forced a disconnect', 159 | 822 => 'FMI files not found; reinstall missing files', 160 | 823 => 'Cannot set file to single-user, guests are connected', 161 | 824 => 'File is damaged or not a FileMaker file', 162 | 900 => 'General spelling engine error', 163 | 901 => 'Main spelling dictionary not installed', 164 | 902 => 'Could not launch the Help system ', 165 | 903 => 'Command cannot be used in a shared file ', 166 | 904 => 'Command can only be used in a file hosted under FileMaker Server', 167 | 905 => 'No active field selected; command can only be used if there is an active field', 168 | 920 => 'Can\'t initialize the spelling engine', 169 | 921 => 'User dictionary cannot be loaded for editing', 170 | 922 => 'User dictionary cannot be found', 171 | 923 => 'User dictionary is read-only', 172 | 951 => 'An unexpected error occurred', 173 | 954 => 'Unsupported XML grammar', 174 | 955 => 'No database name', 175 | 956 => 'Maximum number of database sessions exceeded', 176 | 957 => 'Conflicting commands', 177 | 958 => 'Parameter missing in query', 178 | 1200 => 'Generic calculation error', 179 | 1201 => 'Too few parameters in the function', 180 | 1202 => 'Too many parameters in the function', 181 | 1203 => 'Unexpected end of calculation', 182 | 1204 => 'Number, text constant, field name or "(" expected', 183 | 1205 => 'Comment is not terminated with "*/"', 184 | 1206 => 'Text constant must end with a quotation mark', 185 | 1207 => 'Unbalanced parenthesis', 186 | 1208 => 'Operator missing, function not found or "(" not expected', 187 | 1209 => 'Name (such as field name or layout name) is missing', 188 | 1210 => 'Plug-in function has already been registered', 189 | 1211 => 'List usage is not allowed in this function', 190 | 1212 => 'An operator (for example, +, -, *) is expected here', 191 | 1213 => 'This variable has already been defined in the Let function', 192 | 1214 => 'AVERAGE, COUNT, EXTEND, GETREPETITION, MAX, MIN, NPV, STDEV, SUM and GETSUMMARY: expression found where a field alone is needed', 193 | 1215 => 'This parameter is an invalid Get function parameter', 194 | 1216 => 'Only Summary fields allowed as first argument in GETSUMMARY', 195 | 1217 => 'Break field is invalid ', 196 | 1218 => 'Cannot evaluate the number', 197 | 1219 => 'A field cannot be used in its own formula', 198 | 1220 => 'Field type must be normal or calculated ', 199 | 1221 => 'Data type must be number, date, time, or timestamp ', 200 | 1222 => 'Calculation cannot be stored', 201 | 1223 => 'The function referred to does not exist', 202 | 1400 => 'ODBC client driver initialization failed; make sure the ODBC client drivers are properly installed. Note: The plug-in component for sharing data via ODBC is installed automatically with FileMaker Server; the ODBC client drivers are installed using the FileMaker Server Web Publishing CD. For information, see Installing FileMaker ODBC and JDBC Client Drivers.', 203 | 1401 => 'Failed to allocate environment (ODBC)', 204 | 1402 => 'Failed to free environment (ODBC)', 205 | 1403 => 'Failed to disconnect (ODBC)', 206 | 1404 => 'Failed to allocate connection (ODBC)', 207 | 1405 => 'Failed to free connection (ODBC)', 208 | 1406 => 'Failed check for SQL API (ODBC)', 209 | 1407 => 'Failed to allocate statement (ODBC)', 210 | 1408 => 'Extended error (ODBC)', 211 | 1450 => 'Action requires PHP privilege extension', 212 | 1451 => 'Action requires that current file be remote', 213 | 1501 => 'SMTP authentication failed', 214 | 1502 => 'Connection refused by SMTP server', 215 | 1503 => 'Error with SSL', 216 | 1504 => 'SMTP server requires the connection to be encrypted', 217 | 1505 => 'Specified authentication is not supported by SMTP server', 218 | 1506 => 'Email message(s) could not be sent successfully', 219 | 1507 => 'Unable to log in to the SMTP server', 220 | 8003 => 'Record is in use by another user', 221 | ); 222 | -------------------------------------------------------------------------------- /src/Error/ja.php: -------------------------------------------------------------------------------- 1 | '原因不明のエラー', 18 | 0 => 'エラーなし', 19 | 1 => 'ユーザによるキャンセル', 20 | 2 => 'メモリエラー', 21 | 3 => 'コマンドが使用できません(たとえば誤ったオペレーティングシステム、誤ったモードなど)', 22 | 4 => 'コマンドが見つかりません', 23 | 5 => 'コマンドが無効です(たとえば、[フィールド設定] スクリプトステップに計算式が指定されていない場合など)', 24 | 6 => 'ファイルが読み取り専用です', 25 | 7 => 'メモリ不足', 26 | 8 => '空白の結果', 27 | 9 => 'アクセス権が不十分です', 28 | 10 => '要求されたデータが見つかりません', 29 | 11 => '名前が有効ではありません', 30 | 12 => '名前がすでに存在します', 31 | 13 => 'ファイルまたはオブジェクトが使用中です', 32 | 14 => '範囲外', 33 | 15 => '0で割ることができません', 34 | 16 => '処理に失敗したため、再試行が必要です(たとえば、ユーザクエリーなど)', 35 | 17 => '外国語の文字セットの UTF-16 への変換に失敗しました', 36 | 18 => '続行するには、クライアントはアカウント情報を指定する必要があります', 37 | 19 => '文字列に A から Z、a から z、0 から 9(ASCII)以外の文字が含まれています', 38 | 20 => 'コマンドまたは操作がスクリプトトリガによってキャンセルされました', 39 | 100 => 'ファイルが見つかりません', 40 | 101 => 'レコードが見つかりません', 41 | 102 => 'フィールドが見つかりません', 42 | 103 => 'リレーションシップが見つかりません', 43 | 104 => 'スクリプトが見つかりません', 44 | 105 => 'レイアウトが見つかりません', 45 | 106 => 'テーブルが見つかりません', 46 | 107 => '索引が見つかりません', 47 | 108 => '値一覧が見つかりません', 48 | 109 => 'アクセス権セットが見つかりません', 49 | 110 => '関連テーブルが見つかりません', 50 | 111 => 'フィールドの繰り返しが無効です', 51 | 112 => 'ウインドウが見つかりません', 52 | 113 => '関数が見つかりません', 53 | 114 => 'ファイル参照が見つかりません', 54 | 130 => 'ファイルが損傷しているか見つからないため、再インストールする必要があります', 55 | 131 => '言語パックファイルが見つかりません(テンプレートなど)', 56 | 200 => 'レコードアクセスが拒否されました', 57 | 201 => 'フィールドを変更できません', 58 | 202 => 'フィールドアクセスが拒否されました', 59 | 203 => 'ファイルに印刷するレコードがないか、入力したパスワードでは印刷できません', 60 | 204 => 'ソート優先順位に指定されたフィールドにアクセスできません', 61 | 205 => 'ユーザに新規レコードを作成するアクセス権がありません。既存のデータはインポートしたデータで上書きされます', 62 | 206 => 'ユーザにパスワードの変更アクセス権がないか、変更可能なファイルではありません', 63 | 207 => 'ユーザにデータベーススキーマを変更する十分なアクセス権がないか、変更可能なファイルではありません', 64 | 208 => 'パスワードに十分な文字が含まれていません', 65 | 209 => '既存のパスワードと新規パスワードを同一にすることはできません', 66 | 210 => 'ユーザアカウントが非アクティブです', 67 | 211 => 'パスワードが期限切れです', 68 | 212 => 'ユーザアカウントまたはパスワードが無効です。再試行してください', 69 | 213 => 'ユーザアカウントまたはパスワードが存在しません', 70 | 214 => 'ログイン試行回数が多すぎます', 71 | 215 => '管理者権限は複製できません', 72 | 216 => 'ゲストアカウントは複製できません', 73 | 217 => 'ユーザに管理者アカウントを変更する十分なアクセス権がありません', 74 | 300 => 'ファイルがロックされているか、使用中です', 75 | 301 => '別のユーザがレコードを使用中です', 76 | 302 => '別のユーザがテーブルを使用中です', 77 | 303 => '別のユーザがデータベーススキーマを使用中です', 78 | 304 => '別のユーザがレイアウトを使用中です', 79 | 306 => 'レコード修正 ID が一致しません', 80 | 400 => '検索条件が空です', 81 | 401 => '検索条件に一致するレコードがありません', 82 | 402 => '選択したフィールドはルックアップの照合フィールドではありません', 83 | 403 => '評価版の FileMaker Pro に設定されている最大レコード数の制限を超過しています', 84 | 404 => 'ソート優先順位が無効です', 85 | 405 => '指定したレコード数が除外可能なレコード数を超過しています', 86 | 406 => '全置換またはシリアル番号の再入力に指定された条件が無効です', 87 | 407 => '片方または両方の照合フィールドが欠けています(無効なリレーションシップ)', 88 | 408 => '指定されたフィールドのデータが不適切なため、この処理を実行できません', 89 | 409 => 'インポート順が無効です', 90 | 410 => 'エクスポート順が無効です', 91 | 412 => 'ファイルの修復に、誤ったバージョンの FileMaker Pro が使用されました', 92 | 413 => '指定されたフィールドのフィールドタイプが不適切です', 93 | 414 => 'レイアウトに結果を表示できません', 94 | 415 => '必要な1つまたは複数の関連テーブルが使用できません', 95 | 500 => '日付の値が入力値の制限を満たしていません', 96 | 501 => '時刻の値が入力値の制限を満たしていません', 97 | 502 => '数字の値が入力値の制限を満たしていません', 98 | 503 => 'フィールドの値が入力値の制限オプションに指定されている範囲内に入っていません', 99 | 504 => 'フィールドの値が入力値の制限オプションで要求されているようにユニークな値になっていません', 100 | 505 => 'フィールドの値が入力値の制限オプションで要求されているようにデータベースファイル内の既存値になっていません', 101 | 506 => 'フィールドの値が入力値の制限オプションに指定されている値一覧に含まれていません', 102 | 507 => 'フィールドの値が入力値の制限オプションに指定されている計算式を満たしません', 103 | 508 => '検索モードに無効な値が入力されました', 104 | 509 => 'フィールドに有効な値が必要です', 105 | 510 => '関連する値が空であるか、使用できません', 106 | 511 => 'フィールド内の値が最大文字数を超過しました', 107 | 600 => '印刷エラーが発生しました', 108 | 601 => 'ヘッダとフッタの高さを加算するとページの高さを超えます', 109 | 602 => '現在の段数設定ではボディ部分がページ内に収まりません', 110 | 603 => '印刷接続が遮断されました', 111 | 700 => 'インポートできないファイルタイプです', 112 | 706 => 'EPSF ファイルにプレビューイメージがありません', 113 | 707 => 'グラフィックの変換ファイルが見つかりません', 114 | 708 => 'ファイルをインポートできないか、ファイルをインポートするにはカラーのコンピュータが必要です', 115 | 709 => 'QuickTime ムービーのインポートに失敗しました', 116 | 710 => 'データベースファイルが読み取り専用になっているため QuickTime ファイルの参照を更新できません', 117 | 711 => 'インポートの変換ファイルが見つかりません', 118 | 714 => '入力したパスワードでは設定されている権限が不足しているためこの操作は認められていません', 119 | 715 => '指定された Excel ワークシートまたは名前の付いた範囲がありません', 120 | 716 => 'ODBC インポートでは、DELETE、INSERT、または UPDATE を使用する SQL クエリーは使用できません', 121 | 717 => 'インポートまたはエクスポートを続行するための十分な XML/XSLT 情報がありません', 122 | 718 => '(Xerces からの)XML ファイルの解析エラーです', 123 | 719 => '(Xalan からの)XSL を使用したXML 変換エラーです', 124 | 720 => 'エクスポート時のエラー。対象のドキュメントフォーマットでは繰り返しフィールドはサポートされていません', 125 | 721 => 'パーサまたはトランスフォーマで原因不明のエラーが発生しました', 126 | 722 => 'フィールドのないファイルにデータをインポートすることはできません', 127 | 723 => 'インポート先のテーブルでレコードを追加または変更する権限がありません', 128 | 724 => 'インポート先のテーブルにレコードを追加する権限がありません', 129 | 725 => 'インポート先のテーブルでレコードを変更する権限がありません', 130 | 726 => 'インポートファイルのレコードの方がインポート先のテーブルのレコードよりも多くなっています。一部のレコードはインポートされません', 131 | 727 => 'インポート先のテーブルのレコードの方がインポートファイルのレコードよりも多くなっています。一部のレコードは更新されません', 132 | 729 => 'インポート中にエラーが発生しました。レコードをインポートすることができません', 133 | 730 => 'サポートされていない Excel バージョンです。ファイルを Excel 7.0 (Excel 95)、97、2000、XP または 2007 に変換してから再試行してください', 134 | 731 => 'インポート元のファイルにデータが含まれていません', 135 | 732 => 'このファイルには内部に他のファイルが含まれているため、挿入できません', 136 | 733 => 'テーブルをテーブル自体にインポートすることはできません', 137 | 734 => 'このファイルタイプをピクチャとして表示することはできません', 138 | 735 => 'このファイルタイプをピクチャとして表示することはできません。ファイルとして挿入および表示されます', 139 | 800 => 'ファイルをディスク上に作成できません', 140 | 801 => 'システムディスクにテンポラリファイルを作成できません', 141 | 802 => 'ファイルを開くことができません', 142 | 803 => 'ファイルが単独使用に設定されているか、またはホストが見つかりません', 143 | 804 => 'ファイルは現在の状態では読み取り専用として開くことができません', 144 | 805 => 'ファイルが損傷しています。修復コマンドを使用してください', 145 | 806 => 'このバージョンの FileMaker Pro ではファイルを開くことができません', 146 | 807 => 'ファイルが FileMaker Pro のファイルではないか、重大な損傷があります', 147 | 808 => 'アクセス権情報が壊れているため、ファイルを開くことができません', 148 | 809 => 'ディスク/ボリュームがいっぱいです', 149 | 810 => 'ディスク/ボリュームがロックされています', 150 | 811 => 'テンポラリファイルを FileMaker Pro ファイルとして開くことができません', 151 | 813 => 'ネットワーク上でレコードの同期エラーが発生しました', 152 | 814 => '最大数のファイルがすでに開いているため、ファイルを開くことができません', 153 | 815 => 'ルックアップファイルを開くことができません', 154 | 816 => 'ファイルを変換できません', 155 | 817 => 'このソリューションに属していないため、ファイルを開くことができません', 156 | 819 => 'リモートファイルのローカルコピーを保存できません', 157 | 820 => 'ファイルを閉じる途中です', 158 | 821 => 'ホストによって接続解除されました', 159 | 822 => 'FMI ファイルが見つかりません。見つからないファイルを再インストールしてください', 160 | 823 => 'ファイルをシングルユーザに設定できません。ゲストが接続しています', 161 | 824 => 'ファイルが損傷しているか、FileMaker のファイルではありません', 162 | 900 => 'スペルチェックのエンジンにエラーが発生しています', 163 | 901 => 'スペルチェック用のメイン辞書がインストールされていません', 164 | 902 => 'ヘルプシステムを起動できませんでした', 165 | 903 => '共有ファイルではコマンドを使用できません', 166 | 904 => 'コマンドは、FileMaker Server がホスト管理しているファイル内でのみ使用できます', 167 | 905 => 'アクティブなフィールドが選択されていません。アクティブなフィールドが存在する場合のみコマンドを使用することができます', 168 | 920 => 'スペルチェックエンジンを初期化できません', 169 | 921 => '編集するユーザ辞書をロードできません', 170 | 922 => 'ユーザ辞書が見つかりません', 171 | 923 => 'ユーザ辞書が読み取り専用です', 172 | 951 => '予期しないエラーが発生しました', 173 | 954 => 'サポートされていない XML 文法です', 174 | 955 => 'データベース名がありません', 175 | 956 => 'データベースセッションが最大数を超過しました', 176 | 957 => 'コマンドが競合しています', 177 | 958 => 'クエリーに引数がありません', 178 | 1200 => '一般的な計算エラーです', 179 | 1201 => '関数の引数が足りません', 180 | 1202 => '関数の引数が多すぎます', 181 | 1203 => '計算式が未完了です', 182 | 1204 => '数字、テキスト、フィールド名、または「(」を入れてください', 183 | 1205 => 'コメントは「*/」で終了できません', 184 | 1206 => 'テキストは半角のダブルクォーテーションマークで終わらなければなりません', 185 | 1207 => 'カッコが一致していません', 186 | 1208 => '演算子または関数が見つからないか、「(」は指定できません', 187 | 1209 => '名前(フィールド名またはレイアウト名)が見つかりません', 188 | 1210 => 'プラグイン関数はすでに登録されています', 189 | 1211 => 'この関数では一覧を使用できません', 190 | 1212 => '演算子(+、-、* など)を入れてください', 191 | 1213 => 'この変数はすでに Let 関数で定義されています', 192 | 1214 => 'AVERAGE、COUNT、EXTEND、GETREPETITION、MAX、MIN、NPV、STDEV、SUM、および GETSUMMARY 関数で、フィールドの値を指定できない部分に式が使われています', 193 | 1215 => 'この引数は Get 関数の無効な引数です', 194 | 1216 => 'GetSummary 関数の1番目の引数は、集計フィールドのみに限られます', 195 | 1217 => '区分けフィールドが無効です', 196 | 1218 => '数字を評価できません', 197 | 1219 => 'フィールド固有の式にフィールドは使用できません', 198 | 1220 => 'フィールドタイプは標準にするか、計算する必要があります', 199 | 1221 => 'データタイプは数字、日付、時刻、またはタイムスタンプでなければなりません', 200 | 1222 => '計算式を保存できません', 201 | 1223 => '指定された関数は存在しません', 202 | 1400 => 'ODBC クライアントドライバの初期化に失敗しました。ODBC クライアントドライバが適切にインストールされていることを確認してください。 注意 ODBC 経由でデータを共有するプラグインコンポーネントは、FileMaker Server によって自動的にインストールされます。ODBC クライアントドライバは、FileMaker Server Web Publishing CD を使用してインストールされます。詳細については、『FileMaker ODBC および JDBC クライアントドライバのインストール』を参照してください。', 203 | 1401 => '環境の割り当てに失敗しました(ODBC)', 204 | 1402 => '環境の解放に失敗しました(ODBC)', 205 | 1403 => '切断に失敗しました(ODBC)', 206 | 1404 => '接続の割り当てに失敗しました(ODBC)', 207 | 1405 => '接続の解放に失敗しました(ODBC)', 208 | 1406 => 'SQL API のチェックに失敗しました(ODBC)', 209 | 1407 => 'ステートメントの割り当てに失敗しました(ODBC)', 210 | 1408 => '拡張エラー(ODBC)', 211 | 1450 => 'PHP アクセス権を拡張する操作が必要です', 212 | 1451 => '現在のファイルをリモートにする操作が必要です', 213 | 1501 => 'SMTP の認証に失敗しました', 214 | 1502 => 'SMTP サーバーによって接続が拒否されました', 215 | 1503 => 'SSL でエラーが発生しました', 216 | 1504 => 'SMTP サーバーの接続を暗号化する必要があります', 217 | 1505 => '指定された認証方法は SMTP サーバーではサポートされていません', 218 | 1506 => 'E メールは正常に送信されませんでした', 219 | 1507 => 'SMTP サーバーにログインできませんでした', 220 | 8003 => '別のユーザがレコードを使用中です', 221 | ); 222 | -------------------------------------------------------------------------------- /src/Error/sv.php: -------------------------------------------------------------------------------- 1 | 'Okänt fel', 18 | 0 => 'Inget fel', 19 | 1 => 'Användaren har avbrutit åtgärden', 20 | 2 => 'Minnesfel', 21 | 3 => 'Kommandot är inte tillgängligt (exempelvis fel operativsystem, fel läge osv)', 22 | 4 => 'Okänt kommando', 23 | 5 => 'Ogiltigt kommando (exempelvis ett Tilldela fält-steg som inte har någon beräkning angiven)', 24 | 6 => 'Filen är skrivskyddad', 25 | 7 => 'Minnet räcker inte.', 26 | 8 => 'Tomt resultat', 27 | 9 => 'Otillräcklig behörighet', 28 | 10 => 'Begärda data saknas', 29 | 11 => 'Namnet är ogiltigt', 30 | 12 => 'Namnet finns redan', 31 | 13 => 'Filen eller objektet används', 32 | 14 => 'Utanför det tillåtna intervallet', 33 | 15 => 'Går inte att dela med noll', 34 | 16 => 'Åtgärden misslyckades, begär nytt försök (t.ex. en användarfråga)', 35 | 17 => 'Försök att konvertera främmande teckenuppsättning till UTF-16 misslyckades', 36 | 18 => 'Klienten måste ange kontoinformation för att fortsätta', 37 | 19 => 'Strängen innehåller andra tecken än A-Z, a-z, 0-9 (ASCII)', 38 | 20 => 'Kommando eller åtgärd avbröts av utlöst script', 39 | 100 => 'Fil saknas', 40 | 101 => 'Post saknas', 41 | 102 => 'Fält saknas', 42 | 103 => 'Relation saknas', 43 | 104 => 'Script saknas', 44 | 105 => 'Layout saknas', 45 | 106 => 'Tabell saknas', 46 | 107 => 'Index saknas', 47 | 108 => 'Värdelista saknas', 48 | 109 => 'Behörighetsuppsättning saknas', 49 | 110 => 'Relaterade tabeller saknas', 50 | 111 => 'Fältrepetitionen är ogiltig', 51 | 112 => 'Fönstret saknas', 52 | 113 => 'Funktionen saknas', 53 | 114 => 'Filreferensen saknas', 54 | 130 => 'Filerna är skadade eller saknas och måste installeras om', 55 | 131 => 'Språkpaketfiler saknas (t.ex. mallfiler)', 56 | 200 => 'Tillgång till posten nekad', 57 | 201 => 'Det går inte att ändra fältet', 58 | 202 => 'Tillgång till fältet nekad', 59 | 203 => 'Det finns inga poster att skriva ut i filen eller också tillåter inte lösenordet behörighet för utskrift', 60 | 204 => 'Ingen tillgång till fält i sorteringsordning', 61 | 205 => 'Användaren har inte behörighet att skapa nya poster; importen kommer att skriva över befintliga data', 62 | 206 => 'Användaren har inte behörighet att ändra lösenord eller så kan filen inte ändras', 63 | 207 => 'Användaren har inte tillräcklig behörighet för att ändra databasschemat eller så kan filen inte ändras', 64 | 208 => 'Lösenordet innehåller inte tillräckligt många tecken', 65 | 209 => 'Det nya lösenordet måste skilja sig från det befintliga', 66 | 210 => 'Användarkontot är inte aktivt', 67 | 211 => 'Lösenordet har upphört att gälla', 68 | 212 => 'Ogiltigt användarkonto och/eller lösenord. Försök igen', 69 | 213 => 'Användarkontot och/eller lösenordet finns inte', 70 | 214 => 'För många inloggningsförsök', 71 | 215 => 'Administratörsbehörighet kan inte dupliceras', 72 | 216 => 'Gästkontot kan inte dupliceras', 73 | 217 => 'Användaren har inte tillräcklig behörighet för att ändra administratörskontot', 74 | 300 => 'Filen är låst eller används', 75 | 301 => 'Posten används av en annan användare', 76 | 302 => 'Tabellen används av en annan användare', 77 | 303 => 'Databasschemat används av en annan användare', 78 | 304 => 'Layouten används av en annan användare', 79 | 306 => 'Postens ändrings-ID matchar inte', 80 | 400 => 'Sökvillkoren är tomma', 81 | 401 => 'Inga poster matchar sökposten', 82 | 402 => 'Det valda fältet är inte ett matchande fält för en länk', 83 | 403 => 'Maxpostgränsen för testversionen av FileMakerPro har överskridits', 84 | 404 => 'Sorteringsordningen är ogiltig', 85 | 405 => 'Det angivna antalet poster överskrider antalet poster som kan utelämnas', 86 | 406 => 'Ogiltiga villkor för ersättning/ändring av löpnummer', 87 | 407 => 'Ett eller båda av de matchande fälten saknas (ogiltig relation)', 88 | 408 => 'Det angivna fältet har fel datatyp för den här åtgärden', 89 | 409 => 'Importordningen är ogiltig', 90 | 410 => 'Exportordningen är ogiltig', 91 | 412 => 'Fel version av FileMakerPro används för att reparera filen', 92 | 413 => 'Det angivna fältet är av fel typ', 93 | 414 => 'Resultatet kan inte visas i layouten', 94 | 415 => 'En eller flera begärda relaterade poster är inte tillgänglig(a)', 95 | 500 => 'Datumvärdet stämmer inte med kontrolltillvalen', 96 | 501 => 'Tidsvärdet stämmer inte med kontrolltillvalen', 97 | 502 => 'Det numeriska värdet stämmer inte med kontrolltillvalen', 98 | 503 => 'Värdet i fältet stämmer inte med intervallet som angetts i kontrolltillvalen', 99 | 504 => 'Värdet i fältet är inte unikt, vilket angetts som ett krav i kontrolltillvalen', 100 | 505 => 'Värdet i fältet är inte ett befintligt värde i databasfilen, vilket angetts som ett krav i kontrolltillvalen', 101 | 506 => 'Värdet i fältet finns inte med i värdelistan som angetts i kontrolltillvalet', 102 | 507 => 'Värdet i fältet klarade inte beräkningstestet för kontrolltillvalet', 103 | 508 => 'Ett ogiltigt värde har angetts i sökläget', 104 | 509 => 'Fältet kräver ett giltigt värde', 105 | 510 => 'Det relaterade värdet är tomt eller inte tillgängligt', 106 | 511 => 'Värdet i fältet överskrider antalet tillåtna tecken', 107 | 600 => 'Utskriftsfel', 108 | 601 => 'Sidhuvudet och sidfoten ryms inte tillsammans på en sida', 109 | 602 => 'Huvuddelen ryms inte på en sida med den aktuella kolumninställningen', 110 | 603 => 'Ingen skrivaranslutning', 111 | 700 => 'Fel filformat för import', 112 | 706 => 'EPSF-filen saknar granskningsbild', 113 | 707 => 'Det går inte att hitta grafikkonverteraren', 114 | 708 => 'Det går inte att importera filen eller så krävs stöd för färgskärm för att importera filen', 115 | 709 => 'Import av QuickTime-film misslyckades', 116 | 710 => 'Det går inte att uppdatera QuickTime-filmreferensen eftersom databasfilen är skrivskyddad', 117 | 711 => 'Det går inte att hitta importkonverteraren', 118 | 714 => 'Lösenordet saknar behörighet för den här åtgärden', 119 | 715 => 'Det angivna Excel-kalkylbladet eller intervallet saknas', 120 | 716 => 'En SQL-fråga med DELETE, INSERT eller UPDATE är inte tillåten vid ODBC-import', 121 | 717 => 'Det finns inte tillräcklig XML/XSL-information för att importen eller exporten ska kunna fortsätta', 122 | 718 => 'Ett fel inträffade vid tolkningen av XML-filen (från Xerces)', 123 | 719 => 'Ett fel inträffade vid konverteringen av XML med hjälp av XSL (från Xalan)', 124 | 720 => 'Ett fel inträffade vid exporten, det angivna formatet har inte stöd för repeterade fält', 125 | 721 => 'Ett okänt fel inträffade vid tolkningen eller konverteringen', 126 | 722 => 'Det går inte att importera data till en fil utan fält', 127 | 723 => 'Du har inte behörighet att lägga till eller ändra poster i måltabellen', 128 | 724 => 'Du har inte behörighet att lägga till poster i måltabellen', 129 | 725 => 'Du har inte behörighet att ändra poster i måltabellen', 130 | 726 => 'Importfilen innehåller fler poster än måltabellen. Alla poster importerades inte', 131 | 727 => 'Måltabellen innehåller fler poster än importfilen. Alla poster uppdaterades inte', 132 | 729 => 'Fel inträffade vid importen. Posterna kunde inte importeras', 133 | 730 => 'Excel-versionen stöds inte. Konvertera filen till Excel 7.0 (Excel 95), 97, 2000, XP, eller 2007 och försök igen.', 134 | 731 => 'Filen som du importerar från innehåller inga data', 135 | 732 => 'Den här filen kan inte sättas in eftersom den innehåller andra filer', 136 | 733 => 'En tabell kan inte importeras till sig själv', 137 | 734 => 'Den här filtypen kan inte visas som en bild', 138 | 735 => 'Den här filtypen kan inte visas som en bild. Den kommer att sättas in och visas som en fil.', 139 | 800 => 'Det går inte att skapa filen på disken', 140 | 801 => 'Det går inte att skapa någon tillfällig fil på systemdisken', 141 | 802 => 'Det går inte att öppna filen', 142 | 803 => 'Filen är en enanvändarfil eller så går det inte att hitta värden', 143 | 804 => 'Filen går inte att öppna som skrivskyddad i aktuellt tillstånd', 144 | 805 => 'Filen är skadad; använd kommandot Reparera', 145 | 806 => 'Det går inte att öppna filen i den här versionen av FileMaker Pro', 146 | 807 => 'Filen är inte en FileMakerPro-fil eller så är den allvarligt skadad', 147 | 808 => 'Det går inte att öppna filen eftersom behörigheten är skadad', 148 | 809 => 'Disken/volymen är full', 149 | 810 => 'Disken/volymen är låst', 150 | 811 => 'Det går inte att öppna den tillfälliga filen som en FileMaker Pro-fil', 151 | 813 => 'Postsynkroniseringsfel i nätverket', 152 | 814 => 'Det går inte att öppna filen eftersom maximala antalet tillåtna filer redan är öppna', 153 | 815 => 'Det går inte att öppna den länkade filen', 154 | 816 => 'Det går inte att konvertera filen', 155 | 817 => 'Det går inte att öppna filen eftersom den inte hör till denna lösning', 156 | 819 => 'Det går inte att spara en lokal kopia av en fjärrfil', 157 | 820 => 'Filen håller på att stängas', 158 | 821 => 'Frånkoppling framtvingad av värden', 159 | 822 => 'FMI-filer hittades inte; installera om saknade filer', 160 | 823 => 'Det går inte att göra filen till en enanvändarfil eftersom gäster är anslutna', 161 | 824 => 'Filen är skadad eller så är den inte en FileMaker-fil', 162 | 900 => 'Allmänt fel i stavningsmotorn', 163 | 901 => 'Standardordlistan är inte installerad', 164 | 902 => 'Det gick inte att starta hjälpsystemet', 165 | 903 => 'Detta kommando kan inte användas i en delad fil', 166 | 904 => 'Kommandot kan bara användas i en fil som delas under FileMaker Server', 167 | 905 => 'Inget aktivt fält har valts och kommandot kan bara användas om det finns ett aktivt fält', 168 | 920 => 'Det går inte att initiera stavningsmotorn', 169 | 921 => 'Det går inte att öppna den egna ordlistan för redigering', 170 | 922 => 'Det går inte att hitta den egna ordlistan', 171 | 923 => 'Den egna ordlistan är skrivskyddad', 172 | 951 => 'Ett oväntat fel har inträffat', 173 | 954 => 'XML-grammatiken stöds inte', 174 | 955 => 'Inget databasnamn', 175 | 956 => 'Det maximala antalet databassessioner har överskridits', 176 | 957 => 'Motstridiga kommandon', 177 | 958 => 'Parameter saknas i fråga', 178 | 1200 => 'Allmänt beräkningsfel', 179 | 1201 => 'För få parametrar i funktionen', 180 | 1202 => 'För många parametrar i funktionen', 181 | 1203 => 'Oväntat slut på beräkningen', 182 | 1204 => 'Ett numeriskt värde, en textkonstant, ett fältnamn eller "(" krävs', 183 | 1205 => 'Kommentaren avslutas inte med "*/"', 184 | 1206 => 'Textkonstanten måste avslutas med ett citationstecken', 185 | 1207 => 'Udda parentes', 186 | 1208 => 'Operator saknas, funktionen hittades inte eller "(" förväntades inte', 187 | 1209 => 'Ett namn (t.ex. ett fältnamn eller ett layoutnamn) saknas', 188 | 1210 => 'Plugin-funktionen har redan registrerats', 189 | 1211 => 'Det är inte tillåtet att använda listor i den här funktionen', 190 | 1212 => 'En operator (t.ex. +, -, *) behövs här', 191 | 1213 => 'Den här variabeln har redan definierats i Let-funktionen', 192 | 1214 => 'AVERAGE, COUNT, EXTEND, GETREPETITION, MAX, MIN, NPV, STDEV, SUM och GETSUMMARY: ett uttryck påträffades där ett fält behövdes', 193 | 1215 => 'Den här parametern är en ogiltig Get-funktionsparameter', 194 | 1216 => 'Första argumentet i en GETSUMMARY måste vara ett statistikfält', 195 | 1217 => 'Brytfältet är ogiltigt', 196 | 1218 => 'Det går inte att utvärdera talet', 197 | 1219 => 'Ett fält kan inte användas i sin egen formel', 198 | 1220 => 'Fälttypen måste vara normal eller beräkning', 199 | 1221 => 'Datatypen måste vara numerisk, datum, tid eller tidsstämpel', 200 | 1222 => 'Beräkningen får inte vara lagrad', 201 | 1223 => 'Den refererade funktionen finns inte', 202 | 1400 => 'Det gick inte att initiera drivrutinen för ODBC-klient. Kontrollera att drivrutinerna för ODBC-klient är korrekt installerade. Obs! Plugin-programmet för delning av data via ODBC har installerats automatiskt med FileMaker Server. Drivrutinerna för ODBC-klienten installeras med CD-skivan för FileMaker Server Web Publishing. Mer information finns i Installera FileMakerdrivrutiner för ODBC- och JDBC-klienter.', 203 | 1401 => 'Det gick inte att ställa in miljön (ODBC)', 204 | 1402 => 'Det gick inte att frigöra miljön (ODBC)', 205 | 1403 => 'Det gick inte att koppla från (ODBC)', 206 | 1404 => 'Det gick inte att tilldela en anslutning (ODBC)', 207 | 1405 => 'Det gick inte att frigöra en anslutning (ODBC)', 208 | 1406 => 'Kontroll av SQL-API misslyckades (ODBC)', 209 | 1407 => 'Det gick inte att tilldela en instruktion (ODBC)', 210 | 1408 => 'Utökat fel (ODBC)', 211 | 1450 => 'Åtgärden kräver PHP-behörighetstillägg', 212 | 1451 => 'Åtgärden kräver att aktuell fil är en fjärrfil', 213 | 1501 => 'SMTP-verifieringen misslyckades', 214 | 1502 => 'Anslutningen avvisades av SMTP-servern', 215 | 1503 => 'Fel med SSL', 216 | 1504 => 'SMTP-servern kräver att anslutningen är krypterad', 217 | 1505 => 'Angiven verifiering stöds inte av SMTP-servern', 218 | 1506 => 'E-post kunde inte skickas', 219 | 1507 => 'Det går inte att logga in på SMTP-servern', 220 | 8003 => 'Posten används av en annan användare', 221 | ); 222 | -------------------------------------------------------------------------------- /src/FileMakerException.php: -------------------------------------------------------------------------------- 1 | fm = $fm; 38 | if (empty($message)) { 39 | $message = $this->getErrorString($code); 40 | } 41 | 42 | parent::__construct($message, $code, $previous); 43 | } 44 | 45 | /** 46 | * Returns the string representation of $this->code in the language 47 | * currently set for PHP error messages in FileMaker Server Admin 48 | * Console. 49 | * 50 | * You should call getMessage() in most cases, if you are not sure whether 51 | * the error is a FileMaker Web Publishing Engine error with an error code. 52 | * 53 | * @param int $code Error code 54 | * @return string Error description. 55 | */ 56 | public function getErrorString($code) 57 | { 58 | // Default to English. 59 | $lang = basename($this->fm->getProperty('locale')); 60 | if (!$lang) { 61 | $lang = 'en'; 62 | } 63 | 64 | if (empty(self::$strings[$lang])) { 65 | if (file_exists(dirname(__FILE__) . '/Error/' . $lang . '.php')) { 66 | $path = dirname(__FILE__) . '/Error/' . $lang . '.php'; 67 | } else { 68 | $path = dirname(__FILE__) . '/Error/en.php'; 69 | } 70 | self::$strings[$lang] = require($path); 71 | } 72 | 73 | if (isset(self::$strings[$lang][$code])) { 74 | return self::$strings[$lang][$code]; 75 | } 76 | 77 | return self::$strings[$lang][-1]; 78 | } 79 | 80 | /** 81 | * Indicates whether the error is a detailed pre-validation error 82 | * or a FileMaker Web Publishing Engine error. 83 | * 84 | * @return boolean FALSE, to indicate that this is an error from the 85 | * Web Publishing Engine. 86 | */ 87 | public function isValidationError() 88 | { 89 | return false; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/FileMakerValidationException.php: -------------------------------------------------------------------------------- 1 | errors[] = [$field, $rule, $value, $message]; 42 | $messages = empty($this->getMessage()) ? [] : explode("\n", $this->getMessage()); 43 | $messages[] = $message; 44 | $this->message = implode("\n", $messages); 45 | } 46 | 47 | /** 48 | * Return validation error message 49 | * @param Field $field 50 | * @param int $rule 51 | * @param string $value 52 | * @return string 53 | */ 54 | private static function getValidationErrorString(Field $field, $rule, $value) 55 | { 56 | switch ($rule) { 57 | case FileMaker::RULE_FOURDIGITYEAR: 58 | $message = 'Please enter a four digit number for the year in field %1$s'; 59 | break; 60 | case FileMaker::RULE_DATE_FIELD: 61 | $message = 'Please enter a valid date for field %1$s'; 62 | break; 63 | case FileMaker::RULE_MAXCHARACTERS: 64 | $message = 'Value in field %1$s exceeds maximum number of allowed characters (%2$d)'; 65 | break; 66 | case FileMaker::RULE_NOTEMPTY: 67 | $message = 'Field %1$s is required'; 68 | break; 69 | case FileMaker::RULE_NUMERICONLY: 70 | $message = 'Only numbers are allowed for field %1$s'; 71 | break; 72 | case FileMaker::RULE_TIMEOFDAY: 73 | case FileMaker::RULE_TIME_FIELD: 74 | $message = 'Please enter a valid time in field %1$s'; 75 | break; 76 | case FileMaker::RULE_TIMESTAMP_FIELD: 77 | $message = 'Please enter a valid timestamp in field %1$s'; 78 | break; 79 | default: 80 | $message = 'Incorrect value for field %1$s (%3$s)'; 81 | } 82 | 83 | return sprintf($message, $field->getName(), $field->maxCharacters, $value); 84 | } 85 | 86 | /** 87 | * Indicates whether the error is a detailed pre-validation error 88 | * or a FileMaker Web Publishing Engine error. 89 | * 90 | * @return boolean TRUE, to indicate that this is a pre-validation 91 | * error object. 92 | */ 93 | public function isValidationError() 94 | { 95 | return true; 96 | } 97 | 98 | /** 99 | * Returns the number of pre-validation rules that failed. 100 | * 101 | * @return integer Number of failures. 102 | */ 103 | public function numErrors() 104 | { 105 | return count($this->errors); 106 | } 107 | 108 | /** 109 | * Returns an array of arrays describing the pre-validation errors that 110 | * occurred. 111 | * 112 | * Each entry in the outer array represents a pre-validation failure. 113 | * Each failure is represented by a three-element array with the 114 | * following members: 115 | * 116 | * - 0 => The field object for the field that failed pre-validation. 117 | * - 1 => The pre-validation rule that failed specified as a 118 | * FileMaker::RULE_* constant. 119 | * - 2 => The value entered for the field that failed pre-validation. 120 | * - 3 => The validation error message. 121 | * 122 | * Multiple pre-validation rules can fail on a single field. If you set the 123 | * optional $fieldName parameter, then failures for only the specified 124 | * field are returned. 125 | * 126 | * @param string $fieldName Name of the field to get errors for. 127 | * 128 | * @return array Pre-validation error details. 129 | */ 130 | public function getErrors($fieldName = null) 131 | { 132 | if ($fieldName === null) { 133 | return $this->errors; 134 | } 135 | 136 | $errors = []; 137 | foreach ($this->errors as $error) { 138 | if ($error[0]->getName() == $fieldName) { 139 | $errors[] = $error; 140 | } 141 | } 142 | 143 | return $errors; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Helpers/DateFormat.php: -------------------------------------------------------------------------------- 1 | null, 23 | '/[#|@]+/' => "*", 24 | //'/#/' => "*", 25 | '/~/' => null 26 | ]; 27 | 28 | public static $byPassOperators = ['!', '?', ]; 29 | /** 30 | * @param string $value 31 | * @param string $inputFormat 32 | * @param string $outputFormat 33 | * @return string 34 | * @throws \Exception 35 | */ 36 | public static function convert($value, $inputFormat = null, $outputFormat = null) 37 | { 38 | if (empty($value) || $inputFormat === null || $outputFormat === null) { 39 | return $value; 40 | } 41 | 42 | //Parse value to detect incorrect date format 43 | $parsedDate = date_parse_from_format($inputFormat, $value); 44 | if ($parsedDate['error_count'] || $parsedDate['warning_count']) { 45 | throw new \Exception('invalid date format'); 46 | } 47 | 48 | $date = \DateTime::createFromFormat($inputFormat, $value); 49 | 50 | return $date->format($outputFormat); 51 | } 52 | 53 | /** 54 | * @param $value 55 | * @param string|null $inputFormat 56 | * @param string|null $outputFormat 57 | * @return string 58 | */ 59 | public static function convertSearchCriteria($value, $inputFormat = null, $outputFormat = null) 60 | { 61 | if (empty($value) 62 | || in_array($value, self::$byPassOperators) 63 | || $inputFormat == null 64 | || $outputFormat == null 65 | ) { 66 | return $value; 67 | } 68 | 69 | $value = self::sanitizeDateSearchString($value); 70 | 71 | $inputRegExp = '#' . self::dateFormatToRegex($inputFormat) . '#'; 72 | 73 | //$regex = "#[<|>|≤|≥|<=|>=]?($inputRegExp)\.{0}|\.{3}($inputRegExp)?#"; 74 | $value = preg_replace_callback( 75 | $inputRegExp, 76 | function ($matches) use ($inputFormat, $outputFormat) { 77 | return self::convertWithWildCards($matches[0], $inputFormat, $outputFormat); 78 | }, 79 | $value 80 | ); 81 | 82 | return $value; 83 | } 84 | 85 | /** 86 | * @param string $value 87 | * @return string 88 | */ 89 | public static function sanitizeDateSearchString($value) 90 | { 91 | foreach (self::$omitOperatorsPattern as $pattern => $replacement) { 92 | $value = preg_replace($pattern, $replacement, $value); 93 | } 94 | return $value; 95 | } 96 | 97 | /** 98 | * @param $format 99 | * @return string 100 | */ 101 | public static function dateFormatToRegex($format) 102 | { 103 | $keys = [ 104 | 'Y' => ['year', '\d{4}|\*'], 105 | 'y' => ['year', '\d{2}|\*'], 106 | 'm' => ['month', '\d{2}|\*'], 107 | 'n' => ['month', '\d{1,2}|\*'], 108 | //'M' => ['month', '[A-Z][a-z]{3}'], 109 | //'F' => ['month', '[A-Z][a-z]{2,8}'], 110 | 'd' => ['day', '\d{2}|\*'], 111 | 'j' => ['day', '\d{1,2}|\*'], 112 | //'D' => ['day', '[A-Z][a-z]{2}'], 113 | //'l' => ['day', '[A-Z][a-z]{6,9}'], 114 | 'u' => ['hour', '\d{1,6}'], 115 | 'h' => ['hour', '\d{2}|\*'], 116 | 'H' => ['hour', '\d{2}|\*'], 117 | 'g' => ['hour', '\d{1,2}|\*'], 118 | 'G' => ['hour', '\d{1,2}|\*'], 119 | 'i' => ['minute', '\d{2}|\*'], 120 | 's' => ['second', '\d{2}|\*'] 121 | ]; 122 | 123 | // convert format string to regex 124 | $regex = ''; 125 | $chars = str_split($format); 126 | foreach ($chars as $n => $char) { 127 | $lastChar = isset($chars[$n - 1]) ? $chars[$n - 1] : ''; 128 | $skipCurrent = '\\' == $lastChar; 129 | if (!$skipCurrent && isset($keys[$char])) { 130 | $regex .= '(?P<' . $keys[$char][0] . '>' . $keys[$char][1] . ')'; 131 | } elseif ('\\' == $char) { 132 | $regex .= $char; 133 | } else { 134 | $regex .= preg_quote($char); 135 | } 136 | } 137 | return $regex; 138 | } 139 | 140 | /** 141 | * @param string $value 142 | * @param string $inputFormat 143 | * @param string $outputFormat 144 | * @return string 145 | */ 146 | public static function convertWithWildCards($value, $inputFormat, $outputFormat) 147 | { 148 | $inputRegex = "#" . self::dateFormatToRegex($inputFormat) . "#"; 149 | preg_match($inputRegex, $value, $parsedDate); 150 | 151 | $keys = [ 152 | 'Y' => ['year', '%04d'], 153 | 'y' => ['year', '%02d'], 154 | 'm' => ['month', '%02d'], 155 | 'n' => ['month', '%02d'], 156 | //'M' => [('month', '%3s'], 157 | //'F' => array('month', '%8s'], 158 | 'd' => ['day', '%02d'], 159 | 'j' => ['day', '%02d'], 160 | //'D' => ['day', '%2s'], 161 | //'l' => ['day', '%9s'], 162 | //'u' => ['hour', '%06d'], 163 | 'h' => ['hour', '%02d'], 164 | 'H' => ['hour', '%02d'], 165 | 'g' => ['hour', '%02d'], 166 | 'G' => ['hour', '%02d'], 167 | 'i' => ['minute', '%02d'], 168 | 's' => ['second', '%02d'] 169 | ]; 170 | 171 | //convert to output format 172 | $string = ''; 173 | $chars = str_split($outputFormat); 174 | foreach ($chars as $char) { 175 | if (isset($keys[$char])) { 176 | $val = @$parsedDate[$keys[$char][0]]; 177 | $format = $keys[$char][1]; 178 | $string .= $val == "*" || $val == null ? "*" : sprintf($format, $val); 179 | } else { 180 | $string .= $char; 181 | } 182 | } 183 | return $string; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Object/Layout.php: -------------------------------------------------------------------------------- 1 | fm = $fm; 44 | } 45 | 46 | /** 47 | * Returns the name of this layout. 48 | * 49 | * @return string Layout name. 50 | */ 51 | public function getName() 52 | { 53 | return $this->name; 54 | } 55 | 56 | /** 57 | * Returns the name of the database that this layout is in. 58 | * 59 | * @return string Database name. 60 | */ 61 | public function getDatabase() 62 | { 63 | return $this->database; 64 | } 65 | 66 | /** 67 | * Returns an array with the names of all fields in this layout. 68 | * 69 | * @return array List of field names as strings. 70 | */ 71 | public function listFields() 72 | { 73 | return array_keys($this->fields); 74 | } 75 | 76 | /** 77 | * Returns a Field object that describes the specified field. 78 | * 79 | * @param string $fieldName Name of field. 80 | * 81 | * @return Field|FileMakerException Field object, if successful. 82 | * @throws FileMakerException 83 | */ 84 | public function getField($fieldName) 85 | { 86 | if (isset($this->fields[$fieldName])) { 87 | return $this->fields[$fieldName]; 88 | } 89 | if ($pos = strpos($fieldName, ':')) { 90 | $relatedSet = substr($fieldName, 0, $pos); 91 | //$fieldName = substr($fieldName, $pos+1, strlen($fieldName)); 92 | $result = $this->getRelatedSet($relatedSet); 93 | if (FileMaker::isError($result)) { 94 | return $result; 95 | } 96 | return $result->getField($fieldName); 97 | } 98 | return $this->fm->returnOrThrowException('Field "'.$fieldName.'" Not Found'); 99 | } 100 | 101 | /** 102 | * Returns an associative array with the names of all fields as 103 | * keys and Field objects as the array values. 104 | * 105 | * @return Field[] an array of Field objects. 106 | */ 107 | public function getFields() 108 | { 109 | return $this->fields; 110 | } 111 | 112 | /** 113 | * Returns an array of related table names for all portals on 114 | * this layout. 115 | * 116 | * @return array List of related table names as strings. 117 | */ 118 | public function listRelatedSets() 119 | { 120 | return array_keys($this->relatedSets); 121 | } 122 | 123 | /** 124 | * Returns a RelatedSet object that describes the specified 125 | * portal. 126 | * 127 | * @param string|FileMakerException $relatedSet Name of the related table for a portal. 128 | * @throws FileMakerException 129 | * 130 | * @return RelatedSet|FileMakerException a RelatedSet object 131 | */ 132 | public function getRelatedSet($relatedSet) 133 | { 134 | if (isset($this->relatedSets[$relatedSet])) { 135 | return $this->relatedSets[$relatedSet]; 136 | } 137 | return $this->fm->returnOrThrowException('RelatedSet "'.$relatedSet.'" Not Found in layout '. $this->getName()); 138 | } 139 | 140 | /** 141 | * Check wether a portal based on the given table occurrence exists 142 | * @param string $relatedSet Table occurrence name to test 143 | * @return bool true if related set exist 144 | */ 145 | public function hasRelatedSet($relatedSet) 146 | { 147 | return isset($this->relatedSets[$relatedSet]); 148 | } 149 | 150 | /** 151 | * Returns an associative array with the related table names of all 152 | * portals as keys and RelatedSet objects as the array values. 153 | * 154 | * @return RelatedSet[] Array of {@link RelatedSet} objects. 155 | */ 156 | public function getRelatedSets() 157 | { 158 | return $this->relatedSets; 159 | } 160 | 161 | /** 162 | * Returns the names of any value lists associated with this 163 | * layout. 164 | * 165 | * @return array|FileMakerException List of value list names as strings. 166 | * @throws FileMakerException 167 | */ 168 | public function listValueLists() 169 | { 170 | $extendedInfos = $this->loadExtendedInfo(); 171 | if (FileMaker::isError($extendedInfos)) { 172 | return $extendedInfos; 173 | } 174 | if ($this->valueLists !== null) { 175 | return array_keys($this->valueLists); 176 | } 177 | 178 | return []; 179 | } 180 | 181 | /** 182 | * Returns the list of defined values in the specified value list. 183 | * 184 | * @param string $listName Name of value list. 185 | * @param string $recid Record from which the value list should be 186 | * displayed. 187 | * 188 | * @return array|FileMakerException List of defined values. 189 | 190 | * @throws FileMakerException 191 | * @deprecated Use getValueListTwoFields instead. 192 | 193 | * @see getValueListTwoFields 194 | */ 195 | public function getValueList($listName, $recid = null) 196 | { 197 | $extendedInfos = $this->loadExtendedInfo($recid); 198 | if (FileMaker::isError($extendedInfos)) { 199 | return $extendedInfos; 200 | } 201 | return isset($this->valueLists[$listName]) ? 202 | $this->valueLists[$listName] : null; 203 | } 204 | 205 | 206 | 207 | /** 208 | * Returns the list of defined values in the specified value list. 209 | * This method supports single, 2nd only, and both fields value lists. 210 | * 211 | * @param string $valueList Name of value list. 212 | * @param string $recid Record from which the value list should be 213 | * displayed. 214 | * 215 | * @return array|FileMakerException of display names and its corresponding value from the value list 216 | * @throws FileMakerException 217 | */ 218 | public function getValueListTwoFields($valueList, $recid = null) 219 | { 220 | 221 | $extendedInfos = $this->loadExtendedInfo($recid); 222 | if (FileMaker::isError($extendedInfos)) { 223 | return $extendedInfos; 224 | } 225 | return isset($this->valueLists[$valueList]) ? 226 | $this->valueListTwoFields[$valueList] : []; 227 | } 228 | 229 | /** 230 | * Returns a multi-level associative array of value lists. 231 | * The top-level array has names of value lists as keys and arrays as 232 | * values. The second level arrays are the lists of defined values from 233 | * each value list. 234 | * 235 | * @param string $recid Record from which the value list should be 236 | * displayed. 237 | * 238 | * @return array|FileMakerException Array of value-list arrays. 239 | * @throws FileMakerException 240 | * @deprecated Use getValueListTwoFields instead. 241 | * @see getValueListsTwoFields 242 | */ 243 | public function getValueLists($recid = null) 244 | { 245 | $extendedInfos = $this->loadExtendedInfo($recid); 246 | if (FileMaker::isError($extendedInfos)) { 247 | return $extendedInfos; 248 | } 249 | return $this->valueLists; 250 | } 251 | 252 | /** 253 | * Returns a multi-level associative array of value lists. 254 | * The top-level array has names of value lists as keys and associative arrays as 255 | * values. The second level associative arrays are lists of display name and its corresponding 256 | * value from the value list. 257 | * 258 | * @param string|FileMakerException $recid Record from which the value list should be 259 | * displayed. 260 | * @throws FileMakerException 261 | * 262 | * @return array|FileMakerException Array of value-list associative arrays. 263 | */ 264 | public function getValueListsTwoFields($recid = null) 265 | { 266 | $extendedInfos = $this->loadExtendedInfo($recid); 267 | if (FileMaker::isError($extendedInfos)) { 268 | return $extendedInfos; 269 | } 270 | return $this->valueListTwoFields; 271 | } 272 | 273 | /** 274 | * Loads extended (FMPXMLLAYOUT) layout information. 275 | * 276 | * @access private 277 | * 278 | * @param string $recid Record from which to load extended information. 279 | * 280 | * @return boolean|FileMakerException TRUE, if successful. 281 | * @throws FileMakerException 282 | */ 283 | public function loadExtendedInfo($recid = null) 284 | { 285 | if (!$this->extended || $recid != null) { 286 | if ($recid != null) { 287 | $result = $this->fm->execute([ 288 | '-db' => $this->fm->getProperty('database'), 289 | '-lay' => $this->getName(), 290 | '-recid' => $recid, 291 | '-view' => null 292 | ], 'FMPXMLLAYOUT'); 293 | } else { 294 | $result = $this->fm->execute([ 295 | '-db' => $this->fm->getProperty('database'), 296 | '-lay' => $this->getName(), 297 | '-view' => null 298 | ], 'FMPXMLLAYOUT'); 299 | } 300 | $parser = new FMPXMLLAYOUT($this->fm); 301 | $parseResult = $parser->parse($result); 302 | if (FileMaker::isError($parseResult)) { 303 | return $parseResult; 304 | } 305 | 306 | $parser->setExtendedInfo($this); 307 | $this->extended = true; 308 | 309 | if ($recid === null){ 310 | $this->fm->cacheSet($this->getName(), $this); 311 | } 312 | } 313 | return $this->extended; 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /src/Object/RelatedSet.php: -------------------------------------------------------------------------------- 1 | layout = $layout; 41 | $this->fm = $layout->fm; 42 | } 43 | 44 | /** 45 | * Returns the name of the related table from which this portal displays 46 | * related records. 47 | * 48 | * @return string Name of related table for this portal. 49 | */ 50 | public function getName() 51 | { 52 | return $this->name; 53 | } 54 | 55 | /** 56 | * Returns an array of the names of all fields in this portal. 57 | * 58 | * @return array List of field names as strings. 59 | */ 60 | public function listFields() 61 | { 62 | return array_keys($this->fields); 63 | } 64 | 65 | /** 66 | * Returns a Field object that describes the specified field. 67 | * 68 | * @param string $fieldName Name of field. 69 | * 70 | * @return Field|FileMakerException Field object, if successful. 71 | * @throws FileMakerException 72 | */ 73 | public function getField($fieldName) 74 | { 75 | if (isset($this->fields[$fieldName])) { 76 | return $this->fields[$fieldName]; 77 | } 78 | return $this->fm->returnOrThrowException( 79 | 'Field '.$fieldName.' Not Found in Layout '. $this->layout->getName() 80 | ); 81 | } 82 | 83 | /** 84 | * Returns an associative array with the names of all fields as keys and 85 | * Field objects as the array values. 86 | * 87 | * @return array Array of {@link Field} objects. 88 | */ 89 | public function getFields() 90 | { 91 | return $this->fields; 92 | } 93 | 94 | /** 95 | * Loads extended (FMPXMLLAYOUT) layout information. 96 | * 97 | * @access private 98 | * 99 | * @return boolean|FileMakerException TRUE, if successful. 100 | * @throws FileMakerException 101 | */ 102 | public function loadExtendedInfo() 103 | { 104 | return $this->fm->returnOrThrowException('Related sets do not support extended information.'); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Object/Result.php: -------------------------------------------------------------------------------- 1 | fm = $fm; 41 | } 42 | 43 | /** 44 | * Returns a Layout object that describes the layout of this 45 | * result set. 46 | * 47 | * @return Layout Layout object. 48 | */ 49 | public function getLayout() 50 | { 51 | return $this->layout; 52 | } 53 | 54 | /** 55 | * Returns an array containing each record in the result set. 56 | * 57 | * Each member of the array is a Record object, or an 58 | * instance of the alternate class you specified to use for records 59 | * (see {@link Record}. The array may be empty if 60 | * the result set contains no records. 61 | * 62 | * @return Record[] Record objects. 63 | */ 64 | public function getRecords() 65 | { 66 | return $this->records; 67 | } 68 | 69 | /** 70 | * Returns a list of the names of all fields in the records in 71 | * this result set. 72 | * 73 | * Only the field names are returned. If you need additional 74 | * information, examine the Layout object provided by the 75 | * {@link getLayout()} method. 76 | * 77 | * @return array List of field names as strings. 78 | */ 79 | public function getFields() 80 | { 81 | return $this->layout->listFields(); 82 | } 83 | 84 | /** 85 | * Returns the names of related tables for all portals present in records 86 | * in this result set. 87 | * 88 | * @return array List of related table names as strings. 89 | */ 90 | public function getRelatedSets() 91 | { 92 | return $this->layout->listRelatedSets(); 93 | } 94 | 95 | /** 96 | * Returns the number of records in the table that was accessed. 97 | * 98 | * @return integer Total record count in table. 99 | */ 100 | public function getTableRecordCount() 101 | { 102 | return $this->tableCount; 103 | } 104 | 105 | /** 106 | * Returns the number of records in the entire found set. 107 | * 108 | * @return integer Found record count. 109 | */ 110 | public function getFoundSetCount() 111 | { 112 | return $this->foundSetCount; 113 | } 114 | 115 | /** 116 | * Returns the number of records in the filtered result set. 117 | * 118 | * If no range parameters were specified on the Find command, 119 | * then this value is equal to the result of the {@link getFoundSetCount()} 120 | * method. It is always equal to the value of 121 | * count($response->{@link getRecords()}). 122 | * 123 | * @return integer Filtered record count. 124 | */ 125 | public function getFetchCount() 126 | { 127 | return $this->fetchCount; 128 | } 129 | 130 | /** 131 | * Returns the first record in this result set. 132 | * 133 | * @return Record First record. 134 | */ 135 | public function getFirstRecord() 136 | { 137 | return $this->records[0]; 138 | } 139 | 140 | /** 141 | * Returns the last record in this result set. 142 | * 143 | * @return Record Last record. 144 | */ 145 | public function getLastRecord() 146 | { 147 | return $this->records[sizeof($this->records) - 1]; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Parser/FMPXMLLAYOUT.php: -------------------------------------------------------------------------------- 1 | fm = $fm; 41 | } 42 | 43 | /** 44 | * 45 | * @param string $xmlResponse 46 | * @return boolean|FileMakerException 47 | * @throws FileMakerException 48 | */ 49 | public function parse($xmlResponse) 50 | { 51 | if (empty($xmlResponse)) { 52 | return $this->fm->returnOrThrowException('Did not receive an XML document from the server.'); 53 | } 54 | $this->xmlParser = xml_parser_create(); 55 | xml_set_object($this->xmlParser, $this); 56 | xml_parser_set_option($this->xmlParser, XML_OPTION_CASE_FOLDING, false); 57 | xml_parser_set_option($this->xmlParser, XML_OPTION_TARGET_ENCODING, 'UTF-8'); 58 | /** @psalm-suppress UndefinedFunction */ 59 | xml_set_element_handler($this->xmlParser, 'start', 'end'); 60 | /** @psalm-suppress UndefinedFunction */ 61 | xml_set_character_data_handler($this->xmlParser, 'cdata'); 62 | if (!@xml_parse($this->xmlParser, $xmlResponse)) { 63 | return $this->fm->returnOrThrowException( 64 | sprintf( 65 | 'XML error: %s at line %d', 66 | xml_error_string(xml_get_error_code($this->xmlParser)), 67 | xml_get_current_line_number($this->xmlParser) 68 | ) 69 | ); 70 | } 71 | xml_parser_free($this->xmlParser); 72 | if (!empty($this->errorCode)) { 73 | return $this->fm->returnOrThrowException(null, $this->errorCode); 74 | } 75 | $this->isParsed = true; 76 | return true; 77 | } 78 | 79 | /** 80 | * Add extended infos to a Layout object 81 | * 82 | * @param Layout $layout 83 | * @return FileMakerException 84 | * @throws FileMakerException 85 | */ 86 | public function setExtendedInfo(Layout $layout) 87 | { 88 | if (!$this->isParsed) { 89 | return $this->fm->returnOrThrowException('Attempt to set extended information before parsing data.'); 90 | } 91 | $layout->valueLists = $this->valueLists; 92 | $layout->valueListTwoFields = $this->valueListTwoFields; 93 | foreach ($this->fields as $fieldName => $fieldInfos) { 94 | try { 95 | $field = $layout->getField($fieldName); 96 | if (!FileMaker::isError($field)) { 97 | $field->styleType = $fieldInfos['styleType']; 98 | $field->valueList = $fieldInfos['valueList'] ? $fieldInfos['valueList'] : null; 99 | } 100 | } catch (\Exception $e) { 101 | //Field may be missing when it is stored in a portal, ommit error 102 | } 103 | } 104 | return true; 105 | } 106 | 107 | /** 108 | * xml_parser start element handler 109 | * 110 | * @param resource $parser 111 | * @param string $type 112 | * @param array $datas 113 | */ 114 | private function start($parser, $type, $datas) 115 | { 116 | $datas = $this->fm->toOutputCharset($datas); 117 | switch ($type) { 118 | case 'FIELD': 119 | $this->fieldName = $datas['NAME']; 120 | break; 121 | case 'STYLE': 122 | $this->fields[$this->fieldName]['styleType'] = $datas['TYPE']; 123 | $this->fields[$this->fieldName]['valueList'] = $datas['VALUELIST']; 124 | break; 125 | case 'VALUELIST': 126 | $this->valueLists[$datas['NAME']] = []; 127 | $this->valueListTwoFields[$datas['NAME']] = []; 128 | $this->valueList = $datas['NAME']; 129 | break; 130 | case 'VALUE': 131 | $this->displayValue = $datas['DISPLAY']; 132 | $this->valueLists[$this->valueList][] = ''; 133 | break; 134 | } 135 | $this->insideData = false; 136 | } 137 | 138 | /** 139 | * xml_parser end element handler 140 | * 141 | * @param resource $parser 142 | * @param string $type 143 | */ 144 | private function end($parser, $type) 145 | { 146 | switch ($type) { 147 | case 'FIELD': 148 | $this->fieldName = null; 149 | break; 150 | case 'VALUELIST': 151 | $this->valueList = null; 152 | break; 153 | } 154 | 155 | $this->insideData = false; 156 | } 157 | 158 | /** 159 | * xml_parser character data handler (cdata) 160 | * 161 | * @param resource $parser 162 | * @param string $datas 163 | */ 164 | public function cdata($parser, $datas) 165 | { 166 | if ($this->valueList !== null && preg_match('|\S|', $datas)) { 167 | if ($this->insideData) { 168 | $value = $this->valueListTwoFields[$this->valueList][$this->displayValue]; 169 | $datas = $value . $datas; 170 | } 171 | $arrayVal = [$this->displayValue => $this->fm->toOutputCharset($datas)]; 172 | $this->associativeArrayPush($this->valueListTwoFields[$this->valueList], $arrayVal); 173 | $valueListNum = count($this->valueLists[$this->valueList]) - 1; 174 | $this->valueLists[$this->valueList][$valueListNum] .= $this->fm->toOutputCharset($datas); 175 | $this->insideData = true; 176 | } 177 | } 178 | 179 | /** 180 | * Add values to an existing array 181 | * 182 | * @param array $array 183 | * @param array $values 184 | * @return boolean 185 | */ 186 | public function associativeArrayPush(&$array, $values) 187 | { 188 | if (is_array($values)) { 189 | foreach ($values as $key => $value) { 190 | $array[$key] = $value; 191 | } 192 | return $array; 193 | } 194 | return false; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | fm = new FileMaker( 34 | $GLOBALS['DB_FILE'], 35 | $GLOBALS['DB_HOST'], 36 | $GLOBALS['DB_USER'], 37 | $GLOBALS['DB_PASSWD'], 38 | ['errorHandling' => 'exception'] 39 | ); 40 | $this->fm->newPerformScriptCommand('sample', 'create sample data', 50)->execute(); 41 | 42 | 43 | $layout = $this->fm->getLayout('sample'); 44 | $this->record = new Record($layout); 45 | } 46 | 47 | /** 48 | * Tears down the fixture, for example, closes a network connection. 49 | * This method is called after a test is executed. 50 | */ 51 | protected function tearDown() 52 | { 53 | } 54 | 55 | public function testDefaultDateFormatInput() 56 | { 57 | //default date format handling 58 | $this->record->fm->dateFormat = null; 59 | 60 | $this->record->setField('date_field', date('m/d/Y')); 61 | $this->assertTrue($this->record->validate('date_field')); 62 | 63 | //Test invalid date input 64 | $this->expectException(FileMakerValidationException::class); 65 | $this->record->setField('date_field', date('Y-m-d')); 66 | $this->record->validate('date_field'); 67 | } 68 | 69 | public function testIsoDateFormatInput() 70 | { 71 | //Custom date format (ISO) 72 | $this->record->fm->dateFormat = 'Y-m-d'; 73 | 74 | $this->record->setField('date_field', date('Y-m-d')); 75 | $this->assertTrue($this->record->validate('date_field')); 76 | $this->assertEquals(date('Y-m-d'), $this->record->getField('date_field')); 77 | 78 | //test invalid input 79 | $this->expectException(FileMakerException::class); 80 | $this->record->setField('date_field', date('m/d/Y')); 81 | } 82 | 83 | 84 | public function testCommonDateFormatInput() 85 | { 86 | //Custom date format (International format) 87 | $this->record->fm->dateFormat = 'd/m/Y'; 88 | 89 | $this->record->setField('date_field', date('d/m/Y')); 90 | $this->assertTrue($this->record->validate('date_field')); 91 | $this->assertEquals(date('d/m/Y'), $this->record->getField('date_field')); 92 | } 93 | 94 | public function testCommonToDefaultDateInversion() 95 | { 96 | //Custom date format (International format) 97 | $this->record->fm->dateFormat = 'd/m/Y'; 98 | 99 | //inject invalid m/d/Y date 100 | $this->expectException(FileMakerException::class); 101 | $this->record->setField('date_field', date('06/18/2016')); 102 | } 103 | 104 | public function test1digitDate() 105 | { 106 | //Custom date format (International format) 107 | $this->record->fm->dateFormat = 'd/m/Y'; 108 | $this->record->setField('date_field', date('12/2/2016')); 109 | $this->assertTrue($this->record->validate('date_field')); 110 | } 111 | 112 | public function testDateFormatRequetsActivation() 113 | { 114 | $request = $this->fm->newFindRequest('sample'); 115 | $request->addFindCriterion('date_field', '2016-02-12'); 116 | $this->assertEquals('2016-02-12', $request->findCriteria['date_field']); 117 | 118 | $this->fm->useDateFormatInRequests = true; 119 | $this->fm->dateFormat = 'Y-m-d'; 120 | $request->addFindCriterion('date_field', '2016-02-12'); 121 | $this->assertEquals('02/12/2016', $request->findCriteria['date_field']); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /tests/unit/src/Command/AddTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Command\Add::execute 42 | * @todo Implement testExecute(). 43 | */ 44 | public function testExecute() 45 | { 46 | $addCmd = $this->fm->newAddCommand('sample', [ 47 | 'text_field' => [ 48 | 'sample 1', 49 | 'sample 2', 50 | 'sample 3', 51 | 'sample 4', 52 | ] 53 | ]); 54 | 55 | $result = $addCmd->execute(); 56 | 57 | $this->assertEquals(11, $result->getTableRecordCount()); 58 | } 59 | 60 | /** 61 | * @covers \airmoi\FileMaker\Command\Add::setField 62 | */ 63 | public function testSetField() 64 | { 65 | $addCmd = $this->fm->newAddCommand('sample'); 66 | $this->assertEquals('Sample text', $addCmd->setField('text_field', 'Sample text')); 67 | $this->assertEquals('Sample text', $addCmd->setField('related_sample::text_field', 'Sample text')); 68 | 69 | $date = date('m/d/Y'); 70 | $this->assertEquals($date, $addCmd->setField('date_field', $date)); 71 | 72 | $time = date('H:i:s'); 73 | $this->assertEquals($time, $addCmd->setField('time_field', $time)); 74 | 75 | $timeStamp = date('m/d/Y H:i:s'); 76 | $this->assertEquals($timeStamp, $addCmd->setField('timestamp_field', $timeStamp)); 77 | } 78 | 79 | /** 80 | * @covers \airmoi\FileMaker\Command\Add::setFieldFromTimestamp 81 | */ 82 | public function testSetFieldFromTimestamp() 83 | { 84 | 85 | $addCmd = $this->fm->newAddCommand('sample'); 86 | 87 | $time = time(); 88 | $this->assertEquals(date('m/d/Y', $time), $addCmd->setFieldFromTimestamp('date_field', $time)); 89 | $this->assertEquals(date('m/d/Y H:i:s', $time), $addCmd->setFieldFromTimestamp('timestamp_field', $time)); 90 | $this->assertEquals(date('H:i:s', $time), $addCmd->setFieldFromTimestamp('time_field', $time)); 91 | } 92 | 93 | public function testUseRawData() 94 | { 95 | $this->fm->dateFormat = 'Y-m-d'; 96 | 97 | $time = time(); 98 | $date = date('m/d/Y', $time); 99 | $addCmd = $this->fm->newAddCommand('sample', [ 100 | 'text_field' => [ 101 | 'sample 1', 102 | 'sample 2', 103 | 'sample 3', 104 | 'sample 4', 105 | ], 106 | 'date_field' => $date 107 | ], true); 108 | 109 | $record = $addCmd->execute()->getFirstRecord(); 110 | $this->assertEquals(date('Y-m-d', $time), $record->getField('date_field')); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /tests/unit/src/Command/CommandTest.php: -------------------------------------------------------------------------------- 1 | object = new Command($fm, 'sample'); 25 | } 26 | 27 | /** 28 | * Tears down the fixture, for example, closes a network connection. 29 | * This method is called after a test is executed. 30 | */ 31 | protected function tearDown() 32 | { 33 | } 34 | 35 | /** 36 | * @covers \airmoi\FileMaker\Command\Command::setResultLayout 37 | * @todo Implement testSetResultLayout(). 38 | */ 39 | public function testSetResultLayout() 40 | { 41 | // Remove the following lines when you implement this test. 42 | $this->markTestIncomplete( 43 | 'This test has not been implemented yet.' 44 | ); 45 | } 46 | 47 | /** 48 | * @covers \airmoi\FileMaker\Command\Command::setScript 49 | * @todo Implement testSetScript(). 50 | */ 51 | public function testSetScript() 52 | { 53 | // Remove the following lines when you implement this test. 54 | $this->markTestIncomplete( 55 | 'This test has not been implemented yet.' 56 | ); 57 | } 58 | 59 | /** 60 | * @covers \airmoi\FileMaker\Command\Command::setPreCommandScript 61 | * @todo Implement testSetPreCommandScript(). 62 | */ 63 | public function testSetPreCommandScript() 64 | { 65 | // Remove the following lines when you implement this test. 66 | $this->markTestIncomplete( 67 | 'This test has not been implemented yet.' 68 | ); 69 | } 70 | 71 | /** 72 | * @covers \airmoi\FileMaker\Command\Command::setPreSortScript 73 | * @todo Implement testSetPreSortScript(). 74 | */ 75 | public function testSetPreSortScript() 76 | { 77 | // Remove the following lines when you implement this test. 78 | $this->markTestIncomplete( 79 | 'This test has not been implemented yet.' 80 | ); 81 | } 82 | 83 | /** 84 | * @covers \airmoi\FileMaker\Command\Command::setRecordClass 85 | * @todo Implement testSetRecordClass(). 86 | */ 87 | public function testSetRecordClass() 88 | { 89 | // Remove the following lines when you implement this test. 90 | $this->markTestIncomplete( 91 | 'This test has not been implemented yet.' 92 | ); 93 | } 94 | 95 | /** 96 | * @covers \airmoi\FileMaker\Command\Command::validate 97 | * @todo Implement testValidate(). 98 | */ 99 | public function testValidate() 100 | { 101 | // Remove the following lines when you implement this test. 102 | $this->markTestIncomplete( 103 | 'This test has not been implemented yet.' 104 | ); 105 | } 106 | 107 | /** 108 | * @covers \airmoi\FileMaker\Command\Command::execute 109 | * @todo Implement testExecute(). 110 | */ 111 | public function testExecute() 112 | { 113 | // Remove the following lines when you implement this test. 114 | $this->markTestIncomplete( 115 | 'This test has not been implemented yet.' 116 | ); 117 | } 118 | 119 | /** 120 | * @covers \airmoi\FileMaker\Command\Command::setRecordId 121 | * @todo Implement testSetRecordId(). 122 | */ 123 | public function testSetRecordId() 124 | { 125 | // Remove the following lines when you implement this test. 126 | $this->markTestIncomplete( 127 | 'This test has not been implemented yet.' 128 | ); 129 | } 130 | 131 | /** 132 | * @covers \airmoi\FileMaker\Command\Command::setGlobal 133 | * @todo Implement testSetGlobal(). 134 | */ 135 | public function testSetGlobal() 136 | { 137 | // Remove the following lines when you implement this test. 138 | $this->markTestIncomplete( 139 | 'This test has not been implemented yet.' 140 | ); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /tests/unit/src/Command/CompoundFindTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Command\CompoundFind::add 42 | * @todo Implement testAdd(). 43 | */ 44 | public function testAdd() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | 52 | /** 53 | * @covers \airmoi\FileMaker\Command\CompoundFind::addSortRule 54 | * @todo Implement testAddSortRule(). 55 | */ 56 | public function testAddSortRule() 57 | { 58 | // Remove the following lines when you implement this test. 59 | $this->markTestIncomplete( 60 | 'This test has not been implemented yet.' 61 | ); 62 | } 63 | 64 | /** 65 | * @covers \airmoi\FileMaker\Command\CompoundFind::clearSortRules 66 | * @todo Implement testClearSortRules(). 67 | */ 68 | public function testClearSortRules() 69 | { 70 | // Remove the following lines when you implement this test. 71 | $this->markTestIncomplete( 72 | 'This test has not been implemented yet.' 73 | ); 74 | } 75 | 76 | /** 77 | * @covers \airmoi\FileMaker\Command\CompoundFind::execute 78 | * @todo Implement testExecute(). 79 | */ 80 | public function testExecute() 81 | { 82 | // Remove the following lines when you implement this test. 83 | $this->markTestIncomplete( 84 | 'This test has not been implemented yet.' 85 | ); 86 | } 87 | 88 | /** 89 | * @covers \airmoi\FileMaker\Command\CompoundFind::setRange 90 | * @todo Implement testSetRange(). 91 | */ 92 | public function testSetRange() 93 | { 94 | // Remove the following lines when you implement this test. 95 | $this->markTestIncomplete( 96 | 'This test has not been implemented yet.' 97 | ); 98 | } 99 | 100 | /** 101 | * @covers \airmoi\FileMaker\Command\CompoundFind::getRange 102 | * @todo Implement testGetRange(). 103 | */ 104 | public function testGetRange() 105 | { 106 | // Remove the following lines when you implement this test. 107 | $this->markTestIncomplete( 108 | 'This test has not been implemented yet.' 109 | ); 110 | } 111 | 112 | /** 113 | * @covers \airmoi\FileMaker\Command\CompoundFind::setRelatedSetsFilters 114 | * @todo Implement testSetRelatedSetsFilters(). 115 | */ 116 | public function testSetRelatedSetsFilters() 117 | { 118 | // Remove the following lines when you implement this test. 119 | $this->markTestIncomplete( 120 | 'This test has not been implemented yet.' 121 | ); 122 | } 123 | 124 | /** 125 | * @covers \airmoi\FileMaker\Command\CompoundFind::getRelatedSetsFilters 126 | * @todo Implement testGetRelatedSetsFilters(). 127 | */ 128 | public function testGetRelatedSetsFilters() 129 | { 130 | // Remove the following lines when you implement this test. 131 | $this->markTestIncomplete( 132 | 'This test has not been implemented yet.' 133 | ); 134 | } 135 | 136 | /** 137 | * @covers \airmoi\FileMaker\Command\CompoundFind::setRelatedSetsFiltersParams 138 | * @todo Implement test_setRelatedSetsFilters(). 139 | */ 140 | public function test_setRelatedSetsFilters() 141 | { 142 | // Remove the following lines when you implement this test. 143 | $this->markTestIncomplete( 144 | 'This test has not been implemented yet.' 145 | ); 146 | } 147 | 148 | /** 149 | * @covers \airmoi\FileMaker\Command\CompoundFind::setSortParams 150 | * @todo Implement test_setSortParams(). 151 | */ 152 | public function test_setSortParams() 153 | { 154 | // Remove the following lines when you implement this test. 155 | $this->markTestIncomplete( 156 | 'This test has not been implemented yet.' 157 | ); 158 | } 159 | 160 | /** 161 | * @covers \airmoi\FileMaker\Command\CompoundFind::setRangeParams 162 | * @todo Implement test_setRangeParams(). 163 | */ 164 | public function test_setRangeParams() 165 | { 166 | // Remove the following lines when you implement this test. 167 | $this->markTestIncomplete( 168 | 'This test has not been implemented yet.' 169 | ); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /tests/unit/src/Command/DeleteTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Command\Delete::execute 42 | * @todo Implement testExecute(). 43 | */ 44 | public function testExecute() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/unit/src/Command/DuplicateTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Command\Duplicate::execute 42 | * @todo Implement testExecute(). 43 | */ 44 | public function testExecute() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/unit/src/Command/EditTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 22 | } 23 | 24 | /** 25 | * Sets up the fixture, for example, opens a network connection. 26 | * This method is called before a test is executed. 27 | */ 28 | protected function setUp() 29 | { 30 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 31 | } 32 | 33 | /** 34 | * Tears down the fixture, for example, closes a network connection. 35 | * This method is called after a test is executed. 36 | */ 37 | protected function tearDown() 38 | { 39 | } 40 | 41 | /** 42 | * @covers \airmoi\FileMaker\Command\Edit::execute 43 | */ 44 | public function testExecute() 45 | { 46 | $record = $this->fm->newFindAnyCommand('sample')->execute()->getFirstRecord(); 47 | $timestamp = time(); 48 | $editCmd = $this->fm->newEditCommand('sample', $record->getRecordId(), ['text_field' => $timestamp]); 49 | 50 | $this->assertEquals($timestamp, $editCmd->execute()->getFirstRecord()->getField('text_field')); 51 | } 52 | 53 | /** 54 | * @covers \airmoi\FileMaker\Command\Edit::setField 55 | */ 56 | public function testSetField() 57 | { 58 | $editCmd = $this->fm->newEditCommand('sample', 1); 59 | $this->assertEquals('Sample text', $editCmd->setField('text_field', 'Sample text')); 60 | $this->assertEquals('Sample text', $editCmd->setField('related_sample::text_field', 'Sample text')); 61 | 62 | $date = date('m/d/Y'); 63 | $this->assertEquals($date, $editCmd->setField('date_field', $date)); 64 | 65 | $time = date('H:i:s'); 66 | $this->assertEquals($time, $editCmd->setField('time_field', $time)); 67 | 68 | $timeStamp = date('m/d/Y H:i:s'); 69 | $this->assertEquals($timeStamp, $editCmd->setField('timestamp_field', $timeStamp)); 70 | } 71 | 72 | /** 73 | * @covers \airmoi\FileMaker\Command\Edit::setFieldFromTimestamp 74 | */ 75 | public function testSetFieldFromTimestamp() 76 | { 77 | $editCmd = $this->fm->newEditCommand('sample', 1); 78 | 79 | $time = time(); 80 | $this->assertEquals(date('m/d/Y', $time), $editCmd->setFieldFromTimestamp('date_field', $time)); 81 | $this->assertEquals(date('m/d/Y H:i:s', $time), $editCmd->setFieldFromTimestamp('timestamp_field', $time)); 82 | $this->assertEquals(date('H:i:s', $time), $editCmd->setFieldFromTimestamp('time_field', $time)); 83 | } 84 | 85 | /** 86 | * @covers \airmoi\FileMaker\Command\Edit::setModificationId 87 | */ 88 | public function testSetModificationId() 89 | { 90 | 91 | $record = $this->fm->newFindAnyCommand('sample')->execute()->getFirstRecord(); 92 | $timestamp = time(); 93 | $editCmd = $this->fm->newEditCommand('sample', $record->getRecordId(), ['text_field' => $timestamp]); 94 | $editCmd->setModificationId($record->getModificationId()+1); 95 | 96 | $this->expectException(FileMakerException::class); 97 | $this->expectExceptionCode(306); 98 | $editCmd->execute(); 99 | } 100 | 101 | /** 102 | * @covers \airmoi\FileMaker\Command\Edit::setDeleteRelated 103 | */ 104 | public function testSetDeleteRelated() 105 | { 106 | $record = $this->fm->newFindAnyCommand('sample')->execute()->getFirstRecord(); 107 | $oldRelatedSet = $record->getRelatedSet('related_sample'); 108 | 109 | $editCmd = $this->fm->newEditCommand('sample', $record->getRecordId()); 110 | $editCmd->setDeleteRelated('related_sample.' . $oldRelatedSet[0]->getRecordId()); 111 | 112 | $result = $editCmd->execute(); 113 | 114 | $newRelatedSet = $result->getFirstRecord()->getRelatedSet('related_sample'); 115 | $this->assertEquals(sizeof($oldRelatedSet) - 1, sizeof($newRelatedSet)); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /tests/unit/src/Command/FindAllTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Command\FindAll::execute 42 | * @todo Implement testExecute(). 43 | */ 44 | public function testExecute() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/unit/src/Command/FindAnyTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Command\FindAny::execute 42 | * @todo Implement testExecute(). 43 | */ 44 | public function testExecute() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/unit/src/Command/FindRequestTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Command\FindRequest::setOmit 42 | * @todo Implement testSetOmit(). 43 | */ 44 | public function testSetOmit() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | 52 | /** 53 | * @covers \airmoi\FileMaker\Command\FindRequest::addFindCriterion 54 | * @todo Implement testAddFindCriterion(). 55 | */ 56 | public function testAddFindCriterion() 57 | { 58 | // Remove the following lines when you implement this test. 59 | $this->markTestIncomplete( 60 | 'This test has not been implemented yet.' 61 | ); 62 | } 63 | 64 | /** 65 | * @covers \airmoi\FileMaker\Command\FindRequest::clearFindCriteria 66 | * @todo Implement testClearFindCriteria(). 67 | */ 68 | public function testClearFindCriteria() 69 | { 70 | // Remove the following lines when you implement this test. 71 | $this->markTestIncomplete( 72 | 'This test has not been implemented yet.' 73 | ); 74 | } 75 | 76 | /** 77 | * @covers \airmoi\FileMaker\Command\FindRequest::isEmpty 78 | * @todo Implement testIsEmpty(). 79 | */ 80 | public function testIsEmpty() 81 | { 82 | // Remove the following lines when you implement this test. 83 | $this->markTestIncomplete( 84 | 'This test has not been implemented yet.' 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/unit/src/Command/FindTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Command\Find::addFindCriterion 42 | * @todo Implement testAddFindCriterion(). 43 | */ 44 | public function testAddFindCriterion() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | 52 | /** 53 | * @covers \airmoi\FileMaker\Command\Find::clearFindCriteria 54 | * @todo Implement testClearFindCriteria(). 55 | */ 56 | public function testClearFindCriteria() 57 | { 58 | // Remove the following lines when you implement this test. 59 | $this->markTestIncomplete( 60 | 'This test has not been implemented yet.' 61 | ); 62 | } 63 | 64 | /** 65 | * @covers \airmoi\FileMaker\Command\Find::addSortRule 66 | * @todo Implement testAddSortRule(). 67 | */ 68 | public function testAddSortRule() 69 | { 70 | // Remove the following lines when you implement this test. 71 | $this->markTestIncomplete( 72 | 'This test has not been implemented yet.' 73 | ); 74 | } 75 | 76 | /** 77 | * @covers \airmoi\FileMaker\Command\Find::clearSortRules 78 | * @todo Implement testClearSortRules(). 79 | */ 80 | public function testClearSortRules() 81 | { 82 | // Remove the following lines when you implement this test. 83 | $this->markTestIncomplete( 84 | 'This test has not been implemented yet.' 85 | ); 86 | } 87 | 88 | /** 89 | * @covers \airmoi\FileMaker\Command\Find::execute 90 | * @todo Implement testExecute(). 91 | */ 92 | public function testExecute() 93 | { 94 | // Remove the following lines when you implement this test. 95 | $this->markTestIncomplete( 96 | 'This test has not been implemented yet.' 97 | ); 98 | } 99 | 100 | /** 101 | * @covers \airmoi\FileMaker\Command\Find::setLogicalOperator 102 | * @todo Implement testSetLogicalOperator(). 103 | */ 104 | public function testSetLogicalOperator() 105 | { 106 | // Remove the following lines when you implement this test. 107 | $this->markTestIncomplete( 108 | 'This test has not been implemented yet.' 109 | ); 110 | } 111 | 112 | /** 113 | * @covers \airmoi\FileMaker\Command\Find::setRange 114 | * @todo Implement testSetRange(). 115 | */ 116 | public function testSetRange() 117 | { 118 | // Remove the following lines when you implement this test. 119 | $this->markTestIncomplete( 120 | 'This test has not been implemented yet.' 121 | ); 122 | } 123 | 124 | /** 125 | * @covers \airmoi\FileMaker\Command\Find::getRange 126 | * @todo Implement testGetRange(). 127 | */ 128 | public function testGetRange() 129 | { 130 | // Remove the following lines when you implement this test. 131 | $this->markTestIncomplete( 132 | 'This test has not been implemented yet.' 133 | ); 134 | } 135 | 136 | /** 137 | * @covers \airmoi\FileMaker\Command\Find::setRelatedSetsFilters 138 | * @todo Implement testSetRelatedSetsFilters(). 139 | */ 140 | public function testSetRelatedSetsFilters() 141 | { 142 | // Remove the following lines when you implement this test. 143 | $this->markTestIncomplete( 144 | 'This test has not been implemented yet.' 145 | ); 146 | } 147 | 148 | /** 149 | * @covers \airmoi\FileMaker\Command\Find::getRelatedSetsFilters 150 | * @todo Implement testGetRelatedSetsFilters(). 151 | */ 152 | public function testGetRelatedSetsFilters() 153 | { 154 | // Remove the following lines when you implement this test. 155 | $this->markTestIncomplete( 156 | 'This test has not been implemented yet.' 157 | ); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /tests/unit/src/Command/PerformScriptTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Command\PerformScript::setRange 42 | * @todo Implement testSetRange(). 43 | */ 44 | public function testSetRange() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | 52 | /** 53 | * @covers \airmoi\FileMaker\Command\PerformScript::getRange 54 | * @todo Implement testGetRange(). 55 | */ 56 | public function testGetRange() 57 | { 58 | // Remove the following lines when you implement this test. 59 | $this->markTestIncomplete( 60 | 'This test has not been implemented yet.' 61 | ); 62 | } 63 | 64 | /** 65 | * @covers \airmoi\FileMaker\Command\PerformScript::execute 66 | * @todo Implement testExecute(). 67 | */ 68 | public function testExecute() 69 | { 70 | // Remove the following lines when you implement this test. 71 | $this->markTestIncomplete( 72 | 'This test has not been implemented yet.' 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/unit/src/FileMakerExceptionTest.php: -------------------------------------------------------------------------------- 1 | fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 31 | $this->locales = require(dirname(__FILE__) . '/../../../src/Error/en.php'); 32 | } 33 | 34 | /** 35 | * Tears down the fixture, for example, closes a network connection. 36 | * This method is called after a test is executed. 37 | */ 38 | protected function tearDown() 39 | { 40 | } 41 | 42 | /** 43 | * @covers \airmoi\FileMaker\FileMakerException::getErrorString 44 | */ 45 | public function testGetErrorString() 46 | { 47 | $exception = new FileMakerException($this->fm, null, 0); 48 | $this->assertEquals($this->locales[0], $exception->getErrorString(0)); 49 | 50 | $this->assertEquals($this->locales[15], $exception->getErrorString(15)); 51 | 52 | $this->assertEquals($this->locales[-1], $exception->getErrorString(-5)); 53 | 54 | $this->assertEquals($this->locales[-1], $exception->getErrorString(155555)); 55 | 56 | $this->assertEquals($this->locales[-1], $exception->getErrorString(null)); 57 | } 58 | 59 | /** 60 | * @covers \airmoi\FileMaker\FileMakerException::isValidationError 61 | */ 62 | public function testIsValidationError() 63 | { 64 | $exception = new FileMakerException($this->fm, null, 0); 65 | $this->assertFalse($exception->isValidationError()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/unit/src/FileMakerValidationExceptionTest.php: -------------------------------------------------------------------------------- 1 | fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 26 | $this->locales = require(dirname(__FILE__) . '/../../../src/Error/en.php'); 27 | } 28 | 29 | /** 30 | * Tears down the fixture, for example, closes a network connection. 31 | * This method is called after a test is executed. 32 | */ 33 | protected function tearDown() 34 | { 35 | } 36 | 37 | /** 38 | * @covers \airmoi\FileMaker\FileMakerValidationException::addError 39 | */ 40 | public function testAddError() 41 | { 42 | $exception = new FileMakerValidationException($this->fm); 43 | $field = new Object\Field(new Object\Layout($this->fm)); 44 | $field->name = "sample_field"; 45 | 46 | $exception->addError($field, FileMaker::RULE_DATE_FIELD, "Incorrect date value"); 47 | $this->assertEquals(1, $exception->numErrors()); 48 | 49 | $field2 = new Object\Field(new Object\Layout($this->fm)); 50 | $field2->name = "sample_field2"; 51 | 52 | $exception->addError($field2, FileMaker::RULE_NOTEMPTY, null); 53 | $this->assertEquals(2, $exception->numErrors()); 54 | 55 | return $exception; 56 | } 57 | 58 | /** 59 | * @covers \airmoi\FileMaker\FileMakerValidationException::isValidationError 60 | */ 61 | public function testIsValidationError() 62 | { 63 | $exception = new FileMakerValidationException($this->fm); 64 | $this->assertTrue($exception->isValidationError()); 65 | } 66 | 67 | /** 68 | * @covers \airmoi\FileMaker\FileMakerValidationException::getErrors 69 | * @depends testAddError 70 | */ 71 | public function testGetErrors(FileMakerValidationException $exception) 72 | { 73 | $this->assertEquals(2, sizeof($exception->getErrors())); 74 | 75 | $this->assertEquals(FileMaker::RULE_DATE_FIELD, $exception->getErrors('sample_field')[0][1]); 76 | } 77 | 78 | /** 79 | * @covers \airmoi\FileMaker\FileMakerValidationException::getErrors 80 | * @depends testAddError 81 | */ 82 | public function testGetMessage(FileMakerValidationException $exception) 83 | { 84 | $this->assertEquals(2, sizeof(explode("\n", $exception->getMessage()))); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/unit/src/Helpers/DateFormatTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('12/02/2016', DateFormat::convert('02/12/2016', 'm/d/Y', 'd/m/Y')); 39 | $this->assertEquals('2016-02-12', DateFormat::convert('02/12/2016', 'm/d/Y', 'Y-m-d')); 40 | $this->assertEquals( 41 | '12/02/2016 00:00:00', 42 | DateFormat::convert('02/12/2016 00:00:00', 'm/d/Y H:i:s', 'd/m/Y H:i:s') 43 | ); 44 | 45 | $this->expectException(\Exception::class); 46 | DateFormat::convert('16/02/2016', 'm/d/Y', 'd/m/Y H:i:s'); 47 | } 48 | 49 | public function testConvertISOFormat() 50 | { 51 | $this->assertEquals('12/02/2016', DateFormat::convert('2016-02-12', 'Y-m-d', 'd/m/Y')); 52 | $this->assertEquals('02/12/2016', DateFormat::convert('2016-02-12', 'Y-m-d', 'm/d/Y')); 53 | $this->assertEquals( 54 | '12/02/2016 00:00:00', 55 | DateFormat::convert('2016-02-12 00:00:00', 'Y-m-d H:i:s', 'd/m/Y H:i:s') 56 | ); 57 | 58 | $this->expectException(\Exception::class); 59 | DateFormat::convert('16/02/2016', 'Y-m-d', 'd/m/Y H:i:s'); 60 | } 61 | 62 | public function testSanitizeDateSearchString() 63 | { 64 | $this->assertEquals('12/02/2016', DateFormat::sanitizeDateSearchString("=12/02/2016")); 65 | $this->assertEquals('2016-02-12', DateFormat::sanitizeDateSearchString("==2016-02-12")); 66 | $this->assertEquals('02/*/2016', DateFormat::sanitizeDateSearchString("==02/@/2016")); 67 | $this->assertEquals('02/*/2016', DateFormat::sanitizeDateSearchString("==02/@@/2016")); 68 | $this->assertEquals('*/02/2016', DateFormat::sanitizeDateSearchString("==#/02/2016")); 69 | $this->assertEquals('*/02/2016', DateFormat::sanitizeDateSearchString("==##/02/2016")); 70 | $this->assertEquals('12/02/2016', DateFormat::sanitizeDateSearchString("~12/02/2016")); 71 | $this->assertEquals( 72 | '12/02/2016...12/06/2016', 73 | DateFormat::sanitizeDateSearchString("=12/02/2016...12/06/2016") 74 | ); 75 | } 76 | 77 | public function testConvertSearchCriteria() 78 | { 79 | $this->assertEquals('12/02/2016', DateFormat::convertSearchCriteria("12/02/2016")); 80 | $this->assertEquals('12/02/2016', DateFormat::convertSearchCriteria("02/12/2016", "m/d/Y", 'd/m/Y')); 81 | $this->assertEquals( 82 | '02/12/2016...06/12/2016', 83 | DateFormat::convertSearchCriteria("12/02/2016...12/06/2016", "d/m/Y", 'm/d/Y') 84 | ); 85 | $this->assertEquals( 86 | '02/12/2016...06/12/2016', 87 | DateFormat::convertSearchCriteria("2016-02-12...2016-06-12", "Y-m-d", 'm/d/Y') 88 | ); 89 | $this->assertEquals( 90 | '06/*/2016...12/*/2016', 91 | DateFormat::convertSearchCriteria("2016-06-*...2016-12-*", "Y-m-d", 'm/d/Y') 92 | ); 93 | $this->assertEquals( 94 | '>02/12/2016', 95 | DateFormat::convertSearchCriteria(">2016-02-12", "Y-m-d", 'm/d/Y') 96 | ); 97 | $this->assertEquals( 98 | '<02/12/2016', 99 | DateFormat::convertSearchCriteria("<2016-02-12", "Y-m-d", 'm/d/Y') 100 | ); 101 | $this->assertEquals( 102 | '≤02/12/2016', 103 | DateFormat::convertSearchCriteria("≤2016-02-12", "Y-m-d", 'm/d/Y') 104 | ); 105 | $this->assertEquals( 106 | '<=02/12/2016', 107 | DateFormat::convertSearchCriteria("<=2016-02-12", "Y-m-d", 'm/d/Y') 108 | ); 109 | $this->assertEquals( 110 | '>=02/12/2016', 111 | DateFormat::convertSearchCriteria(">=2016-02-12", "Y-m-d", 'm/d/Y') 112 | ); 113 | $this->assertEquals( 114 | '>=02/*/2016', 115 | DateFormat::convertSearchCriteria(">=2016-02-*", "Y-m-d", 'm/d/Y') 116 | ); 117 | 118 | $this->assertEquals( 119 | '>=02/*/2016', 120 | DateFormat::convertSearchCriteria(">=2016-02-* 12:01:00", "Y-m-d H:i:s", 'm/d/Y') 121 | ); 122 | 123 | $this->assertEquals( 124 | '>=02/12/2016 *:*:*', 125 | DateFormat::convertSearchCriteria(">=2016-02-12", "Y-m-d", 'm/d/Y H:i:s') 126 | ); 127 | 128 | 129 | /*$this->assertEquals( 130 | '>=02/ /2016', 131 | DateFormat::convertSearchCriteria(">=2016-02-*", "Y-m-d", 'D d F Y') 132 | );*/ 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /tests/unit/src/Object/LayoutTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Object\Layout::getName 42 | * @todo Implement testGetName(). 43 | */ 44 | public function testGetName() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | 52 | /** 53 | * @covers \airmoi\FileMaker\Object\Layout::getDatabase 54 | * @todo Implement testGetDatabase(). 55 | */ 56 | public function testGetDatabase() 57 | { 58 | // Remove the following lines when you implement this test. 59 | $this->markTestIncomplete( 60 | 'This test has not been implemented yet.' 61 | ); 62 | } 63 | 64 | /** 65 | * @covers \airmoi\FileMaker\Object\Layout::listFields 66 | * @todo Implement testListFields(). 67 | */ 68 | public function testListFields() 69 | { 70 | // Remove the following lines when you implement this test. 71 | $this->markTestIncomplete( 72 | 'This test has not been implemented yet.' 73 | ); 74 | } 75 | 76 | /** 77 | * @covers \airmoi\FileMaker\Object\Layout::getField 78 | * @todo Implement testGetField(). 79 | */ 80 | public function testGetField() 81 | { 82 | // Remove the following lines when you implement this test. 83 | $this->markTestIncomplete( 84 | 'This test has not been implemented yet.' 85 | ); 86 | } 87 | 88 | /** 89 | * @covers \airmoi\FileMaker\Object\Layout::getFields 90 | * @todo Implement testGetFields(). 91 | */ 92 | public function testGetFields() 93 | { 94 | // Remove the following lines when you implement this test. 95 | $this->markTestIncomplete( 96 | 'This test has not been implemented yet.' 97 | ); 98 | } 99 | 100 | /** 101 | * @covers \airmoi\FileMaker\Object\Layout::listRelatedSets 102 | * @todo Implement testListRelatedSets(). 103 | */ 104 | public function testListRelatedSets() 105 | { 106 | // Remove the following lines when you implement this test. 107 | $this->markTestIncomplete( 108 | 'This test has not been implemented yet.' 109 | ); 110 | } 111 | 112 | /** 113 | * @covers \airmoi\FileMaker\Object\Layout::getRelatedSet 114 | * @todo Implement testGetRelatedSet(). 115 | */ 116 | public function testGetRelatedSet() 117 | { 118 | // Remove the following lines when you implement this test. 119 | $this->markTestIncomplete( 120 | 'This test has not been implemented yet.' 121 | ); 122 | } 123 | 124 | /** 125 | * @covers \airmoi\FileMaker\Object\Layout::hasRelatedSet 126 | * @todo Implement testHasRelatedSet(). 127 | */ 128 | public function testHasRelatedSet() 129 | { 130 | // Remove the following lines when you implement this test. 131 | $this->markTestIncomplete( 132 | 'This test has not been implemented yet.' 133 | ); 134 | } 135 | 136 | /** 137 | * @covers \airmoi\FileMaker\Object\Layout::getRelatedSets 138 | * @todo Implement testGetRelatedSets(). 139 | */ 140 | public function testGetRelatedSets() 141 | { 142 | // Remove the following lines when you implement this test. 143 | $this->markTestIncomplete( 144 | 'This test has not been implemented yet.' 145 | ); 146 | } 147 | 148 | /** 149 | * @covers \airmoi\FileMaker\Object\Layout::listValueLists 150 | * @todo Implement testListValueLists(). 151 | */ 152 | public function testListValueLists() 153 | { 154 | // Remove the following lines when you implement this test. 155 | $this->markTestIncomplete( 156 | 'This test has not been implemented yet.' 157 | ); 158 | } 159 | 160 | /** 161 | * @covers \airmoi\FileMaker\Object\Layout::getValueList 162 | * @todo Implement testGetValueList(). 163 | */ 164 | public function testGetValueList() 165 | { 166 | // Remove the following lines when you implement this test. 167 | $this->markTestIncomplete( 168 | 'This test has not been implemented yet.' 169 | ); 170 | } 171 | 172 | /** 173 | * @covers \airmoi\FileMaker\Object\Layout::getValueListTwoFields 174 | * @todo Implement testGetValueListTwoFields(). 175 | */ 176 | public function testGetValueListTwoFields() 177 | { 178 | // Remove the following lines when you implement this test. 179 | $this->markTestIncomplete( 180 | 'This test has not been implemented yet.' 181 | ); 182 | } 183 | 184 | /** 185 | * @covers \airmoi\FileMaker\Object\Layout::getValueLists 186 | * @todo Implement testGetValueLists(). 187 | */ 188 | public function testGetValueLists() 189 | { 190 | // Remove the following lines when you implement this test. 191 | $this->markTestIncomplete( 192 | 'This test has not been implemented yet.' 193 | ); 194 | } 195 | 196 | /** 197 | * @covers \airmoi\FileMaker\Object\Layout::getValueListsTwoFields 198 | * @todo Implement testGetValueListsTwoFields(). 199 | */ 200 | public function testGetValueListsTwoFields() 201 | { 202 | // Remove the following lines when you implement this test. 203 | $this->markTestIncomplete( 204 | 'This test has not been implemented yet.' 205 | ); 206 | } 207 | 208 | /** 209 | * @covers \airmoi\FileMaker\Object\Layout::loadExtendedInfo 210 | * @todo Implement testLoadExtendedInfo(). 211 | */ 212 | public function testLoadExtendedInfo() 213 | { 214 | // Remove the following lines when you implement this test. 215 | $this->markTestIncomplete( 216 | 'This test has not been implemented yet.' 217 | ); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /tests/unit/src/Object/RecordTest.php: -------------------------------------------------------------------------------- 1 | fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 33 | //$this->fm->newPerformScriptCommand('sample', "create sample data", 50)->execute(); 34 | $this->record = $this->fm->newFindAnyCommand('sample')->execute()->getFirstRecord(); 35 | } 36 | 37 | public static function setUpBeforeClass() 38 | { 39 | $fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 40 | $fm->newPerformScriptCommand('sample', 'create sample data', 50)->execute(); 41 | //$this->record = $this->fm->newFindAnyCommand('sample')->execute()->getFirstRecord(); 42 | } 43 | 44 | /** 45 | * Tears down the fixture, for example, closes a network connection. 46 | * This method is called after a test is executed. 47 | */ 48 | protected function tearDown() 49 | { 50 | } 51 | 52 | /** 53 | * @covers \airmoi\FileMaker\Object\Record::getLayout 54 | */ 55 | public function testGetLayout() 56 | { 57 | $this->assertInstanceOf(Layout::class, $this->record->getLayout()); 58 | } 59 | 60 | /** 61 | * @covers \airmoi\FileMaker\Object\Record::getFields 62 | */ 63 | public function testGetFields() 64 | { 65 | $this->assertTrue(in_array('text_field', $this->record->getFields(), true)); 66 | } 67 | 68 | /** 69 | * @covers \airmoi\FileMaker\Object\Record::getField 70 | */ 71 | public function testGetField() 72 | { 73 | $this->assertStringStartsWith('record #', $this->record->getField('text_field')); 74 | $this->assertStringEndsWith('repeat 2', $this->record->getField('text_field', 1)); 75 | $this->assertStringStartsWith('

', $this->record->getField('text_field', 2, true)); 76 | $this->assertStringStartsWith('<', $this->record->getField('text_field', 2)); 77 | $this->assertEmpty($this->record->getField('text_field', 4)); 78 | } 79 | 80 | /** 81 | * @covers \airmoi\FileMaker\Object\Record::getFieldValueListTwoFields 82 | */ 83 | public function testGetFieldValueListTwoFields() 84 | { 85 | $valueList = $this->record->getFieldValueListTwoFields('id_sample'); 86 | $this->assertEquals(50, count($valueList)); 87 | 88 | $rnd = rand(1, 50); 89 | $this->assertEquals($rnd, $valueList[$rnd . ' record #'.$rnd]); 90 | } 91 | 92 | /** 93 | * @covers \airmoi\FileMaker\Object\Record::getFieldAsTimestamp 94 | */ 95 | public function testGetFieldAsTimestamp() 96 | { 97 | /*$this->markTestIncomplete( 98 | 'This test has not been implemented yet.' 99 | );*/ 100 | $dtObject = \DateTime::createFromFormat('U', $this->record->getFieldAsTimestamp('date_field')); 101 | $dtObject->setTimezone(new \DateTimeZone(date_default_timezone_get())); 102 | $this->assertEquals( $this->record->getField('date_field') , $dtObject->format('m/d/Y') ); 103 | 104 | } 105 | 106 | /** 107 | * @covers \airmoi\FileMaker\Object\Record::setField 108 | */ 109 | public function testSetField() 110 | { 111 | $this->record->setField('text_field', __METHOD__); 112 | $this->assertEquals(__METHOD__, $this->record->getField('text_field')); 113 | 114 | $this->record->setField('text_field', __METHOD__ . ' repeat 1', 1); 115 | $this->assertEquals(__METHOD__ . ' repeat 1', $this->record->getField('text_field', 1)); 116 | 117 | $this->record->setField('related_sample::text_field', __METHOD__ . 'related[1]'); 118 | $this->assertEquals(__METHOD__ . 'related[1]', $this->record->getField('related_sample::text_field')); 119 | 120 | $this->record->setField('related_sample::text_field', __METHOD__ . 'related[2]', 1); 121 | $this->assertEquals(__METHOD__ . 'related[2]', $this->record->getField('related_sample::text_field', 1)); 122 | 123 | $this->record->setField('date_field', date('m/d/Y')); 124 | $this->assertEquals(date('m/d/Y'), $this->record->getField('date_field')); 125 | 126 | 127 | try { 128 | $this->record->setField('missing_field', __METHOD__); 129 | $this->fail(FileMakerException::class . ' should have been thrown'); 130 | } catch (\Exception $e) { 131 | $this->assertInstanceOf(FileMakerException::class, $e); 132 | } 133 | 134 | /** 135 | * Date autoformat 136 | */ 137 | $this->fm->setProperty('dateFormat', 'd/m/Y'); 138 | 139 | $this->record->setField('date_field', '16/06/2016'); 140 | $this->assertEquals('06/16/2016', $this->record->fields['date_field'][0]); 141 | 142 | $this->record->setField('date_field', '01/02/0001'); 143 | $this->assertEquals('02/01/0001', $this->record->fields['date_field'][0]); 144 | 145 | $this->record->setField('date_field', '31/12/4000'); 146 | $this->assertEquals('12/31/4000', $this->record->fields['date_field'][0]); 147 | 148 | 149 | try { 150 | $this->record->setField('date_field', '2016-08-19'); 151 | $this->fail(FileMakerException::class . ' should have been thrown'); 152 | } catch (\Exception $e) { 153 | $this->assertInstanceOf(FileMakerException::class, $e); 154 | } 155 | 156 | $this->record->setField('timestamp_field', '08/01/1942 00:00:00'); 157 | $this->assertEquals('01/08/1942 00:00:00', $this->record->fields['timestamp_field'][0]); 158 | 159 | try { 160 | $this->record->setField('timestamp_field', '2016-08-19'); 161 | $this->fail(FileMakerException::class . ' should have been thrown'); 162 | } catch (\Exception $e) { 163 | $this->assertInstanceOf(FileMakerException::class, $e); 164 | } 165 | } 166 | 167 | /** 168 | * @covers \airmoi\FileMaker\Object\Record::setFieldFromTimestamp 169 | */ 170 | public function testSetFieldFromTimestamp() 171 | { 172 | 173 | /* 174 | * Date fields 175 | */ 176 | $dt = new \DateTime(); 177 | $this->record->setFieldFromTimestamp('date_field', $dt->format('U')); 178 | $this->assertEquals(date('m/d/Y'), $this->record->getField('date_field')); 179 | 180 | $time = time()+3600; 181 | $this->record->setFieldFromTimestamp('date_field', $time); 182 | $this->assertEquals(date('m/d/Y', time()+3600), $this->record->getField('date_field')); 183 | 184 | /* 185 | * To be activated when setFieldFromTimestamp will implement DateTime 186 | */ 187 | //$dt = new \DateTime('0001-01-01T00:00:00'); 188 | //$this->record->setFieldFromTimestamp('date_field', $dt->format('U') ); 189 | //$this->assertEquals('01/01/0001', $this->record->getField('date_field')); 190 | 191 | //$dt = new \DateTime('4000-12-31T23:59:59'); 192 | //$this->record->setFieldFromTimestamp('date_field', $dt->format('U') ); 193 | //$this->assertEquals('12/31/4000', $this->record->getField('date_field')); 194 | 195 | /* 196 | * timestamp fields 197 | */ 198 | $dt = new \DateTime(); 199 | $this->record->setFieldFromTimestamp('timestamp_field', $dt->format('U')); 200 | $this->assertEquals($dt->format('m/d/Y H:i:s'), $this->record->getField('timestamp_field')); 201 | 202 | $time = time()+3600; 203 | $this->record->setFieldFromTimestamp('timestamp_field', $time); 204 | $this->assertEquals(date('m/d/Y H:i:s', $time), $this->record->getField('timestamp_field')); 205 | 206 | /* 207 | * To be activated when setFieldFromTimestamp will implement DateTime 208 | */ 209 | //$dt = new \DateTime('0001-01-01T00:00:00'); 210 | //$this->record->setFieldFromTimestamp('timestamp_field', $dt->format('U') ); 211 | //$this->assertEquals('01/01/0001 00:00:00', $this->record->getField('timestamp_field')); 212 | 213 | //$dt = new \DateTime('4000-12-31T23:59:59'); 214 | //$this->record->setFieldFromTimestamp('timestamp_field', $dt->format('U') ); 215 | //$this->assertEquals('12/31/4000 23:59:59', $this->record->getField('timestamp_field')); 216 | 217 | /* 218 | * time field 219 | */ 220 | $dt = new \DateTime(); 221 | $this->record->setFieldFromTimestamp('time_field', $dt->format('U')); 222 | $this->assertEquals($dt->format('H:i:s'), $this->record->getField('time_field')); 223 | 224 | $time = time()+3600; 225 | $this->record->setFieldFromTimestamp('time_field', $time); 226 | $this->assertEquals(date('H:i:s', $time), $this->record->getField('time_field')); 227 | 228 | /* 229 | * To be activated when setFieldFromTimestamp will implement DateTime 230 | */ 231 | //$dt = new \DateTime('0001-01-01T00:00:00'); 232 | //$this->record->setFieldFromTimestamp('time_field', $dt->format('U') ); 233 | //$this->assertEquals('00:00:00', $this->record->getField('time_field')); 234 | 235 | //$dt = new \DateTime('4000-12-31T23:59:59'); 236 | //$this->record->setFieldFromTimestamp('time_field', $dt->format('U') ); 237 | //$this->assertEquals('23:59:59', $this->record->getField('time_field')); 238 | } 239 | 240 | /** 241 | * @covers \airmoi\FileMaker\Object\Record::getRecordId 242 | */ 243 | public function testGetRecordId() 244 | { 245 | $this->assertNotNull($this->record->getRecordId()); 246 | } 247 | 248 | /** 249 | * @covers \airmoi\FileMaker\Object\Record::getModificationId 250 | */ 251 | public function testGetModificationId() 252 | { 253 | $this->assertNotNull($this->record->getModificationId()); 254 | } 255 | 256 | /** 257 | * @covers \airmoi\FileMaker\Object\Record::getRelatedSet 258 | */ 259 | public function testGetRelatedSet() 260 | { 261 | $this->assertContainsOnlyInstancesOf(Record::class, $this->record->getRelatedSet('related_sample')); 262 | } 263 | 264 | /** 265 | * @covers \airmoi\FileMaker\Object\Record::newRelatedRecord 266 | */ 267 | public function testNewRelatedRecord() 268 | { 269 | $relatedRecord = $this->record->newRelatedRecord('related_sample'); 270 | 271 | $this->assertEquals($this->record, $relatedRecord->getParent()); 272 | $this->assertEquals('related_sample', $relatedRecord->relatedSetName); 273 | } 274 | 275 | /** 276 | * @covers \airmoi\FileMaker\Object\Record::setParent 277 | */ 278 | public function testSetParent() 279 | { 280 | $record = new Record($this->record->getLayout()); 281 | $record->setParent($this->record); 282 | $this->assertEquals($this->record, $record->getParent()); 283 | } 284 | 285 | /** 286 | * @covers \airmoi\FileMaker\Object\Record::validate 287 | */ 288 | public function testValidate() 289 | { 290 | $this->fm->errorHandling = "default"; 291 | $this->record->setField('date_field', 'incorrect Date'); 292 | $this->assertInstanceOf(FileMakerValidationException::class, $this->record->validate('date_field')); 293 | 294 | $e = $this->record->validate(); 295 | $this->assertInstanceOf(FileMakerValidationException::class, $this->record->validate()); 296 | $this->assertEquals(1, $this->record->validate()->numErrors()); 297 | 298 | $this->record->setField('text_field', str_repeat('0', 51)); 299 | $this->assertEquals(2, $this->record->validate()->numErrors()); 300 | 301 | $this->record->setField('text_field', '', 1); 302 | $this->assertEquals(3, $this->record->validate()->numErrors()); 303 | 304 | $this->record->setField('timestamp_field', 'incorrect timestamp', 1); 305 | $this->assertEquals(4, $this->record->validate()->numErrors()); 306 | 307 | $this->record->setField('time_field', 'incorrect time', 1); 308 | $this->assertEquals(5, $this->record->validate()->numErrors()); 309 | 310 | $this->record->setField('number_field', 'incorrect number', 1); 311 | $this->assertEquals(6, $this->record->validate()->numErrors()); 312 | 313 | //$this->record->setField('date_field', '06/16/16', 1); 314 | //$this->assertEquals(7, $this->record->validate()->numErrors()); 315 | } 316 | 317 | /** 318 | * @covers \airmoi\FileMaker\Object\Record::commit 319 | * @todo Implement testCommit(). 320 | */ 321 | public function testCommit() 322 | { 323 | // Remove the following lines when you implement this test. 324 | $this->markTestIncomplete( 325 | 'This test has not been implemented yet.' 326 | ); 327 | } 328 | 329 | /** 330 | * @covers \airmoi\FileMaker\Object\Record::delete 331 | * @todo Implement testDelete(). 332 | */ 333 | public function testDelete() 334 | { 335 | // Remove the following lines when you implement this test. 336 | $this->markTestIncomplete( 337 | 'This test has not been implemented yet.' 338 | ); 339 | } 340 | 341 | /** 342 | * @covers \airmoi\FileMaker\Object\Record::getRelatedRecordById 343 | * @todo Implement testGetRelatedRecordById(). 344 | */ 345 | public function testGetRelatedRecordById() 346 | { 347 | // Remove the following lines when you implement this test. 348 | $this->markTestIncomplete( 349 | 'This test has not been implemented yet.' 350 | ); 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /tests/unit/src/Object/RelatedSetTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Object\RelatedSet::getName 42 | * @todo Implement testGetName(). 43 | */ 44 | public function testGetName() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | 52 | /** 53 | * @covers \airmoi\FileMaker\Object\RelatedSet::listFields 54 | * @todo Implement testListFields(). 55 | */ 56 | public function testListFields() 57 | { 58 | // Remove the following lines when you implement this test. 59 | $this->markTestIncomplete( 60 | 'This test has not been implemented yet.' 61 | ); 62 | } 63 | 64 | /** 65 | * @covers \airmoi\FileMaker\Object\RelatedSet::getField 66 | * @todo Implement testGetField(). 67 | */ 68 | public function testGetField() 69 | { 70 | // Remove the following lines when you implement this test. 71 | $this->markTestIncomplete( 72 | 'This test has not been implemented yet.' 73 | ); 74 | } 75 | 76 | /** 77 | * @covers \airmoi\FileMaker\Object\RelatedSet::getFields 78 | * @todo Implement testGetFields(). 79 | */ 80 | public function testGetFields() 81 | { 82 | // Remove the following lines when you implement this test. 83 | $this->markTestIncomplete( 84 | 'This test has not been implemented yet.' 85 | ); 86 | } 87 | 88 | /** 89 | * @covers \airmoi\FileMaker\Object\RelatedSet::loadExtendedInfo 90 | * @todo Implement testLoadExtendedInfo(). 91 | */ 92 | public function testLoadExtendedInfo() 93 | { 94 | // Remove the following lines when you implement this test. 95 | $this->markTestIncomplete( 96 | 'This test has not been implemented yet.' 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /tests/unit/src/Object/ResultTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Object\Result::getLayout 42 | * @todo Implement testGetLayout(). 43 | */ 44 | public function testGetLayout() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | 52 | /** 53 | * @covers \airmoi\FileMaker\Object\Result::getRecords 54 | * @todo Implement testGetRecords(). 55 | */ 56 | public function testGetRecords() 57 | { 58 | // Remove the following lines when you implement this test. 59 | $this->markTestIncomplete( 60 | 'This test has not been implemented yet.' 61 | ); 62 | } 63 | 64 | /** 65 | * @covers \airmoi\FileMaker\Object\Result::getFields 66 | * @todo Implement testGetFields(). 67 | */ 68 | public function testGetFields() 69 | { 70 | // Remove the following lines when you implement this test. 71 | $this->markTestIncomplete( 72 | 'This test has not been implemented yet.' 73 | ); 74 | } 75 | 76 | /** 77 | * @covers \airmoi\FileMaker\Object\Result::getRelatedSets 78 | * @todo Implement testGetRelatedSets(). 79 | */ 80 | public function testGetRelatedSets() 81 | { 82 | // Remove the following lines when you implement this test. 83 | $this->markTestIncomplete( 84 | 'This test has not been implemented yet.' 85 | ); 86 | } 87 | 88 | /** 89 | * @covers \airmoi\FileMaker\Object\Result::getTableRecordCount 90 | * @todo Implement testGetTableRecordCount(). 91 | */ 92 | public function testGetTableRecordCount() 93 | { 94 | // Remove the following lines when you implement this test. 95 | $this->markTestIncomplete( 96 | 'This test has not been implemented yet.' 97 | ); 98 | } 99 | 100 | /** 101 | * @covers \airmoi\FileMaker\Object\Result::getFoundSetCount 102 | * @todo Implement testGetFoundSetCount(). 103 | */ 104 | public function testGetFoundSetCount() 105 | { 106 | // Remove the following lines when you implement this test. 107 | $this->markTestIncomplete( 108 | 'This test has not been implemented yet.' 109 | ); 110 | } 111 | 112 | /** 113 | * @covers \airmoi\FileMaker\Object\Result::getFetchCount 114 | * @todo Implement testGetFetchCount(). 115 | */ 116 | public function testGetFetchCount() 117 | { 118 | // Remove the following lines when you implement this test. 119 | $this->markTestIncomplete( 120 | 'This test has not been implemented yet.' 121 | ); 122 | } 123 | 124 | /** 125 | * @covers \airmoi\FileMaker\Object\Result::getFirstRecord 126 | * @todo Implement testGetFirstRecord(). 127 | */ 128 | public function testGetFirstRecord() 129 | { 130 | // Remove the following lines when you implement this test. 131 | $this->markTestIncomplete( 132 | 'This test has not been implemented yet.' 133 | ); 134 | } 135 | 136 | /** 137 | * @covers \airmoi\FileMaker\Object\Result::getLastRecord 138 | * @todo Implement testGetLastRecord(). 139 | */ 140 | public function testGetLastRecord() 141 | { 142 | // Remove the following lines when you implement this test. 143 | $this->markTestIncomplete( 144 | 'This test has not been implemented yet.' 145 | ); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /tests/unit/src/Parser/FMPXMLLAYOUTTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Parser\FMPXMLLAYOUT::parse 42 | * @todo Implement testParse(). 43 | */ 44 | public function testParse() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | 52 | /** 53 | * @covers \airmoi\FileMaker\Parser\FMPXMLLAYOUT::setExtendedInfo 54 | * @todo Implement testSetExtendedInfo(). 55 | */ 56 | public function testSetExtendedInfo() 57 | { 58 | // Remove the following lines when you implement this test. 59 | $this->markTestIncomplete( 60 | 'This test has not been implemented yet.' 61 | ); 62 | } 63 | 64 | /** 65 | * @covers \airmoi\FileMaker\Parser\FMPXMLLAYOUT::cdata 66 | * @todo Implement test_cdata(). 67 | */ 68 | public function test_cdata() 69 | { 70 | // Remove the following lines when you implement this test. 71 | $this->markTestIncomplete( 72 | 'This test has not been implemented yet.' 73 | ); 74 | } 75 | 76 | /** 77 | * @covers \airmoi\FileMaker\Parser\FMPXMLLAYOUT::associativeArrayPush 78 | * @todo Implement testAssociative_array_push(). 79 | */ 80 | public function testAssociative_array_push() 81 | { 82 | // Remove the following lines when you implement this test. 83 | $this->markTestIncomplete( 84 | 'This test has not been implemented yet.' 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/unit/src/Parser/FMResultSetTest.php: -------------------------------------------------------------------------------- 1 | newPerformScriptCommand('sample', 'create sample data', 10)->execute(); 21 | } 22 | 23 | /** 24 | * Sets up the fixture, for example, opens a network connection. 25 | * This method is called before a test is executed. 26 | */ 27 | protected function setUp() 28 | { 29 | $this->fm = new FileMaker($GLOBALS['DB_FILE'], $GLOBALS['DB_HOST'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']); 30 | } 31 | 32 | /** 33 | * Tears down the fixture, for example, closes a network connection. 34 | * This method is called after a test is executed. 35 | */ 36 | protected function tearDown() 37 | { 38 | } 39 | 40 | /** 41 | * @covers \airmoi\FileMaker\Parser\FMResultSet::parse 42 | * @todo Implement testParse(). 43 | */ 44 | public function testParse() 45 | { 46 | // Remove the following lines when you implement this test. 47 | $this->markTestIncomplete( 48 | 'This test has not been implemented yet.' 49 | ); 50 | } 51 | 52 | /** 53 | * @covers \airmoi\FileMaker\Parser\FMResultSet::setResult 54 | * @todo Implement testSetResult(). 55 | */ 56 | public function testSetResult() 57 | { 58 | // Remove the following lines when you implement this test. 59 | $this->markTestIncomplete( 60 | 'This test has not been implemented yet.' 61 | ); 62 | } 63 | 64 | /** 65 | * @covers \airmoi\FileMaker\Parser\FMResultSet::setLayout 66 | * @todo Implement testSetLayout(). 67 | */ 68 | public function testSetLayout() 69 | { 70 | // Remove the following lines when you implement this test. 71 | $this->markTestIncomplete( 72 | 'This test has not been implemented yet.' 73 | ); 74 | } 75 | } 76 | --------------------------------------------------------------------------------