Skip to content

Instantly share code, notes, and snippets.

@bartwttewaall
Last active March 20, 2024 13:09
Show Gist options
  • Save bartwttewaall/e00dc6cce8293fc433a7802ab1c786c5 to your computer and use it in GitHub Desktop.
Save bartwttewaall/e00dc6cce8293fc433a7802ab1c786c5 to your computer and use it in GitHub Desktop.
<?php
namespace modules\wirelab;
use Craft;
use craft\elements\Entry;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
class WirelabTwigExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
/**
* Use regex to replace a string inline
*
* @example
*
* ```twig
* {{ "some-thing"|pregReplace('/\\-/', '_') }}
* ```
*/
new TwigFilter('pregReplace', function (array|string $subject, array|string $pattern, array|string $replacement) {
return preg_replace($pattern, $replacement, $subject);
}),
/**
* Convert "(+31) 06 - 123 456 78" to "00310612345678"
*
* @example
* ```twig
* <a href="tel:{{ brand.phoneNumber|phone }}">Our number: {{ brand.phoneNumber }}</a>
* ```
*/
new TwigFilter('phone', function (string $input) {
$input = preg_replace('/\\+/', '00', $input);
return preg_replace('/\\D+/', '', $input);
}),
/**
* @return mixed|null The found value or null when not found
*
* @example
*
* ```twig
* {% set items = [{id:1, name:"one"}, {id:2, name:"two"}, {id:3, name:"three"}] %}
* {{ items|find(i => i.name|length > 3)|json_encode }} // output: '{id:3, name:"three"}'
* ```
*/
new TwigFilter('find', function (array $array, callable $callback) {
foreach ($array as $key => $value) {
if (call_user_func($callback, $value, $key, $array) === true)
return $value;
}
return null;
}),
/**
* @return int Returns the found index, or -1 when not found
*
* @example
*
* ```twig
* {% set items = [{id:1, name:"one"}, {id:2, name:"two"}, {id:3, name:"three"}] %}
* {{ items|findIndex(i => i.name|length > 3) }} // output: 2
* ```
*/
new TwigFilter('findIndex', function (array $array, callable $callback) {
foreach ($array as $key => $value) {
if (call_user_func($callback, $value, $key, $array) === true)
return $key;
}
return -1;
}),
/**
* Format a string or number to a formatted string, using the locale of the current site.
* Use case: English & Dutch write a euro sign before the value, most other EU countries write it after the value
* See: https://en.wikipedia.org/wiki/Euro_sign#:~:text=The%20European%20Union%27s%20Interinstitutional%20Style,amount%20in%20most%20other%20languages
*
* @example
*
* ```twig
* {{ "1234,56"|currency('EUR') }}
* ```
*/
new TwigFilter('currency', function (string|float $input, string $currency = 'EUR', int $precision = 2) {
if (empty($input))
throw new \Exception('Empty input');
if (is_string($input)) $input = floatval(str_replace(',', '.', $input));
$locale = Craft::$app->sites->currentSite->language;
$fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
$fmt->setTextAttribute(\NumberFormatter::CURRENCY_CODE, $currency);
$fmt->setAttribute(\NumberFormatter::FRACTION_DIGITS, $precision);
return $fmt->formatCurrency($input, $currency);
}),
];
}
public function getFunctions(): array
{
/**
* This function returns unordered groups with unordered child sites, enrichted with properties such as isCurrent and translated language properties
*
* @example
*
* ```twig
* {% for group in getGroupedSites()|sort((a, b) => b.name <=> a.name) %}
* <ul>
* <strong>{{ group.name }}</strong>
* {% for site in group.sites|sort((a, b) => b.name <=> a.name) %}
* <li>
* <a href="{{ site.baseUrl }}">{{ site.name }}</a>
* </li>
* {% endfor %}
* </ul>
* {% endfor %}
* ```
*/
return [
new TwigFunction('getGroupedSites', function () {
$entryElement = Craft::$app->getUrlManager()->getMatchedElement();
$groups = Craft::$app->sites->getAllGroups();
$groupsArray = [];
foreach ($groups as $group) {
$groupObject = new \stdClass();
$groupObject->id = $group->id;
$groupObject->uid = $group->uid;
$groupObject->name = $group->name;
// get group sites
$sites = Craft::$app->sites->getSitesByGroupId($group->id);
// get current site
$currentSite = Craft::$app->sites->currentSite;
// add 'sites' property to each group
$groupObject->sites = array_map(function ($site) use ($currentSite, $entryElement) {
$siteObject = new \stdClass();
foreach (get_object_vars($site) as $key => $value) {
$siteObject->$key = $value;
}
$siteObject->name = $site->name;
$siteObject->baseUrl = $site->baseUrl;
// find the current entry at a particular site, if it exists
$siteEntry = !is_bool($entryElement) ? $entryElement->getLocalized()->siteId($site->id)->one() : $entryElement;
$siteObject->entryUrl = $siteEntry ? $siteEntry->url : null;
// add 'isCurrent' property to each site
$siteObject->isCurrent = $site->id == $currentSite->id;
// compose the language code and language name
$languageCode = current(explode('-', $site->language));
$locale = Craft::$app->i18n->getLocaleById($languageCode);
$siteObject->languageCode = $languageCode;
$siteObject->languageName = $locale->displayName;
return $siteObject;
}, $sites);
$groupObject->isCurrent = count(array_filter($groupObject->sites, function($item) { return $item->isCurrent; })) > 0;
$groupsArray[] = $groupObject;
}
return $groupsArray;
}),
/**
* @return Array All related entries with the site's group name as a key and the site url as value
*
* @example
*
* ```twig
* {% set siteUrls = findRelatedEntries(entry, { includeQueryString: true }) %}
* ```
*/
new TwigFunction('findRelatedEntries', function (Entry $entry, bool $includeQueryString = true) {
$queryString = ($includeQueryString) ? Craft::$app->getRequest()->getQueryStringWithoutPath() : '';
return collect($entry->getLocalized()->all())
->mapWithKeys(function (Entry $entry) use ($queryString) {
$url = $entry->url;
if ($queryString !== '') {
$url .= '?' . $queryString;
}
return [$entry->getSite()->getGroup()->name => $url];
})
->toArray();
}),
/**
* @example
*
* ```twig
* {% set links = getCanonicals() %}
* {% set currentLink = links|filter(item => item.isCurrent)|first %}
*
* <link rel="alternate" href="{{ currentLink.url }}" hreflang="x-default"/>
* {% for link in links|filter((item) => not item.isCurrent) %}
* <link rel="alternate" href="{{ link.url }}" hreflang="{{ link.languageCode }}" />
* {% endfor %}
* ```
*/
new TwigFunction('getCanonicals', function () {
// get current site and current element
$currentSite = Craft::$app->sites->currentSite;
$currentElement = Craft::$app->urlManager->matchedElement;
// get all sites by group that have a non-empty baseUrl
$sites = array_filter(Craft::$app->sites->getGroupById($currentSite->groupId)->getSites(), function ($site) {
return !empty($site->baseUrl);
});
$altLinks = [];
foreach ($sites as $site) {
$url = $site->getBaseUrl();
if ($currentElement) {
$otherLocaleElement = Craft::$app->elements->getElementById($currentElement->id, $currentElement->className(), $site->id);
// break early
if (is_null($otherLocaleElement) || !$otherLocaleElement->enabledForSite)
continue;
$url = $otherLocaleElement->url;
}
$link = new \stdClass();
$link->url = $url;
$link->languageCode = current(explode('-', $site->language));
$link->countryCode = last(explode('-', $site->language));
$link->language = Craft::$app->i18n->getLocaleById($site->language)->displayName;
$locale = Craft::$app->i18n->getLocaleById($link->languageCode);
$link->displayName = $locale->displayName;
$link->isCurrent = $site->id == $currentSite->id;
$altLinks[] = $link;
}
return $altLinks;
}),
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment