Resource

A BEAR.Sunday application is a collection of RESTful resources. Each piece of information and each function the application handles is represented as a resource with a URI, just like a web page.

Every resource is accessed through a uniform interface shared with HTTP, such as GET and POST. Because the interface is uniform, the same resource can be used in the same way from the Web, from the console, and through in-process calls from other resources. There is no need to prepare a separate controller or entry point for each access method, and the meaning of a resource can be understood with knowledge of HTTP alone.

Object as a service

A resource expressed in PHP is a ResourceObject. It is an object as a service in which HTTP methods are mapped to PHP methods.

class Index extends ResourceObject
{
    public function onGet(int $a, int $b): static
    {
        $this->body = [
            'sum' => $a + $b  // $_GET['a'] + $_GET['b']
        ];

        return $this;
    }
}

A resource class has a URI such as page://self/index, similar to a web URI, and on methods such as onGet and onPost that correspond to HTTP methods. An on method determines its own resource state (code, headers, body) from the given parameters and returns $this. From a stateless request, the resource state is generated as a resource representation and transferred to the client. (Representational State Transfer)

code and headers have defaults (200 and empty headers), so the example above determines only body. Override them only when returning a response other than the defaults.

class Todo extends ResourceObject
{
    public function onPost(string $id, string $todo): static
    {
        $this->code = 201; // override the default 200
        $this->headers = [
            'Location' => '/todo/new_id'
        ];

        return $this;
    }
}

Page and app resources

Resources are divided into two schemes according to their role.

Scheme Role
page Public resources exposed to the outside
app Private resources that cannot be accessed directly from outside

A page resource receives requests from the outside, such as from the Web or console, and requests app resources to determine its own resource state. App resources own the domain information and functions that describe what the application does, while page resources own how to present it, separating these concerns.

The benefit of this separation appears when the context changes. In an HTML application, for example, page resources output HTML while app resources remain private. When the same application becomes the backend of a mobile application, changing the context exposes the app resources as an API that outputs JSON. The resource code stays the same; only the exposure and representation change.

URI

URIs are mapped to PHP classes. Applications use the URI instead of the class name to access resources.

URI Class
page://self/ Koriym\Todo\Resource\Page\Index
page://self/index Koriym\Todo\Resource\Page\Index
app://self/blog/posts?id=3 Koriym\Todo\Resource\App\Blog\Posts
  • index can be omitted.

Method

Resources can be accessed through six methods corresponding to HTTP methods. These methods are not a mapping to CRUD; they are distinguished by whether they are safe (do not change the resource state) and whether they are idempotent (repeating the same request produces the same result).

Method Meaning Safe Idempotent Cacheable
GET Retrieve a representation of the resource Yes Yes Yes
POST Process the representation contained in the request (for example, add a new resource) No No No
PUT Replace the resource with the payload, or create it if absent No Yes No
PATCH Partially modify the resource 1 No No No
DELETE Delete the resource No Yes No
OPTIONS Retrieve information about the parameters and responses required for a request 2 3 Yes Yes No

Parameters

The request value corresponding to the variable name is passed to the argument of the request method.

class Index extends ResourceObject
{
    // $_GET['id'] is passed to $id
    public function onGet(int $id): static
    {
        return $this;
    }

    // $_POST['name'] is passed to $name
    public function onPost(string $name): static
    {
        return $this;
    }
}

See Resource Parameters for other methods and for how to pass external variables such as cookies as parameters.

Rendering and transfer

The request method of a ResourceObject is not concerned with the representation of the resource. The injected renderer generates the resource representation and the responder outputs it. See Rendering and Transfer for details.

Client

Use the injected resource client to request other resources. The following request executes a request to the app://self/blog/posts resource with the query ?id=1.

use BEAR\Resource\ResourceInterface;

class Index extends ResourceObject
{
    public function __construct(
        private readonly ResourceInterface $resource
    ){}

    public function onGet(): static
    {
        $this->body = [
            'posts' => $this->resource->get('app://self/blog/posts', ['id' => 1])
        ];

        return $this;
    }
}

This is an eager request that executes immediately.4

Lazy evaluation

It is also possible to generate the request itself rather than its result and delay execution.

$request = $this->resource->get->uri('app://self/posts')->withQuery(['id' => 1]); // callable
$posts = $request();

When this request is embedded in a template or resource, it is evaluated lazily. That is, when it is not evaluated, the request is not made and incurs no execution cost.

$this->body = [
    'lazy' => $this->resource->get->uri('app://self/posts')->withQuery(['id' => 3])
];

The #[Embed] attribute provides a declarative way to express the same lazy embedding. See Resource Link for details.

Cache

In addition to regular TTL caching, BEAR.Sunday supports REST client caching and advanced partial caching (donut caching), including CDN. See Cache for details. For the traditional @Cacheable annotation, see the previous Resource (v1) document (Japanese).

One important REST constraint is resource linking. ResourceObject supports both internal and external links. See Resource Link for details.

BEAR.Resource

The functionality of the BEAR.Sunday resource object is also available as a standalone package. See the BEAR.Resource README.


  1. https://www.rfc-editor.org/rfc/rfc5789 ↩

  2. Retrieving response information requires a JsonSchema declaration. ↩

  3. OPTIONS is disabled in the prod context; override OptionsMethodModule to enable it. See Production. ↩

  4. Historical notations such as $this->resource->get->uri('app://self/posts')->withQuery(['id' => 1])->eager->request(); (PHP 5.x and later) and $this->resource->get->uri('app://self/posts')(['id' => 1]); (PHP 7.x and later) remain valid. ↩