Skip to content
Merged
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
16 changes: 14 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ jobs:
strategy:
fail-fast: true
matrix:
php: [ '8.1', '8.2', '8.3']
laravel: [ '9', '10', '11' ]
php: [ '8.1', '8.2', '8.3', '8.4']
laravel: [ '9', '10', '11', '12' ]
exclude:
- laravel: 12
php: 8.1
- laravel: 11
php: 8.1
- laravel: 9
php: 8.4

name: PHP ${{ matrix.php }}; Laravel ${{ matrix.laravel }}

Expand Down Expand Up @@ -57,6 +61,14 @@ jobs:
command: composer require "laravel/framework:11.*" "phpunit/phpunit:^9.3.7" --no-update --no-interaction
if: "matrix.laravel == '11'"

- name: Select Laravel 12
uses: nick-invision/retry@v1
with:
timeout_minutes: 5
max_attempts: 5
command: composer require "laravel/framework:12.*" "phpunit/phpunit:^9.3.7" --no-update --no-interaction
if: "matrix.laravel == '12'"

- name: Install PHP Dependencies
uses: nick-invision/retry@v1
with:
Expand Down
126 changes: 71 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
Allows you to use [Twig](http://twig.sensiolabs.org/) seamlessly in [Laravel](http://laravel.com/).
Allows you to use [Twig](https://twig.symfony.com/) seamlessly in [Laravel](http://laravel.com/).

[![Latest Stable Version](https://poser.pugx.org/rcrowe/twigbridge/v/stable.png)](https://packagist.org/packages/rcrowe/twigbridge)
[![Total Downloads](https://poser.pugx.org/rcrowe/twigbridge/downloads.png)](https://packagist.org/packages/rcrowe/twigbridge)
[![test](https://github.com/rcrowe/TwigBridge/actions/workflows/ci.yml/badge.svg)](https://github.com/rcrowe/TwigBridge/actions/workflows/ci.yml)
[![License](https://poser.pugx.org/rcrowe/twigbridge/license.png)](https://packagist.org/packages/rcrowe/twigbridge)
[![Latest Stable Version](https://poser.pugx.org/rcrowe/twigbridge/v/stable.png)](https://packagist.org/packages/rcrowe/twigbridge) [![Total Downloads](https://poser.pugx.org/rcrowe/twigbridge/downloads.png)](https://packagist.org/packages/rcrowe/twigbridge) [![test](https://github.com/rcrowe/TwigBridge/actions/workflows/ci.yml/badge.svg)](https://github.com/rcrowe/TwigBridge/actions/workflows/ci.yml) [![License](https://poser.pugx.org/rcrowe/twigbridge/license.png)](https://packagist.org/packages/rcrowe/twigbridge)

# Requirements

Expand All @@ -21,34 +18,48 @@ composer require rcrowe/twigbridge

Laravel automatically registers the Service Provider. Use Artisan to publish the twig config file:

```php
```bash
php artisan vendor:publish --provider="TwigBridge\ServiceProvider"
```

At this point you can now begin using twig like you would any other view
Create your Twig file in `resources/views` with the `.twig` file extension, for example:

```php
//app/Http/routes.php
//twig template resources/views/hello.twig
Route::get('/', function () {
return View::make('hello');
});
```html
<!-- resources/views/welcome.twig -->
<h1>
Welcome to TwigBridge!
</h1>
```

You can create the twig files in resources/views with the .twig file extension.
```bash
resources/views/hello.twig
At this point, you can now begin using Twig like you would any other view:

```php
// routes/web.php

use Illuminate\Support\Facades\Route;

Route::get('/', static function () {
return view('welcome');
});
```

# Configuration

Once Composer has installed or updated your packages you need to register TwigBridge with Laravel itself. Open up config/app.php and find the providers key towards the bottom and add:
## Automatic

**For Modern Laravel (5.5+):** Laravel automatically registers the Service Provider and Facade through Package Auto-Discovery. **Manual registration in `config/app.php` is not required**, and you can skip directly to publishing the configuration file below.

## Manual

**For Legacy Laravel (5.4 and below):** If you are using an older version of Laravel without Package Auto-Discovery support, you need to register TwigBridge manually

Once Composer has installed or updated your packages, you need to register TwigBridge with Laravel itself. Open up `config/app.php` and find the providers key towards the bottom and add:

```php
'TwigBridge\ServiceProvider',
```

You can add the TwigBridge Facade, to have easier access to the TwigBridge (or Twig\Environment).
You can add the TwigBridge Facade to have easier access to the TwigBridge (or Twig\Environment).

```php
'Twig' => 'TwigBridge\Facade\Twig',
Expand All @@ -59,19 +70,21 @@ Twig::addExtension('TwigBridge\Extension\Loader\Functions');
Twig::render('mytemplate', $data);
```

TwigBridge's configuration file can be extended in your ConfigServiceProvider, under the `twigbridge` key. You can find the default configuration file at `vendor/rcrowe/twigbridge/config`.
## Publishing Configuration

You _should_ use Artisan to copy the default configuration file from the `/vendor` directory to `/config/twigbridge.php` with the following command:
TwigBridge's configuration file can be extended in your ConfigServiceProvider, under the `twigbridge` key. You can find the default configuration file at `vendor/rcrowe/twigbridge/config`.

```bash
You *should* use Artisan to copy the default configuration file from the `/vendor` directory to `/config/twigbridge.php` with the following command:

```php
php artisan vendor:publish --provider="TwigBridge\ServiceProvider"
```

If you make changes to the `/config/twigbridge.php` file you will most likely have to run the `twig:clean` Artisan command for the changes to take effect.
If you make changes to the `/config/twigbridge.php` file, you will most likely have to run the `twig:clean` Artisan command for the changes to take effect.

# Installation on Lumen

For Lumen, you need to load the same Service Provider, but you have to disable the `Auth`, `Translator` and `Url` extensions in your local configuration.
For Lumen, you need to load the same Service Provider, but you have to disable the `Auth`, `Translator,` and `Url` extensions in your local configuration.
Copy the `config/twigbridge.php` file to your local `config` folder and register the configuration + Service Provider in `bootstrap/app.php`:

```php
Expand Down Expand Up @@ -117,15 +130,15 @@ And output variables, escaped by default. Use the `raw` filter to skip escaping.

# Extensions

Sometimes you want to extend / add new functions for use in Twig templates. Add to the `enabled` array in config/twigbridge.php a list of extensions for Twig to load.
Sometimes you want to extend/add new functions for use in Twig templates. Add to the `enabled` array in config/twigbridge.php a list of extensions for Twig to load.

```php
'enabled' => array(
'TwigBridge\Extensions\Example'
)
```

TwigBridge supports both a string or a closure as a callback, so for example you might implement the [Assetic](https://github.com/kriswallsmith/assetic) Twig extension as follows:
TwigBridge supports both a string or a closure as a callback, so for example, you might implement the [Assetic](https://github.com/kriswallsmith/assetic) Twig extension as follows:

```php
'enabled' => [
Expand All @@ -140,7 +153,7 @@ TwigBridge supports both a string or a closure as a callback, so for example you

TwigBridge comes with the following extensions enabled by default:

- [Twig\Extension\DebugExtension](http://twig.sensiolabs.org/doc/extensions/debug.html)
- [Twig\Extension\DebugExtension](https://twig.symfony.com/doc/3.x/api.html#using-extensions)
- TwigBridge\Extension\Laravel\Auth
- TwigBridge\Extension\Laravel\Config
- TwigBridge\Extension\Laravel\Dump
Expand All @@ -164,52 +177,55 @@ To enable '0.5.x' style Facades, enable the Legacy Facades extension:

These loader extensions exposes Laravel helpers as both Twig functions and filters.

Check out the config/twigbridge.php file to see a list of defined function / filters. You can also add your own.
Check out the `config/twigbridge.php` file to see a list of defined functions/filters. You can also add your own.

## FacadeLoader

The FacadeLoader extension allows you to call any facade you have configured in config/twigbridge.php. This gives your Twig templates integration with any Laravel class as well as any other classes you alias.
The FacadeLoader extension allows you to call any facade you have configured in `config/twigbridge.php`. This gives your Twig templates integration with any Laravel class, as well as any other classes you alias.

To use the Laravel integration (or indeed any aliased class and method), just add your facades to the config and call them like `URL.to(link)` (instead of `URL::to($link)`)

## Functions/Filters/Variables

The following helpers/filters are added by the default Extensions. They are based on the helpers and/or facades, so should be self explaining.

Functions:
* asset, action, url, route, secure_url, secure_asset
* auth_check, auth_guest, auth_user
* can
* config_get, config_has
* dump
* form_* (All the Form::* methods, snake_cased)
* html_* (All the Html::* methods, snake_cased)
* input_get, input_old, input_has
* link_to, link_to_asset, link_to_route, link_to_action
* session_has, session_get, csrf_token, csrf_field, method_field
* str_* (All the Str::* methods, snake_cased)
* trans, trans_choice
* url_* (All the URL::* methods, snake_cased)

Filters:
* camel_case, snake_case, studly_case
* str_* (All the Str::* methods, snake_cased)
* trans, trans_choice

Global variables:
* app: the Illuminate\Foundation\Application object
* errors: The $errors MessageBag from the Validator (always available)
The following helpers/filters are added by the default Extensions. They are based on the helpers and/or facades, so they should be self-explanatory.

### Functions:

* `asset, action, url, route, secure_url, secure_asset`
* `auth_check, auth_guest, auth_user`
* `can`
* `config_get, config_has`
* `dump`
* `form_*` (All the Form::* methods, snake_cased)
* `html_*` (All the Html::* methods, snake_cased)
* `input_get`, `input_old`, `input_has`
* `link_to`, `link_to_asset`, `link_to_route`, `link_to_action`
* `session_has`, `session_get`, `csrf_token`, `csrf_field`, `method_field`
* `str_*` (All the Str::* methods, snake_cased)
* `trans`, `trans_choice`
* `url_*` (All the URL::* methods, snake_cased)

### Filters:

* `camel_case`, `snake_case`, `studly_case`
* `str_*` (All the Str::* methods, snake_cased)
* `trans`, `trans_choice`

### Global variables:

* `app`: the Illuminate\Foundation\Application object
* `errors`: The `$errors` MessageBag from the Validator (always available)

# Artisan Commands

TwigBridge offers a command for CLI Interaction.

Empty the Twig cache:
```
```bash
$ php artisan twig:clean
```

Lint all Twig templates:
```
```bash
$ php artisan twig:lint
```
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
],
"require": {
"php": "^8.1",
"twig/twig": "~3.9",
"illuminate/support": "^9|^10|^11",
"illuminate/view": "^9|^10|^11"
"twig/twig": "~3.21",
"illuminate/support": "^9|^10|^11|^12",
"illuminate/view": "^9|^10|^11|^12"
},
"require-dev": {
"ext-json": "*",
"laravel/framework": "^9|^10|^11",
"laravel/framework": "^9|^10|^11|^12",
"mockery/mockery": "^1.3.1",
"phpunit/phpunit": "^8.5.8 || ^9.3.7",
"phpunit/phpunit": "^8.5.8 || ^9.3.7 || ^10.0 || ^11.0 || ^12.0",
"squizlabs/php_codesniffer": "^3.6"
},
"autoload": {
Expand Down
3 changes: 2 additions & 1 deletion config/twigbridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@
'TwigBridge\Extension\Laravel\Translator',
'TwigBridge\Extension\Laravel\Url',
'TwigBridge\Extension\Laravel\Model',
// 'TwigBridge\Extension\Laravel\Gate',
'TwigBridge\Extension\Laravel\Gate',
'TwigBridge\Extension\Laravel\Vite',

// 'TwigBridge\Extension\Laravel\Form',
// 'TwigBridge\Extension\Laravel\Html',
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Bridge extends Environment
/**
* {@inheritdoc}
*/
public function __construct(LoaderInterface $loader, $options = [], Container $app = null)
public function __construct(LoaderInterface $loader, $options = [], ?Container $app = null)
{
// Twig 2.0 doesn't support `true` anymore
if (isset($options['autoescape']) && $options['autoescape'] === true) {
Expand Down
63 changes: 63 additions & 0 deletions src/Extension/Laravel/Vite.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/**
* This file is part of the TwigBridge package.
*
* @copyright Robert Crowe <hello@vivalacrowe.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace TwigBridge\Extension\Laravel;

use Illuminate\Foundation\Vite as IlluminateVite;
use Twig\TwigFunction;
use Twig\Extension\AbstractExtension;

/**
* Access Laravels vite class in your Twig templates.
*/
class Vite extends AbstractExtension
{
/**
* @var \Illuminate\Foundation\Vite
*/
protected $vite;

/**
* Create a new Vite extension
*
* @param \Illuminate\Foundation\Vite
*/
public function __construct(IlluminateVite $vite)
{
$this->vite = $vite;
}

/**
* {@inheritDoc}
*/
public function getName()
{
return 'TwigBridge_Extension_Laravel_Vite';
}

/**
* {@inheritDoc}
*/
public function getFunctions()
{
return [
new TwigFunction(
'vite',
[$this->vite, '__invoke'],
[
'is_safe' => [
'html',
],
],
),
];
}
}
6 changes: 6 additions & 0 deletions src/Node/EventNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ public static function triggerLaravelEvents(string $templateName, array &$contex
if (Str::endsWith($templateName, '.twig')) {
$templateName = Str::substr($templateName, 0, mb_strlen($templateName) - 5);
}

$viewPath = resource_path('views');
if (Str::startsWith($templateName, $viewPath)) {
$templateName = Str::substr($templateName, mb_strlen($viewPath)+1);
}

/** @var \Illuminate\View\Factory $factory */
$env = resolve('view');
$viewName = ViewName::normalize($templateName);
Expand Down
8 changes: 4 additions & 4 deletions src/Node/GetAttrNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ class GetAttrNode extends GetAttrExpression
/**
* @inheritdoc
*/
public function __construct(array $nodes = [], array $attributes = [], int $lineno = 0, string $tag = null)
public function __construct(array $nodes = [], array $attributes = [], int $lineno = 0)
{
// Skip parent::__construct()
Node::__construct($nodes, $attributes, $lineno, $tag);
Node::__construct($nodes, $attributes, $lineno);
}

/**
Expand All @@ -47,7 +47,7 @@ public function compile(Compiler $compiler): void
// optimize array calls
if ($this->getAttribute('optimizable')
&& (!$env->isStrictVariables() || $this->getAttribute('ignore_strict_check'))
&& !$this->getAttribute('is_defined_test')
&& !$this->isDefinedTestEnabled()
&& Template::ARRAY_CALL === $this->getAttribute('type')
) {
$var = '$'.$compiler->getVarName();
Expand Down Expand Up @@ -91,7 +91,7 @@ public function compile(Compiler $compiler): void

$compiler->raw(', ')
->repr($this->getAttribute('type'))
->raw(', ')->repr($this->getAttribute('is_defined_test'))
->raw(', ')->repr($this->isDefinedTestEnabled())
->raw(', ')->repr($this->getAttribute('ignore_strict_check'))
->raw(', ')->repr($env->hasExtension(SandboxExtension::class))
->raw(', ')->repr($this->getNode('node')->getTemplateLine())
Expand Down
Loading
Loading