Skip to content

Instantly share code, notes, and snippets.

@wodCZ
Last active February 19, 2016 07:56
Show Gist options
  • Save wodCZ/aa0828bf03cdcf74eb8f to your computer and use it in GitHub Desktop.
Save wodCZ/aa0828bf03cdcf74eb8f to your computer and use it in GitHub Desktop.
<?php
//mapping:
private $mapping = [ // as I'm using the same mapping for setting defaults and updating entity back, I've defined it as property
'estateArea',
'usableArea',
'balconyArea',
'basinArea',
'buildingArea',
'ceilingHeight',
'cellarArea',
'floorArea',
'gardenArea',
'loggiaArea',
'noliveTotalArea',
'officesArea',
'productionArea',
'shopArea',
'storageArea',
'terraceArea',
'usableGroundArea',
'workshopArea',
'floorNumber',
'floors',
'undergroundFloors',
'garrageCount',
'parkingLots',
'buildingCondition',
'buildingType',
'flatClass',
'objectType',
'roomCount',
];
// usage (on form submitted for example)
$values = $form->getValues();
FillerUtil::fill(function($field, $value){
$value = empty($value) ? null : $value;
if($field === 'ceilingHeight' && $value){
$value /= 100;
}
$this->realEstate->{$field} = $value;
}, $values, $this->mapping);
// without custom callback
FillerUtil::fill($this->realEstate, $values, $this->mapping);
<?php
//// v BaseForm:
public function fillDefaultsFromObject($entity, $array)
{
FillerUtil::fillFormDefaults($this, $entity, $array);
}
//// pouziti
$form->fillDefaultsFromObject($this->realEstate, [
'title',
'rkid',
'operation',
'type',
'subtype',
'address',
'sold',
'reserved',
'concept',
'description',
]);
///// alternativne bez baseform:
FillerUtil::fillFormDefaults($form, $this->realEstate, [
'title',
'rkid',
'operation',
'type',
'subtype',
'address',
'sold',
'reserved',
'concept',
'description',
]);
<?php
namespace Libs\Utils;
use Kdyby\Doctrine\Entities\BaseEntity;
use Nette\ComponentModel\IContainer;
use Nette\Forms\Controls\BaseControl;
use Nette\Utils\ArrayHash;
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)
{
$setter = function ($field, $value) use ($container) {
$component = $container->getComponent($field);
if($value instanceof ArrayHash){
$value = (array)$value;
}
if ($component instanceof BaseControl) {
$component->setDefaultValue($value);
}
};
self::fill($setter, $source, $map);
}
/**
* @param $targetOrSetter
* @param $sourceOrGetter
* @param $map
*/
public static function fill($targetOrSetter, $sourceOrGetter, $map)
{
if ( ! is_callable($targetOrSetter) || $targetOrSetter instanceof BaseEntity) {
$targetOrSetter = self::createAccessorSetter($targetOrSetter);
}
if ( ! is_callable($sourceOrGetter) || $targetOrSetter instanceof BaseEntity) {
$sourceOrGetter = self::createAccessorGetter($sourceOrGetter);
}
self::fillUsingCallback($map, $sourceOrGetter, $targetOrSetter);
}
/**
* @param $sourceOrGetter
* @param $map
* @return array
*/
public static function fillNewArray($sourceOrGetter, $map)
{
if ( ! is_callable($sourceOrGetter)) {
$sourceOrGetter = self::createAccessorGetter($sourceOrGetter);
}
$array = [];
self::fillUsingCallback($map, $sourceOrGetter, function ($field, $value) use (&$array) {
$array[$field] = $value;
});
return $array;
}
/**
* @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) {
if($value instanceof ArrayHash){
$value = (array)$value;
}
$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, callable $setCallback)
{
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) {
throw $e;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment