Skip to content

Commit 0009ad9

Browse files
committed
add html formatter
1 parent 2f61d46 commit 0009ad9

File tree

6 files changed

+217
-1
lines changed

6 files changed

+217
-1
lines changed

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
},
1818
"require-dev": {
1919
"codeigniter4/devkit": "^1.0",
20-
"codeigniter4/framework": "^4.1"
20+
"codeigniter4/framework": "^4.3"
2121
},
2222
"minimum-stability": "dev",
2323
"prefer-stable": true,

docs/html_formatter.md

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# HTML Formatter
2+
3+
There are times when we want to work with HTMX through an API. In such cases, we may want to use [Response Trait API](https://codeigniter.com/user_guide/outgoing/api_responses.html).
4+
5+
Unfortunately, CodeIgniter does not support HTML formatting for API by default. That's why such a formatter is included here. It will make it easier to work with the API when we want to return data in HTML format.
6+
7+
### Configuration
8+
9+
We should edit the `app/Config/Format.php` file to include the necessary changes. We should add `'text/html'` to the `$supportedResponseFormats` array, and `'text/html' => HTMLFormatter::class` to the `$formatters` array. This will be done automatically when you run the command:
10+
11+
php spark htmx:publish
12+
13+
Since content negotiation will be triggered for any format other than `json` or `xml`, we have two options:
14+
15+
1. Set the custom headers for every request via HTML tag
16+
```html
17+
hx-headers='{"Accept":"text/html"}'
18+
```
19+
2. Move the `'text/html'` entry from the `$supportedResponseFormats` config array to the first position in the array - this way it will be used as the default value.
20+
```php
21+
public array $supportedResponseFormats = [
22+
'text/html'
23+
'application/json',
24+
'application/xml', // machine-readable XML
25+
'text/xml', // human-readable XML
26+
];
27+
```
28+
29+
### Example
30+
31+
This is an sample of using HTML formatter:
32+
33+
```php
34+
<?php
35+
36+
namespace App\Controllers;
37+
38+
use CodeIgniter\API\ResponseTrait;
39+
use CodeIgniter\RESTful\ResourcePresenter;
40+
41+
class Photos extends ResourcePresenter
42+
{
43+
use ResponseTrait;
44+
45+
public function index()
46+
{
47+
$this->format = 'html';
48+
return $this->respondCreated('<div>Some data</div>');
49+
}
50+
}
51+
```

docs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ It also provides some additional help with **handling errors** and **Debug Toolb
1919
* [Response](response.md)
2020
* [RediretResponse](redirect_response.md)
2121
* [Debug Toolbar](debug_toolbar.md)
22+
* [HTML Formatter](html_formatter.md)
2223
* [Troubleshooting](troubleshooting.md)
2324

2425
### Demos

src/Commands/HtmxPublish.php

+20
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,26 @@ public function run(array $params)
4040
file_put_contents($file, $contents);
4141
}
4242

43+
$file = APPPATH . 'Config/Format.php';
44+
45+
$publisher->addLineAfter(
46+
$file,
47+
" 'text/html'",
48+
"'text/xml', // human-readable XML",
49+
);
50+
51+
$publisher->addLineAfter(
52+
$file,
53+
" 'text/html' => HTMLFormatter::class,",
54+
"'text/xml' => XMLFormatter::class,",
55+
);
56+
57+
$publisher->addLineAfter(
58+
$file,
59+
'use Michalsn\\CodeIgniterHtmx\\Format\\HTMLFormatter;',
60+
'use CodeIgniter\\Format\\XMLFormatter;',
61+
);
62+
4363
CLI::write(CLI::color(' Published! ', 'green') . 'You can customize the configuration by editing the "app/Config/Htmx.php" file.');
4464
}
4565
}

src/Format/HTMLFormatter.php

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace Michalsn\CodeIgniterHtmx\Format;
4+
5+
use CodeIgniter\Format\FormatterInterface;
6+
use CodeIgniter\View\Table;
7+
8+
/**
9+
* HTML data formatter
10+
*/
11+
class HTMLFormatter implements FormatterInterface
12+
{
13+
/**
14+
* Takes the given data and formats it.
15+
*
16+
* @param array|string|null $data
17+
*/
18+
public function format($data): string
19+
{
20+
if ($data === null || $data === '' || $data === []) {
21+
return '';
22+
}
23+
24+
if (is_array($data)) {
25+
if (array_keys($data) === ['status', 'error', 'messages']) {
26+
if (isset($data['messages']['error']) && count($data['messages']) === 1) {
27+
return $data['messages']['error'];
28+
}
29+
30+
$data = $data['messages'];
31+
}
32+
33+
$data = $this->formatTable($data);
34+
}
35+
36+
return $data;
37+
}
38+
39+
/**
40+
* Format data as a table.
41+
*
42+
* @param mixed $data
43+
*/
44+
protected function formatTable($data): string
45+
{
46+
if (isset($data[0]) && count($data) !== count($data, COUNT_RECURSIVE)) {
47+
// Multi-dimensional array
48+
$headings = array_keys($data[0]);
49+
} else {
50+
// Single array
51+
$headings = array_keys($data);
52+
$data = [$data];
53+
}
54+
55+
$table = new Table();
56+
$table->setHeading($headings);
57+
58+
foreach ($data as $row) {
59+
// Suppressing the "array to string conversion" notice
60+
// Keep the "evil" @ here
61+
$row = @array_map('strval', $row);
62+
63+
$table->addRow($row);
64+
}
65+
66+
return $table->generate();
67+
}
68+
}

tests/Format/HTMLFormatterTest.php

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
namespace Tests\Format;
4+
5+
use CodeIgniter\Test\CIUnitTestCase;
6+
use Michalsn\CodeIgniterHtmx\Format\HTMLFormatter;
7+
8+
/**
9+
* @internal
10+
*/
11+
final class HTMLFormatterTest extends CIUnitTestCase
12+
{
13+
private HTMLFormatter $htmlFormatter;
14+
15+
protected function setUp(): void
16+
{
17+
parent::setUp();
18+
$this->htmlFormatter = new HTMLFormatter();
19+
}
20+
21+
public function testEmptyHTML(): void
22+
{
23+
$data = [];
24+
25+
$expected = '';
26+
27+
$this->assertSame($expected, $this->htmlFormatter->format($data));
28+
}
29+
30+
public function testBasicHTML(): void
31+
{
32+
$data = '<div>Hello</div>';
33+
34+
$expected = '<div>Hello</div>';
35+
36+
$this->assertSame($expected, $this->htmlFormatter->format($data));
37+
}
38+
39+
public function testFormatFailArray(): void
40+
{
41+
$data = [
42+
'status' => 400,
43+
'error' => 400,
44+
'messages' => ['error' => '<div>Error</div>'],
45+
];
46+
47+
$expected = '<div>Error</div>';
48+
49+
$this->assertSame($expected, $this->htmlFormatter->format($data));
50+
}
51+
52+
public function testFormatFailArrayWithManyMessages(): void
53+
{
54+
$data = [
55+
'status' => 400,
56+
'error' => 400,
57+
'messages' => ['error' => '<div>Error</div>', 'example' => '<div>Example</div>'],
58+
];
59+
60+
$expected = <<<'EOH'
61+
<table border="0" cellpadding="4" cellspacing="0">
62+
<thead>
63+
<tr>
64+
<th>error</th><th>example</th></tr>
65+
</thead>
66+
<tbody>
67+
<tr>
68+
<td><div>Error</div></td><td><div>Example</div></td></tr>
69+
</tbody>
70+
</table>
71+
EOH;
72+
73+
$this->assertSame($expected, $this->htmlFormatter->format($data));
74+
}
75+
76+
}

0 commit comments

Comments
 (0)