Skip to content

How to Make Requests from the Frontend

William Tempest Wright Ferrer edited this page Apr 15, 2018 · 11 revisions

Introduction

Scribe route work like normal RESTful routes but with added features, and a standard way of transforming information.

Be aware that Scribe also has a powerful permissioning systems on the backend, so just because you request something, does not mean the backend developer has allowed you that access.

Features of Frontend Requests

Batching and Chaining

Other solutions for RESTful API’s out of the box will only let you Create, Update and Delete one resource at a time, and do not let you delve into making changes to associated resources in the same request.

This can be system resource inefficient. Due to the overhead of having to make multiple requests, and the user experience can be impacted when multiple calls need to be made to the API in sequence to store all the needed information. This also requires developers to write custom code any time that more powerful endpoints beyond what is automatically supported are needed.

Furthermore, you start to lose the simplicity and clarity that RESTful API’s provide when you need to create multiple endpoints for the same resource in order to support different types of requests from the front end.

Out of the box Scribe automatically supports Batching and Chaining, meaning that any number of entities may be created/updated/deleted with each request, and any associated entities (at any depth of association) may be created/updated/deleted as well.

With Scribes wide range of security, and configurability features you can use Batching and Chaining with no risk that operations will take place that the user should not be able to complete. Each entity that is modified, is modified exactly as if a separate RESTful request had been made to modify it with all the same security/validation features in place.

Filterable Index Actions

With most other solutions if you want to make your index actions filterable based on parameters passed from the front end you will have to write lots of custom code for each index. Additionally, the scope of what is returned by the index is hardcoded, and unless you want to create additional endpoints, and methods in your entity repositories you will be stuck with this limited functionality.

Along with its Extendable Query functionality Scribe has these problems covered. You may make additional routes to be able to specify what data is returned but you will not need to make additional controllers or methods in your repositories.

Out of the box, Scribe allows you to filter any endpoint by a filter passed from the front end. You can send it: where, having, order by and group by clauses as well as passing in placeholders that are used in your query.

With Scribes wide range of security, and configurability features you can use these filters with no risk that operations will take place that the user should not be able to complete (provided you configured the endpoint sufficiently for your own requirements).

This functionality allows an index to service any range of requests that the front end developer can imagine.

All data passed from the front end is processed as MySQL placeholders and uses additional vetting methods to prevent any sort of attack from taking place.

Other solutions that offer similar, but less feature rich functionality make you pass in the filter parameters in the body of the get request. This is not standards compliant and will break many popular edge systems your infrastructure may have in place. Scribe allows you to pass the filter many different ways to meet your preference. For instance: as the body of the get request (least standards compliant and most like other systems), a json encoded query passed as a get parameter, or as a sequence of "get" parameters (most standards compliant option).

GET Requests

Get Requests are filterable, allowing the frontend to filter/arrange the results by: where, having, orderBy, groupBy, and placeholders.

There are also options that can be passed to modify what is returned.

Specifying how the Filters are Passed

Pass one of the following get params to let Scribe know where to look for your filter query:

queryLocation = {body | singleParam | params}

If "body" then the query was passed as a json in the body of the request

If "singleParam" it was passed in another get param called "query" as a json encoded string.

If "params" the query was passed as param syntax listed further down. By default 'params' is used, because it's the most standards complaint.

Queries using Body or singleParam

When using these types of filters, include a "query" key at the top of your JSON. Inside that query may be the following sub keys.

where / having (array, Optional)

The where and having blocks work the same way, with one going to the where part of the query and the other going to the having part of the query.

Include in this block pass a linear array of filters. The values you may put in the filters are as follows:

  • field -- (string) Field name with the alias of the table the field is on, such as t.id.
  • type -- ("and"|"or") Specifiy if this criteeria a an 'and' or an 'or' criteria.
  • operator -- (string) An expression name that will be used to generate the operator. If operator is 'andX' or 'orX' then 'conditions' value with a nested list of conditions is used instead of the 'arguments' value. By default the following operators are allowed: 'andX', 'orX', 'eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'in', 'notIn', 'isNull', 'isNotNull', 'like', 'notLike', 'between'.
  • arguments -- (array|null, Optional) Arguments to pass to the expression. If operator is 'andX' or 'orX' this is omitted. Conditions are used in instead. Generally just 1 argument is needed, for "between" operators use 2 arguments, and for "in" and "notIn" operators use a nested array of values -- 'arguments'=>[['', '']].
  • conditions -- (array|null, Optional) Contains an array of additional criteria. This nested array has the exact same structure as the block above it in this example. If operator is not 'andX' or 'orX' this is omitted. This allows conditional nesting.

Example:


"where": [{
	"field": "t.name",
	"type": "and",
	"operator": "eq",
	"arguments": ["BEETHOVEN"]
}],

orderBy (array, Optional)

In this array passes keys that are the field name (including the table alias -- such as t.id) and the value is the direction of the order by (either: ASC or DESC).

Example:

"orderBy": {
	"t.name": "ASC",
	"t.id": "DESC"
},

groupBy (array, Optional)

Each value in the array is a field name including the table alias -- such as t.id) to group by. Should only be used in queries that inherently have an aggregate in the select.

Example:

"groupBy": [
	"t.name",
	"t.id"
],

placeholders (array, Optional)

The keys in this array are a placeholder name that is waiting to be used in the query.

The value is another array that contains the following:

  • value -- (mixed) //Value to put in the placeholder.
  • type -- (PDO::PARAM_* | \Doctrine\DBAL\Types\Type::* constant | null, Optional) type of the placeholders.

Example:

"placeholders": {
	"test": {
		"value": 42,
	}
}

Options using body or singleParam

The basic option settings you can pass from the frontend are as follows:

  • returnCount -- (boolean|null, Defaults to true) Whether or not the count should be returned from and index action.
  • limit -- (int|null, Defaults to 100) The limit to apply to the query (max number of rows that will be returned).
  • offset -- (int|null, Default to 0) The offset for the query. If omitted index actions will return data starting at the first available row.
  • useGetParams -- (boolean|null, Defaults to true) Whether or not to expect the params to be in discrete get params format as illustrated below. You would not pass this option directly from the frontend -- it will instead be detected from if you called the url like so: ?queryLocation=params, or if you omitted the query location altogether.

Example:

"options": {
	"returnCount": true,
	"limit": 1,
	"offset": 1
}

There is also one additional option you can pass

resourceIds (array|null, Optional)

In this array the key is the name of the resource id (as it would appear in the construction of the route in Laravel such as: users/{user}). The value is the placeholder name the key will be converted to. Placeholders created in this way will be added to the query as parameters for placeholders which the back end developer has put in the query to receive them.

This array is automatically populated or appended to by the controller based on parameters passed through the url. The ability for the frontend to specify resourceIds is included for use with custom logic only.

GET Example of what is Possible with Body or singleParam

// This would be passed in a get request to an end point as json encoded string if either: <url>?queryLocation=body or <url>?queryLocation=singleParam&query=<json>
$frontEndQueryToGet = [
    'query'=>[
        'where'=>[ // Optional block. In this block filters can be set that will be applied to the where clause of the query. Tested in: testGeneralQueryBuilding
            [
                'field'=>'<string>', //Field name with the alias of the table the field is on, such as t.id. Tested in: testGeneralQueryBuilding
                'type'=>'<"and"|"or">', // Is it a an 'and' or an 'or' criteria. Tested in: testGeneralQueryBuilding
                'operator'=>'<string>', // An expression name that will be used to generate the operator. If operator is 'andX' or 'orX' then 'conditions' value with a nested list of conditions is used instead of the 'arguments' value. By default the following operators are allowed: 'andX', 'orX', 'eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'in', 'notIn', 'isNull', 'isNotNull', 'like', 'notLike', 'between'. Tested in: testGeneralQueryBuilding
                'arguments'=>['<mixed>'],  // Arguments to pass to the expression. If operator is 'andX' or 'orX' this is omitted. Conditions are used in instead. Generally just 1 argument is needed, for between operators use 2 arguments, and for in and notIn operators use a nested array of values -- 'arguments'=>[['<string>', '<string>']]. //Tested in: testGeneralQueryBuilding
                'conditions'=>['<array>'] // Contains an array of additional criteria. This nested array has the exact same structure as the block above it in this example. If operator is not 'andX' or 'orX' this is omitted. This allows condition nesting. //Tested in: testGeneralQueryBuilding
            ]
        ],
        'having'=>[ // Optional block. Works the same as the where block but applied to criteria to the having clause of the query. Tested in: testGeneralQueryBuilding
            [
                'field'=>'<string>', //Field name with the alias of the table the field is on, such as t.id. Tested in: testGeneralQueryBuilding
                'type'=>'<"and"|"or">', // Is it a an 'and' or an 'or' criteria. Tested in: testGeneralQueryBuilding
                'operator'=>'<string>', // An expression name that will be used to generate the operator. If operator is 'andX' or 'orX' then 'conditions' value with a nested list of conditions is used instead of the 'arguments' value. By default the following operators are allowed: 'andX', 'orX', 'eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'in', 'notIn', 'isNull', 'isNotNull', 'like', 'notLike', 'between'. Tested in: testGeneralQueryBuilding
                'arguments'=>['<mixed>'],  // Arguments to pass to the expression. If operator is 'andX' or 'orX' this is omitted. Conditions are used in instead. Generally just 1 argument is needed, for between operators use 2 arguments, and for in and notIn operators use a nested array of values -- 'arguments'=>[['<string>', '<string>']]. //Tested in: testGeneralQueryBuilding
                'conditions'=>['<array>'] // Contains an array of additional criteria. This nested array has the exact same structure as the block above it in this example. If operator is not 'andX' or 'orX' this is omitted. This allows condition nesting. //Tested in: testGeneralQueryBuilding
            ]
        ],
        'orderBy'=>[ // Optional block. This block will add criteria to the order by clause of the query. Tested in: testGeneralQueryBuilding
            '<string>'=>'<"ASC" | "DESC">' // The key is the field name (including the table alias -- such as t.id) and the value is the direction of the order by. Tested in: testGeneralQueryBuilding
        ],
        'groupBy'=>[ // Optional block. Tested in: testGeneralQueryBuilding
            '<string>' // Each value is the a field name including the table alias -- such as t.id) to group by. Should only be used in queries that inherently have an aggregate in the select. Tested in: testGeneralQueryBuilding
        ],
        'placeholders'=>[ // Optional block. This blocks lets the frontend pass in values for placeholders which the back end developer has added to the base query. Tested in: testGeneralQueryBuilding
            '<string>'=>[ // The key is a placeholder name that is waiting to be used in the query. Tested in: testGeneralQueryBuilding
                'value'=>'<mixed>', //Value to put in the placeholder. Tested in: testGeneralQueryBuilding
                'type'=>'<PDO::PARAM_* | \Doctrine\DBAL\Types\Type::* constant | null>' // Optional type of the placeholders. Tested in: testGeneralQueryBuilding
            ]
        ],
    ],
    'options'=>[ // Optional block. This block lets you pass in options related to the query. You may add your own keys and values for your own implementations as well. These options will be passed to the repository actions that are called and may be referenced by your custom event listeners and closures.
        'returnCount'=>'<boolean|null>', // Defaults to true. Whether or not the count should be returned from and index action. Tested in: testGeneralDataRetrieval
        'limit'=>'<int|null>', // Defaults to 100. The limit to apply to the query (max number of rows that will be returned).  Tested in: testGeneralQueryBuilding
        'offset'=>'<int|null>', // Default to 0. The offset for the query. If omitted index actions will return data starting at the first available row. Tested in: testGeneralQueryBuilding
        'useGetParams'=>'<boolean|null>',// Defaults to true. Whether or not to expect the params to be in discrete get params format as illustrated below.  You would not pass this option directly from the frontend -- it will instead be detected from if you called the url like so: <url>?queryLocation=params, or if you omitted the queryLocation all together.  Tested in testGeneralQueryBuildingWithGetParams
        'resourceIds'=>[ // Optional block.
            '<string>'=>'<string>' // The key is the name of the resource id (as it would appear in the construction of the route in Laravel such as: users/{user}). The value is the placeholder name the key will be converted to. Placeholders created in this way will be added to the query as parameters for placeholders which the back end developer has put in the query to receive them.
        ] // This is automatically populated or appended to by the controller based on parameters passed through the url. The ability for the frontend to specify resourceIds is included for use with custom logic.
    ]
];

GET Example JSON with body or singleParam

{
	"query": {
		"where": [{
			"field": "t.name",
			"type": "and",
			"operator": "eq",
			"arguments": ["BEETHOVEN"]
		}],
		"having": [{
			"field": "t.name",
			"type": "and",
			"operator": "eq",
			"arguments": ["BEETHOVEN"]
		}],
		"orderBy": {
			"t.name": "ASC",
			"t.id": "DESC"
		},
		"groupBy": [
			"t.name",
			"t.id"
		],
		"placeholders": {
			"test": {
				"value": 42,
			}
		}
	},
	"options": {
		"returnCount": true,
		"limit": 1,
		"offset": 1
	}
}

GET filtering using params (Default Method)

All the same, functionality detailed above when using Body or SingleParam style filters is also available for passing in discrete get params to filter the results of a request.

Note: In field names always replace the dot (such as t.name) with a dash.

The following details how to pass your filter and options from the frontend as discrete params instead of as a json encoded string.

Example:

<url>?queryLocation=params&and_where_eq_a-name=Brahms

Get param query syntax:

where / having

Where or having:

<and|or>_<where|having>_<string>_<string>_<?int>=<mixed>

The optional number at the end is so you can have conditions that are are identical in key name, but have different values.

The first string placeholder above is the operator name, and the second string is the "field name".

If the where operator is "in" or "between" then use an array for the addition arguments, IE:

<and|or>_<where|having>_<string>_<string>_<?int>[]=value1
<and|or>_<where|having>_<string>_<string>_<?int>[]=value2

Example Non Array Values:

and_where_eq_t-name=bob
and_where_eq_t-name_2=rob

Example Array Values:

and_where_in_t-name[]=bob
and_where_in_t-name[]=rob

The above example would search for the name either "bob" or "rob".

Example:

and_where_between_t-id[]=1
and_where_between_t-id[]=2

The above example would search for a range between 1 and 2.

When using an andX or orX operator. Json encode the value using standard syntax described above

Example:

and_where_andX=[[{"field":"t.name","operator":"eq","arguments":["BEETHOVEN"]},{"field":"t.name","operator":"neq","arguments":["BACH"]}]]

orderBy

orderBy:

orderBy_<string>=<ASC|DESC>

The first string placeholder in the example is the field name with the table alias (such as t-id), the value is the direction to order by.

Example:

orderBy_t-name=ASC

groupBy

groupBy:

groupBy[]=<string>

The strings placeholder in the example are the field names with the table alias (such as t-id).

Example:

groupBy[]=t-name

placeholder

placeholder:

placeholder_<string>_<?string>=<mixed>

The first string in the example is the placeholder name. The second string is an optional placeholder type.

Example:

placeholder_myPlaceholder1=1
placeholder_myPlaceholder2_integer=2

option

option:

option_<string>=<mixed>

The string is an option name, the value is whatever you would like to set that option too.

Example:

option_returnCount=1
option_limit=1
option_offset=1

Non GET Requests

These type of requests feature Batching and Chaining and a standardized way of dealing with whole object graphs of resources from the frontend.

Here are some important notes:

  • POST -- with POST requests you may either pass a linear array containing sub arrays full of params (for a batch of resources, one in each sub-array), or you may pass just key-value pairs of params to create just one resource.
  • PUT -- with a PUT request you may pass a resource id to the URL (such as /users/{user}) to update a single resource, or you may pass an array of params and use a URL where the resource id is "batch" (such as /users/batch) to update multiple resources with one request.
  • DELETE -- DELETE requests work the same way as PUT requests (described above).

Non GET Requests can use either simplifiedParams or not. simplifiedParams are not quite as efficiently processed on the backend, but they are simpler to work with. simplifiedParams analyze the data you pass from the front end to convert to standard params on the back end.

All Non Get Requests have up to 2 blocks in the request.

params (array)

This holds the params that will be converted to values stored on resources on the back end.

Params will contain key-value pairs in them. The keys are the names of fields or associations. If the key is a field then the value is the value to set that field to. If the key is an association, then the value is either a scalar value that corresponds to an id of an associated resources in the database (used in instances where you are assigning a resource to an association without altering it) or the value is an associate array containing information about the resource/s being manipulated or created and then assigned to the association. The way these association related arrays are formatted depends on whether you are using verbose or simplified params for the request.

More details are below.

options (array, Optional)

This contains options for the request.

Here are the options that may be passed (note you also can pass your own custom options for custom code to detect):

simplifiedParams (boolean, Default to false)

May also be set as a different default on the controller. This value is whether or not to process the params as standard verbose params or simplified params. Both param examples are below.

testMode (boolean, Defaults to false)

If set to true then data will be rolled back instead of committed. This lets you write test cases that use the API but not store anything in the db.

toArray (array, Optional)

  • completeness -- ("full" | "limited" | "minimal" | "none" | null, Defaults to "full") if "full" then all data will be shown so long as it wouldn't trigger an infinite loop (relations between entities are omitted as soon as they loop back on them selves), if "limited" then all data will be shown but relations leading to already processed entities will not be shown, if "minimal" the same entity will never be shown twice in the return and an empty array will be in its place, if "none" nothing is returned.
  • maxDepth -- (int|null, Optional) How deep should the to array go. You can use this option to prevent unnecessary levels of depth in your return. Tested in: testToArrayBasicFunctionality
  • excludeKeys' -- (string, Optional) Each string is the name of a field or association that should not be returned in the resulting array from the back end. Use this to prevent certain keys from being converted to array to trim the return.
  • allowOnlyRequestedParams -- (boolean|null, Defaults to true) If true only the params that you requested to be changed on the affected entities will be shown on the return. This filters out any fields or associations that you did not request to be changed directly with this request.
  • forceIncludeKeys -- (string, Defaults to: ['id']) These are keys to include in the result even if you didn't request to change them.

Example of Non GET Requests options

$exampleFrontEndNonGetRequest = [
    'params'=>['<array>'], // See below for examples for different types of param sets that can be passed
    'options'=>[ // Optional. This block relates to the options for the request.
        'simplifiedParams'=>'<boolean|null>', // Defaults to false. May also be set as a different default on the controller. This value is whether or not to process the params as standard verbose params or simplified params. Both param examples are below.
        'testMode'=>'<boolean|null>', // Defaults to false. If set to true then data will be rolled back instead of committed. This lets you write test cases that use the api but not store anything to the db.
        'toArray'=>[ // Optional. This blocks relates to the data that should be returned -- specifying how the entities will be transformed to an array.
            'completeness'=>'<"full" | "limited" | "minimal" | "none" | null>', // Defaults to 'full'. if 'full' then all data will be shown so long as it wouldn't trigger an infinite loop (relations between entities are omitted as soon as they loop back on them selves), if 'limited' then all data will be shown but relations leading to already processed entities will not be shown, if 'minimal' the same entity will never be shown twice in the return and an empty array will be in it's place, if 'none' nothing is returned. Tested in: testToArrayBasicFunctionality
            'maxDepth'=>'<int|null>', // Optional. How deep should the to array go. You can use this option to prevent unnecessary levels of depth in your return. Tested in: testToArrayBasicFunctionality
            'excludeKeys'=> ['<string>'], // Optional. Each string is the name of a field or association that should be returned in the resulting array from the back end. Use this to prevent certain keys from being converted to array to trim the return. Tested in: testToArrayBasicFunctionality
            'allowOnlyRequestedParams'=>'<boolean|null>',// Defaults to true. If true only the params that you requested to be changed on the effected entities will be shown in the return. This filters out any fields or associations that you did not request to be changed directly with this request. Tested in: testToArrayBasicFunctionality
            'forceIncludeKeys'=>['<string>'] // Defaults to: ['id']. These are keys to include in the result even if you didn't request to change them. Tested in: testToArrayBasicFunctionality
        ]
    ]
];

Using verbose params

Standard params are the default option, though this can be changed on the controller. I personally prefer simplified params because I find them easier to work with, but simplified params are slightly slower to process.

Verbose params contain exact instructions about what Scribe should do to satisfy the request. The instructions are also formatted in a way that is very fast to process on the backend.

Verbose POST requests

The top level of a verbose post request is either an associative array containing params to create a single resource, or it is a linear array where each entry is an associative array containing the params to create a single resource (a batch request).

Verbose PUT and DELETE Requests

With these requests, you may pass a resource id to the URL (such as /users/{user}) to update a single resource, or you may pass an array of params and use a URL where the resource id is "batch" (such as /users/batch) to update multiple resources with one request.

In a batch request, the top level array is an associative array where each key is the id of resource to retrieve and manipulate. In a non-batch request, the top level array is an associate array where the keys and values relate to a single resource.

Verbose Associations

Verbose Associations contain an array where the keys are a type of action to take on the Entity referenced by the association, before assigning it to the association.

The options are:

  • create -- This means that we are going to create a new resource and assign it to the association. When this happens the value of the association should be a linear array, containing sub arrays in it, with each subarray containing params to be used in creating a new entity.
  • read -- This means we are going to read an Entity/s and assign them to the association. The array placed here should have keys that represent the ids of the Entities to be assigned, and values that are an array that only includes an assignType.
  • update -- These work the same as read except the value array can also include key-value pairs that relate to fields and associations on the associated Entity that will result in that Entity's properties being changed and persisted by the request.
  • delete -- Works the same as read accept any Entity being retrieved in this manner will also be deleted.

assignType ("set", "add", "remove", "setSingle", "addSingle", "removeSingle", "null", "setNull", null, Defaults to "set")

This is the assignment type that the entity will be assigned to the association using. It corresponds to the method of the entity that will be called (IE: set where the string is the field name to set, such as setName).

Anytime "Single" is at the end of the assignType, then we strip the 's' off the end of the association name before calling the method. For instance, if you have an association of users, but you have a method of addUser you need to use an assignType of addSingle.

Use this as a hint about what method should be used under the hood to assign one entity to the association on another.

For more on assignTypes see:

Setting-Up-Your-Entities

Puting it all Together for Verbose Non GET Requests.

The following examples should illustrate how to use verbose non GET requests better than simply describing it with words.

Note: All following examples use the following Entities: Album with a ManyToOne association to Artist with a ManyToMany association to User Artist with a oneToMany association to Album User with a ManyToMany association to Album

Example: Create an album in a batch request using POST, chain a new artist on to it, and assign an existing user to the album:

{
	"params": [{
		"name": "BEETHOVEN: THE COMPLETE PIANO SONATAS",
		"releaseDate": "2017-10-31 01:43:01",
		"artist": {
			"create": [{
				"name": "BEETHOVEN",
				"assignType": "set" //calls setArtist method to assign the artist to the album
			}]
		},
		"users": {
			"read": {
				"1": {
					"assignType": "addSingle" // Calls addUser method to add the user to the album
				}
			}
		}
	}]
}

Example: The same as above, not as a batch request

{
	"params": {
		"name": "BEETHOVEN: THE COMPLETE PIANO SONATAS",
		"releaseDate": "2017-10-31 01:43:01",
		"artist": {
			"create": [{
				"name": "BEETHOVEN",
				"assignType": "set"
			}]
		},
		"users": {
			"read": {
				"1": {
					"assignType": "addSingle"
				}
			}
		}
	}
}

Example: update artist with an id of 1, then update an album with an id of 1 to have a new name as well, using a PUT request.

{
	"params": {
		"1": { // Effects artist with an id of 1
			"name": "The artist formerly known as BEETHOVEN",
			"albums": {
				"update": {
					"1": { // Updates an asociated alumb with an id of 1
						"name": "Kick Ass Piano Solos!"
					}
				}
			}
		}
	}
}

Example: Same as above non-batch

{
	"params": {
		"name": "The artist formerly known as BEETHOVEN",
		"albums": {
			"update": {
				"1": {
					"name": "Kick Ass Piano Solos!"
				}
			}
		}
	}
}

Example: Remove an album from an artist as part of an update request

 {
 	"params": {
 		"1": {
 			"albums": {
 				"update": {
 					"1": {
 						"assignType": "removeSingle" // Calls removeAlbum method on the artist to remove it
 					}
 				}
 			}
 		}
 	}
 }

Example: With a DELETE request remove a artist and an album related to it

 {
 	"params": {
 		"1": {
 			"albums": {
 				"delete": {
 					"1": {
 						"assignType": "removeSingle"
 					}
 				}
 			}
 		}
 	}
 }

Using simplified params

To use simplified params on your non get request pass an option of "simplifiedParams":true in your json request. This also can be set to be defaultly on by the backend developer (recommended).

When simplified params is on Scribe will inspect your request and make the best guess about how to turn it into verbose params.

Sometimes you will need to add hints because it's default guess can't always be the right one. I have listed below the circumstances when you will need to add a hint.

In simplified params, instead of using array keys of "create", "read", "update", "delete", or keys that represent the id of an entity, you simply use arrays where the keys relate only to fields and associations of the entities.

As with verbose params you may include an id value on an association field to read it from the db and assign it to the association without modifying it.

The rules about batching, vs accessing a single entity are the same as they are for verbose params.

chainType ("create", "read", "update", "delete")

Sometimes you need to hint using the simplified params what you want to do. A "chainType" key can be passed to let Scribe know what kind of chain you are attempting to do. The "chainType" may be: "create", "read", "update", "delete".

The only one that is needed to be included generally is "delete". This will let scribe know that when you are done dealing with the associated entity in question, it should be deleted.

chainType is never used at the top level params of a request.

assignType ("set", "add", "remove", "setSingle", "addSingle", "removeSingle", "null", "setNull", null)

Scribe will assume an "assignType" of "set" if it encounters an association where a single associative array is assigned, and it will assume an "assignType" of "addSingle" on each entry, if it encounters an association where the assigned value is a linear array of associative arrays (multiple assignments at once).

Sometimes you will need to hint an "assignType". Generally, you only need to hint if the "assignType" should be: "remove", "removeSingle", "null", "setNull",

assignType is never used at the top level params of a request.

creates

With simplified params, if you include an array that does not have an id in it, it will result in a "create" during which a new entity is made based on the passed in params.

Example:

{
	"params": [// Note lack of id triggers create
		{
			"<string>":"<mixed>", // Field name and value to set it to too. Can also be a association with an array of data relating to the association.
		}
	],
	"options": {
		"simplifiedParams": true
	}
}

reads

As mentioned above reads can be done by just including an id, instead of a whole array on an association. They also can be accomplished with an array that only has an "id" in it.

Example :

{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
		}
	],
	"options": {
		"simplifiedParams": true
	}
}

updates

If you include an id, and other field values, it will trigger an update, where the entity with the associated id is retrieved and then have its fields updated to match the passed in params.

Example as code:

{
	"params": [ // Including an id triggers an update when there is another field referenced
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":"<mixed>", // Field name and value to set it to too. Can also be a association with an array of data relating to the association.
		}
	],
	"options": {
		"simplifiedParams": true
	}
}

deletes

Deletes work the same as reads, but they need a hint of "chainType":"delete"

Example as code

{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
		}
	],
	"options": {
		"simplifiedParams": true
	}
}

Lots of examples as code

This section contains a long block of code with examples of simplified params as code.

// Simplified Param Syntax for Create Update and Delete. To activate pass a frontend option of 'simplifiedParams'=>true

// creates
{
	"params": [ // Note lack of id triggers create
		{
			"<string>":"<mixed>", // Field name and value to set it to too. Can also be a association with an array of data relating to the association.
		}
	],
	"options": {
		"simplifiedParams": true
	}
}
// updates
{
	"params": [ // Including an id triggers an update when there is another field referenced
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":"<mixed>", // Field name and value to set it to too. Can also be a association with an array of data relating to the association.
		}
	],
	"options": {
		"simplifiedParams": true
	}
}
// deletes
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
		}
	],
	"options": {
		"simplifiedParams": true
	}
}

// Chaining Examples:
// Note: These are all example chains from inside an update action
// singleAssociationCreate
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":{ //Association name. Note the lack of the id triggers the create. This automatically triggers an assignType='set' (used with *ToOne type relationships). If you included an array of arrays it would trigger a assignType=addSingle one for each element in the array (used with *ToMany relationships)
				"<string>":"<mixed>", // Field name and value to set it to too. Can also be a association with an array of data relating to the association.
			},
		}
	],
	"options": {
		"simplifiedParams": true
	}
}
// singleAssociationUpdate
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":{ //Association name. Note the id and the field means it's an update
				"id":"<string|int>", // The id of the entity being accessed.
				"<string>":"<mixed>", // Field name and value to set it to too. Can also be a association with an array of data relating to the association.
			},
		}
	],
	"options": {
		"simplifiedParams": true
	}
}
// singleAssociationRead
{
	"params": [
		{
			'id'=>'<string|int>', // The id of the entity being accessed.
			'<string>'=>[ //Association name. Note  just using the id triggers a read and then assigns the entity to the association
				'id'=>'<string|int>', // The id of the entity being accessed.
			],
		]
	],
	"options": {
		"simplifiedParams": true
	}
}
// singleAssociationDelete
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":{ //Association name. Note that specifying chainType=delete triggers a delete
				"id":"<string|int>", // The id of the entity being accessed.
				"chainType":"delete" // This instructs Scribe to delete the associated entity.
			},
		}
	],
	"options": {
		"simplifiedParams": true
	}
}
// When you are chaining to a *ToMany relationship you use an array of arrays to automatically trigger an addSingle assign type.
// multipleAssociationCreate
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":{ //Association name. Note the lack of the id triggers the create. These will automatically be added to the association with the same assignment behavior as assignType:addSingle
				{
					"<string>":"<mixed>", // Field name and value to set it to too. Can also be a association with an array of data relating to the association.
				},
			}
		}
	],
	"options": {
		"simplifiedParams": true
	}
}
// multipleAssociationUpdate
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":{ //Association name. Note the id and the field means it"s an update. These will automatically be added to the association with the same assignment behavior as assignType:addSingle
				{
					"id":"<string|int>", // The id of the entity being accessed.
					"<string>":"<mixed>", // Field name and value to set it to too. Can also be a association with an array of data relating to the association.
				},
			}
		}
	],
	"options": {
		"simplifiedParams": true
	}
}	
// singleAssociationRead
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":{ //Association name. Note the just using the id triggers a read and then assigns the entity to the association. These will automatically be added to the association with the same assignment behavior as assignType:addSingle
				{
					"id":"<string|int>",
				},
			}
		}
	],
	"options": {
		"simplifiedParams": true
	}
}
// singleAssociationReadSimplierStill
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":"<string|int>" // With the key as the association name and the value as an id it will automatically read the associated entity. This is simplified version of the above example
		}
	],
	"options": {
		"simplifiedParams": true
	}
}
// singleAssociationDelete
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":{ //Association name. Note that specifying chain type delete triggers a delete. These will automatically be added to the association with the same assignment behavior as assignType:addSingle
				{
					"id":"<string|int>", // The id of the entity being accessed.
					"chainType":"delete" // This instructs Scribe to delete the associated entity.
				},
			}
		}
	],
	"options": {
		"simplifiedParams": true
	}
}
// Any update, read or delete chain can cause the entity referenced to be removed instead of added to the association by using "assignType":"removeSingle". You can also use "remove" if you have a plural in the name of your method on the entity -- IE: removeUser vs removeUsers
// singleAssociationRead
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":{  //Association name. Note the just using the id triggers a read and then assigns the entity to the association. These will automatically be added to the association with the same assignment behavior as assignType:addSingle
				{
					"id":"<string|int>", // The id of the entity being accessed.
					"assignType":"removeSingle" // This triggers script to remove the associated entity form it"s parent.
				},
			}
		}
	],
	"options": {
		"simplifiedParams": true
	}
}
// Sometimes you want to update an entity through an association, but you don"t want to trigger any assignType (because if you trigger an assignType you will be adding the same entity to an association that already exists).
// multipleAssociationUpdateWithOutAddingIt
{
	"params": [
		{
			"id":"<string|int>", // The id of the entity being accessed.
			"<string>":{ //Association name. Note the id and the field means it"s an update. These will automatically be added to the association with the same assignment behavior as assignType:addSingle
				{
					"id":"<string|int>", // The id of the entity being accessed.
					"<string>":"<mixed>", // Field name and value to set it to too. Can also be an association with an array of data relating to the association.
					"assignType":"null" // This causes the entity to be updated, but not assigned to the parent entity through the association.
				},
			}
		}
	],
	"options": {
		"simplifiedParams": true
	}
}

Examples of simplified params with JSON requests

Here are many JSON request examples.

Example with a POST request create an artist, and album via chaining. Notice that whether or not you are doing a batch request is automatically detected when you use a POST. This example shows the end point will see this is not a batch request (since it only has 1 artist being created).

The url might look like: /artists

{
	"params": {
		"name": "Test Artist",
		"albums": [{
			"name": "Test Album",
			"releaseDate": "2017-10-31 01:43:01"
		}]
	},
	"options": {
		"simplifiedParams": true
	}
}

Example with PUT request. Update a album to have a new name, and then remove a user from it via chaining. This would be done via a batch request. The url might look like: /albums/batch

{
	"params": [{
		"id": 1,
		"name": "Name Redacted",
		"users": [{
			"id": 1,
			"assignType": "removeSingle"
		}]
	}],
	"options": {
		"simplifiedParams": true
	}
}

Example with PUT request. Update an album, and update it's user, with out triggering an assignType=addSingle that would try to add the user back to the album a second time. The url might look like: /albums/batch

{
	"params": [{
		"id": 1,
		"name": "Name Redacted",
		"users": [{
			"id": 1,
			"name": "Your name is Dave now",
			"assignType": "null"
		}]
	}],
	"options": {
		"simplifiedParams": true
	}
}

Example with PUT request. You can access a single resource by including its id in the url and providing params for just 1 resource and not putting the id in the params. Here is another way to trigger the same behavior demonstrated above. The url might look like: /albums/1 Note that in the same manner you can batch PUT requests, or have them access a single resource you can do the same with DELETE requests.

{
	"params": {
		"name": "Name Redacted",
		"users": [{
			"id": 1,
			"assignType": "removeSingle"
		}]
	},
	"options": {
		"simplifiedParams": true
	}
}

Example with a DELETE request. Delete an album and the artist it is associate with The url might look like: /albums/batch

{
	"params": [{
		"id": 1,
		"artist": {
			"id": 1,
			"chainType": "delete"
		}
	}],
	"options": {
		"simplifiedParams": true
	}
}
Clone this wiki locally