Skip to content

Commit

Permalink
Init Twig Extra component
Browse files Browse the repository at this point in the history
  • Loading branch information
loic425 committed Jun 21, 2024
1 parent 3c7ec7a commit 4588d89
Show file tree
Hide file tree
Showing 20 changed files with 747 additions and 2 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
},
"autoload": {
"psr-4": {
"Sylius\\TwigExtra\\": "src/TwigExtra/src/",
"Sylius\\TwigHooks\\": "src/TwigHooks/src/"
}
},
Expand Down
1 change: 1 addition & 0 deletions config/bundles.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
Sylius\TwigHooks\TwigHooksBundle::class => ['all' => true],
Sylius\TwigExtra\Symfony\TwigExtraBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
Symfony\UX\LiveComponent\LiveComponentBundle::class => ['all' => true],
Expand Down
4 changes: 4 additions & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

* [About Sylius Stack](README.md)

## 🍀 Twig Extra

* [Getting started](twig-extra/getting-started.md)

## 🌱 Twig Hooks

* [Getting started](twig-hooks/getting-started.md)
Expand Down
93 changes: 93 additions & 0 deletions docs/twig-extra/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
description: >-
Twig Extra is a set of Twig extensions to bring more Twig helpers.
---

# Getting started

## Installation

Install the package using Composer and Symfony Flex:

```bash
composer require sylius/twig-extra
```

## Features

### Sort by

This sort by extension allows to sort an array of objects by a specific property.

```php
class Book {
public function __construct() {
public string $name,
}
}

$books = [new Book('Shinning'), new Book('A Lord Of The Rings')];
```

```twig
<ul>
{% for book in books|sort_by('name') %}
<li>{{ book.name }}</li>
{% endif %}
</ul>
```

```text
. A Lord Of The Rings
. Shinning
```

You can also sort array of arrays.

```php

$books = [['name' => 'Shinning'], ['name' => 'A Lord Of The Rings']];
```

You just need to encapsulate the key with `[]`.

```twig
<ul>
{% for book in books|sort_by('[name]') %}
<li>{{ book.name }}</li>
{% endif %}
</ul>
```

### Test HTML attribute

This Twig extension allows you to add some data attributes in test environment or when debug is enabled.
This allows to identify your data easily in E2E tests without being too much dependant of your HTML changes.

```twig
<h1 {{ sylius_test_html_attribute('title')>Shinning</h1>
```

```html
<h1 data-test-title>Shinning</h1>
```

### Test Form HTML attribute

Like the `sylius_test_html_attribute` Twig extension, this one allows you to add some data attributes in test environment or when debug is enabled.
This function adds the data attribute via the `attr` Twig variable on a form theme block.

```twig
{{ form_row(form.title, sylius_test_form_attribute('title')) }}
```

```html
<!-- Actual html output bellow depends on your form theme -->
<label for="book_title">Title</label>
<input
type="text"
id="book_title"
name="title"
data-test-title <!-- This is the added data attribute -->
/>
```
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ parameters:
level: 8
reportUnmatchedIgnoredErrors: false
paths:
- 'src/TwigExtra/src'
- 'src/TwigHooks/src'
5 changes: 4 additions & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
</php>

<testsuites>
<testsuite name="TwigExtra Test Suite">
<directory>src/TwigExtra/tests</directory>
</testsuite>

<testsuite name="TwigHooks Test Suite">
<directory>src/TwigHooks/tests</directory>

</testsuite>
</testsuites>

Expand Down
33 changes: 33 additions & 0 deletions src/TwigExtra/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "sylius/twig-extra",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Sylius project",
"homepage": "https://sylius.com"
},
{
"name": "Community contributions",
"homepage": "https://github.com/Sylius/Stack/contributors"
}
],
"require": {
"php": "^8.1",
"symfony/http-kernel": "^6.4 || ^7.0",
"symfony/twig-bundle": "^6.4 || ^7.0"
},
"conflict": {
"sylius/ui-bundle": "<2.0"
},
"autoload": {
"psr-4": {
"Sylius\\TwigExtra\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\Sylius\\TwigExtra\\": "tests/"
}
}
}
33 changes: 33 additions & 0 deletions src/TwigExtra/config/services.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Sylius\TwigExtra\Twig\Extension\SortByExtension;
use Sylius\TwigExtra\Twig\Extension\TestFormAttributeExtension;
use Sylius\TwigExtra\Twig\Extension\TestHtmlAttributeExtension;

return function (ContainerConfigurator $configurator): void {
$services = $configurator->services();

$services->set('sylius_twig_extra.twig.extension.sort_by', SortByExtension::class)
->tag(name: 'twig.extension')
;

$services->set('sylius_twig_extra.twig.extension.test_form_attribute', TestFormAttributeExtension::class)
->args([
param('kernel.environment'),
param('kernel.debug'),
])
->tag(name: 'twig.extension')
;

$services->set('sylius_twig_extra.twig.extension.test_html_attribute', TestHtmlAttributeExtension::class)
->args([
param('kernel.environment'),
param('kernel.debug'),
])
->tag(name: 'twig.extension')
;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Sylius\TwigExtra\Symfony\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;

final class TwigExtraExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container): void
{
$loader = new PhpFileLoader(
$container,
new FileLocator(dirname(__DIR__, 3) . '/config'),
);

$loader->load('services.php');
}
}
11 changes: 11 additions & 0 deletions src/TwigExtra/src/Symfony/TwigExtraBundle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Sylius\TwigExtra\Symfony;

use Symfony\Component\HttpKernel\Bundle\Bundle;

final class TwigExtraBundle extends Bundle
{
}
65 changes: 65 additions & 0 deletions src/TwigExtra/src/Twig/Extension/SortByExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Sylius\TwigExtra\Twig\Extension;

use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;

class SortByExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
new TwigFilter('sort_by', [$this, 'sortBy']),
];
}

/**
* @param iterable<array<array-key, mixed>|object> $iterable
*
* @return array<array<array-key, mixed>|object>
*
* @throws NoSuchPropertyException
*/
public function sortBy(iterable $iterable, string $field, string $order = 'ASC'): array
{
$array = $this->transformIterableToArray($iterable);

usort(
$array,
function (array|object $firstElement, array|object $secondElement) use ($field, $order): int {
$accessor = PropertyAccess::createPropertyAccessor();

$firstProperty = (string) $accessor->getValue($firstElement, $field);
$secondProperty = (string) $accessor->getValue($secondElement, $field);

$result = strnatcasecmp($firstProperty, $secondProperty);
if ('DESC' === $order) {
$result *= -1;
}

return $result;
},
);

return $array;
}

/**
* @param iterable<array<array-key, mixed>|object> $iterable
*
* @return array<array<array-key, mixed>|object>
*/
private function transformIterableToArray(iterable $iterable): array
{
if (is_array($iterable)) {
return $iterable;
}

return iterator_to_array($iterable);
}
}
35 changes: 35 additions & 0 deletions src/TwigExtra/src/Twig/Extension/TestFormAttributeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Sylius\TwigExtra\Twig\Extension;

use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

final class TestFormAttributeExtension extends AbstractExtension
{
public function __construct(
private readonly string $environment,
private readonly bool $isDebugEnabled,
) {
}

/** @return TwigFunction[] */
public function getFunctions(): array
{
return [
new TwigFunction(
'sylius_test_form_attribute',
function (string $name, ?string $value = null): array {
if (str_starts_with($this->environment, 'test') || $this->isDebugEnabled) {
return ['attr' => ['data-test-' . $name => (string) $value]];
}

return [];
},
['is_safe' => ['html']],
),
];
}
}
35 changes: 35 additions & 0 deletions src/TwigExtra/src/Twig/Extension/TestHtmlAttributeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Sylius\TwigExtra\Twig\Extension;

use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

final class TestHtmlAttributeExtension extends AbstractExtension
{
public function __construct(
private readonly string $environment,
private readonly bool $isDebugEnabled,
) {
}

/** @return TwigFunction[] */
public function getFunctions(): array
{
return [
new TwigFunction(
'sylius_test_html_attribute',
function (string $name, ?string $value = null): string {
if (str_starts_with($this->environment, 'test') || $this->isDebugEnabled) {
return sprintf('data-test-%s="%s"', $name, (string) $value);
}

return '';
},
['is_safe' => ['html']],
),
];
}
}
Loading

0 comments on commit 4588d89

Please sign in to comment.