Skip to content

Commit 72fa5cd

Browse files
authored
Merge pull request #9 from cerpus/add-lineitem-support
Add LineItem with ScoreConstraints
2 parents 1943c6e + 253942c commit 72fa5cd

17 files changed

+251
-1
lines changed

src/Lti/Edlib/DeepLinking/EdlibContentItemMapper.php

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public function map(array $data): ContentItem
3131
title: $item->getTitle(),
3232
url: $item->getUrl(),
3333
custom: $item->getCustom(),
34+
lineItem: $item->getLineItem(),
3435
))
3536
->withEdlibVersionId(Prop::getOfType($data, 'edlibVersionId', Prop::TYPE_NORMALIZED_STRING))
3637
->withLanguageIso639_3(Prop::getOfType($data, 'languageIso639_3', Prop::TYPE_NORMALIZED_STRING))

src/Lti/Lti11/Context/DeepLinkingProps.php

+17
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,27 @@ final class DeepLinkingProps
1818
public const EXPIRES_AT = 'expiresAt';
1919
public const DISPLAY_HEIGHT = 'displayHeight';
2020
public const DISPLAY_WIDTH = 'displayWidth';
21+
public const EXTRA_CREDIT_MAXIMUM = 'extraCreditMaximum';
2122
public const HEIGHT = 'height';
2223
public const ICON = 'icon';
24+
public const LINE_ITEM = 'lineItem';
2325
public const MEDIA_TYPE = 'mediaType';
26+
public const NORMAL_MAXIMUM = 'normalMaximum';
2427
public const PLACEMENT_ADVICE = 'placementAdvice';
2528
public const PRESENTATION_DOCUMENT_TARGET = 'presentationDocumentTarget';
29+
public const SCORE_CONSTRAINTS = 'scoreConstraints';
2630
public const TEXT = 'text';
2731
public const THUMBNAIL = 'thumbnail';
2832
public const TITLE = 'title';
33+
public const TOTAL_MAXIMUM = 'totalMaximum';
2934
public const URL = 'url';
3035
public const WIDTH = 'width';
3136
public const WINDOW_TARGET = 'windowTarget';
3237

3338
public const TYPE_ANY_URI = 'http://www.w3.org/2001/XMLSchema#anyURI';
3439
public const TYPE_BOOLEAN = 'http://www.w3.org/2001/XMLSchema#boolean';
3540
public const TYPE_DATETIME = 'http://www.w3.org/2001/XMLSchema#date';
41+
public const TYPE_FLOAT = 'http://www.w3.org/2001/XMLSchema#float';
3642
public const TYPE_INTEGER = 'http://www.w3.org/2001/XMLSchema#integer';
3743
public const TYPE_NORMALIZED_STRING = 'http://www.w3.org/2001/XMLSchema#normalizedString';
3844

@@ -56,6 +62,11 @@ public static function getExpiresAt(array $data): DateTimeImmutable|null
5662
return self::getOfType($data, self::EXPIRES_AT, self::TYPE_DATETIME);
5763
}
5864

65+
public static function getExtraCreditMaximum(array $data): float|null
66+
{
67+
return self::getOfType($data, self::EXTRA_CREDIT_MAXIMUM, self::TYPE_FLOAT);
68+
}
69+
5970
public static function getHeight(array $data): int|null
6071
{
6172
return self::getOfType($data, self::HEIGHT, self::TYPE_INTEGER);
@@ -66,6 +77,11 @@ public static function getMediaType(array $data): string|null
6677
return self::getOfType($data, self::MEDIA_TYPE, self::TYPE_NORMALIZED_STRING);
6778
}
6879

80+
public static function getNormalMaximum(array $data): float|null
81+
{
82+
return self::getOfType($data, self::NORMAL_MAXIMUM, self::TYPE_FLOAT);
83+
}
84+
6985
public static function getPresentationDocumentTarget(array $data): PresentationDocumentTarget|null
7086
{
7187
if (!isset($data[self::PRESENTATION_DOCUMENT_TARGET])) {
@@ -134,6 +150,7 @@ private static function coerce(mixed $value, string $type): mixed
134150
return match ($type) {
135151
self::TYPE_BOOLEAN => (bool) $value,
136152
self::TYPE_DATETIME => new DateTimeImmutable($value),
153+
self::TYPE_FLOAT => (float) $value,
137154
self::TYPE_INTEGER => (int) $value,
138155
self::TYPE_NORMALIZED_STRING => preg_replace('/[\r\n\t]+/', '', $value),
139156
default => $value,

src/Lti/Lti11/Mapper/DeepLinking/ContentItemMapper.php

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
public function __construct(
1616
private ImageMapperInterface $imageMapper = new ImageMapper(),
1717
private PlacementAdviceMapperInterface $placementAdviceMapper = new PlacementAdviceMapper(),
18+
private LineItemMapperInterface $lineItemMapper = new LineItemMapper(),
1819
) {
1920
}
2021

@@ -30,6 +31,7 @@ public function map(array $data): ContentItem
3031

3132
$icon = $this->imageMapper->map($data[Prop::ICON] ?? []);
3233
$thumbnail = $this->imageMapper->map($data[Prop::THUMBNAIL] ?? []);
34+
$lineItem = $this->lineItemMapper->map($data[Prop::LINE_ITEM] ?? []);
3335

3436
if ($type === 'LtiLinkItem') {
3537
return new LtiLinkItem(
@@ -41,6 +43,7 @@ public function map(array $data): ContentItem
4143
Prop::getTitle($data),
4244
Prop::getUrl($data),
4345
custom: [], // TODO
46+
lineItem: $lineItem,
4447
);
4548
}
4649

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Cerpus\EdlibResourceKit\Lti\Lti11\Mapper\DeepLinking;
4+
5+
use Cerpus\EdlibResourceKit\Lti\Lti11\Context\DeepLinkingProps as Prop;
6+
use Cerpus\EdlibResourceKit\Lti\Message\DeepLinking\LineItem;
7+
8+
final readonly class LineItemMapper implements LineItemMapperInterface
9+
{
10+
public function __construct(
11+
private ScoreConstraintsMapperInterface $constraintsMapper = new ScoreConstraintsMapper(),
12+
) {
13+
}
14+
15+
public function map(array $data): LineItem|null
16+
{
17+
$scoreConstraints = $this->constraintsMapper->map($data[Prop::SCORE_CONSTRAINTS] ?? []);
18+
19+
return new LineItem(
20+
$scoreConstraints
21+
);
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cerpus\EdlibResourceKit\Lti\Lti11\Mapper\DeepLinking;
6+
7+
use Cerpus\EdlibResourceKit\Lti\Message\DeepLinking\LineItem;
8+
9+
interface LineItemMapperInterface
10+
{
11+
public function map(array $data): LineItem|null;
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Cerpus\EdlibResourceKit\Lti\Lti11\Mapper\DeepLinking;
4+
5+
use Cerpus\EdlibResourceKit\Lti\Lti11\Context\DeepLinkingProps as Prop;
6+
use Cerpus\EdlibResourceKit\Lti\Message\DeepLinking\ScoreConstraints;
7+
8+
final readonly class ScoreConstraintsMapper implements ScoreConstraintsMapperInterface
9+
{
10+
public function map(array $data): ScoreConstraints|null
11+
{
12+
return new ScoreConstraints(
13+
Prop::getNormalMaximum($data),
14+
Prop::getExtraCreditMaximum($data),
15+
);
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Cerpus\EdlibResourceKit\Lti\Lti11\Mapper\DeepLinking;
4+
5+
use Cerpus\EdlibResourceKit\Lti\Message\DeepLinking\ScoreConstraints;
6+
7+
interface ScoreConstraintsMapperInterface
8+
{
9+
public function map(array $data): ScoreConstraints|null;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cerpus\EdlibResourceKit\Lti\Lti11\Serializer\DeepLinking;
6+
7+
use Cerpus\EdlibResourceKit\Lti\Lti11\Context\DeepLinkingProps as Prop;
8+
use Cerpus\EdlibResourceKit\Lti\Message\DeepLinking\LineItem;
9+
10+
final readonly class LineItemSerializer implements LineItemSerializerInterface
11+
{
12+
public function __construct(
13+
private ScoreConstraintsSerializerInterface $scoreConstraintsSerializer = new ScoreConstraintsSerializer(),
14+
) {
15+
}
16+
17+
public function serialize(LineItem $lineItem): array
18+
{
19+
$serialized = [
20+
'@type' => 'LineItem',
21+
];
22+
23+
if ($lineItem->getScoreConstraints() !== null) {
24+
$serialized[Prop::SCORE_CONSTRAINTS] = $this
25+
->scoreConstraintsSerializer
26+
->serialize($lineItem->getScoreConstraints());
27+
}
28+
29+
return $serialized;
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cerpus\EdlibResourceKit\Lti\Lti11\Serializer\DeepLinking;
6+
7+
use Cerpus\EdlibResourceKit\Lti\Message\DeepLinking\LineItem;
8+
9+
interface LineItemSerializerInterface
10+
{
11+
/**
12+
* @return array<mixed>
13+
* The compact form JSON-LD representation of LineItem
14+
*/
15+
public function serialize(LineItem $lineItem): array;
16+
}

src/Lti/Lti11/Serializer/DeepLinking/LtiLinkItemSerializer.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
namespace Cerpus\EdlibResourceKit\Lti\Lti11\Serializer\DeepLinking;
66

77
use Cerpus\EdlibResourceKit\Lti\Message\DeepLinking\LtiLinkItem;
8+
use Cerpus\EdlibResourceKit\Lti\Lti11\Context\DeepLinkingProps as Prop;
89

910
final readonly class LtiLinkItemSerializer implements LtiLinkItemSerializerInterface
1011
{
1112
public function __construct(
1213
private ContentItemSerializer $serializer = new ContentItemSerializer(),
14+
private LineItemSerializerInterface $lineItemSerializer = new LineItemSerializer(),
1315
) {
1416
}
1517

@@ -18,9 +20,17 @@ public function __construct(
1820
*/
1921
public function serialize(LtiLinkItem $item): array
2022
{
21-
return [
23+
$serialized = [
2224
...$this->serializer->serialize($item),
2325
'@type' => 'LtiLinkItem',
2426
];
27+
28+
if ($item->getLineItem() !== null) {
29+
$serialized[Prop::LINE_ITEM] = $this
30+
->lineItemSerializer
31+
->serialize($item->getLineItem());
32+
}
33+
34+
return $serialized;
2535
}
2636
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Cerpus\EdlibResourceKit\Lti\Lti11\Serializer\DeepLinking;
4+
5+
use Cerpus\EdlibResourceKit\Lti\Lti11\Context\DeepLinkingProps as Prop;
6+
use Cerpus\EdlibResourceKit\Lti\Message\DeepLinking\ScoreConstraints;
7+
8+
final readonly class ScoreConstraintsSerializer implements ScoreConstraintsSerializerInterface
9+
{
10+
public function serialize(ScoreConstraints $scoreConstraints): array
11+
{
12+
return [
13+
'@type' => 'NumericLimits',
14+
Prop::NORMAL_MAXIMUM => $scoreConstraints->getNormalMaximum() ?? 0,
15+
Prop::EXTRA_CREDIT_MAXIMUM => $scoreConstraints->getExtraCreditMaximum() ?? 0,
16+
Prop::TOTAL_MAXIMUM => $scoreConstraints->getTotalMaximum(),
17+
];
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cerpus\EdlibResourceKit\Lti\Lti11\Serializer\DeepLinking;
6+
7+
use Cerpus\EdlibResourceKit\Lti\Message\DeepLinking\ScoreConstraints;
8+
9+
interface ScoreConstraintsSerializerInterface
10+
{
11+
public function serialize(ScoreConstraints $scoreConstraints): array;
12+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Cerpus\EdlibResourceKit\Lti\Message\DeepLinking;
4+
5+
class LineItem
6+
{
7+
public function __construct(
8+
private readonly ScoreConstraints|null $scoreConstraints = null,
9+
) {
10+
}
11+
12+
public function getScoreConstraints(): ScoreConstraints|null
13+
{
14+
return $this->scoreConstraints;
15+
}
16+
}

src/Lti/Message/DeepLinking/LtiLinkItem.php

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public function __construct(
1818
string|null $title = null,
1919
string|null $url = null,
2020
private readonly array $custom = [],
21+
private readonly LineItem|null $lineItem = null,
2122
) {
2223
parent::__construct(
2324
$mediaType,
@@ -34,4 +35,9 @@ public function getCustom(): array
3435
{
3536
return $this->custom;
3637
}
38+
39+
public function getLineItem(): LineItem|null
40+
{
41+
return $this->lineItem;
42+
}
3743
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cerpus\EdlibResourceKit\Lti\Message\DeepLinking;
6+
7+
class ScoreConstraints
8+
{
9+
public function __construct(
10+
private readonly float|null $normalMaximum = null,
11+
private readonly float|null $extraCreditMaximum = null,
12+
) {
13+
}
14+
15+
public function getNormalMaximum(): float|null
16+
{
17+
return $this->normalMaximum;
18+
}
19+
20+
public function getExtraCreditMaximum(): float|null
21+
{
22+
return $this->extraCreditMaximum;
23+
}
24+
25+
public function getTotalMaximum(): float
26+
{
27+
return ($this->normalMaximum ?? 0) + ($this->extraCreditMaximum ?? 0);
28+
}
29+
}

tests/Lti/Lti11/Mapper/ContentItemsMapperTest.php

+14
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ public function testItMapsAllTheStuff(): void
5151
'height' => 240,
5252
],
5353
'url' => 'http://example.com/lti',
54+
'lineItem' => [
55+
'@type' => 'LineItem',
56+
'scoreConstraints' => [
57+
'@type' => 'NumericLimits',
58+
'normalMaximum' => 39.5,
59+
'extraCreditMaximum' => 2.5,
60+
'totalMaximum' => 42.0,
61+
],
62+
],
5463
],
5564
[
5665
'@type' => 'FileItem',
@@ -91,6 +100,11 @@ public function testItMapsAllTheStuff(): void
91100
$this->assertArrayHasKey(2, $contentItems);
92101
$this->assertSame(ContentItem::class, $contentItems[2]::class);
93102
$this->assertSame('Some content', $contentItems[2]->getTitle());
103+
104+
$score = $contentItems[0]->getLineItem()->getScoreConstraints();
105+
$this->assertSame(39.5, $score->getNormalMaximum());
106+
$this->assertSame(2.5, $score->getExtraCreditMaximum());
107+
$this->assertSame(42.0, $score->getTotalMaximum());
94108
}
95109

96110
public function testMapsDataWithAdditionalJsonldContexts(): void

0 commit comments

Comments
 (0)