Skip to content

Commit

Permalink
improved: structure, functionality and consistency of twig extension (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
tomdavies authored May 5, 2023
1 parent ebed510 commit c8e0b4e
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 71 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ jobs:
php_version: latest
args: --profile --ignore-platform-reqs
- name: Run phpstan
uses: php-actions/composer@v6
uses: php-actions/phpstan@v3
with:
command: run-script phpstan
php_version: latest
memory_limit: 1G
- name: Run Check ECS
uses: php-actions/composer@v6
with:
Expand Down
36 changes: 0 additions & 36 deletions .github/workflows/run-tests.yml

This file was deleted.

3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@
"newridetech/php-classnames": "^1.2"
},
"require-dev": {
"roave/security-advisories": "dev-latest"
,
"craftcms/cms": "^4.1",
"craftcms/ecs": "dev-main",
"phpstan/phpstan": "^1.7",
"nunomaduro/collision": "^5.10",
"phpstan/phpstan": "^1.7",
"pestphp/pest": "^1.2",
"symplify/easy-coding-standard": "^10.2"
},
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default {
text: 'Usage',
items: [
{ text: 'Utility Functions', link: '/01-utility-fns' },
{ text: 'String Helpers', link: '/01.5-string-helpers' },
{ text: 'SVG Helpers', link: '/02-svg-helpers' },
{ text: 'Query / Collection Helpers', link: '/03-query-helpers' },
{ text: 'Eager Loading Helpers', link: '/04-eager-loading-helpers' },
Expand Down
6 changes: 1 addition & 5 deletions docs/01-utility-fns.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
# Utility functions

## `parseUrl(string $url)`

Wraps PHP's native [`parse_url()`](https://www.php.net/manual/en/function.parse-url.php)
# Utility Template Functions

## `classNames` / `cx()` *

Expand Down
35 changes: 35 additions & 0 deletions docs/01.5-string-helpers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# String Helpers

Toolbelt provides a wide number of Twig filters and functions for manipulating strings.

## Wrapped PHP Functions

The following functions/filters are wrappers for PHP native functions:

| Twig name | Available in Twig as | Wrapped function / signature | Docs |
|-------------|----------------------|--------------------------------------------------------------------------------|------------------------------------------------------------|
| `parse_url` | `function` | `parse_url(string $url, int $component = -1): int\|string\|array\|null\|false` | [🔗](https://www.php.net/manual/en/function.parse-url.php) |
| `dirname` | `function` | `dirname(string $path, int $levels = 1): string` | [🔗](https://www.php.net/manual/en/function.dirname.php) |
| `pathinfo` | `function` | `pathinfo(string $path, int $flags = PATHINFO_ALL): array\|string` | [🔗](https://www.php.net/manual/en/function.pathinfo.php) |
| `md5` | `function`, `filter` | `md5(string $string, bool $binary = false)` | [🔗](https://www.php.net/manual/en/function.md5.php) |

## Wrapped `craft\helpers\StringHelper` Functions

The following functions/filters are wrappers for functions in Craft's [`craft\helpers\StringHelper`](https://docs.craftcms.com/api/v4/craft-helpers-stringhelper.html) class:

| Twig name | Available in Twig as | Wrapped method / signature | Docs |
|---------------------|----------------------|-------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
| `UUID` | `function` | `UUID(): string` | [🔗](https://docs.craftcms.com/api/v4/craft-helpers-stringhelper.html#method-uuid) |
| `basename` | `function`, `filter` | `basename(string $path, string $suffix = ''): string` | [🔗](https://docs.craftcms.com/api/v4/craft-helpers-stringhelper.html#public-methods) |
| `humanize` | `function`, `filter` | `humanize(string $str): string` | [🔗](https://docs.craftcms.com/api/v4/craft-helpers-stringhelper.html#method-humanize) |
| `slugify` | `function`, `filter` | `slugify(string $str, string $replacement = '-', ?string $language = null): string` | [🔗](https://docs.craftcms.com/api/v4/craft-helpers-stringhelper.html#method-slugify) |
| `titleize` | `function`, `filter` | `titleize(string $str, ?[] $ignore = null): string` | [🔗](https://docs.craftcms.com/api/v4/craft-helpers-stringhelper.html#method-titleize) |
| `titleizeForHumans` | `function`, `filter` | `titleizeForHumans(string $str, ?[] $ignore = null): string` | [🔗](https://docs.craftcms.com/api/v4/craft-helpers-stringhelper.html#method-titleizeForHumans) |

## Extra String Helpers

The following functions/filters are custom to Toolbelt:

| Twig name | Available in Twig as | Signature | Docs |
|----------------------|----------------------|------------------------------------------|-------------------------------------------------------------------------------------|
| `stringify()`, `s()` | `function`, `filter` | stringify(string $str): \Stringy\Stringy | Converts a string to a [Stringy instance](https://github.com/danielstjules/Stringy) |
12 changes: 6 additions & 6 deletions docs/03-query-helpers.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Query / Collection helpers
# Query & Collection Helpers

`take()` / `takeOne` allow your templates to indifferently consume `ElementQuery`s, `Collections`, plain `array`s, single `Model` instances and even hashes / assoc arrays, and handle them all in the same way when you want to consume them.


## `take()`

`take()` accepts any of the above types and intelligently returns a Collection based on what you provided.

It also accepts an optional second `limit` parameter that will limit the quantity of items in the returned collection:
It also accepts an optional second `limit` parameter that will limit the quantity of items in the returned collection.

It is available as both a Twig function and a filter.

```twig
{% set featuredItems = take(aQueryOrArrayOrCollection, 4) %}
Expand Down Expand Up @@ -44,8 +45,7 @@ Expanded example:

## `takeOne()`

`takeOne()` returns the first item from a array-like set, `null` if the set was empty, or just the item itself in any other case.

`takeOne()` returns the first item from a array-like set, `null` if the set was empty, or just the item itself in any other case. It is available as both a Twig function and a filter.

That means ugly code like this:

Expand Down Expand Up @@ -94,7 +94,7 @@ Expanded example:

## `fill()`

`fill()` is useful when you want to be sure to end up with a set number of list items in total, drawing from a series of sources in preferential order:
`fill()` is useful when you want to be sure to end up with a set number of list items in total, drawing from a series of sources in preferential order. It is available as a Twig function.

```twig
{% set featuredItems = craft.entries.section('news').isFeatured(true) %}
Expand Down
2 changes: 1 addition & 1 deletion docs/05-debugging-helpers.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Debugging helpers

Out of the box, Craft gives us a `{% dd %}` tag in addition to Twig's native `dump` filter. Both are fine, but neither are great, particularly when you want to quick modify existing code to sanity check something.
Out of the box, Craft gives us a `{% dd %}` tag in addition to Twig's native `dump` filter. Both are fine, but neither are great, particularly when you want to quickly modify existing code to sanity check something.

## `dd()` / `d()` all the things

Expand Down
11 changes: 10 additions & 1 deletion docs/06-operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,13 @@ The plugin adds the following operators to Twig:

## Empty Coalesce / `???`

[This is lifted from the plugin of the same name by nystudio107](https://github.com/nystudio107/craft-emptycoalesce)
The `???` operator will return the first thing that is defined, not null, and not empty.

```twig
{% set foo = '' %}
{% set bar = 'bar' %}
{{ foo ??? bar }} {# outputs 'bar' #}
```

[This operator is lifted from the plugin of the same name by nystudio107](https://github.com/nystudio107/craft-emptycoalesce).
15 changes: 15 additions & 0 deletions src/Helpers/DataHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace zaengle\Toolbelt\Helpers;

class DataHelper
{
public static function json_decode(string|array $value, bool $assoc = false, int $depth = 512, int $flags = 0): mixed
{
if (is_array($value)) {
return $value;
}

return json_decode(html_entity_decode($value), $assoc, $depth, $flags);
}
}
11 changes: 11 additions & 0 deletions src/Helpers/StringHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace zaengle\Toolbelt\Helpers;

class StringHelper
{
public static function md5(string $string, bool $binary = false): string
{
return md5($string, $binary);
}
}
66 changes: 47 additions & 19 deletions src/TwigExtensions/ToolbeltTwigExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

namespace zaengle\Toolbelt\TwigExtensions;

use craft\helpers\StringHelper;
use craft\helpers\StringHelper as CraftStringHelper;
use Newride\Classnames\Classnames as PhpClassnames;
use Stringy\Stringy;
use Symfony\Component\VarDumper\VarDumper;
use Twig\ExpressionParser;

use Twig\ExpressionParser;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;


use zaengle\Toolbelt\Helpers\DataHelper;
use zaengle\Toolbelt\Helpers\ElementHelper as ToolbeltElementHelper;
use zaengle\Toolbelt\Helpers\StringHelper as ToolbeltStringHelper;
use zaengle\Toolbelt\Helpers\SvgHelper;

use zaengle\Toolbelt\Node\Expression\EmptyCoalesceExpression;
Expand All @@ -32,50 +34,76 @@ public function getName(): string
public function getFunctions(): array
{
return [
// debugging
// Debugging
new TwigFunction('d', [$this, 'dump']),
new TwigFunction('dump', [$this, 'dump']),
new TwigFunction('dd', fn(...$args) => dd(...$args)),

// string helpers
new TwigFunction('slugify', [StringHelper::class, 'slugify']),
new TwigFunction('basename', [StringHelper::class, 'basename']),
new TwigFunction('UUID', [StringHelper::class, 'UUID']),
new TwigFunction('md5', fn($value) => md5($value)),

// template helpers
// Wrapped native PHP functions
new TwigFunction('dirname', fn(string $path, int $levels = 1): string => dirname($path, $levels)),
new TwigFunction('md5', [ToolbeltStringHelper::class, 'md5']),
new TwigFunction('parse_url', fn(string $url) => parse_url($url), ['is_safe' => ['html']]),
new TwigFunction('pathinfo', fn(string $path, int $flags = PATHINFO_ALL): array|string => pathinfo($path, $flags)),

// Wrapped Craft StringHelper fns
new TwigFunction('UUID', [CraftStringHelper::class, 'UUID']),
new TwigFunction('humanize', [CraftStringHelper::class, 'humanize']),
new TwigFunction('slugify', [CraftStringHelper::class, 'slugify']),
new TwigFunction('basename', [CraftStringHelper::class, 'basename']),
new TwigFunction('titleize', [CraftStringHelper::class, 'titleize']),
new TwigFunction('titleizeForHumans', [CraftStringHelper::class, 'titleizeForHumans']),

// Extra String helpers
new TwigFunction('stringify', [Stringy::class, 'create']),
new TwigFunction('s', [Stringy::class, 'create']),

// Template helpers
new TwigFunction('classNames', [$this, 'classNames']),
new TwigFunction('cx', [$this, 'classNames']),
new TwigFunction('parseUrl', fn(string $url) => parse_url($url), ['is_safe' => ['html']]),
new TwigFunction('pathinfo', fn(string $path) => pathinfo($path)),

// SVG helpers
new TwigFunction('inlineSvg', [SvgHelper::class, 'renderInline'], ['is_safe' => ['html']]),
new TwigFunction('useSvgSprite', [SvgHelper::class, 'useSvgSprite'], ['is_safe' => ['html']]),
new TwigFunction('svgSlug', [SvgHelper::class, 'svgSlug'], ['is_safe' => ['html']]),

// Query / Collection helpers
new TwigFunction('eagerLoad', [ToolbeltElementHelper::class, 'eagerLoad']),
new TwigFunction('fill', [ToolbeltElementHelper::class, 'fill']),
new TwigFunction('take', [ToolbeltElementHelper::class, 'take']),
new TwigFunction('takeOne', [ToolbeltElementHelper::class, 'takeOne']),
new TwigFunction('fill', [ToolbeltElementHelper::class, 'fill']),
new TwigFunction('eagerLoad', [ToolbeltElementHelper::class, 'eagerLoad']),

// Data helpers
new TwigFunction('json_decode', fn($value, $assoc = false, $depth = 512, $options = 0) => json_decode(html_entity_decode($value), $assoc, $depth, $options)),
new TwigFunction('json_decode', [DataHelper::class, 'json_decode']),
];
}

public function getFilters(): array
{
return [
// Debugging helpers
new TwigFilter('d', [$this, 'dump']),
new TwigFilter('dump', [$this, 'dump']),

// Wrapped native PHP functions
new TwigFilter('md5', [ToolbeltStringHelper::class, 'md5']),

// Craft String helpers
new TwigFilter('basename', [CraftStringHelper::class, 'basename']),
new TwigFilter('humanize', [CraftStringHelper::class, 'humanize']),
new TwigFilter('slugify', [CraftStringHelper::class, 'slugify']),
new TwigFilter('titleize', [CraftStringHelper::class, 'titleize']),
new TwigFilter('titleizeForHumans', [CraftStringHelper::class, 'titleizeForHumans']),

// Query / Collection helpers
new TwigFilter('take', [ToolbeltElementHelper::class, 'take']),
new TwigFilter('takeOne', [ToolbeltElementHelper::class, 'takeOne']),
// string helpers
new TwigFilter('slugify', [StringHelper::class, 'slugify']),
new TwigFilter('basename', [StringHelper::class, 'basename']),

// Extra String helpers
new TwigFilter('stringify', [Stringy::class, 'create']),
new TwigFilter('s', [Stringy::class, 'create']),

// Data helpers
new TwigFilter('json_decode', [DataHelper::class, 'json_decode']),
];
}

Expand Down

0 comments on commit c8e0b4e

Please sign in to comment.