Cockpit-HQ / Cockpit

Cockpit Core - Content Platform
https://getcockpit.com
Other
367 stars 45 forks source link

[Feature] API endpoint to fetch items of multiple models at once #195

Closed BFS-JWesseler closed 2 months ago

BFS-JWesseler commented 3 months ago

API endpoint to fetch items of multiple models at once

Currently, we are facing the issue that we have around 40-50 models which require as many singular HTTP requests. It would be nice if we could fetch all of the items of multiple models with the same filter settings at once. It also does not need to be optimized on the data structure, basically can be done with a loop and adding the models to a JSON array which then contains the items of the respective models as usual.

Current it looks like:

api/content/items/<model1>?filter=<filter>
api/content/items/<model2>?filter=<filter>
api/content/items/<model3>?filter=<filter>

The idea would be to have something like: api/content/multiple-items?models=[<model1>,<model2>,...]&filter=<filter>

You could also add an array to the filters for something like (so that you can have filters per model if needed - even if we don't need it): api/content/multiple-items?models=[<model1>,<model2>,...]&filters=[<filter1>,<filter2>,...]

Or something similar. Anyway, that would be awesome to have and would speed up our initialization time by a lot. The easiest solution will suffice, as long as we don't need to make 40-50 HTTP requests for fetching the items of the different models with the same filter. It would be greatly appreciated.

aheinze commented 3 months ago

Hi 👋

You can already do this with a custom api endpoint.

Example

Create the following file: config/api/batch-content-items.php

With the following contents:

<?php

$batch = $this->param('batch', null);

if (!$batch) {
    return $this->stop(412);
}

try {
    $batch = json5_decode($batch, true);
} catch(\Throwable $e) {
    $this->stop(['error' => "<batch> is not valid json"], 412);
}

$return = [];
$content = $this->module('content');

$userRole = $this->helper('auth')->getUser('role');

foreach ($batch as $model => $opts) {

    if (!$content->model($model) || !$this->helper('acl')->isAllowed("content/{$model}/read", $userRole)) {
        continue;
    }

    $options = [];
    $process = ['locale' => $opts['locale'] ?? $this->param('locale', 'default')];

    if (isset($opts['filter'])) $options['filter'] = $opts['filter'];
    if (isset($opts['sort'])) $options['sort'] = $opts['sort'];
    if (isset($opts['fields'])) $options['fields'] = $opts['fields'];
    if (isset($opts['limit'])) $options['limit'] = $opts['limit'];
    if (isset($opts['skip'])) $options['skip'] = $opts['skip'];

    if (isset($opts['populate'])) {
        $process['populate'] = $opts['populate'];
        $process['user'] = $this->helper('auth')->getUser();
    }

    if (!isset($options['filter']) || !is_array($options['filter'])) {
        $options['filter'] = [];
    }

    // only published items
    $options['filter']['_state'] = 1;

    $return[$model] = $content->items($model, $options, $process);
}

return $return;

Now you can request the following endpoint:

/api/batch-content-items?batch={model1:{filter:{...}}, model2:{filter:{...}}, ...}
BFS-JWesseler commented 2 months ago

Thanks, it is working flawlessly.

aheinze commented 2 months ago

FYI: This is now part of the core: https://getcockpit.com/documentation/core/api/content#get-content-items