# Operations

OData's operations allow for completely customized interactions with the service, outside the data-driven CRUD capabilities seen so far. Through operations, you can perform remote procedure calls that operate like static methods, or class instance methods on entities and entity sets.

Lodata supports both Functions (opens new window) and Actions (opens new window). By OData convention operations that define themselves as Functions MUST return data and MUST have no observable side effects, and Actions MAY have side effects when invoked and MAY return data. Lodata does not enforce the side effect restriction, but does enforce the return data requirement.

TIP

As you implement these examples in your own application it is useful to observe changes to the model in the CSDL metadata documents.

In the examples that follow each has the code, the request in cURL format, and the expected response. All of these examples can be used with the same syntax for actions, but should implement the ActionInterface.

# Hello World!

Here is the 'Hello world!' example. It is a class that extends Operation, and defines as a Function by implementing the FunctionInterface. It implements the required invoke() method. The class uses the identifier hello when being added to the model.

# Return types

If the return type of the invoke() method is void then Lodata assumes an Edm.String response. To properly type a string response you can add the PHP return type string.

This method also works with int (converted to Edm.Int64), float (converted to Edm.Double) and bool (converted to Edm.Boolean):


To use more specific OData types such as Edm.DateTimeOffset you can use Lodata's Type objects as return types:

# Arguments

Arguments can be specified in the definition of invoke(), and then supplied as key/value pairs in the URL:


More complex arguments using OData types can also be passed. Here we have an Edm.DateTimeOffset and an Edm.Duration, where the duration is added to the timestamp, and the new value returned.

TIP

Here the get() method is used to return the Carbon object contained within the DateTimeOffset, and the float number of seconds within the Duration.

# Nullable types

In the previous examples none of the arguments or the return types supported being null. You can type-hint that an argument can be null. For Action operations, the return type can be nullable.

# Bound parameters

Internally, OData URLs are resolved through function composition. This enables the output of one operation to be passed into the input of the next operation, as the second operation's "bound parameter".

In this example, the DateTimeOffset generated by the first function call is automatically inserted as the bound parameter of the second function call.

A further use of path composition is to pick a property from an entity and pass it into a function. For example a function that took an Edm.Date as its bound property and returned the age of the person could be called as: http://localhost:8000/odata/people/1/dob/age().

# Binding entities

In addition to primitive types, entities picked from a set can be passed into a function. In this example a simple collection is defined, and the URL picks the first one by its entity ID and passes it into the function using its binding parameter.

# Returning entities

Generated entities can also be returned from an operation. In this case the return type of invoke() is Entity. The returned entity type must be configured for OData to present it in the metadata document. This can be done in the constructor of the Operation. In this example a random person entity is created by the operation. This example also shows the identifier of the operation being configured in the constructor.

# Parameter aliases

A parameter alias can be used in place of an inline parameter value. The value for the alias is specified as a separate query option using the name of the parameter alias.

For example via the function import EmployeesByManager, passing 3 for the ManagerID parameter:

http://localhost:8000/odata/EmployeesByManager(ManagerID=@p1)?@p1=3
1

# Creating using artisan

Actions and Function classes can be generated through artisan commands, and can be added to Lodata in the boot() method of a service provider:

php artisan lodata:function Add
1
php artisan lodata:action Subtract
1

# Transaction

To provide additional context to a Function that may require it, the Function can ask for the current Transaction by adding that argument to the invoke method. In this example the invoke method would receive the Transaction on the $transaction method parameter. The transaction contains all the available context for the request, and can provide items such as the current system query options.

public class MethodResponder extends Operation implements FunctionInterface {
    public function invoke(Transaction $transaction): string
    {
      return $transaction->getRequest()->getMethod();
    }
}
1
2
3
4
5
6