6

Is there some way I can split the element-api.php file into separate files and import them all? It's getting quite long and unwieldy.

1 Answers1

7

Sure – the element-api.php config file is just plain ol' PHP, and the functions that make up the individual endpoints (e.g. 'news.json' => function () { ...) doesn't necessarily have to be declared inside that file.

One way to split up the file would be to create one PHP class per element type or section. You could compose these classes however you'd like, but something like the below would work (this is an example file containing two endpoints for the news section, and the example assumes that the file is saved under /config/endpoints/News.php):

<?php

namespace config\endpoints;

use craft\elements\Entry;
use craft\helpers\UrlHelper;

class News {

    public static function index()
    {
        return [
            'elementType' => Entry::class,
            'criteria' => ['section' => 'news'],
            'transformer' => function(Entry $entry) {
                ...
            }
        ];
    }

    public static function view($entryId)
    {
        return [
            'elementType' => Entry::class,
            'criteria' => ['id' => $entryId],
            'one' => true,
            'transformer' => function(Entry $entry) {
                 ...
            }
        ];
    }
}

The next step is to make the file available to the element-api.php config file. One way to do it, is to simply include() it at the top of the element-api.php file:

include __DIR__ . '/endpoints/News.php';

Then, you can add the endpoints from the News endpoint class to the config in element-api.php, i.e.:

<?php

include __DIR__ . '/endpoints/News.php';

use config\endpoints\News;

return [
    'endpoints' => [
        // use an anonymous function to prevent errors when running via cli
        'news.json' => function () {
            return News::index();
        },
        'news/<entryId:\d+>.json' => function ($entryId) {
            return News::view($entryId);
        },
    ],
];

If you want to take this a step further, a much nicer approach than using include() is to have Composer autoload your endpoint classes by adding the below to your composer.json file (again, this assumes that the custom endpoint classes live under /config/endpoints/):

"autoload": {
    "psr-4": {
        "config\\endpoints\\": "config/endpoints"
    }
},

After editing your composer.json file, make sure to regenerate the autoloader by running the following from your command line (root of project, same directory where you keep your composer.json:

composer dump-autoload -o

Then, you can strip out the include() statement(s) in your element-api.php file, because Composer will autoload all the classes under /config/endpoints.

Finally, to make things super clean you could of course create a custom Yii Module (or a Craft plugin) to contain your endpoint classes. This would also avoid having to manually add the classpath to the composer.json file, for the autoloading.

Urs
  • 639
  • 4
  • 13
Mats Mikkel Rummelhoff
  • 22,361
  • 3
  • 38
  • 69
  • I've given it a try as you've described but for some reason the slug / entry ID don't get passed through to the view function at all so it just returns the most recent entry every time. So you have any idea why this wouldn't be working? – George Feast-Parker Aug 22 '18 at 09:32
  • @GeorgeFeast-Parker Yeah, I took another look at it, and I messed up the original example. Check out the latest edit; it should work. – Mats Mikkel Rummelhoff Aug 22 '18 at 09:52
  • Wonderful, thanks a lot for that!

    One question: why does it need the anonymous function when a parameter is passed, what is the reason 'news/<entryId:\d+>.json' => News::view($entryId); won't work?

    – Urs Nov 12 '19 at 19:15
  • And here's the answer to that from Pixel&Tonic: "make sure all of your endpoint config are inside an anonymous function. Doing so will ensure your endpoint config will only be generated when the endpoint is actually requested, rather than on every web & console request". When element-api was triggerd by cli, I got many errors. I'll edit above. – Urs Nov 24 '19 at 08:39