diff --git a/doc/making-requests/querying-data.md b/doc/making-requests/querying-data.md
index 9c48292fa..4a72fb349 100644
--- a/doc/making-requests/querying-data.md
+++ b/doc/making-requests/querying-data.md
@@ -43,6 +43,33 @@ GET http://localhost:8000/odata/People?$filter=FirstName eq 'Scott'
+`$filter` can also be used as a path segment, and used multiple times to sequentially add filter parameters.
+
+
+
+```uri
+GET http://localhost:8000/odata/People/$filter(@a)/$filter(@b)?@a=DOB gt 2000-01-01&@b=endswith(FirstName, 'tt')
+```
+
+
+
+```json
+{
+ "@context": "http://localhost:8000/odata/$metadata#People",
+ "value": [
+ {
+ "id": 1,
+ "FirstName": "Scott",
+ "LastName": "Bumble",
+ "Email": "scott.bumble@gmail.com",
+ "DOB": "2001-01-01"
+ }
+ ]
+}
+```
+
+
+
## $orderby
The `$orderby` system query option allows clients to request resources in either ascending order using asc or descending
@@ -457,3 +484,61 @@ GET http://localhost:8000/odata/People?$filter=pets/any(s:endswith(s/Name, 'The
```
+
+## $each
+
+The `$each` path segment enables actions and odata operations such as deletes and updates to be run on sequences of
+entities server-side. To filter the sequence the `$filter` path segment must be used.
+
+Members of a collection can be updated by submitting a PATCH request to the URL constructed by appending `/$each` to the
+resource path of the collection. The additional path segment expresses that the request body describes an update to
+each member of the collection, not an update to the collection itself.
+
+
+
+```uri
+PATCH http://localhost/odata/People/$filter(@bar)/$each?@bar=Color eq 'beige-brown'
+{
+ "Color": "taupe"
+}
+```
+
+
+
+```json
+{
+ "@context": "http://localhost:8000/odata/$metadata#People",
+ "value": [
+ {
+ "id": 1,
+ "FirstName": "Scott",
+ "LastName": "Bumble",
+ "Color": "taupe"
+ },
+ {
+ "id": 3,
+ "FirstName": "Michael",
+ "LastName": "Scott",
+ "Color": "taupe"
+ }
+ ]
+}
+```
+
+
+
+Members of a collection can be deleted by submitting a DELETE request to the URL constructed by appending `/$each`
+to the resource path of the collection. The additional path segment expresses that the collection itself is not
+deleted.
+
+```uri
+DELETE http://localhost/odata/People/$filter(@bar)/$each?@bar=Color eq 'beige-brown'
+```
+
+A bound operation with a single-valued binding parameter can be applied to each member of a collection by appending
+the path segment `$each` to the resource path of the collection, followed by a forward slash and the namespace- or
+alias-qualified name of the bound operation.
+
+```uri
+GET http://localhost/odata/People/$each/SampleModel.MostRecentOrder()
+```
diff --git a/src/Controller/Transaction.php b/src/Controller/Transaction.php
index 6c647379f..9a4e0a723 100644
--- a/src/Controller/Transaction.php
+++ b/src/Controller/Transaction.php
@@ -5,7 +5,6 @@
namespace Flat3\Lodata\Controller;
use Exception;
-use Flat3\Lodata\Drivers\StaticEntitySet;
use Flat3\Lodata\Entity;
use Flat3\Lodata\EntitySet;
use Flat3\Lodata\EntityType;
@@ -59,6 +58,7 @@
use Flat3\Lodata\Transaction\Parameter;
use Flat3\Lodata\Transaction\ParameterList;
use Flat3\Lodata\Transaction\Version;
+use Flat3\Lodata\Type\Collection;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Str;
@@ -227,6 +227,7 @@ class Transaction
PathSegment\Filter::class,
PathSegment\Query::class,
PathSegment\Reference::class,
+ PathSegment\Each::class,
Operation::class,
Singleton::class,
PropertyValue::class,
@@ -1433,7 +1434,8 @@ public function processDeltaPayloads(Entity $parentEntity): void
/** @var NavigationProperty $navigationProperty */
foreach ($navigationProperties as $navigationProperty) {
$deltaPayloads = $body[$navigationProperty->getName()] ?? [];
- $deltaResponseSet = new StaticEntitySet($navigationProperty->getEntityType());
+ $deltaResponseSet = new Collection();
+ $deltaResponseSet->setUnderlyingType($navigationProperty->getEntityType());
foreach ($deltaPayloads as $deltaPayload) {
$entity = null;
diff --git a/src/Drivers/EnumerableEntitySet.php b/src/Drivers/EnumerableEntitySet.php
index b044da5c6..a55811d96 100644
--- a/src/Drivers/EnumerableEntitySet.php
+++ b/src/Drivers/EnumerableEntitySet.php
@@ -6,6 +6,7 @@
use Flat3\Lodata\Entity;
use Flat3\Lodata\EntitySet;
+use Flat3\Lodata\EntityType;
use Flat3\Lodata\Exception\Protocol\NotFoundException;
use Flat3\Lodata\Expression\Parser\Common;
use Flat3\Lodata\Expression\Parser\Search;
@@ -29,6 +30,13 @@ abstract class EnumerableEntitySet extends EntitySet implements ReadInterface, Q
/** @var Enumerable|Collection|LazyCollection $enumerable */
protected $enumerable;
+ public function __construct(string $identifier, ?EntityType $entityType = null)
+ {
+ parent::__construct($identifier, $entityType);
+
+ $this->enumerable = new Collection();
+ }
+
/**
* Query this entity set
* @return Generator
diff --git a/src/Drivers/SQL/SQLSchema.php b/src/Drivers/SQL/SQLSchema.php
index 351b46b3a..ee2bcc557 100644
--- a/src/Drivers/SQL/SQLSchema.php
+++ b/src/Drivers/SQL/SQLSchema.php
@@ -68,7 +68,10 @@ function () {
$key = $this->columnToDeclaredProperty($column);
if (null === $key) {
- throw new ConfigurationException('missing_key', sprintf('The table %s had no resolvable key', $this->getTable()));
+ throw new ConfigurationException(
+ 'missing_key',
+ sprintf('The table %s had no resolvable key', $this->getTable())
+ );
}
if ($column->getAutoincrement()) {
diff --git a/src/Drivers/StaticEntitySet.php b/src/Drivers/StaticEntitySet.php
deleted file mode 100644
index ddaf2d2a7..000000000
--- a/src/Drivers/StaticEntitySet.php
+++ /dev/null
@@ -1,105 +0,0 @@
-applyQueryOptions = false;
- }
-
- /**
- * Return all results
- */
- public function query(): Generator
- {
- foreach ($this->results as $result) {
- yield $result;
- }
- }
-
- public function offsetExists($offset): bool
- {
- return array_key_exists($offset, $this->results);
- }
-
- public function offsetGet($offset): ?Entity
- {
- return $this->results[$offset];
- }
-
- public function offsetSet($offset, $value): void
- {
- if ($offset) {
- $this->results[$offset] = $value;
- } else {
- $this->results[] = $value;
- }
- }
-
- public function offsetUnset($offset): void
- {
- unset ($this->results[$offset]);
- }
-
- public function rewind()
- {
- reset($this->results);
- }
-
- /**
- * Filter the objects in the array
- * @param callable $callback
- * @return $this
- */
- public function filter(callable $callback): self
- {
- $this->results = array_filter($this->results, $callback);
-
- return $this;
- }
-
- /**
- * Sort the objects in the array
- * @param callable $callback
- * @return $this
- */
- public function sort(callable $callback): self
- {
- uasort($this->results, $callback);
-
- return $this;
- }
-
- /**
- * Count the objects in the array
- * @return int
- */
- public function count(): int
- {
- return count($this->results);
- }
-}
\ No newline at end of file
diff --git a/src/Entity.php b/src/Entity.php
index 7b3bd3745..a1c5b54e7 100644
--- a/src/Entity.php
+++ b/src/Entity.php
@@ -241,9 +241,8 @@ public function getResourceUrl(Transaction $transaction): string
* Delete this entity
* @param Transaction $transaction Related transaction
* @param ContextInterface|null $context Current context
- * @return Response Client response
*/
- public function delete(Transaction $transaction, ?ContextInterface $context = null): Response
+ public function delete(Transaction $transaction, ?ContextInterface $context = null): void
{
$entitySet = $this->entitySet;
@@ -255,8 +254,6 @@ public function delete(Transaction $transaction, ?ContextInterface $context = nu
$transaction->assertIfMatchHeader($this->getETag());
$entitySet->delete($this->getEntityId());
-
- throw new NoContentException('deleted', 'Content was deleted');
}
/**
@@ -331,7 +328,8 @@ public function response(Transaction $transaction, ?ContextInterface $context =
return $this->patch($transaction, $context);
case Request::METHOD_DELETE:
- return $this->delete($transaction, $context);
+ $this->delete($transaction, $context);
+ throw new NoContentException('deleted', 'Content was deleted');
case Request::METHOD_GET:
return $this->get($transaction, $context);
diff --git a/src/Operation.php b/src/Operation.php
index b2f17f706..3b5b45c81 100644
--- a/src/Operation.php
+++ b/src/Operation.php
@@ -13,6 +13,7 @@
use Flat3\Lodata\Exception\Protocol\ConfigurationException;
use Flat3\Lodata\Exception\Protocol\InternalServerErrorException;
use Flat3\Lodata\Exception\Protocol\NoContentException;
+use Flat3\Lodata\Exception\Protocol\NotImplementedException;
use Flat3\Lodata\Expression\Lexer;
use Flat3\Lodata\Facades\Lodata;
use Flat3\Lodata\Helper\Arguments;
@@ -22,6 +23,7 @@
use Flat3\Lodata\Helper\JSON;
use Flat3\Lodata\Helper\PropertyValue;
use Flat3\Lodata\Interfaces\AnnotationInterface;
+use Flat3\Lodata\Interfaces\EntitySet\QueryInterface;
use Flat3\Lodata\Interfaces\IdentifierInterface;
use Flat3\Lodata\Interfaces\PipeInterface;
use Flat3\Lodata\Interfaces\RepositoryInterface;
@@ -36,6 +38,7 @@
use Flat3\Lodata\Operation\Repository;
use Flat3\Lodata\Operation\TransactionArgument;
use Flat3\Lodata\Operation\ValueArgument;
+use Flat3\Lodata\PathSegment\Each;
use Flat3\Lodata\Traits\HasAnnotations;
use Flat3\Lodata\Traits\HasIdentifier;
use Flat3\Lodata\Traits\HasTitle;
@@ -637,6 +640,41 @@ public static function pipe(
);
}
+ if ($argument instanceof Each) {
+ $entitySet = $argument->getArgument();
+
+ if (!$entitySet instanceof QueryInterface) {
+ throw new NotImplementedException(
+ 'entityset_cannot_query',
+ 'This entity set cannot be queried',
+ );
+ }
+
+ $result = new Collection();
+ $result->setUnderlyingType($operation->getReturnType());
+
+ foreach ($entitySet->query() as $entity) {
+ $operationInstance = clone $operation;
+ $operationTransaction = new Transaction();
+ $request = Request::create(
+ '',
+ Request::METHOD_POST,
+ [],
+ [],
+ [],
+ [],
+ $transaction->getRequest()->getContent()
+ );
+ $request->headers->replace($transaction->getRequestHeaders());
+ $operationTransaction->initialize(new Controller\Request($request));
+ $operationInstance->setTransaction($operationTransaction);
+ $operationInstance->setBoundParameter($entity);
+ $result[] = $operationInstance->executeAction();
+ }
+
+ return $result;
+ }
+
$operation = clone $operation;
$operation->setTransaction($transaction);
$operation->setBoundParameter($argument);
@@ -734,11 +772,16 @@ public function ensureResult($result): ?PipeInterface
);
}
- if ($returnType instanceof EntityType && !$result->getType() instanceof $returnType) {
- throw new InternalServerErrorException(
- 'invalid_entity_type_returned',
- 'The operation returned an entity type that did not match its defined type',
- );
+ if ($returnType instanceof EntityType) {
+ if (
+ ($result instanceof Collection && !$result->getUnderlyingType() instanceof $returnType) ||
+ ($result instanceof ComplexValue && !$result->getType() instanceof $returnType)
+ ) {
+ throw new InternalServerErrorException(
+ 'invalid_entity_type_returned',
+ 'The operation returned an entity type that did not match its defined type',
+ );
+ }
}
if ($returnType instanceof PrimitiveType && !$result instanceof Primitive) {
diff --git a/src/PathSegment/Each.php b/src/PathSegment/Each.php
new file mode 100644
index 000000000..6db1b0c85
--- /dev/null
+++ b/src/PathSegment/Each.php
@@ -0,0 +1,70 @@
+argument = $argument;
+ }
+
+ public static function pipe(
+ Transaction $transaction,
+ string $currentSegment,
+ ?string $nextSegment,
+ ?PipeInterface $argument
+ ): PipeInterface {
+ if ($currentSegment !== '$each') {
+ throw new PathNotHandledException();
+ }
+
+ if (!$argument instanceof QueryInterface || !$argument instanceof EntitySet) {
+ throw new PathNotHandledException();
+ }
+
+ if ($argument instanceof DeleteInterface && $transaction->getMethod() === Request::METHOD_DELETE) {
+ foreach ($argument->query() as $entity) {
+ $entity->delete($transaction);
+ }
+
+ throw new NoContentException();
+ }
+
+ if ($argument instanceof UpdateInterface && $transaction->getMethod() === Request::METHOD_PATCH) {
+ foreach ($argument->query() as $entity) {
+ $propertyValues = $argument->arrayToPropertyValues($transaction->getBodyAsArray());
+ $argument->update($entity->getEntityId(), $propertyValues);
+ }
+
+ $argument->getTransaction()->getRequest()->setMethod(Request::METHOD_GET);
+
+ return $argument;
+ }
+
+ return new self($argument);
+ }
+
+ public function getArgument(): PipeInterface
+ {
+ return $this->argument;
+ }
+}
diff --git a/src/PathSegment/Filter.php b/src/PathSegment/Filter.php
index 2853a3e27..664e16a47 100644
--- a/src/PathSegment/Filter.php
+++ b/src/PathSegment/Filter.php
@@ -9,6 +9,7 @@
use Flat3\Lodata\Exception\Internal\PathNotHandledException;
use Flat3\Lodata\Exception\Protocol\BadRequestException;
use Flat3\Lodata\Expression\Lexer;
+use Flat3\Lodata\Interfaces\EntitySet\FilterInterface;
use Flat3\Lodata\Interfaces\PipeInterface;
/**
@@ -38,6 +39,13 @@ public static function pipe(
);
}
+ if (!$argument instanceof FilterInterface) {
+ throw new BadRequestException(
+ 'entityset_cannot_filter',
+ 'The requested entity set does not support filter',
+ );
+ }
+
$filter = $lexer->matchingParenthesis();
$transaction->getFilter()->addExpression($filter);
diff --git a/src/Type/Collection.php b/src/Type/Collection.php
index a89a317f5..02383777d 100644
--- a/src/Type/Collection.php
+++ b/src/Type/Collection.php
@@ -22,14 +22,15 @@
*/
class Collection extends Primitive implements ArrayAccess
{
- /** @var Primitive[] $value */
- protected $value = [];
+ /** @var Primitive[]|\Illuminate\Support\Collection $value */
+ protected $value = null;
/** @var CollectionType $type */
protected $type;
public function __construct($value = null)
{
+ $this->value = collect();
$this->setCollectionType(new CollectionType);
parent::__construct($value);
}
@@ -48,9 +49,9 @@ public function toJson()
public function toMixed(): ?array
{
- return array_map(function (SerializeInterface $value) {
+ return $this->value->map(function (SerializeInterface $value) {
return $value->toMixed();
- }, $this->value);
+ })->toArray();
}
/**
@@ -170,14 +171,14 @@ public function emitJson(Transaction $transaction): void
{
$transaction->outputJsonArrayStart();
- reset($this->value);
+ $iterator = $this->value->getIterator();
- while (current($this->value)) {
- $value = current($this->value);
+ while ($iterator->current()) {
+ $value = $iterator->current();
$value->emitJson($transaction);
- next($this->value);
+ $iterator->next();
- if (current($this->value)) {
+ if ($iterator->current()) {
$transaction->outputJsonSeparator();
}
}
diff --git a/tests/Entity/EntityTest.php b/tests/Entity/EntityTest.php
index 216131bff..31293bbce 100644
--- a/tests/Entity/EntityTest.php
+++ b/tests/Entity/EntityTest.php
@@ -9,7 +9,6 @@
use Flat3\Lodata\GeneratedProperty;
use Flat3\Lodata\Tests\Helpers\Request;
use Flat3\Lodata\Tests\TestCase;
-use Flat3\Lodata\Transaction\MediaType;
use Flat3\Lodata\Transaction\MetadataType;
use Flat3\Lodata\Type;
use Flat3\Lodata\Type\Int32;
diff --git a/tests/EntityEach/DatabaseTest.php b/tests/EntityEach/DatabaseTest.php
new file mode 100644
index 000000000..635b494c2
--- /dev/null
+++ b/tests/EntityEach/DatabaseTest.php
@@ -0,0 +1,9 @@
+assertNoContent(
+ (new Request)
+ ->path($this->entitySetPath.'/$filter(age ge 3)/$each')
+ ->delete()
+ );
+ }
+
+ public function test_update_each()
+ {
+ $this->assertJsonResponseSnapshot(
+ (new Request)
+ ->path($this->entitySetPath.'/$filter(@bar)/$each')
+ ->query('@bar', 'age ge 3')
+ ->patch()
+ ->body(['name' => 'Oop'])
+ );
+ }
+
+ public function test_action_each()
+ {
+ $action = new Action('testaction');
+ $action->setCallable(function (Entity $entity): String_ {
+ return $entity['name']->getPrimitive();
+ });
+
+ $action->setBindingParameterName('entity');
+ Lodata::add($action);
+
+ $this->assertJsonResponseSnapshot(
+ (new Request)
+ ->path($this->entitySetPath.'/$filter(@bar)/$each/testaction()')
+ ->query('@bar', 'age ge 3')
+ ->post()
+ );
+ }
+
+ public function test_action_each_arg()
+ {
+ $action = new Action('testaction');
+ $action->setCallable(function (Entity $entity, string $pfx): string {
+ return $pfx.' '.$entity['name']->getPrimitive()->toMixed();
+ });
+
+ $action->setBindingParameterName('entity');
+ Lodata::add($action);
+
+ $this->assertJsonResponseSnapshot(
+ (new Request)
+ ->path($this->entitySetPath.'/$filter(@bar)/$each/testaction()')
+ ->query('@bar', 'age ge 3')
+ ->body([
+ 'pfx' => 'Hello',
+ ])
+ ->post()
+ );
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityEach/KeyedCollectionTest.php b/tests/EntityEach/KeyedCollectionTest.php
new file mode 100644
index 000000000..754ed23ce
--- /dev/null
+++ b/tests/EntityEach/KeyedCollectionTest.php
@@ -0,0 +1,12 @@
+setCallable(function (String_ $field, EntitySet $passengers): EntitySet {
- $result = new StaticEntitySet($passengers->getType());
- $result->setIdentifier($passengers->getIdentifier());
+ $sorter->setCallable(function (String_ $field, EntitySet $passengers): Collection {
+ $result = new Collection();
+ $result->setUnderlyingType($passengers->getType());
foreach ($passengers->query() as $airport) {
$result[] = $airport;
}
- $result->sort(function (Entity $a1, Entity $a2) use ($field) {
+ $result->get()->sort(function (Entity $a1, Entity $a2) use ($field) {
return $a1[$field->get()]->getPrimitiveValue() <=> $a2[$field->get()]->getPrimitiveValue();
});
diff --git a/tests/Queries/AllTest.php b/tests/Queries/AllTest.php
index 6cbbd58d9..3d211bf89 100644
--- a/tests/Queries/AllTest.php
+++ b/tests/Queries/AllTest.php
@@ -2,7 +2,7 @@
namespace Flat3\Lodata\Tests\Queries;
-use Flat3\Lodata\Drivers\StaticEntitySet;
+use Flat3\Lodata\Drivers\CollectionEntitySet;
use Flat3\Lodata\EntityType;
use Flat3\Lodata\Facades\Lodata;
use Flat3\Lodata\Tests\Drivers\WithNumericCollectionDriver;
@@ -90,7 +90,7 @@ public function test_bad_type()
public function test_read_empty()
{
- Lodata::add(new StaticEntitySet(new EntityType('basic')));
+ Lodata::add(new CollectionEntitySet(new EntityType('basic')));
$this->assertJsonResponseSnapshot(
(new Request)
diff --git a/tests/__snapshots__/EntityEach/EloquentTest__test_action_each__1.json b/tests/__snapshots__/EntityEach/EloquentTest__test_action_each__1.json
new file mode 100644
index 000000000..2e6cad344
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/EloquentTest__test_action_each__1.json
@@ -0,0 +1,7 @@
+{
+ "@context": "http://localhost/odata/$metadata#Collection(Edm.String)",
+ "value": [
+ "Alpha",
+ "Beta"
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/EloquentTest__test_action_each_arg__1.json b/tests/__snapshots__/EntityEach/EloquentTest__test_action_each_arg__1.json
new file mode 100644
index 000000000..e440f8802
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/EloquentTest__test_action_each_arg__1.json
@@ -0,0 +1,7 @@
+{
+ "@context": "http://localhost/odata/$metadata#Collection(Edm.String)",
+ "value": [
+ "Hello Alpha",
+ "Hello Beta"
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/EloquentTest__test_delete_each__1.txt b/tests/__snapshots__/EntityEach/EloquentTest__test_delete_each__1.txt
new file mode 100644
index 000000000..ff97eafd4
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/EloquentTest__test_delete_each__1.txt
@@ -0,0 +1,38 @@
+@@ @@
+ ],
+ "passengers": [
+ {
+- "id": 1,
+- "flight_id": 1,
+- "name": "Alpha",
+- "dob": "2000-01-01 04:04:04",
+- "age": 4,
+- "chips": true,
+- "dq": "2000-01-01",
+- "in_role": 86400,
+- "open_time": "05:05:05",
+- "colour": 2,
+- "sock_colours": 6,
+- "emails": [
+- "alpha@example.com",
+- "alpha@beta.com"
+- ]
+- },
+- {
+- "id": 2,
+- "flight_id": null,
+- "name": "Beta",
+- "dob": "2001-02-02 05:05:05",
+- "age": 3,
+- "chips": false,
+- "dq": "2001-02-02",
+- "in_role": 191105.3,
+- "open_time": null,
+- "colour": null,
+- "sock_colours": null,
+- "emails": null
+- },
+- {
+ "id": 3,
+ "flight_id": 1,
+ "name": "Gamma",
diff --git a/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__1.json b/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__1.json
new file mode 100644
index 000000000..0acfd8425
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__1.json
@@ -0,0 +1,36 @@
+{
+ "@context": "http://localhost/odata/$metadata#Passengers",
+ "value": [
+ {
+ "id": 1,
+ "flight_id": 1,
+ "name": "Oop",
+ "dob": "2000-01-01T04:04:04+00:00",
+ "age": 4,
+ "chips": true,
+ "dq": "2000-01-01",
+ "in_role": "P1DT0S",
+ "open_time": "05:05:05.000000",
+ "colour": "Green",
+ "sock_colours": "Green,Blue",
+ "emails": [
+ "alpha@example.com",
+ "alpha@beta.com"
+ ]
+ },
+ {
+ "id": 2,
+ "flight_id": null,
+ "name": "Oop",
+ "dob": "2001-02-02T05:05:05+00:00",
+ "age": 3,
+ "chips": false,
+ "dq": "2001-02-02",
+ "in_role": "P2DT5H5M5.2999999999884S",
+ "open_time": null,
+ "colour": null,
+ "sock_colours": null,
+ "emails": []
+ }
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__2.txt b/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__2.txt
new file mode 100644
index 000000000..b9249df3a
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/EloquentTest__test_update_each__2.txt
@@ -0,0 +1,18 @@
+@@ @@
+ {
+ "id": 1,
+ "flight_id": 1,
+- "name": "Alpha",
++ "name": "Oop",
+ "dob": "2000-01-01 04:04:04",
+ "age": 4,
+ "chips": true,
+@@ @@
+ {
+ "id": 2,
+ "flight_id": null,
+- "name": "Beta",
++ "name": "Oop",
+ "dob": "2001-02-02 05:05:05",
+ "age": 3,
+ "chips": false,
diff --git a/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each__1.json b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each__1.json
new file mode 100644
index 000000000..2e6cad344
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each__1.json
@@ -0,0 +1,7 @@
+{
+ "@context": "http://localhost/odata/$metadata#Collection(Edm.String)",
+ "value": [
+ "Alpha",
+ "Beta"
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each_arg__1.json b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each_arg__1.json
new file mode 100644
index 000000000..e440f8802
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_action_each_arg__1.json
@@ -0,0 +1,7 @@
+{
+ "@context": "http://localhost/odata/$metadata#Collection(Edm.String)",
+ "value": [
+ "Hello Alpha",
+ "Hello Beta"
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_delete_each__1.txt b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_delete_each__1.txt
new file mode 100644
index 000000000..fa514f075
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_delete_each__1.txt
@@ -0,0 +1,31 @@
+@@ @@
+ {
+- "alpha": {
+- "name": "Alpha",
+- "age": 4,
+- "dob": "2000-01-01 04:04:04",
+- "chips": true,
+- "dq": "2000-01-01",
+- "in_role": 86400,
+- "open_time": "05:05:05",
+- "flight_id": 1,
+- "colour": 2,
+- "sock_colours": 6,
+- "emails": [
+- "alpha@example.com",
+- "alpha@beta.com"
+- ]
+- },
+- "beta": {
+- "name": "Beta",
+- "age": 3,
+- "dob": "2001-02-02 05:05:05",
+- "chips": false,
+- "dq": "2001-02-02",
+- "in_role": 191105.3,
+- "colour": null,
+- "sock_colours": null
+- },
+ "gamma": {
+ "name": "Gamma",
+ "age": 2,
diff --git a/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__1.json b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__1.json
new file mode 100644
index 000000000..540bc602c
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__1.json
@@ -0,0 +1,36 @@
+{
+ "@context": "http://localhost/odata/$metadata#passengers",
+ "value": [
+ {
+ "id": "alpha",
+ "name": "Oop",
+ "age": 4,
+ "dob": "2000-01-01T04:04:04+00:00",
+ "chips": true,
+ "dq": "2000-01-01",
+ "in_role": "P1DT0S",
+ "open_time": "05:05:05.000000",
+ "flight_id": 1,
+ "colour": "Green",
+ "sock_colours": "Green,Blue",
+ "emails": [
+ "alpha@example.com",
+ "alpha@beta.com"
+ ]
+ },
+ {
+ "id": "beta",
+ "name": "Oop",
+ "age": 3,
+ "dob": "2001-02-02T05:05:05+00:00",
+ "chips": false,
+ "dq": "2001-02-02",
+ "in_role": "P2DT5H5M5.2999999999884S",
+ "colour": null,
+ "sock_colours": null,
+ "open_time": null,
+ "flight_id": null,
+ "emails": []
+ }
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__2.txt b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__2.txt
new file mode 100644
index 000000000..1cfce203b
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/KeyedCollectionTest__test_update_each__2.txt
@@ -0,0 +1,29 @@
+@@ @@
+ {
+ "alpha": {
+- "name": "Alpha",
++ "name": "Oop",
+ "age": 4,
+ "dob": "2000-01-01 04:04:04",
+ "chips": true,
+@@ @@
+ ]
+ },
+ "beta": {
+- "name": "Beta",
++ "name": "Oop",
+ "age": 3,
+ "dob": "2001-02-02 05:05:05",
+ "chips": false,
+ "dq": "2001-02-02",
+ "in_role": 191105.3,
+- "colour": null,
+- "sock_colours": null
++ "colour": 0,
++ "sock_colours": 0,
++ "open_time": null,
++ "flight_id": null,
++ "emails": []
+ },
+ "gamma": {
+ "name": "Gamma",
diff --git a/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each__1.json b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each__1.json
new file mode 100644
index 000000000..2e6cad344
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each__1.json
@@ -0,0 +1,7 @@
+{
+ "@context": "http://localhost/odata/$metadata#Collection(Edm.String)",
+ "value": [
+ "Alpha",
+ "Beta"
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each_arg__1.json b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each_arg__1.json
new file mode 100644
index 000000000..e440f8802
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_action_each_arg__1.json
@@ -0,0 +1,7 @@
+{
+ "@context": "http://localhost/odata/$metadata#Collection(Edm.String)",
+ "value": [
+ "Hello Alpha",
+ "Hello Beta"
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/NumericCollectionTest__test_delete_each__1.txt b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_delete_each__1.txt
new file mode 100644
index 000000000..c89b3a48f
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_delete_each__1.txt
@@ -0,0 +1,22 @@
+@@ @@
+ [
+ {
+- "name": "Alpha",
+- "age": 4,
+- "dob": "2000-01-01 04:04:04",
+- "chips": true,
+- "dq": "2000-01-01",
+- "in_role": 86400,
+- "open_time": "05:05:05",
+- "flight_id": 1,
+- "colour": 2,
+- "sock_colours": 6,
+- "emails": [
+- "alpha@example.com",
+- "alpha@beta.com"
+- ]
+- },
+- {
+ "name": "Beta",
+ "age": 3,
+ "dob": "2001-02-02 05:05:05",
diff --git a/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__1.json b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__1.json
new file mode 100644
index 000000000..2b94266bf
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__1.json
@@ -0,0 +1,36 @@
+{
+ "@context": "http://localhost/odata/$metadata#passengers",
+ "value": [
+ {
+ "id": 0,
+ "name": "Oop",
+ "age": 4,
+ "dob": "2000-01-01T04:04:04+00:00",
+ "chips": true,
+ "dq": "2000-01-01",
+ "in_role": "P1DT0S",
+ "open_time": "05:05:05.000000",
+ "flight_id": 1,
+ "colour": "Green",
+ "sock_colours": "Green,Blue",
+ "emails": [
+ "alpha@example.com",
+ "alpha@beta.com"
+ ]
+ },
+ {
+ "id": 1,
+ "name": "Oop",
+ "age": 3,
+ "dob": "2001-02-02T05:05:05+00:00",
+ "chips": false,
+ "dq": "2001-02-02",
+ "in_role": "P2DT5H5M5.2999999999884S",
+ "colour": null,
+ "sock_colours": null,
+ "open_time": null,
+ "flight_id": null,
+ "emails": []
+ }
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__2.txt b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__2.txt
new file mode 100644
index 000000000..aacd84068
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/NumericCollectionTest__test_update_each__2.txt
@@ -0,0 +1,29 @@
+@@ @@
+ [
+ {
+- "name": "Alpha",
++ "name": "Oop",
+ "age": 4,
+ "dob": "2000-01-01 04:04:04",
+ "chips": true,
+@@ @@
+ ]
+ },
+ {
+- "name": "Beta",
++ "name": "Oop",
+ "age": 3,
+ "dob": "2001-02-02 05:05:05",
+ "chips": false,
+ "dq": "2001-02-02",
+ "in_role": 191105.3,
+- "colour": null,
+- "sock_colours": null
++ "colour": 0,
++ "sock_colours": 0,
++ "open_time": null,
++ "flight_id": null,
++ "emails": []
+ },
+ {
+ "name": "Gamma",
diff --git a/tests/__snapshots__/EntityEach/SQLTest__test_action_each__1.json b/tests/__snapshots__/EntityEach/SQLTest__test_action_each__1.json
new file mode 100644
index 000000000..2e6cad344
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/SQLTest__test_action_each__1.json
@@ -0,0 +1,7 @@
+{
+ "@context": "http://localhost/odata/$metadata#Collection(Edm.String)",
+ "value": [
+ "Alpha",
+ "Beta"
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/SQLTest__test_action_each_arg__1.json b/tests/__snapshots__/EntityEach/SQLTest__test_action_each_arg__1.json
new file mode 100644
index 000000000..e440f8802
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/SQLTest__test_action_each_arg__1.json
@@ -0,0 +1,7 @@
+{
+ "@context": "http://localhost/odata/$metadata#Collection(Edm.String)",
+ "value": [
+ "Hello Alpha",
+ "Hello Beta"
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/SQLTest__test_delete_each__1.txt b/tests/__snapshots__/EntityEach/SQLTest__test_delete_each__1.txt
new file mode 100644
index 000000000..ff97eafd4
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/SQLTest__test_delete_each__1.txt
@@ -0,0 +1,38 @@
+@@ @@
+ ],
+ "passengers": [
+ {
+- "id": 1,
+- "flight_id": 1,
+- "name": "Alpha",
+- "dob": "2000-01-01 04:04:04",
+- "age": 4,
+- "chips": true,
+- "dq": "2000-01-01",
+- "in_role": 86400,
+- "open_time": "05:05:05",
+- "colour": 2,
+- "sock_colours": 6,
+- "emails": [
+- "alpha@example.com",
+- "alpha@beta.com"
+- ]
+- },
+- {
+- "id": 2,
+- "flight_id": null,
+- "name": "Beta",
+- "dob": "2001-02-02 05:05:05",
+- "age": 3,
+- "chips": false,
+- "dq": "2001-02-02",
+- "in_role": 191105.3,
+- "open_time": null,
+- "colour": null,
+- "sock_colours": null,
+- "emails": null
+- },
+- {
+ "id": 3,
+ "flight_id": 1,
+ "name": "Gamma",
diff --git a/tests/__snapshots__/EntityEach/SQLTest__test_update_each__1.json b/tests/__snapshots__/EntityEach/SQLTest__test_update_each__1.json
new file mode 100644
index 000000000..2c756d5f5
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/SQLTest__test_update_each__1.json
@@ -0,0 +1,36 @@
+{
+ "@context": "http://localhost/odata/$metadata#passengers",
+ "value": [
+ {
+ "id": 1,
+ "name": "Oop",
+ "age": 4,
+ "dob": "2000-01-01T04:04:04+00:00",
+ "chips": true,
+ "dq": "2000-01-01",
+ "in_role": "P1DT0S",
+ "open_time": "05:05:05.000000",
+ "flight_id": 1,
+ "colour": "Green",
+ "sock_colours": "Green,Blue",
+ "emails": [
+ "alpha@example.com",
+ "alpha@beta.com"
+ ]
+ },
+ {
+ "id": 2,
+ "name": "Oop",
+ "age": 3,
+ "dob": "2001-02-02T05:05:05+00:00",
+ "chips": false,
+ "dq": "2001-02-02",
+ "in_role": "P2DT5H5M5.2999999999884S",
+ "open_time": null,
+ "flight_id": null,
+ "colour": null,
+ "sock_colours": null,
+ "emails": []
+ }
+ ]
+}
diff --git a/tests/__snapshots__/EntityEach/SQLTest__test_update_each__2.txt b/tests/__snapshots__/EntityEach/SQLTest__test_update_each__2.txt
new file mode 100644
index 000000000..b9249df3a
--- /dev/null
+++ b/tests/__snapshots__/EntityEach/SQLTest__test_update_each__2.txt
@@ -0,0 +1,18 @@
+@@ @@
+ {
+ "id": 1,
+ "flight_id": 1,
+- "name": "Alpha",
++ "name": "Oop",
+ "dob": "2000-01-01 04:04:04",
+ "age": 4,
+ "chips": true,
+@@ @@
+ {
+ "id": 2,
+ "flight_id": null,
+- "name": "Beta",
++ "name": "Oop",
+ "dob": "2001-02-02 05:05:05",
+ "age": 3,
+ "chips": false,
diff --git a/tests/__snapshots__/Operation/FunctionTest__test_callback_bound_entity_set_with_filter__1.json b/tests/__snapshots__/Operation/FunctionTest__test_callback_bound_entity_set_with_filter__1.json
index 55d05ef13..00d851c13 100644
--- a/tests/__snapshots__/Operation/FunctionTest__test_callback_bound_entity_set_with_filter__1.json
+++ b/tests/__snapshots__/Operation/FunctionTest__test_callback_bound_entity_set_with_filter__1.json
@@ -1,5 +1,5 @@
{
- "@context": "http://localhost/odata/$metadata#passengers",
+ "@context": "http://localhost/odata/$metadata#Collection(com.example.odata.passenger)",
"value": [
{
"id": 0,