kenny1911/twig-view

Type-safe Twig views with template validation and testing capabilities

Installs: 1

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/kenny1911/twig-view

0.1.0 2025-11-11 13:28 UTC

This package is auto-updated.

Last update: 2025-11-12 20:48:01 UTC


README

[English][Русский]

A package for typing Twig templates and verifying their correctness during testing.

The Problem

When using Twig templates, errors are often discovered only at runtime because static analyzers cannot effectively verify the correctness of data passed to templates. Common issues include:

  • Passing non-existent variables to templates
  • Lack of null checks
  • Data type mismatches

The Solution

This package provides a system of typed views that explicitly define data structures for templates, along with tools for testing these views.

Installation

composer require kenny1911/twig-view

Usage

Creating a View

<?php

use Kenny1911\TwigView\View;

/**
 * @implements View<array{
 *     name: string
 * }>
 */
final class HelloView implements View
{
    public function __construct(
        private readonly string $name,
    ) {}

    #[\Override]
    public function template(): string
    {
        return 'hello.html.twig';
    }

    #[\Override]
    public function context(): array
    {
        return ['name' => $this->name];
    }
}

Twig Template

hello.html.twig:

<p>Hello {{ name }}</p>

Rendering a View

$viewRenderer->render(new HelloView('World'));
// Result: "<p>Hello World</p>"

Symfony Integration

The package includes an event subscriber for automatic integration with Symfony:

# config/services.yaml
services:
    twig.view_renderer:
        class: Kenny1911\TwigView\ViewRenderer
        arguments:
            - '@twig'

    Kenny1911\TwigView\Symfony\ViewListener:
        arguments:
            - '@twig.view_renderer'
        tags:
            - { name: kernel.event_subscriber }

Now when a controller returns a View instance, it will automatically be rendered into a Response:

<?php

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

final class HelloController
{
    #[Route('/hello/{name}', name: 'hello')]
    public function hello(string $name): HelloView
    {
        return new HelloView($name);
    }
}

Testing

Use PHPUnit to verify the correctness of all views:

<?php

use Kenny1911\TwigView\Test\ViewTest;
use Kenny1911\TwigView\ViewRenderer;
use PHPUnit\Framework\TestCase;
use Twig\Environment;
use Twig\Loader\ArrayLoader;

final class HelloViewTest extends TestCase
{
    use ViewTest;

    #[\Override]
    protected function createViewRenderer(): \Kenny1911\TwigView\ViewRenderer
    {
        return new ViewRenderer(
            new Environment(
                new ArrayLoader([
                    'hello.html.twig' => 'Hello {{ name }}',
                ]),
                [
                    'strict_variables' => true,
                ],
            ),
        );
    }

    #[\Override]
    protected function iterateViews(): iterable
    {
        yield new HelloView('World');
        // Add all test cases for your views here
    }
}

Testing Recommendations

  1. Use the Real Twig Environment - Tests should use the same Twig\Environment instance as in production

  2. Cover All Data Variants - The iterateViews method should return all possible data combinations for each template

  3. Test Edge Cases - Pay special attention to cases with null values and empty data

  4. Enable strict_variables - This ensures strict variable checking in Twig

Creating a Custom TestCase

In practice, it's recommended to create a base class for testing views that uses the same templates as in production:

<?php

abstract class ViewTestCase extends KernelTestCase
{
    use ViewTest;
    
    #[\Override]
    protected function createViewRenderer(): \Kenny1911\TwigView\ViewRenderer
    {
        self::bootKernel();
        
        return self::getContainer()->get('twig.view_renderer');
    }
}

Then your view test would look like this:

final class HelloViewTest extends ViewTestCase
{
    #[\Override]
    protected function iterateViews(): iterable
    {
        yield new HelloView('World');
        // Add all test cases for your views here
    }
}

Benefits

  • Early Error Detection - Problems are identified during testing, not in production
  • Typing - Explicit definition of data structures for each template
  • Autocompletion - Better IDE understanding of data structures in templates
  • Integration - Easy integration with Symfony and other frameworks
  • Flexibility - Ability to test all possible template usage scenarios

Common Issues and Recommendations

  1. Always Check for null - This is the most common cause of errors in Twig templates
  2. Use strict_variables - Enable this option for strict variable checking
  3. Test All Data Variants - Ensure you cover all possible data states
  4. Use Real Templates - Tests should use the same templates as in production

License

MIT