diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index 11b4009..0000000 --- a/.coveralls.yml +++ /dev/null @@ -1,3 +0,0 @@ -service_name: travis-ci -coverage_clover: coverage/clover.xml -json_path: coverage/coveralls-upload.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a15defe --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,124 @@ +name: CI + +on: + push: + branches: + - '2.x-next' + pull_request: + branches: + - '*' + schedule: + - cron: "0 0 1 * *" + +jobs: + testsuite: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php-version: ['7.2', '7.4', '8.0', '8.1', '8.2'] + db-family: ['mysql'] + db-version: ['mysql-8'] + coverage: [''] + prefer-lowest: [''] + include: + - php-version: '7.4' + db-family: 'mysql' + db-version: 'mysql-8' + coverage: 'pcov' + - php-version: '7.4' + db-family: 'mysql' + db-version: 'mysql-5.7' + prefer-lowest: 'prefer-lowest' + # - php-version: '7.4' + # db-family: 'mariadb' + # db-version: '10.2' + # - php-version: '7.4' + # db-family: 'mariadb' + # db-version: 'latest' + + env: + PHP_VERSION: ${{ matrix.php-version }} + DB_FAMILY: ${{ matrix.db-family }} + DB_VERSION: ${{ matrix.db-version }} + + steps: + - uses: actions/checkout@v3 + + - name: Setup MySQL 5.7 + if: matrix.db-version == 'mysql-5.7' + run: docker run --rm --name=mysqld -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=cakephp_orm_json -p 3306:3306 -d mysql:5.7 --character-set-server=utf8 + + - name: Setup MySQL 8 (latest) + if: matrix.db-version == 'mysql-8' + run: docker run --rm --name=mysqld -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=cakephp_orm_json -p 3306:3306 -d mysql --default-authentication-plugin=mysql_native_password --disable-log-bin + + - name: Setup MariaDB + uses: getong/mariadb-action@v1.1 + if: matrix.db-family == 'mariadb' + with: + mariadb version: ${{ matrix.db-version }} + mysql database: 'cakephp_orm_json' + mysql root password: 'root' + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: mbstring, intl, apcu, memcached, redis, pdo_${{ matrix.db-family }} + ini-values: apc.enable_cli = 1 + tools: cs2pr + coverage: ${{ matrix.coverage }} + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Get date part for cache key + id: key-date + run: echo "::set-output name=date::$(date +'%Y-%m')" + + - name: Cache composer dependencies + uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }} + + - name: Composer install + run: | + if ${{ matrix.prefer-lowest == 'prefer-lowest' }}; then + composer enable-compat + elif ${{ matrix.php-version == '8.2' }}; then + composer update --ignore-platform-req=php + else + composer update + fi + + - name: Wait for MySQL/MariaDB + if: matrix.db-family == 'mysql' || matrix.db-family == 'mariadb' + run: while ! `mysqladmin ping -h 127.0.0.1 --silent`; do printf 'Waiting for MySQL...\n'; sleep 2; done; + + - name: Run PHPUnit (No coverage) + if: matrix.coverage == 'none' && matrix.prefer-lowest != 'prefer-lowest' + run: vendor/bin/phpunit + + - name: Run PHPUnit (With coverage) + if: matrix.coverage == 'pcov' + run: | + vendor/bin/phpunit -c phpunit.coverage.xml.dist --coverage-clover logs/clover.xml + composer global require php-coveralls/php-coveralls + php-coveralls --coverage_clover=logs/clover.xml --json_path=logs/coveralls-upload.json -v + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Run PHPUnit (Legacy) + if: matrix.prefer-lowest == 'prefer-lowest' + run: vendor/bin/phpunit -c phpunit.compat.xml.dist + + - name: Run PHP CodeSniffer + if: ${{ matrix.php-version == '7.4' && matrix.db-version == 'mysql-8'}} + run: vendor/bin/phpcs --report=checkstyle src/ tests/ | cs2pr + + - name: Run PHPstan + if: ${{ matrix.php-version == '7.4' && matrix.db-version == 'mysql-8'}} + run: composer stan-github diff --git a/.gitignore b/.gitignore index 77c4903..b3c5deb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ phpDocumentor.phar node_modules package-lock.json package.json +.phpunit.result.cache +logs +old diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 219d140..0000000 --- a/.travis.yml +++ /dev/null @@ -1,54 +0,0 @@ -dist: xenial -sudo: required - -env: - global: - - CI_BUILD_NUMBER=$TRAVIS_BUILD_NUMBER - - CI_PULL_REQUEST=$TRAVIS_PULL_REQUEST - - CI_BRANCH=$TRAVIS_BRANCH - -language: php -php: - - 7.1 - - 7.2 - -services: - - mysql - -before_install: - #- sudo mysql -e "use mysql; update user set authentication_string=PASSWORD('datpassword') where User='root'; update user set plugin='mysql_native_password';FLUSH PRIVILEGES;" - #- sudo mysql_upgrade - #- sudo service mysql restart - - mysql -e 'CREATE DATABASE cakeormjson_test;' - -install: - - travis_retry composer install --no-interaction --no-suggest - - wget -c -nc --retry-connrefused --tries=0 https://github.com/php-coveralls/php-coveralls/releases/download/v2.0.0/php-coveralls.phar -O coveralls.phar - - chmod +x coveralls.phar - - php coveralls.phar --version - -# Create a storage folder for coverage report -before_script: - - mkdir -p coverage - - ls -al - -# Testing the app (see phpunit.xml) for configs, generating Code Coverage report -script: - - ./vendor/bin/phpunit --coverage-clover coverage/clover.xml - - composer phpstan - -after_success: -# Submit coverage report to Coveralls servers, see .coveral ls.yml - - travis_retry php coveralls.phar -v - -#after_failure: - -# Tell Travis CI to monitor only 'master' branch -branches: - only: master - -# You can delete the cache using travis-ci web interface -cache: - directories: - - vendor - - $HOME/.cache/composer diff --git a/README.md b/README.md index 0ddfe67..be96b27 100644 --- a/README.md +++ b/README.md @@ -1,292 +1,621 @@ -[![Latest Stable Version](https://img.shields.io/github/release/liqueurdetoile/cakephp-orm-json.svg?style=flat-square)](https://packagist.org/packages/liqueurdetoile/cakephp-orm-json -) -[![Build Status](https://travis-ci.org/liqueurdetoile/cakephp-orm-json.svg?branch=master)](https://travis-ci.org/liqueurdetoile/cakephp-orm-json) +[![Latest Stable Version](https://img.shields.io/github/release/liqueurdetoile/cakephp-orm-json.svg?style=flat-square)](https://packagist.org/packages/liqueurdetoile/cakephp-orm-json) +![2.x-next status](https://github.com/liqueurdetoile/cakephp-orm-json/actions/workflows/ci.yml/badge.svg?branch=2.x-next) [![Coverage Status](https://coveralls.io/repos/github/liqueurdetoile/cakephp-orm-json/badge.svg?branch=master)](https://coveralls.io/github/liqueurdetoile/cakephp-orm-json?branch=master) +![PR_Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen) [![license](https://img.shields.io/github/license/liqueurdetoile/cakephp-orm-json.svg?style=flat-square)](https://packagist.org/packages/liqueurdetoile/cakephp-orm-json) - # Cake-orm-json plugin -**This branch is for CakePHP 3.5+** - -This plugin adds support to perform usual CakePHP ORM operations on JSON types fields. +**This branch is for CakePHP ^3.7|^4.0 and supports PHP ^7.2|^8.0** +**For previous CakePHP versions, please use v1 of this plugin** + +This plugin extends usual CakePHP ORM operations with JSON fields. It embeds a special [datfield notation](#datfield-format) that allow to easily target a path into a JSON field data. With it, you can : + +- select, order and filter queries : `$q = $table->find('json')->where(['jfield->darn.deep.key' => true])->all()` +- apply data types inside JSON data +- easily access, mutate and delete JSON data in entity : `$e->get('jfield->darn.deep.key')` +- use JSON data as foreign keys for associations (quite extreme indeed and not really recommended but it can be useful at margin) + +**Relational databases are not primarily designed** to handle non-schemed data and using JSON data fields can issue really bad performances. Nevertheless the newest releases of engines have also shown significant improvements in dealing with JSON data and raising of NoSQL has created different needs and constraints. + +**Caution : As with version 2.0.0, it only works with Mysql databases >= 5.7.8. Setup is done to allow adding other engines to this plugin and I hope to release it at least for MariaDB and SQLite, maybe PostgreSQL. Any help would be very appreciated though :smile** + + + +- [Cake-orm-json plugin](#cake-orm-json-plugin) + - [Installation](#installation) + - [Install plugin](#install-plugin) + - [Recommended setup](#recommended-setup) + - [Embeds `DatFieldAwareTrait` in models](#embeds-datfieldawaretrait-in-models) + - [Embeds `DatFieldBehavior` in models](#embeds-datfieldbehavior-in-models) + - [Enbeds `DatFieldTrait` with entities](#enbeds-datfieldtrait-with-entities) + - [Datfield format](#datfield-format) + - [Usage](#usage) + - [Quick guide](#quick-guide) + - [Selecting fields](#selecting-fields) + - [Filtering and ordering data](#filtering-and-ordering-data) + - [Using aggregation and functions](#using-aggregation-and-functions) + - [Marshaling data](#marshaling-data) + - [What brings `DatFieldTrait` within entities ?](#what-brings-datfieldtrait-within-entities-) + - [For PHPStan users](#for-phpstan-users) + - [Using JSON data types](#using-json-data-types) + - [Using JSON data types](#using-json-data-types) + - [Registering JSON data type permanently](#registering-json-data-type-permanently) + - [Registering JSON data types for a single query](#registering-json-data-types-for-a-single-query) + - [Linking models together](#linking-models-together) + - [Limitations with CakePHP 3.x](#limitations-with-cakephp-3x) + - [Advanced setup](#advanced-setup) + - [Use the upgraded driver for all models](#use-the-upgraded-driver-for-all-models) + - [Enable or disable upgraded driver per model](#enable-or-disable-upgraded-driver-per-model) + - [Use upgraded driver per query](#use-upgraded-driver-per-query) + - [Some tricky things to know](#some-tricky-things-to-know) + - [API reference](#api-reference) + - [Difference from v1.x](#difference-from-v1x) + - [Changelog](#changelog) + - [Disclaimer](#disclaimer) -*Never forget that relational databases **are not primarily designed** to manage non-schemed data and using JSON data fields can issue bad performances.* - -However, there is always some cases where JSON fields are handy, especially with EAV data model and this plugin can ease the pain to use them with CakePHP. + -**Caution : It only works with Mysql databases > 5.7 (supporting JSON type field) by now.** +## Installation -This plugin brings : -- JsonBehavior behavior for models -- JsonTrait trait for entities -- Underlying JsonQuery class extending core Query to manage datfield notation and translate queries +### Install plugin +You can install the latest version of this plugin into your CakePHP application using [composer](http://getcomposer.org). -Provided behavior and query are relying on [Mysql JSON_EXTRACT function](https://dev.mysql.com/doc/refman/8.0/en/json-functions.html) to work from CakePHP inside JSON data nearly as if each property was a regular field. +```bash +composer require liqueurdetoile/cakephp-orm-json +``` -Provided trait provides functions to quick get/set values in JSON data. +The base namespace of the plugin is `Lqdt\OrmJson`. -Both are based on a [specific custom notation](#Datfield-format-explanation). +> **Important :** If you plan to use this plugin with Cakephp 3.x, you must enable compatibility mode by adding this line to your `config/bootstrap.php` in order to setup required classes alias from Cakephp 4.x : - +```php +\Lqdt\OrmJson\DatField\Compat3x::enable(); +``` -- [Installation](#installation) - - [Install plugin](#install-plugin) - - [Loading plugin `Lqdt/OrmJson`](#loading-plugin-lqdtormjson) - - [Add JSON behavior to tables](#add-json-behavior-to-tables) - - [Add JSON Trait](#add-json-trait) -- [Usage](#usage) - - [Datfield format explanation](#datfield-format-explanation) - - [Performs finds in JSON data](#performs-finds-in-json-data) - - [Selecting datfields](#selecting-datfields) - - [filtering datfields](#filtering-datfields) - - [Sorting datfields](#sorting-datfields) - - [Create and update JSON in an entity from model](#create-and-update-json-in-an-entity-from-model) - - [Use JSON setter/getter methods with entities](#use-json-settergetter-methods-with-entities) - - [API reference](#api-reference) -- [Changelog](#changelog) -- [Disclaimer](#disclaimer) +### Recommended setup +This plugin is working by cloning the used connection in order to upgrade its driver and insert a translation step that will allow to parse datfield notation into a suitable form that can then be used by cakePHP ORM. Obviously, adding this layer if not using datfield notation is pretty useless though resource consuming. - +There's many ways to setup the plugin in order to optimize things but we recommend this one as it will fit most of use cases : +- Add `DatFieldBehavior` that have JSON fields without upgrading table connection, and add `DatFieldTrait` to their corresponding entities; +- Add `DatFieldAwareTrait` to models without JSON fields but which uses associations relying on datfield foreign keys; +- Always call `find('datfields')` or `find('json')` when querying if using datfield notation to ensure that translation is correctly enabled. -## Installation +Keep in my mind that you keep full control on using regular or upgraded connection. If you have some performance issues with this setup, please check [advanced setup](#advanced_setup) for more informations. -### Install plugin -You can install this plugin into your CakePHP application using [composer](http://getcomposer.org). +#### Embeds `DatFieldAwareTrait` in models +Usually, you will use this trait in models that needs to be linked to another model with a foreign key living in JSON data. The trait allows you to link models based on datfield foreign key(s) and to easily switch between regular or upgraded connection. -``` -composer require liqueurdetoile/cakephp-orm-json -``` +```php + +``` +#### Embeds `DatFieldBehavior` in models +Behavior brings up all of the convenience of [`DatFieldAwareTrait`](#embeds-datfieldawaretrait-in-models) and takes care of marshaling datfield notation when creating/patching entities. The behavior is targetted to models which contains JSON fields. It can also be used to store permanent JSON data types when marshaling or persisting data. -### Add JSON behavior to tables -See [CakePHP 3.x reference for behaviors](https://book.cakephp.org/3.0/en/orm/behaviors.html#creating-a-behavior) ```php -// App/Model/Table/UsersTable.php +addBehavior('Lqdt/OrmJson.json'); - // [...] + $this->addBehavior('Lqdt/OrmJson.DatField'); } } +?> ``` +You can pass `['upgrade' => true]` as behavior config options to request an immediate connection upgrade for the model. + +The behavior can be used without `DatFieldTrait` in entities and vice-versa. + +#### Enbeds `DatFieldTrait` with entities +Datfield trait brings up tools to access and manipulate with ease the content of JSON fields. Obviously, it's only useful with entities that contain JSON fieds. -### Add JSON Trait -See [CakePHP 3.x reference for traits](https://book.cakephp.org/3.0/en/orm/entities.html#creating-re-usable-code-with-traits) ```php -// App/Model/Entity/User.php namespace App\Model\Entity; use Cake\ORM\Entity; -use Lqdt\OrmJson\Model\Entity\JsonTrait; +use Lqdt\OrmJson\Model\Entity\DatFieldTrait; class User extends Entity { - use JsonTrait; + use DatFieldTrait; } ``` +The trait can be used without `DatFieldAwareTrait` or `DatFieldBehavior` in models and vice-versa. + +## Datfield format +In order to work with inner JSON data, we need to know which field to use and which path to use in this field. You can, obviously use SQL fragments and/or native Mysql JSON functions but, believe me, it's very prone to error, needs securing user input, and, well, what about using an ORM if we have to write raw SQL each time ? + +This plugin solves this difficulty by providing quick syntax to target JSON data. In fact, this version brings up two ways and you can choose or mix which one will best suit your way. + +This plugin introduced the `datfield` format (contraction of `dot` and `at field`) whick looks like : path@[Model.]field and can be used in the same way regular fields are used. As usual, `Model` part is optional if no name conflict may occurs. + +Since v2, this plugin also supports a more *object* way which looks like : [Model.]field->path + +For instance, let's say you have a JSON field named `position`, exposing two keys `lat` and `lon` in a `Locations` model. + +For query operations or with special entity getters/setters, You may consider using `'lat@position'` or `'position->lat'` to easily manipulate and access the `lat` data in the `position` field. + +Datfields become especially handy when accessing deep nested keys : `'lastknown.computed.lat@position'` (or `'position->lastknown.computed.lat'`) will target `'position['lastknown']['computed']['lat']'` value in data. + +It also partially supports JSON path with (as now) the [syntax used by Mysql](https://dev.mysql.com/doc/refman/8.0/en/json.html#json-path-syntax) itself to target arrays : [Model.]field->path[*].prop will target the `prop` key of all items stored in the array at `path`. + ## Usage -### Datfield format explanation -This plugin introduces the `datfield` format (contraction of `dot` and `at field`) like this : path@[Model.]field and can be used in any Json specific functions brought by behavior and trait in the same way fields are used in regular core data functions. +### Quick guide +DatField notation can be used in any statements involving fields : -Path represents the properties way inside Model JSON field. For instance, with this schema : -``` -Model = Users -Field = id INT PRIMARY KEY, attributes JSON - -attributes field content = { - "username": "user", - "prefs": { - "theme": "lovely", - "color": "dark" - }, - logs: [ - "log0", - "log1" - ] +```php +// Assuming $table has DatFieldBehavior attached and its entity has DatFieldTrait attached +$customers = $table->find('json') + // You can mix v1 and v2 syntax at will + ->select(['id', 'attributes', 'name' => 'attributes->id.person.name']) + ->where(['attributes->id.person.age >' => 40]) + ->order(['attributes->id.person.age']) + ->all(); + +// Change the manager for all this customers +$customers = $table->patchEntities($customers, ['attributes->manager.id' => 153]); + +// Update status +foreach ($customers as $customer) { + $total = $customer->get('attributes->command.last.total'); + $stingy = $total < 50; + $customer->set('attributes->status.stingy', $stingy); + // You can also use array-like syntax + $customer['attributes->status.vip'] = ($total > 500); + // or curly syntax + $customer->{'attributes->status.tobeCalled'} = !$stingy; } -username@attributes will yield 'user' -username@Users.attributes can be used for model disambiguation +$table->saveMany($customers); +``` + +> **In short** : just target and use JSON data as you would do with regular fields by using datfield notation. If you know some troubles, feel free to open an issue as needed. + +### Selecting fields +You can easily select specific paths in your data among paths with a regular select statement. Without alias provided, it will create a composite key from datfield `__` : -prefs.theme@attributes will yield 'lovely' +```php +$e = $table->find()->select(['id', 'attributes->deep.nested.key'])->first(); + +/** Entity data will look like +* [ +* 'id' => 0, +* 'attributes_deep_nested_key' => true +* ] +**/ ``` -When querying, this notation is automatically converted to JSON_EXTRACT short notation `field->"$.path"`. -Please note that queries can't be filtered by now on JSON array indexes (like kinda logs.0 inside the previous example). +You can also use field alias to control key in result : -### Performs finds in JSON data -When the JsonBehavior is added to a table, you can get a `JsonQuery` instance like this : ```php -// In your controller -$query = $this->Users->jsonQuery(); -$query = $this->Users->find('json'); +$e = $table->find()->select(['id', 'key' => 'attributes->deep.nested.key'])->first(); + +/** Entity data will look like +* [ +* 'id' => 0, +* 'key' => true, +* ] +**/ ``` -You can late bind a previous `Query` to a `JsonQuery` like this : +`enableAutoFields` will work fine to expose some data while loading all other fields : + ```php -// In your controller -$query = $this->Users->find('stuff'); -$query = $this->Users->jsonQuery($query); +$e = $table->find()->select(['key' => 'attributes->deep.nested.key'])->enableAutoFields()->first(); + +/** Entity data will look like +* [ +* 'id' => 0, +* 'key' => true, +* 'attributes' => [ +* 'deep' => [ +* 'nested' => [ +* 'key' => true +* ] +* ] +* ] +* ] +**/ ``` -All existent query options will be cloned into the new `JsonQuery`. -`JsonQuery` extends core `Query` and all core methods are available, plus three specific json chainable functions. +### Filtering and ordering data +Filtering or ordering with datfields can be done like with any other fields and by any usual means of the ORM. Expressions will be automatically translated to usable ones in SQL. You can use datfields at any place. + ```php -// In your controller -$query = $this->Users - ->find('json') - ->jsonSelect([ - 'prefs.theme@attributes' - ]) - ->jsonWhere([ - 'username@attributes' => 'user' - ]) - ->jsonOrder([ - 'created@attributes' => 'DESC' - ]) - ->all(); +// Simple search using v2 datfield notation +$data = $table->find()->where(['attributes->key' => 'key'])->first(); +$data = $table->find()->where(['attributes->really.deep.number >=' => 10])->first(); +$data = $table->find()->where(['attributes->key LIKE' => '%key%', 'attributes->really.deep.nested.key' => 'deep.nested.key'])->first(); + +// Looking for null will return all fields where key is missing or equals to null as default behavior +$data = $table->find()->where(['attributes->fool IS' => null])->first(); + +// Query builder is also fine +$data = $table + ->find() + ->where(function($exp) { + return $exp->between('attributes->lastkwown.position.lat', 2.257, 2.260); + }) + ->first(); ``` -Alternatively, you can provide parameters to the `find` query : +> **Note :** When filtering on null values, default behavior is to consider that any record that don't have the target path in its JSON field also fulfills `IS NULL` condition. To avoid this, you can pass `['ignoreMissingPath' => true]` as query option to target only records that have the path in their JSON field with a value set to `null`. + + +There's some caveats with data types not natively supported by JSON format, like datetime, but it can be handled by using [JSON data types](using-json-data-types). + +### Using aggregation and functions +Datfield are also fully supported and can be used as any regular fields. + ```php -// In your controller -$query = $this->Users - ->find('json', [ - 'json.fields' => ['prefs.theme@attributes'], - 'json.conditions' => ['username@attributes' => 'user']), - 'json.sort' => ['created@attributes' => 'DESC'] +$q = $this->table->find(); +$res = $q + ->select(['kind' => 'data->group', 'total' => 'data->i + data->j', 'count' => $q->func()->count('*')]) + ->group('kind') + ->having(['total >' => 10]) + ->distinct() ->all(); ``` -#### Selecting datfields -It works exactly in the same manner than the `fields` option or the `select` method. +### Marshaling data -**Note: you can mix "regular" fields from table with JSON field internal data when using `json.fields` or `jsonSelect`.** +**Note :** Marshaling datfields does not require to upgrade connection -Aliases are fully supported in the same manner as CakePHP does through associative array. +In some cases, you may want to use datfield notation in data provided to `createEntity` or `patchEntity` methods and there's no trouble in doing so : -You can use any usual regular options and mix methods with any of the syntaxes. +```php +$e = $table->createEntity([ + 'data->key' => 'foo', + 'data->really.deep.key' => 'not annoying' +]); -When using `jsonSelect`, returned field name is aliased like this : `[Model_]field_path`. You can provide a string as second parameter to change default `_` one. A third boolean parameter can be used to force lowercasing of the key when set to `true`. +// $e will looks like +[ + 'id' => null, + 'data' => [ + 'key' => 'foo', + 'really' => [ + 'deep' => [ + 'key' => 'not annoying' // maybe yes if having to type array + ] + ] + ] +] +``` -By setting separator to `false`, the field key (aliased or not) won't be kept flattened but instead used to rebuild an associative array of data : +When patching entities, the *default* behavior is to consider that the **whole** JSON structure is provided in data. Therefore, all previous data is lost and gone. To avoid this, you can either pass `jsonMerge` as `true` in `patchEntity` options or call `jsonMerge` on the resulting entity (if using `DatFieldTrait`) or through table : ```php -$this->Users->find('json')->jsonSelect('the.deep.key@attributes', '.')->first()->toArray(); -// will return ['attributes.the.deep.key' => 'deepvalue'] - -// With delimiter set to false -$this->Users->find('json')->jsonSelect('the.deep.key@attributes', false)->first()->toArray(); -// will return ['attributes' => ['the' => ['deep' => ['key' => 'deepvalue']]]] +// Keep our previously created entity and patch it +$e = $table->patchEntity(['data->hacked' => true); + +// $e will looks like +[ + 'id' => null, + 'data' => [ + 'hacked' => true, + ] +] + +// Damnit, let's restore lost data +$e->jsonMerge(); +// or +$table->jsonMerge($e); + +// $e will now looks like +[ + 'id' => null, + 'data' => [ + 'hacked' => true, // Not anymore + 'key' => 'foo', + 'really' => [ + 'deep' => [ + 'key' => 'not annoying' // maybe yes if using arrays + ] + ] + ] +] -// With dotted alias and delimiter set to false -$this->Users->find('json')->jsonSelect(['my.key' => 'the.deep.key@attributes'], false)->first()->toArray(); -// will return ['my' => ['key' => 'deepvalue']] +// Next time, use option +$e = $table->patchEntity(['data->hacked' => true, ['jsonMerge' => true]); ``` -#### filtering datfields -When using `jsonWhere`, you can use any of regular nesting and operator provided as an array. You can also use plain query. In this last case, string values won't be escaped. +> You can fine tune which field(s) should be merged by passing an array of the JSON fields name to `jsonMerge` option or method : `['data']` for instance. + +### What brings `DatFieldTrait` within entities ? -**Note**: you can mix "regular" fields from table with JSON field internal data when using `json.conditions` or `jsonWhere`. +**Note :** Using this trait does not require to upgrade connection nor adding `DatFieldAwareTrait` or `DatFieldBehavior` to model. + +All regular methods are replaced when an entity uses this trait to support datfield notation. Their behavior remains the same and they still can be used for any regular field. + +To get/set data with datfield, simply use `get` or `set`, array-like syntax or [curly syntax]((https://www.php.net/manual/en/language.types.string.php#language.types.string.parsing.complex)) : ```php -// In your controller -$query = $this->Users - ->find('json') - ->jsonWhere([ // Classic array way - 'OR' => [ - 'username@attributes =' => 'user' - 'prefs.color@attributes LIKE' => '%dark%' - ] - ]); - - // Dangerous raw SQL way - $query = $this->Users - ->find('json') - ->jsonWhere("username@attributes = 'user' OR prefs.color@attributes LIKE '\"%dark\"'"); - - // Query expression way - $query = $this->Users - ->find('json') - ->jsonWhere(function($q) { - return $q->_or(['username@attributes' => 'user'])->like('prefs.color@attributes', '%dark%'); - }); +$e->get('attributes->deep.nested.value'); +$e->get('deep.nested.value@attributes'); // both notations are supported +$e['attributes->deep.nested.value']; +$e['attributes->deep.nested.value']; +$e->{'attributes->deep.nested.value'}; +$e->{'deep.nested.value@attributes'}; ``` -#### Sorting datfields -It's exactly the same syntax than `order`|`sort` option or `order` method. If the provided parameter is a string, it will be treated as a default ASC ordering on this field. If the provided parameter is an array of strings, default ASC ordering will also be applied. +[Dirty state](https://book.cakephp.org/4/en/orm/entities.html#checking-if-an-entity-has-been-modified) is available at path level and field level : -**Note**: you can mix "regular" fields from table with JSON field internal data when using `json.sort` or `jsonOrder`. +```php +$e->set('attributes->deep.nested.value', 'foo'); +$e->isDirty('attributes->deep.nested.value'); // true +$e->isDirty('attributes->deep.nested.othervalue'); // false +$e->isDirty('attributes'); // true +$e->isDirty(); // true +``` + +> **Note :** If you call `setDirty('attributes', false)`, all currently dirty paths in`attributes` will be cleared as well. + +#### For PHPStan users + +If you're using PHPStan and curly syntax to access your data, you will obviously have errors about accessing undefined properties on entities. To cope with these, this plugin provides a service to check if owning field exists in entity class and if it is typed as an array. To enable the service, simply add this snippet in your `phpstan.neon.dist` or whatsoever configuration file : + +``` +services: + - + class: Lqdt\OrmJson\PHPStan\CurlyDatFieldNotation + tags: + - phpstan.broker.propertiesClassReflectionExtension +``` + +### Using JSON data types + +There's some caveats when dealing with data types inside JSON. By itself JSON type handles natively null and usual scalar types : boolean, integer, float or string, plus arrays and objects of previous. Troubles may begin when you want to handle other types stored in JSON and the perfect example is datetime. + +Usually, datetime/time/date/timestamp fields are mapped to a `FrozenTime` object in cakePHP and a [registered type](https://book.cakephp.org/4/en/orm/database-basics.html#datetime-type) takes care of handling needed castings. Most of the time, this type is inferred from reflected schema and it's working out of the box. + +If a datetime is nested in some JSON data, it can't work like this as it is merely a string. When dealing with some usual string representations of datetimes, like Mysql one, ISO8601 or timestamps, it can be absolutely fine to simply do nothing as ordering will work. You only have to take care to pass the right string format when saving data. + +Nevertheless, you miss all the convenience that brings datetime data type for manipulating values. Moreover, if you have some nasty formats, queries may lead to wrong results. Due to JSON versatility, many APIs make use of custom string formats and it can be tricky to handle them. + +To ease troubleshooting these things, `DatFieldBehavior`allow to define JSON data types permanently and/or per query. Because of JSON versatility, it extends regular typemaps by allowing the use of callbacks to cast data instead of multiplicating data types. + +### Using JSON data types + +> **Note :** Connection **must** be upgraded in order to support JSON data types. + +JSON data types are stored within an upgraded schema alongside regular fields that is created when upgrading connection. Therefore you will get errors if upgrade is not done *before* setting them up. -### Create and update JSON in an entity from model -Since v1.1.0, fields names are filtered before marshalling when using `Model::newEntity` or `Model::patchEntity`. +When registering JSON data type, you can either only provide a regular data type as string or an extended one to register callbacks for one or more of casting operations between : +- `marshal`: Callback will be called when marshaling data. +- `toPHP`: Callback will be called when processing fetched data +- `toDatabase`: Callback will be called when persisting data -When using patchEntity, the whole JSON field will be replaced by new value. If you want to only mass update some properties, you can call `jsonMerge` on returned entity. + +> All callbacks will receive the target value as first argument, the whole row data as second argument and the query (if available in operation) as third argument. + +If a callback is provided for a given operation (`marshal`, `toPHP` or `toDatabase`) alongside a regular data type, only callback will be applied to data. This way, you can override given data type operations instead of creating a new one. + +#### Registering JSON data type permanently +When using `DatfieldBehavior`, you can easily and permanently register JSON types that will persist through each queries. : ```php -// In your controller -$user = $this->Users->newEntity([ - 'nickname@attributes' => 'Foo' +// Upgrade connection if not already done +$table->useDatFields(); + +// Register a single datfield as datetime type +$table->getSchema()->setJsonTypes('data->time', 'datetime'); + +// Register a single datfield as date and overrides marshal hook with a callback +$table->getSchema()->setJsonTypes('data->frenchDateFormat', [ + 'type' => 'date', + 'marshal' => function(string $value): FrozenDate { + return FrozenDate::createFromFormat('d/m/Y', $value); + } ]); -// Replace field value by {"update":"Bar"} -$user = $this->Users->patchEntity([ - 'update@attributes' => 'Bar' +// Register many datfields as datetime type +$table->getSchema()->setJsonTypes([ + 'data->time' => 'datetime', + 'date->anothertime' => 'datetime' ]); -// Update/create attributes field value -$user = $this->Users->patchEntity([ - 'update@attributes' => 'Bar' -])->jsonMerge(); +// Register multiple datfields with full syntax +$table->getSchema()->setJsonTypes([ + 'data->time' => [ + 'type' => 'datetime', + 'marshal' => array($table, 'marshalTime'), // overrides datetime type marshal operation + ], + 'data->weirdthing' => [ // providing a type is not mandatory + 'marshal' => array($table, 'importWeirdthing'), + 'toPHP' => array($table, 'weirdthingToPHP'), + 'toDatabase' => array($table, 'weirdthingToDatabase'), + ], +]); ``` -### Use JSON setter/getter methods with entities -When trait is used in an entity, you can use : -- `Entity::jsonGet` to fetch a value inside JSON data. It will return an object by default. You can get an associative array by providing true as second parameter. -- `Entity::jsonSet` to set a value inside JSON data. Method is chainable or accepts array -- `Entity::jsonIsset` to check if a key is defined inside JSON data -- `Entity::jsonUnset` to delete a key inside JSON data. Method is chainable or accepts array +Please note that all JSON data types will be lost if connection is downgraded as regular schema will be restored. -All of these methods are relying on regular get/set/unset and triggers dirty state of the entity. +#### Registering JSON data types for a single query +You can also register JSON data type per query by providing a `jsonTypeMap` option. In case of conflict, it overrides any JSON data type stored in the model. ```php -// In your controller -$user = $this->Users->get(1); -$username = $user->jsonGet('username@attributes'); -$user - ->jsonSet('prefs.theme@attributes', 'notSoLovely') - ->jsonSet([ - 'metas.blue@attributes' => 'sea', - 'metas.red@attributes' => 'apple' - ]); +// Upgrade connection if not already done +$table->useDatFields(); + +$q = $table->find('json', ['jsonTypeMap' => ['data->time' => 'datetime']])->all(); ``` -If providing only field name string to `jsonGet`, the whole data is returned as an object. This way, you can easily fetch field properties like this : +You can as well provide callbacks by using full syntax. + +### Linking models together +**Special upgraded associations are available both in `DatFieldAwareTrait` and `DatFieldBehavior`** + +The plugin allows to use datfield notation to reference a foreignKey and links tables on this basis. It will not be as efficient as regular foreign keys that will indexed but it can be handy in some edge cases. + +In order to use datfield as foreign key, simply use datfield counterpart of any association and use dafield notation for foreign key option and/or targetFoeignKey option : + ```php -// In your controller -$user = $this->Users->get(1); -$username = $user->jsonGet('attributes')->username; +$Clients->datFieldHasOne('Agents', ['foreignKey' => 'data->agent_id']); +$Clients->datFieldBelongsToMany('Products', [ + 'foreignKey' => 'data->client_id' + 'targetForeignKey' => 'data->product_id', + 'through' => 'Orders' +]); ``` +All others options and functionnalities remains the same. + +The counterparts list : + +- `belongsTo` <=> `datFieldBelongsToMany` +- `hasOne` <=> `datFieldHasOne` +- `hasMany` <=> `datFieldHasMany` +- `belongsToMany` <=> `datFieldBelongsToMany` + +**Note :** No need to say that connection must be upgraded for these queries to work. + +> In MySQL, you may use [virtual columns](https://vladmihalcea.com/index-json-columns-mysql/) to index JSON data as a more efficient solution. + +#### Limitations with CakePHP 3.x +The only limitation is that you cannot use `link`, `unlink` or save associated data when models are joined by `datFieldBelongsToMany` or `datFieldBelongsToManyThrough`. This is due to an heavy refactoring of how it is handled by CakePHP since version 4 release and there's now way to handle both with this plugin. + +## Advanced setup +This plugin contains : +- `Lqdt\OrmJson\Database\Driver\DatFieldMysql` driver: The driver will traverse all query parts in order to translate datFields in clauses to their MySQL counterpart, usually JSON_EXTRACT. Traversal can be disabled at runtime by providing `[useDatFields => false]` in query options; +- `Lqdt\OrmJson\ORM\DatFieldAwareTrait`: The trait is providing convenient methods to upgrade/downgrade connection driver and table schema at will and brings up special associations to allow linking models on JSON data; +- `Lqdt\OrmJson\Model\Table\DatFieldBehavior`: The behavior does exactly the same thing that `DatFieldAwareTrait`, plus handling marshalling with datfields when using `newEntity`, `patchEntity` or their plural counterparts; +- `Lqdt\OrmJson\Model\Entity\DatFieldTrait`: The trait overrides all regular accessors, mutators and utilities to handle datfield notation within entities while keeping full compatibility for regular fields. + +Depending on what you're aiming for, you have different alternatives when using this plugin. +- **Use datfield notation and/or JSON data types to query database or persist data** : You must ensure that the model(s) that will rely on datfield notation for querying are using the upgraded connection; +- **Use datfield notation when patching data** : You must have embedded `DatFieldBehavior` in the model; +- **Use datfield notation to manipulate data in entities**: You must have added `DatFieldTrait` to related entities classes; +- **Use datfield notation to link models** : You must use the special associations methods provided by `DatFieldAwareTrait` or `DatFieldBehavior`; + +It's up to you to find the right balance based on your needs between the connection upgrade step overhead and the datfield translation step overhead. + +#### Use the upgraded driver for all models +Obviously, you can simply use upgraded driver in your [connection configuration](https://book.cakephp.org/4/en/orm/database-basics.html#database-configuration). This can be a real good option if all of your models will mostly use datfield notation. You can still disable datfield translation by providing `['useDatFields' => false]` as query option to avoid useless translation process when not using datfields. + +```php +// Assuming that DatFieldAwareTrait or DatFieldBehavior are set in UsersTable +$user = $this->Users + ->find() // Special finder is not required here as driver is already upgraded + ->where(['attributes->phones.tel' => 'wathever_number']) + ->first(); + +$users = $this->Users + ->find('all', ['useDatFields' => false]) // Disable translation overhead as not needed in this query + ->all(); +``` + +#### Enable or disable upgraded driver per model +With addition of `DatFieldAwareTrait` or `DatFieldBehavior` to a model, you can enable/disable upgraded connection at runtime by using `Model::useDatFields()`/`Model::useDatFields(false)`. If you want to permanently use upgraded connection in the model, simply call `Model::useDatFields()` in the `initialize` hook or add the behavior with `['upgrade' => true]` as option. You can still disable datfield translation per query by providing `['useDatFields' => false]` as query option. + +```php +// Assuming that DatFieldAwareTrait or DatFieldBehavior are set in UsersTable +// Connection is not already upgraded +$user1 = $this->Users + ->useDatFields() // returns model instance, so it's chainable + ->find() // Special finder is not required here as driver is already upgraded + ->where(['attributes->phones.tel' => 'wathever_number1']) + ->first(); + +$user2 = $this->Users + ->find() + ->where(['attributes->phones.tel' => 'wathever_number2']) + ->first(); + +$user2 = $this->Users + ->find() + ->where(['attributes->phones.tel' => 'wathever_number3']) + ->first(); + +$users = $this->Users + ->find('all', ['useDatFields' => false]) // Disable translation overhead as not needed in this query + ->all(); + +// Restore genuine driver +$this->Users->useDatFields(false); +``` + +**Caution** : As model instances are stored as singleton in a registry, I do recommend to cut off upgraded driver after all datfield queries are settled. + +#### Use upgraded driver per query +It's probably the most usual case as datfield queries will mostly be occasional. With addition of `DatFieldAwareTrait` or `DatFieldBehavior` to a model, simply call find('datfields') or find('json') and the query will be provided with an upgraded connection though model connection remains genuine. You cannot use permanent JSON data types this way but still can provide `jsonTypeMap` option in query. + +```php +// Assuming that DatFieldAwareTrait or DatFieldBehavior are set in UsersTable +// We're in a controller that is loading Users model and connection is not already upgraded +// We request connection upgrade as datfields will be used in query +$user = $this->Users + ->find('datfields') // or ->find('json') + ->where(['attributes->phones.tel' => 'wathever_number']) + ->first(); +``` + +#### Some tricky things to know +Lastly, you may face some issues with nested queries, when joining data. If doing a single query, CakePHP will logically populate query connection with the driver of the **root** model. In contrary, when launching subqueries, connection configuration of dependent models wii be used. + +For instance, say you have a `Vehicles` model that has many `Locations` model. Upgraded driver is permanently used in `Locations` but not in `Vehicles`. + +```php +// This will work fine because 2 databases requests will be made, 1 per model with respective connection setup +$this->Vehicles->find()->contain(['Locations'])->all(); + +// This will fail because only 1 request with an INNER JOIN will be done from `Locations` not upgraded connection +$this->Vehicles->find()->innerJoinWith('Locations', function($q) { + return $q->where(['Locations.attributes->position->lat <' => 45.6]); +})->all(); + +// This will work because we're upgrading connection on Vehicles with `datfields` custom finder +// Vehicles model must at least use `DatFieldAwareTrait` +$this->Vehicles->find('datfields')->innerJoinWith('Locations', function($q) { + return $q->where(['Locations.attributes->position->lat <' => 45.6]); +})->all(); +``` + +If you begin to have Mysql syntax errors with unparsed datfields, it means that you have some datfields used in a not upgraded connection. + ### API reference -See [API reference](https://liqueurdetoile.github.io/cakephp-orm-json/) +See [API reference](https://liqueurdetoile.github.io/cakephp-orm-json/api) + +## Difference from v1.x +In previous versions, we've tried to convert clauses within Query by dedicating the JsonQuery that extends it to bring up functionnalities. It worked very well but it was still limited to Query overrides and support for other engines was clearly impossible. + +From version 2.0.0, translation is done at MySQL driver level. The behavior now creates an upgraded connection with the new driver that is able to translate any datfield notation before native CakePHP ORM processing. + +CakePHP makers are great guys because they meant to plan many overrides that makes this plugin feasible. + +Version 2.x is a quite a breaking change from 1.x as JsonQuery is nor needed nor available anymore. Similarly, you don't need any `jsonXXX` methods on entities. Regular mutators, accessors and magic properties will work well with datfields. + +**Migrating is quite simple though**, simply stick to regular query statements and use `select`, `order`, `where` instead of previous ones `jsonSelect`, `jsonOrder`, `jsonWhere`. In entities, use regular accessors and mutators to cope with data in JSON. ## Changelog +**v2.0.0** +- *BREAKING CHANGE* : Replace JsonQuery logic by a dedicated database driver that handles seamlessly the parsing of dat fields +- *BREAKING CHANGE* : Replace JsonXXX entity methods and use regular accessors and mutators +- Add v2 datfield notation support `'[Model.]field->path'` +- Completely rework and optimize query translations of datfield syntax +- Fully rework `DatFieldBehavior` and `DatFieldTrait` for entities +- Migrate CI to Github Actions +- Upgrade test environment +- Add a bunch of tests for a wide variety of situations + **v1.5.0** - Full rework of `jsonWhere` to replace previous conditions array parsing by a full `QueryExpression` build that allows the use of query expressions callbacks diff --git a/composer.json b/composer.json index adb878e..dca9feb 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "Cakephp plugin to provide easy control over JSON type fields in database", "type": "cakephp-plugin", "license": "MIT", - "version": "1.5.4", + "version": "2.0.0-dev", "authors": [ { "name": "Liqueur de Toile", @@ -29,14 +29,21 @@ "filter" ], "require": { - "php": "^7.1", - "cakephp/cakephp": "^3.5|^4", - "adbario/php-dot-notation": "^2.0" + "php": "^7.2|^8.0", + "cakephp/cakephp": ">=3.7 <5", + "adbario/php-dot-notation": "^2.1", + "mustache/mustache": "^2.13" }, "require-dev": { - "phpunit/phpunit": "^5.7.14|^6.0", - "phpunit/php-code-coverage": "5.3", - "phpstan/phpstan": "^0.10.1" + "phpunit/phpunit": "^6|^8.5|^9.3", + "phpstan/phpstan": "^0.10|^1.8", + "phpstan/extension-installer": "^1.1", + "cakephp/cakephp-codesniffer": "^4.5", + "cakephp/migrations": "^2.4|^3.2", + "fakerphp/faker": "^1.20", + "cakedc/cakephp-phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^1.1", + "vierge-noire/cakephp-test-suite-light": "^2.4" }, "autoload": { "psr-4": { @@ -50,8 +57,25 @@ } }, "scripts": { - "test": "phpunit && phpstan analyse -l 5 src", + "test": "phpunit && phpstan analyze --memory-limit 1G", "doc": "php phpDocumentor.phar -d ./src -t ./docs", - "phpstan": "phpstan analyse -l 5 src" + "stan": "phpstan analyze --memory-limit 1G", + "stan-github": "phpstan analyze --memory-limit 1G --error-format=github", + "enable-compat": [ + "composer remove cakedc/cakephp-phpstan phpstan/phpstan-phpunit vierge-noire/cakephp-test-suite-light --dev", + "composer update --prefer-lowest", + "composer require vierge-noire/cakephp-test-suite-light:^1.2 vierge-noire/cakephp-test-migrator:^1.5 --dev --prefer-lowest -W" + ], + "disable-compat": [ + "composer remove vierge-noire/cakephp-test-suite-light vierge-noire/cakephp-test-migrator --dev", + "composer update", + "composer require --dev cakedc/cakephp-phpstan phpstan/phpstan-phpunit vierge-noire/cakephp-test-suite-light" + ] + }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true, + "dealerdirect/phpcodesniffer-composer-installer": true + } } } diff --git a/docs/classes/Lqdt.OrmJson.DatField.Compat3x.html b/docs/classes/Lqdt.OrmJson.DatField.Compat3x.html new file mode 100644 index 0000000..c475fe4 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.DatField.Compat3x.html @@ -0,0 +1,368 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\DatFieldCompat3x

+

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
enable()
+
No public properties found
+
No constants found
+
+
+
No protected methods found
+
No protected properties found
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+

Methods

+ +
+
+ +
+

enable()

+ +
enable(): void
+

Enables Cakephp 3.x compatibility by aliasing some classes that have been renamed in CakePHP 4.x

It must be called at bootstrap step

+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.DatField.Exception.MissingPathInDataDatFieldException.html b/docs/classes/Lqdt.OrmJson.DatField.Exception.MissingPathInDataDatFieldException.html new file mode 100644 index 0000000..e4ce702 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.DatField.Exception.MissingPathInDataDatFieldException.html @@ -0,0 +1,370 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\DatField\ExceptionMissingPathInDataDatFieldException

+

Used when a behavior cannot be found.

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
No public methods found
+
No public properties found
+
No constants found
+
+
+
No protected methods found
+
$_messageTemplate
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+
+

Properties

+
+ +
+
+ +
+

$_messageTemplate

+
$_messageTemplate :string
+

Type

string
+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.DatField.Exception.UnparsableDatFieldException.html b/docs/classes/Lqdt.OrmJson.DatField.Exception.UnparsableDatFieldException.html new file mode 100644 index 0000000..8f5772e --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.DatField.Exception.UnparsableDatFieldException.html @@ -0,0 +1,370 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\DatField\ExceptionUnparsableDatFieldException

+

Used when a behavior cannot be found.

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
No public methods found
+
No public properties found
+
No constants found
+
+
+
No protected methods found
+
$_messageTemplate
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+
+

Properties

+
+ +
+
+ +
+

$_messageTemplate

+
$_messageTemplate :string
+

Type

string
+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Database.DatFieldDriverInterface.html b/docs/classes/Lqdt.OrmJson.Database.DatFieldDriverInterface.html new file mode 100644 index 0000000..9587de2 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.Database.DatFieldDriverInterface.html @@ -0,0 +1,451 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\DatabaseDatFieldDriverInterface

+

+ +
+

Summary

+
+
+ Methods +
+
+ Constants +
+
+
+
isDatField()
translateExpression()
translateDatField()
translateSetDatField()
+
No constants found
+
+
+
No protected methods found
+
+ N/A +
+
+
+
No private methods found
+
+ N/A +
+
+
+
+ +
+
+

Methods

+
+ +
+
+ +
+

isDatField()

+ +
isDatField(mixed  $datfield): integer
+

Utility function to check if a field is datfield in driver +It must detect unstranslated and already translated datfield as well

Parameters

+ + + + +
mixed$datfield

Field name

Returns

integer —

0 if not a datfield, 1 for v1 notation, 2 for v2 notation, 3 for DatfieldExpression

+
+ +
+
+ +
+

translateExpression()

+ +
translateExpression(string|array|\Cake\Database\ExpressionInterface  $expression,\Cake\Database\Query  $query,\Lqdt\OrmJson\Database\JsonTypeMap  $jsonTypes): string|array|\Cake\Database\ExpressionInterface
+

Applies datfield translation to any expression or SQL snippets

Parameters

+ + + + + + + + + + + + +
string|array|\Cake\Database\ExpressionInterface$expression

Literal or object expression

\Cake\Database\Query$query

Query

\Lqdt\OrmJson\Database\JsonTypeMap$jsonTypes

JSON types definition

Returns

string|array|\Cake\Database\ExpressionInterface —

Updated expression

+
+ +
+
+ +
+

translateDatField()

+ +
translateDatField(array|string|\Cake\Database\ExpressionInterface  $datfield,boolean  $unquote = false,string|null|false  $repository = null): array|string|\Cake\Database\ExpressionInterface
+

Translates a datfield notation into a valid driver dependent SQL FunctionExpression that allows +to identify and target data into a JSON field.

If a repository is prepended to datfield, il will be kept as is unless passing false as last argument

Parameters

+ + + + + + + + + + + + +
array|string|\Cake\Database\ExpressionInterface$datfield

Datfield

boolean$unquote

If true, returned data should be unquoted

string|null|false$repository

Repository alias

Returns

array|string|\Cake\Database\ExpressionInterface
+
+ +
+
+ +
+

translateSetDatField()

+ +
translateSetDatField(\Cake\Database\Expression\ComparisonExpression  $expr,\Cake\Database\Query  $query,\Lqdt\OrmJson\Database\JsonTypeMap  $map): \Cake\Database\Expression\ComparisonExpression
+

Translates a SET comparison expression to directly update JSON data based on datfield

It is used for UPDATE statements

Parameters

+ + + + + + + + + + + + +
\Cake\Database\Expression\ComparisonExpression$expr

Expression

\Cake\Database\Query$query

Query

\Lqdt\OrmJson\Database\JsonTypeMap$map

JSON type map

Returns

\Cake\Database\Expression\ComparisonExpression —

Updated expression

+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlJoinTrait.html b/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlJoinTrait.html new file mode 100644 index 0000000..3d9dc0d --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlJoinTrait.html @@ -0,0 +1,483 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\Database\DialectDatFieldMysqlJoinTrait

+

+ + + +
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
+ No public methods found +
+
+ No public properties found +
+
+ No constants found +
+
+
+
+ _joinedFieldsConverter()
+
+
+ No protected properties found +
+
+ N/A +
+
+
+
+ No private methods found +
+
+ No private properties found +
+
+ N/A +
+
+
+
+ +
+ + + + +
+

Methods

+ +
+ +
+
+ +
+

_joinedFieldsConverter()

+ +
_joinedFieldsConverter(array  $joints, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + +
array$joints
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ + + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlOrderTrait.html b/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlOrderTrait.html new file mode 100644 index 0000000..0be61a5 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlOrderTrait.html @@ -0,0 +1,577 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\Database\DialectDatFieldMysqlOrderTrait

+

+ + + +
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
+ No public methods found +
+
+ No public properties found +
+
+ No constants found +
+
+
+
+ _removeOrderingFields()
+ _registerDatFieldForSorting()
+ _orderedFieldsConverter()
+
+
+ No protected properties found +
+
+ N/A +
+
+
+
+ No private methods found +
+
+ No private properties found +
+
+ N/A +
+
+
+
+ +
+ + + + +
+

Methods

+ +
+ +
+
+ +
+

_removeOrderingFields()

+ +
_removeOrderingFields(  $row, array  $aliases) 
+

+ + +

Parameters

+ + + + + + + + + + + +
$row
array$aliases
+ + + +
+
+ +
+ +
+
+ +
+

_registerDatFieldForSorting()

+ +
_registerDatFieldForSorting(\Lqdt\OrmJson\Database\Dialect\string  $datfield, \Lqdt\OrmJson\Database\Dialect\string  $model, array  $aliases, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + + + + + + + + + + + +
\Lqdt\OrmJson\Database\Dialect\string$datfield
\Lqdt\OrmJson\Database\Dialect\string$model
array$aliases
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_orderedFieldsConverter()

+ +
_orderedFieldsConverter(\Cake\Database\Expression\OrderByExpression  $order, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + +
\Cake\Database\Expression\OrderByExpression$order
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ + + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlSelectTrait.html b/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlSelectTrait.html new file mode 100644 index 0000000..1069b68 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlSelectTrait.html @@ -0,0 +1,723 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\Database\DialectDatFieldMysqlSelectTrait

+

+ + + +
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
+ No public methods found +
+
+ No public properties found +
+
+ No constants found +
+
+
+
+ _decorateSelectedDatFields()
+ _decorateExtractedDatFields()
+ _selectRegularField()
+ _selectDatField()
+ _extractDatField()
+ _selectedFieldsConverter()
+
+
+ No protected properties found +
+
+ N/A +
+
+
+
+ No private methods found +
+
+ No private properties found +
+
+ N/A +
+
+
+
+ +
+ + + + +
+

Methods

+ +
+ +
+
+ +
+

_decorateSelectedDatFields()

+ +
_decorateSelectedDatFields(array  $row, array  $filteredFields) 
+

+ + +

Parameters

+ + + + + + + + + + + +
array$row
array$filteredFields
+ + + +
+
+ +
+ +
+
+ +
+

_decorateExtractedDatFields()

+ +
_decorateExtractedDatFields(  $row, \Lqdt\OrmJson\Database\Dialect\string  $separator, \Lqdt\OrmJson\Database\Dialect\bool  $keepNestedOnExtract) 
+

+ + +

Parameters

+ + + + + + + + + + + + + + + + +
$row
\Lqdt\OrmJson\Database\Dialect\string$separator
\Lqdt\OrmJson\Database\Dialect\bool$keepNestedOnExtract
+ + + +
+
+ +
+ +
+
+ +
+

_selectRegularField()

+ +
_selectRegularField(\Lqdt\OrmJson\Database\Dialect\string  $field, \Lqdt\OrmJson\Database\Dialect\string  $alias, array  $fields) 
+

+ + +

Parameters

+ + + + + + + + + + + + + + + + +
\Lqdt\OrmJson\Database\Dialect\string$field
\Lqdt\OrmJson\Database\Dialect\string$alias
array$fields
+ + + +
+
+ +
+ +
+
+ +
+

_selectDatField()

+ +
_selectDatField(\Lqdt\OrmJson\Database\Dialect\string  $datfield, array  $fields, array  $filteredFields, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + + + + + + + + + + + +
\Lqdt\OrmJson\Database\Dialect\string$datfield
array$fields
array$filteredFields
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_extractDatField()

+ +
_extractDatField(\Lqdt\OrmJson\Database\Dialect\string  $datfield, \Lqdt\OrmJson\Database\Dialect\string  $alias, array  $fields, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + + + + + + + + + + + +
\Lqdt\OrmJson\Database\Dialect\string$datfield
\Lqdt\OrmJson\Database\Dialect\string$alias
array$fields
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_selectedFieldsConverter()

+ +
_selectedFieldsConverter(array  $fields, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + +
array$fields
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ + + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlWhereTrait.html b/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlWhereTrait.html new file mode 100644 index 0000000..528ee40 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.Database.Dialect.DatFieldMysqlWhereTrait.html @@ -0,0 +1,748 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\Database\DialectDatFieldMysqlWhereTrait

+

+ + + +
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
+ No public methods found +
+
+ No public properties found +
+
+ No constants found +
+
+
+
+ _filtersConverter()
+ _rawSqlConverter()
+ _comparisonConverter()
+ _unaryExpressionConverter()
+ _queryExpressionConverter()
+
+
+ No protected properties found +
+
+ N/A +
+
+
+
+ No private methods found +
+
+ No private properties found +
+
+ N/A +
+
+
+
+ +
+ + + + +
+

Methods

+ +
+ +
+
+ +
+

_filtersConverter()

+ +
_filtersConverter(string|\Cake\Database\ExpressionInterface  $expression, \Cake\Database\Query  $query) 
+

Returns the appropriate ExpressionInterface regarding the incoming one

+ + +

Parameters

+ + + + + + + + + + + +
string|\Cake\Database\ExpressionInterface$expression

Incoming expression

\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_rawSqlConverter()

+ +
_rawSqlConverter(string  $expression, \Cake\Database\Query  $query) : \Cake\Database\Expression\QueryExpression
+

Returns a new QueryExpression built upon the parsing of the expression to +update datfield names

+ + +

Parameters

+ + + + + + + + + + + +
string$expression

Raw expression to transform

\Cake\Database\Query$query
+ + +

Returns

+ \Cake\Database\Expression\QueryExpression + —

QueryExpression

+ +
+
+ +
+ +
+
+ +
+

_comparisonConverter()

+ +
_comparisonConverter(\Cake\Database\Expression\Comparison  $expression, \Cake\Database\Query  $query) : \Cake\Database\Expression\Comparison|\Cake\Database\Expression\QueryExpression
+

Update or replace the Comparison expression to perform comparisons on +datFields. In some cases, PDO limitations implies to replace the +expression with a raw SQL fragment. It can be a bit dangerous when +using raw user input to perform global matching in `array` mode.

+

Regular fields expressions are left as is.

+ +

Parameters

+ + + + + + + + + + + +
\Cake\Database\Expression\Comparison$expression

Comparison expression

\Cake\Database\Query$query
+ + +

Returns

+ \Cake\Database\Expression\Comparison|\Cake\Database\Expression\QueryExpression + —

Updated expression

+ +
+
+ +
+ +
+
+ +
+

_unaryExpressionConverter()

+ +
_unaryExpressionConverter(\Cake\Database\Expression\UnaryExpression  $expression, \Cake\Database\Query  $query) : \Cake\Database\Expression\UnaryExpression
+

Parses the unary expression to apply conversions on childrens and returns +an updated UnaryExpression

+

Note : This a VERY hacky way because the UnaryExpression class doesn't expose +getter/setter for protected _value property.

+

In this implementation, it causes an infinite loop when used directly with a SQL fragment :

+

['NOT' => 'sub.prop@datfield like "%buggy%"]

+

That's why, an exception is thrown as soon as $value is extracted

+ +

Parameters

+ + + + + + + + + + + +
\Cake\Database\Expression\UnaryExpression$expression

Expression

\Cake\Database\Query$query
+ + +

Returns

+ \Cake\Database\Expression\UnaryExpression + —

New expression

+ +
+
+ +
+ +
+
+ +
+

_queryExpressionConverter()

+ +
_queryExpressionConverter(\Cake\Database\Expression\QueryExpression  $expression, \Cake\Database\Query  $query) : \Cake\Database\Expression\QueryExpression
+

Iterates over a QueryExpression and replace Comparison expressions +to handle JSON comparison in datfields.

+ + +

Parameters

+ + + + + + + + + + + +
\Cake\Database\Expression\QueryExpression$expression

QueryExpression

\Cake\Database\Query$query
+ + +

Returns

+ \Cake\Database\Expression\QueryExpression + —

Updated QueryExpression

+ +
+
+ +
+ +
+
+ + + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Database.Driver.DatFieldMysql.html b/docs/classes/Lqdt.OrmJson.Database.Driver.DatFieldMysql.html new file mode 100644 index 0000000..9a33175 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.Database.Driver.DatFieldMysql.html @@ -0,0 +1,1334 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\Database\DriverDatFieldMysql

+

+ + + +
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
+ queryTranslator()
+ prepare()
+
+
+ No public properties found +
+
+ No constants found +
+
+
+
+ _joinedFieldsConverter()
+ _removeOrderingFields()
+ _registerDatFieldForSorting()
+ _orderedFieldsConverter()
+ _decorateSelectedDatFields()
+ _decorateExtractedDatFields()
+ _selectRegularField()
+ _selectDatField()
+ _extractDatField()
+ _selectedFieldsConverter()
+ _filtersConverter()
+ _rawSqlConverter()
+ _comparisonConverter()
+ _unaryExpressionConverter()
+ _queryExpressionConverter()
+ _removeAliasesFromConditions()
+
+
+ No protected properties found +
+
+ N/A +
+
+
+
+ No private methods found +
+
+ No private properties found +
+
+ N/A +
+
+
+
+ +
+ + + + +
+

Methods

+ +
+ +
+
+ +
+

queryTranslator()

+ +
queryTranslator(  $type) 
+

+ + +

Parameters

+ + + + + + +
$type
+ + + +
+
+ +
+ +
+
+ +
+

prepare()

+ +
prepare(  $query) 
+

+ + +

Parameters

+ + + + + + +
$query
+ + + +
+
+ +
+ +
+
+ +
+

_joinedFieldsConverter()

+ +
_joinedFieldsConverter(array  $joints, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + +
array$joints
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_removeOrderingFields()

+ +
_removeOrderingFields(  $row, array  $aliases) 
+

+ + +

Parameters

+ + + + + + + + + + + +
$row
array$aliases
+ + + +
+
+ +
+ +
+
+ +
+

_registerDatFieldForSorting()

+ +
_registerDatFieldForSorting(\Lqdt\OrmJson\Database\Dialect\string  $datfield, \Lqdt\OrmJson\Database\Dialect\string  $model, array  $aliases, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + + + + + + + + + + + +
\Lqdt\OrmJson\Database\Dialect\string$datfield
\Lqdt\OrmJson\Database\Dialect\string$model
array$aliases
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_orderedFieldsConverter()

+ +
_orderedFieldsConverter(\Cake\Database\Expression\OrderByExpression  $order, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + +
\Cake\Database\Expression\OrderByExpression$order
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_decorateSelectedDatFields()

+ +
_decorateSelectedDatFields(array  $row, array  $filteredFields) 
+

+ + +

Parameters

+ + + + + + + + + + + +
array$row
array$filteredFields
+ + + +
+
+ +
+ +
+
+ +
+

_decorateExtractedDatFields()

+ +
_decorateExtractedDatFields(  $row, \Lqdt\OrmJson\Database\Dialect\string  $separator, \Lqdt\OrmJson\Database\Dialect\bool  $keepNestedOnExtract) 
+

+ + +

Parameters

+ + + + + + + + + + + + + + + + +
$row
\Lqdt\OrmJson\Database\Dialect\string$separator
\Lqdt\OrmJson\Database\Dialect\bool$keepNestedOnExtract
+ + + +
+
+ +
+ +
+
+ +
+

_selectRegularField()

+ +
_selectRegularField(\Lqdt\OrmJson\Database\Dialect\string  $field, \Lqdt\OrmJson\Database\Dialect\string  $alias, array  $fields) 
+

+ + +

Parameters

+ + + + + + + + + + + + + + + + +
\Lqdt\OrmJson\Database\Dialect\string$field
\Lqdt\OrmJson\Database\Dialect\string$alias
array$fields
+ + + +
+
+ +
+ +
+
+ +
+

_selectDatField()

+ +
_selectDatField(\Lqdt\OrmJson\Database\Dialect\string  $datfield, array  $fields, array  $filteredFields, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + + + + + + + + + + + +
\Lqdt\OrmJson\Database\Dialect\string$datfield
array$fields
array$filteredFields
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_extractDatField()

+ +
_extractDatField(\Lqdt\OrmJson\Database\Dialect\string  $datfield, \Lqdt\OrmJson\Database\Dialect\string  $alias, array  $fields, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + + + + + + + + + + + +
\Lqdt\OrmJson\Database\Dialect\string$datfield
\Lqdt\OrmJson\Database\Dialect\string$alias
array$fields
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_selectedFieldsConverter()

+ +
_selectedFieldsConverter(array  $fields, \Cake\Database\Query  $query) 
+

+ + +

Parameters

+ + + + + + + + + + + +
array$fields
\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_filtersConverter()

+ +
_filtersConverter(string|\Cake\Database\ExpressionInterface  $expression, \Cake\Database\Query  $query) 
+

Returns the appropriate ExpressionInterface regarding the incoming one

+ + +

Parameters

+ + + + + + + + + + + +
string|\Cake\Database\ExpressionInterface$expression

Incoming expression

\Cake\Database\Query$query
+ + + +
+
+ +
+ +
+
+ +
+

_rawSqlConverter()

+ +
_rawSqlConverter(string  $expression, \Cake\Database\Query  $query) : \Cake\Database\Expression\QueryExpression
+

Returns a new QueryExpression built upon the parsing of the expression to +update datfield names

+ + +

Parameters

+ + + + + + + + + + + +
string$expression

Raw expression to transform

\Cake\Database\Query$query
+ + +

Returns

+ \Cake\Database\Expression\QueryExpression + —

QueryExpression

+ +
+
+ +
+ +
+
+ +
+

_comparisonConverter()

+ +
_comparisonConverter(\Cake\Database\Expression\Comparison  $expression, \Cake\Database\Query  $query) : \Cake\Database\Expression\Comparison|\Cake\Database\Expression\QueryExpression
+

Update or replace the Comparison expression to perform comparisons on +datFields. In some cases, PDO limitations implies to replace the +expression with a raw SQL fragment. It can be a bit dangerous when +using raw user input to perform global matching in `array` mode.

+

Regular fields expressions are left as is.

+ +

Parameters

+ + + + + + + + + + + +
\Cake\Database\Expression\Comparison$expression

Comparison expression

\Cake\Database\Query$query
+ + +

Returns

+ \Cake\Database\Expression\Comparison|\Cake\Database\Expression\QueryExpression + —

Updated expression

+ +
+
+ +
+ +
+
+ +
+

_unaryExpressionConverter()

+ +
_unaryExpressionConverter(\Cake\Database\Expression\UnaryExpression  $expression, \Cake\Database\Query  $query) : \Cake\Database\Expression\UnaryExpression
+

Parses the unary expression to apply conversions on childrens and returns +an updated UnaryExpression

+

Note : This a VERY hacky way because the UnaryExpression class doesn't expose +getter/setter for protected _value property.

+

In this implementation, it causes an infinite loop when used directly with a SQL fragment :

+

['NOT' => 'sub.prop@datfield like "%buggy%"]

+

That's why, an exception is thrown as soon as $value is extracted

+ +

Parameters

+ + + + + + + + + + + +
\Cake\Database\Expression\UnaryExpression$expression

Expression

\Cake\Database\Query$query
+ + +

Returns

+ \Cake\Database\Expression\UnaryExpression + —

New expression

+ +
+
+ +
+ +
+
+ +
+

_queryExpressionConverter()

+ +
_queryExpressionConverter(\Cake\Database\Expression\QueryExpression  $expression, \Cake\Database\Query  $query) : \Cake\Database\Expression\QueryExpression
+

Iterates over a QueryExpression and replace Comparison expressions +to handle JSON comparison in datfields.

+ + +

Parameters

+ + + + + + + + + + + +
\Cake\Database\Expression\QueryExpression$expression

QueryExpression

\Cake\Database\Query$query
+ + +

Returns

+ \Cake\Database\Expression\QueryExpression + —

Updated QueryExpression

+ +
+
+ +
+ +
+
+ +
+

_removeAliasesFromConditions()

+ +
_removeAliasesFromConditions(  $query) 
+

We need to override this method from SqlDialectTrait as it breaks apart datfields with delete

+ + +

Parameters

+ + + + + + +
$query
+ + + +
+
+ +
+ +
+
+ + + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Database.Schema.DatFieldTableSchema.html b/docs/classes/Lqdt.OrmJson.Database.Schema.DatFieldTableSchema.html new file mode 100644 index 0000000..b4a231e --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.Database.Schema.DatFieldTableSchema.html @@ -0,0 +1,584 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\Database\SchemaDatFieldTableSchema

+

These upgraded schema provides convenience to handle data types into JSON data

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
addJsonTypes()
addTransientJsonTypes()
setJsonTypes()
setTransientJsonTypes()
getJsonTypeMap()
clearJsonTypes()
clearTransientJsonTypes()
typeMap()
+
No public properties found
+
No constants found
+
+
+
_parseJsonTypes()
+
$_jsonTypeMap
$_transientJsonTypeMap
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+
+

Properties

+
+ +
+
+ +
+

$_jsonTypeMap

+
$_jsonTypeMap :\Lqdt\OrmJson\Database\Schema\array<string,
+

Stores permanent datfield JSON data types or JSON types definition

Type

\Lqdt\OrmJson\Database\Schema\array +
+ +
+
+ +
+

$_transientJsonTypeMap

+
$_transientJsonTypeMap :array
+

Stores transient datfield JSON types that will be removed after +next call of typeMap()

Type

array
+
+ +
+
+

Methods

+ +
+
+ +
+

addJsonTypes()

+ +
addJsonTypes(array|string  $types,array|null  $type = null): void
+

Permanently register a JSON type(s) in schema. provided type(s) will be added to current styles +In case of target conflict

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => type definition>,...]

array|null$type

Type definition

+
+ +
+
+ +
+

addTransientJsonTypes()

+ +
addTransientJsonTypes(array|string  $types,array|null  $type = null): void
+

Register transient JSON type(s) in schema

These types will be removed after next call to DatFieldTableSchema::typeMap()

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => type definition>,...]

array|null$type

Type definition

+
+ +
+
+ +
+

setJsonTypes()

+ +
setJsonTypes(array|string  $types,array|null  $type = null): void
+

Permanently register a JSON type(s) in schema. provided type(s) will replace currently stored +JSON types

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => type definition>,...]

array|null$type

Type definition

+
+ +
+
+ +
+

setTransientJsonTypes()

+ +
setTransientJsonTypes(array|string  $types,array|null  $type = null): void
+

Register transient JSON type(s) in schema

These types will be removed after next call to DatFieldTableSchema::typeMap()

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => type definition>,...]

array|null$type

Type definition

+
+ +
+
+ +
+

getJsonTypeMap()

+ +
getJsonTypeMap()
+

+
+ +
+
+ +
+

clearJsonTypes()

+ +
clearJsonTypes(): self
+

Clears permanent JSON types

Returns

self
+
+ +
+
+ +
+

clearTransientJsonTypes()

+ +
clearTransientJsonTypes(): self
+

Clears transient JSON types

Returns

self
+
+ +
+
+ +
+

typeMap()

+ +
typeMap()
+

+
+ +
+
+ +
+

_parseJsonTypes()

+ +
_parseJsonTypes(array|string  $types,array|null  $type = null): array
+

Process a single or a set of json types and returns it as a suitable array to be stored as json types

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => type definition>,...]

array|null$type

Type definition

Returns

array —

Processed JSON types

+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Database.Schema.DatFieldTableSchemaInterface.html b/docs/classes/Lqdt.OrmJson.Database.Schema.DatFieldTableSchemaInterface.html new file mode 100644 index 0000000..073976b --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.Database.Schema.DatFieldTableSchemaInterface.html @@ -0,0 +1,470 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\Database\SchemaDatFieldTableSchemaInterface

+

When a table schema implements this interface, its `typeMap` method should return +an array containing regular field type and datfield type definition as well.

Datfield type definition can be either a regular CakePHP data type string string or an array which provides one or more of the following keys :

+
    +
  • type => <string> : regular CakePHP registered data type
  • +
  • marshal => <callable> : Callback to apply on marshal operations, overrides data type marshal if one is defined
  • +
  • toPHP => <callable> : Callback to apply on toPHP operations, overrides data type toPHP if one is defined
  • +
  • toDatabase => <callable> : Callback to apply on toDatabase operations, overrides data type toDatabase if one is defined
  • +
+

The callable callback will be provided with following arguments :

+
    +
  1. Current value in data
  2. +
  3. Full row of data
  4. +
  5. Current query
  6. +
+

Returned value will be used to replace value at datfield path

+ +
+

Summary

+
+
+ Methods +
+
+ Constants +
+
+
+
isDatField()
setJsonTypes()
setTransientJsonTypes()
getJsonTypeMap()
clearJsonTypes()
clearTransientJsonTypes()
+
No constants found
+
+
+
No protected methods found
+
+ N/A +
+
+
+
No private methods found
+
+ N/A +
+
+
+
+ +
+
+

Methods

+
+ +
+
+ +
+

isDatField()

+ +
isDatField(mixed  $field = null): integer
+

Utility function to check if a field is datfield and if it's v1 or v2 notation

Parameters

+ + + + +
mixed$field

Field name

Returns

integer —

0 for non datfield strings, 1 for path@field notation and 2 for field->path notation

+
+ +
+
+ +
+

setJsonTypes()

+ +
setJsonTypes(array|string  $types,array|string|null  $type = null): void
+

Permanently register JSON type(s) in schema

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => ,...]

array|string|null$type

Type definition

+
+ +
+
+ +
+

setTransientJsonTypes()

+ +
setTransientJsonTypes(array|string  $types,array|null  $type = null): void
+

Register transient JSON type(s) in schema

These types will be removed after next call to DatFieldTableSchema::typeMap()

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => ,...]

array|null$type

Type definition

+
+ +
+
+ +
+

getJsonTypeMap()

+ +
getJsonTypeMap(): \Lqdt\OrmJson\Database\JsonTypeMap
+

Returns the JSON type map based on schema type map

Transient JSON types have to be removed afterwards

Returns

\Lqdt\OrmJson\Database\JsonTypeMap
+
+ +
+
+ +
+

clearJsonTypes()

+ +
clearJsonTypes(): self
+

Clears permanent JSON types

Returns

self
+
+ +
+
+ +
+

clearTransientJsonTypes()

+ +
clearTransientJsonTypes(): self
+

Clears transient JSON types

Returns

self
+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Database.Schema.DatFieldTableSchemaTrait.html b/docs/classes/Lqdt.OrmJson.Database.Schema.DatFieldTableSchemaTrait.html new file mode 100644 index 0000000..aed615a --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.Database.Schema.DatFieldTableSchemaTrait.html @@ -0,0 +1,581 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\Database\SchemaDatFieldTableSchemaTrait

+

These upgraded schema is needed to handle applying data type into JSON data

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
addJsonTypes()
addTransientJsonTypes()
setJsonTypes()
setTransientJsonTypes()
getJsonTypeMap()
clearJsonTypes()
clearTransientJsonTypes()
typeMap()
+
No public properties found
+
No constants found
+
+
+
_parseJsonTypes()
+
$_jsonTypeMap
$_transientJsonTypeMap
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+
+

Properties

+
+ +
+
+ +
+

$_jsonTypeMap

+
$_jsonTypeMap :\Lqdt\OrmJson\Database\Schema\array<string,
+

Stores permanent datfield JSON data types or JSON types definition

Type

\Lqdt\OrmJson\Database\Schema\array +
+ +
+
+ +
+

$_transientJsonTypeMap

+
$_transientJsonTypeMap :array
+

Stores transient datfield JSON types that will be removed after +next call of typeMap()

Type

array
+
+ +
+
+

Methods

+ +
+
+ +
+

addJsonTypes()

+ +
addJsonTypes(array|string  $types,array|null  $type = null): void
+

Permanently register a JSON type(s) in schema. provided type(s) will be added to current styles +In case of target conflict

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => type definition>,...]

array|null$type

Type definition

+
+ +
+
+ +
+

addTransientJsonTypes()

+ +
addTransientJsonTypes(array|string  $types,array|null  $type = null): void
+

Register transient JSON type(s) in schema

These types will be removed after next call to DatFieldTableSchema::typeMap()

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => type definition>,...]

array|null$type

Type definition

+
+ +
+
+ +
+

setJsonTypes()

+ +
setJsonTypes(array|string  $types,array|null  $type = null): void
+

Permanently register a JSON type(s) in schema. provided type(s) will replace currently stored +JSON types

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => type definition>,...]

array|null$type

Type definition

+
+ +
+
+ +
+

setTransientJsonTypes()

+ +
setTransientJsonTypes(array|string  $types,array|null  $type = null): void
+

Register transient JSON type(s) in schema

These types will be removed after next call to DatFieldTableSchema::typeMap()

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => type definition>,...]

array|null$type

Type definition

+
+ +
+
+ +
+

getJsonTypeMap()

+ +
getJsonTypeMap()
+

+
+ +
+
+ +
+

clearJsonTypes()

+ +
clearJsonTypes(): self
+

Clears permanent JSON types

Returns

self
+
+ +
+
+ +
+

clearTransientJsonTypes()

+ +
clearTransientJsonTypes(): self
+

Clears transient JSON types

Returns

self
+
+ +
+
+ +
+

typeMap()

+ +
typeMap()
+

+
+ +
+
+ +
+

_parseJsonTypes()

+ +
_parseJsonTypes(array|string  $types,array|null  $type = null): array
+

Process a single or a set of json types and returns it as a suitable array to be stored as json types

Parameters

+ + + + + + + + +
array|string$types

Datfield to type or array of [ => type definition>,...]

array|null$type

Type definition

Returns

array —

Processed JSON types

+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Model.Entity.DatFieldTrait.html b/docs/classes/Lqdt.OrmJson.Model.Entity.DatFieldTrait.html new file mode 100644 index 0000000..83c4865 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.Model.Entity.DatFieldTrait.html @@ -0,0 +1,852 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\Model\EntityDatFieldTrait

+

This trait adds useful methods to get and set values in JSON fields

+

All methods can safely be called on regular fields

+ + +
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
+ __construct()
+ jsonGet()
+ jsonSet()
+ jsonIsset()
+ jsonUnset()
+ jsonMerge()
+
+
+ No public properties found +
+
+ No constants found +
+
+
+
+ No protected methods found +
+
+ $_hidden
+
+
+ N/A +
+
+
+
+ No private methods found +
+
+ No private properties found +
+
+ N/A +
+
+
+
+ +
+ + + +
+
+

Properties

+
+ +
+ +
+
+ +
+

$_hidden

+
$_hidden : 
+

+ + +

Type

+ +
+
+ +
+ + + +
+

Methods

+ +
+ +
+
+ +
+

__construct()

+ +
__construct(array  $properties = array(), array  $options = array()) 
+

+ + +

Parameters

+ + + + + + + + + + + +
array$properties
array$options
+ + + +
+
+ +
+ +
+
+ +
+

jsonGet()

+ +
jsonGet(string  $datfield, boolean  $assoc = false) : mixed
+

Get a value inside field JSON data

+ + +

Parameters

+ + + + + + + + + + + +
string$datfield

Datfield

boolean$assoc

Returns associative array if true instead of an object

+ + +

Returns

+ mixed + —

Field value

+ +
+
+ +
+ +
+
+ +
+

jsonSet()

+ +
jsonSet(string|array  $datfield, mixed  $value = null) : self
+

Set a value inside field JSON data

+ + +

Parameters

+ + + + + + + + + + + +
string|array$datfield

Dafield or array of [datfield => value]

mixed$value

Value to set

+ + +

Returns

+ self + +
+
+ +
+ +
+
+ +
+

jsonIsset()

+ +
jsonIsset(string  $datfield) : boolean
+

Check if a key exists within path

+ + +

Parameters

+ + + + + + +
string$datfield

Datfield or regular field

+ + +

Returns

+ boolean + —

true if key exists

+ +
+
+ +
+ +
+
+ +
+

jsonUnset()

+ +
jsonUnset(string|array  $datfield) : self
+

Delete a key from JSON data

+ + +

Parameters

+ + + + + + +
string|array$datfield

Datfield

+ + +

Returns

+ self + +
+
+ +
+ +
+
+ +
+

jsonMerge()

+ +
jsonMerge() : self
+

Merge new json values into fields when called after patchEntity

+ + + + +

Returns

+ self + +
+
+ +
+ +
+
+ + + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldBelongsTo.html b/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldBelongsTo.html new file mode 100644 index 0000000..822c361 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldBelongsTo.html @@ -0,0 +1,378 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\ORM\AssociationDatFieldBelongsTo

+

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
eagerLoader()
+
No public properties found
+
No constants found
+
+
+
No protected methods found
+
No protected properties found
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+

Methods

+ +
+
+ +
+

eagerLoader()

+ +
eagerLoader(array  $options)
+

Parameters

+ + + + +
array$options
+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldBelongsToMany.html b/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldBelongsToMany.html new file mode 100644 index 0000000..e0d38c3 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldBelongsToMany.html @@ -0,0 +1,409 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\ORM\AssociationDatFieldBelongsToMany

+

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
eagerLoader()
replaceLinks()
+
No public properties found
+
No constants found
+
+
+
No protected methods found
+
No protected properties found
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+

Methods

+ +
+
+ +
+

eagerLoader()

+ +
eagerLoader(array  $options)
+

Parameters

+ + + + +
array$options
+
+ +
+
+ +
+

replaceLinks()

+ +
replaceLinks(\Cake\Datasource\EntityInterface  $sourceEntity,array  $targetEntities,array  $options = array())
+

Parameters

+ + + + + + + + + + + + +
\Cake\Datasource\EntityInterface$sourceEntity
array$targetEntities
array$options
+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldHasMany.html b/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldHasMany.html new file mode 100644 index 0000000..3cce97a --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldHasMany.html @@ -0,0 +1,378 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\ORM\AssociationDatFieldHasMany

+

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
eagerLoader()
+
No public properties found
+
No constants found
+
+
+
No protected methods found
+
No protected properties found
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+

Methods

+ +
+
+ +
+

eagerLoader()

+ +
eagerLoader(array  $options)
+

Parameters

+ + + + +
array$options
+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldHasOne.html b/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldHasOne.html new file mode 100644 index 0000000..d10e109 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.ORM.Association.DatFieldHasOne.html @@ -0,0 +1,378 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\ORM\AssociationDatFieldHasOne

+

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
eagerLoader()
+
No public properties found
+
No constants found
+
+
+
No protected methods found
+
No protected properties found
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+

Methods

+ +
+
+ +
+

eagerLoader()

+ +
eagerLoader(array  $options)
+

Parameters

+ + + + +
array$options
+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.ORM.Association.Loader.DatFieldSelectLoader.html b/docs/classes/Lqdt.OrmJson.ORM.Association.Loader.DatFieldSelectLoader.html new file mode 100644 index 0000000..f39c739 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.ORM.Association.Loader.DatFieldSelectLoader.html @@ -0,0 +1,382 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\ORM\Association\LoaderDatFieldSelectLoader

+

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
No public methods found
+
No public properties found
+
No constants found
+
+
+
_buildResultMap()
+
No protected properties found
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+

Methods

+ +
+
+ +
+

_buildResultMap()

+ +
_buildResultMap(  $fetchQuery,  $options)
+

Parameters

+ + + + + + + + +
$fetchQuery
$options
+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.ORM.Association.Loader.DatFieldSelectWithPivotLoader.html b/docs/classes/Lqdt.OrmJson.ORM.Association.Loader.DatFieldSelectWithPivotLoader.html new file mode 100644 index 0000000..6e5bda5 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.ORM.Association.Loader.DatFieldSelectWithPivotLoader.html @@ -0,0 +1,382 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\ORM\Association\LoaderDatFieldSelectWithPivotLoader

+

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
No public methods found
+
No public properties found
+
No constants found
+
+
+
_buildResultMap()
+
No protected properties found
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+

Methods

+ +
+
+ +
+

_buildResultMap()

+ +
_buildResultMap(  $fetchQuery,  $options)
+

Parameters

+ + + + + + + + +
$fetchQuery
$options
+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.PHPStan.CurlyDatFieldNotation.html b/docs/classes/Lqdt.OrmJson.PHPStan.CurlyDatFieldNotation.html new file mode 100644 index 0000000..a0176f3 --- /dev/null +++ b/docs/classes/Lqdt.OrmJson.PHPStan.CurlyDatFieldNotation.html @@ -0,0 +1,402 @@ + + + + + + API Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ + + +

\Lqdt\OrmJson\PHPStanCurlyDatFieldNotation

+

+
+

Summary

+
+
+ Methods +
+
+ Properties +
+
+ Constants +
+
+
+
hasProperty()
getProperty()
+
No public properties found
+
No constants found
+
+
+
No protected methods found
+
No protected properties found
+
+ N/A +
+
+
+
No private methods found
+
No private properties found
+
+ N/A +
+
+
+
+ +
+
+

Methods

+ +
+
+ +
+

hasProperty()

+ +
hasProperty(\PHPStan\Reflection\ClassReflection  $classReflection,string  $propertyName): boolean
+

Check that json field owning datfield exists in class

Parameters

+ + + + + + + + +
\PHPStan\Reflection\ClassReflection$classReflection

Class reflection

string$propertyName

Datfield

Returns

boolean
+
+ +
+
+ +
+

getProperty()

+ +
getProperty(\PHPStan\Reflection\ClassReflection  $classReflection,string  $propertyName): \PHPStan\Reflection\PropertyReflection
+

Returns property reflection for datfield

Parameters

+ + + + + + + + +
\PHPStan\Reflection\ClassReflection$classReflection

Class reflection

string$propertyName

Property name

Throws

+
\PHPStan\Reflection\MissingPropertyFromReflectionException
+

Returns

\PHPStan\Reflection\PropertyReflection
+
+ +
+
+ + + +
+ + + diff --git a/docs/classes/Lqdt.OrmJson.Plugin.html b/docs/classes/Lqdt.OrmJson.Plugin.html index c97ad90..9af839f 100644 --- a/docs/classes/Lqdt.OrmJson.Plugin.html +++ b/docs/classes/Lqdt.OrmJson.Plugin.html @@ -11,7 +11,6 @@ - @@ -20,8 +19,7 @@ - - - @@ -86,16 +83,12 @@ API Documentation -
-
-
-