This filter is somewhat more complicated than it used to be because I wanted to be able to use it with objects (such as post!) without the filter returning an array. Instead, the object is converted into an array temporarily but after the merge it is converted back into an object of the original class :) Accomplishing this requires that the filter have access to a PHP function. Making that PHP function available to the filter without redefining it (and thus causing an error) every time the filter is called ended up being a bit tricky:
The PHP function is:
/**
* Function to convert class of given object
*/
function convertObjectClass($array, $final_class)
{
return unserialize(sprintf(
'O:%d:"%s"%s',
strlen($final_class),
$final_class,
strstr(serialize($array), ':')
));
}
- To make it available to Pattern Lab, we include it in the upsert.filter.php, but enclose it in a conditional so if it's already declared it doesn't get redeclared. (I admit, this is kind of a weasel-y solution. I couldn't figure out where to put php functions to make them universally available to PL's twig functions and filters...)
- To make it available to Timber/Twig in WordPress, copy and paste it into the bottom of RadicatiSite.php or some other PHP file that gets called in functions.php.
While you're there, add the filter to Timber/Twig by copying and pasting the following into the add_filter('timber/twig', function (\Twig_Environment $twig) {
in RadicatiSite.php:
// I don't know why, but for some reason Timber\Twig_Filter (which is what the documentation recommends) was *not* working here, and was generating errors.
$twig->addFilter(new Twig_SimpleFilter('upsert', function ($arr1, $arr2) {
if ($arr1 instanceof Traversable) {
$arr1 = iterator_to_array($arr1);
} elseif (!is_array($arr1)) {
throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', gettype($arr1)));
}
if ($arr2 instanceof Traversable) {
$arr2 = iterator_to_array($arr2);
} elseif (!is_array($arr2) && !is_object($arr2)) {
$arr2 = [];
}
if (is_object($arr2)) {
$class = get_class($arr2);
return convertObjectClass(array_merge(
(array) $arr2,
(array) $arr1
), $class);
}
return array_merge($arr2, $arr1);
}));
Finally, add the filter to Pattern Lab (including the needed PHP function inside its little conditional):
Create a upsert.filter.php
file in patterns/_twig-components/functions/
It should contain the following:
<?php
/**
* This filter is similar to the Twig merge filter:
*
* This will allow you to merge a null value, allowing
* you to use the following pattern:
*
* {% set classes = ['class1', 'class2']|merge(classes) %}
*
* If classes is empty/null, then a new array will be
* created containing class1 and class2.
*
* Note: This is compatible with objects, so if for some
* reason you wanted to upsert an array into the post
* object you could do that, and it would return an object
* of the same type you upsert into.
*
* Please note that if you are upserting a key/value pair
* and the key you upsert into the array already exists,
* its value will be overwritten with the new value.
*
*
* Example uses:
* To add values, whose key will be the next avaiable
* integer:
* {% set post = ['one', 'two', 'three']|upsert(post) %}
*
* To add key/value pairs to your object:
* {% set post = {'FOO': 'BAR', 'xyzzy': 'the cake is a lie'}|upsert(post) %}
*
* Or even:
* {% set test = {'key1': 'value1', 'key2': ['value2a', 'value2b'], 'key3': {'key3a': 'value3a', 'key3b': 'value3b'}}|upsert(test) %}
*/
$filter = new Twig_SimpleFilter('upsert', function ($arr1, $arr2) {
if ($arr1 instanceof Traversable) {
$arr1 = iterator_to_array($arr1);
} elseif (!is_array($arr1)) {
throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', gettype($arr1)));
}
if ($arr2 instanceof Traversable) {
$arr2 = iterator_to_array($arr2);
} elseif (!is_array($arr2) && !is_object($arr2)) {
$arr2 = [];
}
if (is_object($arr2)) {
$class = get_class($arr2);
return convertObjectClass(array_merge(
(array) $arr2,
(array) $arr1
), $class);
}
return array_merge($arr2, $arr1);
});
if (!function_exists('convertObjectClass')) {
/**
* Function to convert class of given object
*/
function convertObjectClass($array, $final_class) {
return unserialize(sprintf(
'O:%d:"%s"%s',
strlen($final_class),
$final_class,
strstr(serialize($array), ':')
));
}
}