Create custom content types for Edlib.
- PHP 8.2
- A PSR-17 implementation, e.g. guzzlehttp/psr7
- A PSR-18 compatible HTTP client, e.g. Guzzle 7
- Network access to Edlib internal services
- A RabbitMQ instance shared with Edlib (optional)
composer require cerpus/edlib-resource-kit guzzlehttp/guzzle:^7 guzzlehttp/psr7
Rather than communicating via bespoke APIs over private network connections, Edlib 3 is notified of new content via the LTI Content-Item Message standard. Edlib Resource Kit provides message objects, mappers, and serialisers for working with Content-Item messages.
Map serialised Content-Item graphs to message objects:
use Cerpus\EdlibResourceKit\Lti\Lti11\Mapper\DeepLinking\ContentItemsMapper;
$mapper = new ContentItemsMapper();
$items = $mapper->map(<<<EOJSON
{
"@context": "http://purl.imsglobal.org/ctx/lti/v1/ContentItem",
"@graph": [
{
"@type": "LtiLinkItem",
"mediaType": "application/vnd.ims.lti.v1.ltilink",
"title": "My Cool LTI Content",
"url": "https://example.com/my-lti-content"
}
]
}
EOJSON);
echo count($items), "\n"; // 1
echo $items[0]->getTitle(), "\n"; // My Cool LTI Content
The JSON input must match the compacted JSON-LD representation, as can be seen in the LTI Deep Linking 1.0 specification. If the input does not match, a JSON-LD processor can be used to make the input compliant.
Convert Content-Item message objects to their serialised JSON representations:
use Cerpus\EdlibResourceKit\Lti\Message\DeepLinking\LtiLinkItem;
use Cerpus\EdlibResourceKit\Lti\Lti11\Serializer\DeepLinking\ContentItemsSerializer;
$items = [
new LtiLinkItem(
mediaType: 'application/vnd.ims.lti.v1.ltilink',
title: 'My Cool LTI Content',
url: 'https://example.com/my-lti-content',
),
];
$serializer = new ContentItemsSerializer();
$serialized = $serializer->serialize($items);
echo json_encode($serialized);
Output:
{
"@context": "http://purl.imsglobal.org/ctx/lti/v1/ContentItem",
"@graph": [
{
"@type": "LtiLinkItem",
"mediaType": "application/vnd.ims.lti.v1.ltilink",
"title": "My Cool LTI Content",
"url": "https://example.com/my-lti-content"
}
]
}
We provide integration with Laravel that simplifies use of this package.
There are two ways of making Edlib aware of new resources:
- Using the message bus
- Synchronous HTTP request
The HTTP approach is slower, but waits for the publishing of a resource to be completed, allowing for error handling.
When using the message bus approach, a RabbitMQ connection must be provided:
use Cerpus\EdlibResourceKit\ResourceKit;
use Cerpus\PubSub\Connection\ConnectionFactory;
$connectionFactory = new ConnectionFactory('localhost', 5672, 'guest', 'guest');
$resourceKit = new ResourceKit($connectionFactory);
For the HTTP approach, other than providing the synchronousResourceManager
flag, there is no mandatory configuration:
use Cerpus\EdlibResourceKit\ResourceKit;
$resourceKit = new ResourceKit(synchronousResourceManager: true);
Once you have a ResourceKit instance, you can begin to access the various components of it:
$resourceManager = $resourceKit->getResourceManager();
$versionManager = $resourecKit->getResourceVersionManager();
Given a model class representing an item of your custom content type (in this case an article):
namespace App\Models;
use Cerpus\EdlibResourceKit\Contract\EdlibResource;
class Article
{
// Database-mapped article ID
private string $id;
public function toEdlibResource(): EdlibResource
{
return new ArticleEdlibResource($this->id, /* ... */);
}
}
Create an accompanying EdlibResource
class:
namespace App\DataObjects;
use Cerpus\EdlibResourceKit\Contract\EdlibResource;
class ArticleEdlibResource implements EdlibResource
{
public function __construct(private string $systemId, /* ... */)
{
}
public function getSystemName(): string
{
return 'my-unique-and-persistent-system-name';
}
public function getSystemId(): string
{
return $this->systemId;
}
public function getContentType(): string|null
{
return 'article';
}
// ... implement the remaining EdlibResource methods here
}
When the article is created or updated, a corresponding call to the resource manager must take place. The procedure will vary depending on your framework of choice, but here it is demonstrated using the observer pattern:
use Cerpus\EdlibResourceKit\Contract\EdlibResource;
use Cerpus\EdlibResourceKit\Resource\ResourceManagerInterface;
class ArticleObserver
{
public function __construct(private ResourceManagerInterface $manager)
{
}
public function onCreate(Article $article): void
{
$this->save($article->toEdlibResource());
}
public function onUpdate(Article $article): void
{
$this->save($article->toEdlibResource());
}
private function save(EdlibResource $resource): void
{
try {
return $this->manager->save($resource);
} catch (ResourceSaveFailedException $e) {
// handle the failure somehow
}
}
}
If everything went well, the resource should now be accessible from within Edlib.
This library will look for and make use of any PSR-17 compatible message factories and PSR-18 compatible HTTP clients that are installed (via HTTPlug Discovery). You can override these with factories & clients of your choosing:
use App\Http\MyClient;
use App\Http\MyRequestFactory;
use App\Http\MyStreamFactory;
use Cerpus\EdlibResourceKit\ResourceKit;
$resourceKit = new ResourceKit(
httpClient: new MyClient(),
requestFactory: new MyRequestFactory(),
streamFactory: new MyStreamFactory(),
synchronousResourceManager: true,
);
By wiring up the HTTP client provided by your web framework, you may get better logging & debugging capabilities.
It might be necessary to add extra data to a resource before publishing it. This can be done using a custom serializer:
use Cerpus\EdlibResourceKit\Contract\EdlibResource;
use Cerpus\EdlibResourceKit\ResourceKit;
use Cerpus\EdlibResourceKit\Serializer\ResourceSerializer;
class MySerializer extends ResourceSerializer
{
public function serialize(EdlibResource $resource): array
{
$data = parent::serialize($resource);
if ($resource instanceof MyEdlibResource) {
$data['some_custom_key'] => $resource->getMyCustomData();
}
return $data;
}
}
$resourceKit = new ResourceKit($pubSub, resourceSerializer: new MySerializer());
This package is released under the GNU General Public License 3.0. See the
LICENSE
file for more information.