Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .code-samples.meilisearch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -612,3 +612,12 @@ patch_dynamic_search_rule_1: |-
]);
delete_dynamic_search_rule_1: |-
$client->deleteDynamicSearchRule('RULE_UID');
render_template_1: |-
$client->patch('/experimental-features', ['renderRoute' => true]);

$client->renderTemplate(
new TemplateRenderQuery(
['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.breed }} called {{ doc.name }}'],
['kind' => 'inlineDocument', 'inline' => ['breed' => 'Jack Russell', 'name' => 'Iko']],
)
);
Comment thread
Strift marked this conversation as resolved.
4 changes: 4 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Meilisearch\Endpoints\Delegates\HandlesSnapshots;
use Meilisearch\Endpoints\Delegates\HandlesSystem;
use Meilisearch\Endpoints\Delegates\HandlesTasks;
use Meilisearch\Endpoints\Delegates\HandlesTemplates;
use Meilisearch\Endpoints\Dumps;
use Meilisearch\Endpoints\DynamicSearchRules;
use Meilisearch\Endpoints\Health;
Expand All @@ -26,6 +27,7 @@
use Meilisearch\Endpoints\Snapshots;
use Meilisearch\Endpoints\Stats;
use Meilisearch\Endpoints\Tasks;
use Meilisearch\Endpoints\Templates;
use Meilisearch\Endpoints\TenantToken;
use Meilisearch\Endpoints\Version;
use Meilisearch\Http\Client as MeilisearchClientAdapter;
Expand All @@ -46,6 +48,7 @@ class Client
use HandlesBatches;
use HandlesNetwork;
use HandlesDynamicSearchRules;
use HandlesTemplates;

/**
* @param array<int, string> $clientAgents
Expand All @@ -72,5 +75,6 @@ public function __construct(
$this->tenantToken = new TenantToken($this->http, $apiKey);
$this->network = new Network($this->http);
$this->dynamicSearchRules = new DynamicSearchRules($this->http);
$this->templates = new Templates($this->http);
}
}
87 changes: 87 additions & 0 deletions src/Contracts/TemplateRenderQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

namespace Meilisearch\Contracts;

class TemplateRenderQuery
{
/**
* @var array{kind: 'inlineDocumentTemplate', inline: string}|array{kind: 'documentTemplate', indexUid: non-empty-string, embedder: non-empty-string}|array{kind: 'documentTemplate', indexUid: non-empty-string, templateUid: non-empty-string}
*/
private array $template;

/**
* @var array{kind: 'inlineDocument', inline: array<string, mixed>}|array{kind: 'indexDocument', indexUid: non-empty-string, id: string|int}|null
*/
private ?array $input = null;

private bool $inputSet = false;

/**
* @param array{kind: 'inlineDocumentTemplate', inline: string}|array{kind: 'documentTemplate', indexUid: non-empty-string, embedder: non-empty-string}|array{kind: 'documentTemplate', indexUid: non-empty-string, templateUid: non-empty-string} $template
* @param array{kind: 'inlineDocument', inline: array<string, mixed>}|array{kind: 'indexDocument', indexUid: non-empty-string, id: string|int}|null $input
*/
public function __construct(array $template, ?array $input = null)
{
$this->template = $template;

if (\func_num_args() >= 2) {
$this->input = $input;
$this->inputSet = true;
}
}

/**
* Set the template to render.
*
* Supports two kinds:
* - inlineDocumentTemplate: ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}']
* - documentTemplate: ['kind' => 'documentTemplate', 'indexUid' => 'movies', 'embedder' => 'myEmbedder']
* or ['kind' => 'documentTemplate', 'indexUid' => 'movies', 'templateUid' => 'myTemplate']
Comment on lines +39 to +41

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there are only these three options then it could be properly documented via phpdoc:

@param array{kind: 'inlineDocumentTemplate', inline: string}|array{kind: 'documentTemplate', indexUid: non-empty-string, embedder: non-empty-string}|array{kind: 'documentTemplate', indexUid: non-empty-string, templateUid: non-empty-string} $template

*
* @param array{kind: 'inlineDocumentTemplate', inline: string}|array{kind: 'documentTemplate', indexUid: non-empty-string, embedder: non-empty-string}|array{kind: 'documentTemplate', indexUid: non-empty-string, templateUid: non-empty-string} $template
*/
public function setTemplate(array $template): self
{
$this->template = $template;

return $this;
}

/**
* Set the input document for template rendering.
*
* Supports two kinds:
* - inlineDocument: ['kind' => 'inlineDocument', 'inline' => ['name' => 'John']]
* - indexDocument: ['kind' => 'indexDocument', 'indexUid' => 'movies', 'id' => '2']

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there are only these two options then it could be properly documented via phpdoc:

@param array{kind: 'inlineDocument', inline: array<string, mixed>}|array{kind: 'indexDocument', indexUid: non-empty-string, id: string|int}|null $input

*
* Pass null to explicitly send null input (API returns rendered: null).
* Omit this call entirely to not include input in the request.
*
* @param array{kind: 'inlineDocument', inline: array<string, mixed>}|array{kind: 'indexDocument', indexUid: non-empty-string, id: string|int}|null $input
*/
public function setInput(?array $input): self
{
$this->input = $input;
$this->inputSet = true;

return $this;
}

/**
* @return array{template: array{kind: 'inlineDocumentTemplate', inline: string}|array{kind: 'documentTemplate', indexUid: non-empty-string, embedder: non-empty-string}|array{kind: 'documentTemplate', indexUid: non-empty-string, templateUid: non-empty-string}, input?: array{kind: 'inlineDocument', inline: array<string, mixed>}|array{kind: 'indexDocument', indexUid: non-empty-string, id: string|int}|null}
*/
public function toArray(): array
{
$result = [
'template' => $this->template,
];

if ($this->inputSet) {
$result['input'] = $this->input;
}

return $result;
}
}
30 changes: 30 additions & 0 deletions src/Contracts/TemplateRenderResult.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Meilisearch\Contracts;

class TemplateRenderResult
{
private string $template;
private ?string $rendered;

/**
* @param array{template: string, rendered: string|null} $data
*/
public function __construct(array $data)
{
$this->template = $data['template'];
$this->rendered = $data['rendered'];
}

public function getTemplate(): string
{
return $this->template;
}

public function getRendered(): ?string
{
return $this->rendered;
}
}
19 changes: 19 additions & 0 deletions src/Endpoints/Delegates/HandlesTemplates.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Meilisearch\Endpoints\Delegates;

use Meilisearch\Contracts\TemplateRenderQuery;
use Meilisearch\Contracts\TemplateRenderResult;
use Meilisearch\Endpoints\Templates;

trait HandlesTemplates
{
protected Templates $templates;

public function renderTemplate(TemplateRenderQuery $query): TemplateRenderResult
{
return $this->templates->render($query);
}
}
21 changes: 21 additions & 0 deletions src/Endpoints/Templates.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Meilisearch\Endpoints;

use Meilisearch\Contracts\Endpoint;
use Meilisearch\Contracts\TemplateRenderQuery;
use Meilisearch\Contracts\TemplateRenderResult;

final class Templates extends Endpoint
{
protected const PATH = '/render-template';

public function render(TemplateRenderQuery $query): TemplateRenderResult
{
$response = $this->http->post(self::PATH, $query->toArray());

return new TemplateRenderResult($response);
}
}
69 changes: 69 additions & 0 deletions tests/Contracts/TemplateRenderQueryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

namespace Tests\Contracts;

use Meilisearch\Contracts\TemplateRenderQuery;
use PHPUnit\Framework\TestCase;

final class TemplateRenderQueryTest extends TestCase
{
public function testToArrayWithInlineTemplateAndInlineInput(): void
{
$query = new TemplateRenderQuery(
['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'],
['kind' => 'inlineDocument', 'inline' => ['name' => 'John']],
);

$result = $query->toArray();

self::assertSame([
'template' => ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'],
'input' => ['kind' => 'inlineDocument', 'inline' => ['name' => 'John']],
], $result);
}

public function testToArrayWithDocumentTemplateAndIndexDocumentInput(): void
{
$query = new TemplateRenderQuery(
['kind' => 'documentTemplate', 'indexUid' => 'movies', 'embedder' => 'myEmbedder'],
['kind' => 'indexDocument', 'indexUid' => 'movies', 'id' => '2'],
);

$result = $query->toArray();

self::assertSame([
'template' => ['kind' => 'documentTemplate', 'indexUid' => 'movies', 'embedder' => 'myEmbedder'],
'input' => ['kind' => 'indexDocument', 'indexUid' => 'movies', 'id' => '2'],
], $result);
}

public function testToArrayOmitsInputWhenNotSet(): void
{
$query = new TemplateRenderQuery(
['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'],
// input omitted — not passed
);

$result = $query->toArray();

self::assertArrayNotHasKey('input', $result);
self::assertSame([
'template' => ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'],
], $result);
}

public function testToArrayIncludesNullInputWhenExplicitlySet(): void
{
$query = new TemplateRenderQuery(
['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'],
null,
);

$result = $query->toArray();

self::assertArrayHasKey('input', $result);
self::assertNull($result['input']);
}
}
54 changes: 54 additions & 0 deletions tests/Endpoints/TemplatesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Tests\Endpoints;

use Meilisearch\Contracts\TemplateRenderQuery;
use Meilisearch\Http\Client;
use Tests\TestCase;

final class TemplatesTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();

$http = new Client($this->host, getenv('MEILISEARCH_API_KEY'));
$http->patch('/experimental-features', ['renderRoute' => true]);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

protected function tearDown(): void
{
$http = new Client($this->host, getenv('MEILISEARCH_API_KEY'));
$http->patch('/experimental-features', ['renderRoute' => false]);

parent::tearDown();
}

public function testCanRenderInlineTemplate(): void
{
$query = new TemplateRenderQuery(
['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.breed }} called {{ doc.name }}'],
['kind' => 'inlineDocument', 'inline' => ['breed' => 'Jack Russell', 'name' => 'Iko']],
);

$response = $this->client->renderTemplate($query);

self::assertSame('{{ doc.breed }} called {{ doc.name }}', $response->getTemplate());
self::assertSame('Jack Russell called Iko', $response->getRendered());
}

public function testCanRenderTemplateWithNullInput(): void
{
$query = new TemplateRenderQuery(
['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.breed }} called {{ doc.name }}'],
null,
);

$response = $this->client->renderTemplate($query);

self::assertSame('{{ doc.breed }} called {{ doc.name }}', $response->getTemplate());
self::assertNull($response->getRendered());
}
}
Loading