Skip to content

Instantly share code, notes, and snippets.

@wodCZ
Created December 16, 2015 13:08
Show Gist options
  • Save wodCZ/ab6b5e5914018dda9918 to your computer and use it in GitHub Desktop.
Save wodCZ/ab6b5e5914018dda9918 to your computer and use it in GitHub Desktop.
FillerUtil
<?php
namespace Libs\Utils;
use Nette\ComponentModel\IContainer;
use Nette\Forms\Controls\BaseControl;
use Nette\Utils\Strings;
use Symfony\Component\PropertyAccess\PropertyAccess;
class InvalidArgumentException extends \InvalidArgumentException
{
}
final class FillerUtil
{
private static $accessor;
private function __construct()
{
}
private static function getAccessor()
{
if (self::$accessor === NULL) {
$accessorBuilder = PropertyAccess::createPropertyAccessorBuilder();
$accessorBuilder->disableExceptionOnInvalidIndex();
self::$accessor = $accessorBuilder->getPropertyAccessor();
}
return self::$accessor;
}
/**
* Tries to access entity property and set its value as related form input default value
*
* @param $container
* @param $source
* @param $map ['field'] in case of same faield names or ['formControlName' => 'sourceFieldName'], for collection set append [] after sourceFieldName
*/
public static function fillFormDefaults(IContainer $container, $source, $map)
{
self::fill(function ($field, $value) use ($container) {
$component = $container->getComponent($field);
if ($component instanceof BaseControl) {
$component->setDefaultValue($value);
}
}, $source, $map);
}
/**
* @param $targetOrSetter
* @param $sourceOrGetter
* @param $map
*/
public static function fill($targetOrSetter, $sourceOrGetter, $map)
{
if ( ! is_callable($targetOrSetter)) {
$targetOrSetter = self::createAccessorSetter($targetOrSetter);
}
if ( ! is_callable($sourceOrGetter)) {
$sourceOrGetter = self::createAccessorGetter($sourceOrGetter);
}
self::fillUsingCallback($map, $sourceOrGetter, $targetOrSetter);
}
/**
* @param $source
* @return \Closure
*/
public static function createAccessorGetter($source)
{
$accessor = self::getAccessor();
return function ($field) use ($source, $accessor) {
return $accessor->getValue($source, $field);
};
}
/**
* @param $target
* @return \Closure
*/
public static function createAccessorSetter($target)
{
$accessor = self::getAccessor();
return function ($field, $value) use ($target, $accessor) {
$accessor->setValue($target, $field, $value);
};
}
/**
* @param array $map ['field'] in case of same faield names or ['targetField' => 'sourceField'], for collection set append [] after collection field
* @param callable|\Closure $setCallback will be called ($field, $value) to set value
* @param callable|\Closure $getCallback will be called ($field) to get value
*/
private static function fillUsingCallback(array $map, callable $getCallback = NULL, callable $setCallback = NULL)
{
foreach ($map as $targetField => $sourceField) {
$sourceSubField = 'id'; // used if $source->$sourceField is array on its elements
$valueIsArray = FALSE;
if (Strings::contains($sourceField, '[]')) { // users[].id or users[]
$valueIsArray = TRUE;
list($sourceField, $sourceSubField) = array_filter(explode('[]', $sourceField, 2) + ['', $sourceSubField]);
$sourceSubField = Strings::trim($sourceSubField, '.');
}
$targetField = is_int($targetField) ? $sourceField : $targetField; // checks for numerical index
try {
$value = $getCallback($sourceField);
if ($valueIsArray) {
if ($value instanceof \Iterator) {
$mapSource = iterator_to_array($value);
} elseif (is_array($value)) {
$mapSource = $value;
} else {
throw new InvalidArgumentException("Field {$sourceField} is not array nor iterator.");
}
$value = array_map(function ($item) use ($sourceSubField, $getCallback) {
return $getCallback($item, $sourceSubField);
}, $mapSource);
}
$setCallback($targetField, $value);
} catch (\Exception $e) {
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment