Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support other template engines #9

Open
jameswilson opened this issue Feb 17, 2024 · 1 comment
Open

Support other template engines #9

jameswilson opened this issue Feb 17, 2024 · 1 comment
Labels
enhancement New feature or request

Comments

@jameswilson
Copy link
Contributor

jameswilson commented Feb 17, 2024

Thanks for your work on this project. I'm planning on using it to put together a simple HTMX example app.

It would be interesting to add in optional support for the Twig template engine. Regardless of anyone's stance on Symfony, it is a fantastic template language and doesn't even require the entire Symfony package ecosystem to use it standalone.

Something like this would be cool:

composer require twig/twig
$app = new \PhpExpress\Application();
$app->set('views', 'path/to/views');
$app->set('twig_options', [
  'cache' => 'path/to/twig_cache',

  // Uncomment for local development only.
  // 'debug' => true,
  // 'auto_reload' => true,
]);

$app->get('/', function ($req, $res) {
    $res->render('base', [
        'title' => 'Home page',
        'message' => 'This is the home page',
    ]);
});

$app->get('about', function ($req, $res) {
    $res->render('about', [
        'title' => 'About page',
        'message' => 'This is the about page',
    ]);
});

Then in template folder we have:

views/base.twig:

<html>
<head>
<title>{{ title }}</title>
</head>
<body>
{% block header %}
<h1>{{ title }}</h1>
{% endblock %}
{% block content %}
<div>{{ content }}</div>
<a href="/about">About</a>
{% endblock %}
</body>
</html>

views/about.html.twig:

{% extends "base.twig" %}
{% block content %}
<div>{{ content }}</div>
<a href="/">Home</a>
{% endblock %}

In Application::render() we can hand down local variables into the Twig template if a matching view filename with extension .twig is found.

Then we can check if Twig FilesystemLoader class exists. The following is working for me:

    public function render(string $view, array $locals=null): void
    {
        if ($locals)
        {
            extract($locals);
        }
        $views_dir = $this->set("views");
        $layout = sprintf("%s/%s.php", $views_dir, $view);
        $_twig = sprintf("%s/%s.twig", $views_dir, $view);
        $_template = sprintf("%s/%s.php", $views_dir, $this->set("template"));
        if (is_file($_template))
        {
            include $_template;
        } elseif (is_file($_twig)) {
            if (!class_exists('\Twig\Loader\FilesystemLoader')) {
                $filepath = realpath($_twig);
                throw new \Exception("Please `composer require twig/twig` to proceed. A Twig template '{$filepath}' was found but the Twig template engine could not be loaded");
            }
            $loader = new \Twig\Loader\FilesystemLoader($views_dir);
            $twig = new \Twig\Environment($loader, $this->set("twig_options"));
            echo $twig->render(sprintf('%s.twig', $view), $locals);
        } else {
            include $layout;
        }
    }
@jameswilson
Copy link
Contributor Author

jameswilson commented Feb 17, 2024

Thinking through this a bit more, maybe the template engine approach should be generalized to support any kind of PHP template engine such as Laravel Blade (here is a Blade template engine standalone package) or the Crow template engine .

For example have a special app setting register a mapping mechanism from file extension to render callback function. That way you don't have to build in 3rd party tools into your codebase.

Something like:

$app = new \PhpExpress\Application();
$app->set('views', 'path/to/views');
$app->set('template_engines', [
  'twig' => function(string $view, array $locals = null): void {
    $loader = new \Twig\Loader\FilesystemLoader('path/to/views');
    $twig = new \Twig\Environment($loader, [
      'cache' => 'path/to/twig_cache',

      // Uncomment for local development only.
      'debug' => true,
      'auto_reload' => true,
    ]);
    echo $twig->render($template, $locals);
  }
]);

Or maybe a way to completely override the render function via callback without needing to subclass \PhpExpress\Application ?

$app->set('renderer', function(string $view, array $locals = null): void {
    $loader = new \Twig\Loader\FilesystemLoader('path/to/views');
    $twig = new \Twig\Environment($loader, [
      'cache' => 'path/to/twig_cache',

      // Uncomment for local development only.
      'debug' => true,
      'auto_reload' => true,
    ]);
    echo $twig->render($view, $locals);
});

$app->get('/', function ($req, $res) {
  $res->render('base.html.twig', [
    'title' => 'Home page',
    'content' => 'This is the home page.',
  ]);
});

@riverside riverside added the enhancement New feature or request label Mar 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants