/** * Filter the WFArray according to function $fn. If calling $fn($item) * returns true, include the item in the result, otherwise exclude it. * * @param WFFunction A WFFunction that returns true to include the item and false to exclude it. * @return WFArray The filtered array. */ public function filter(WFFunction $fn) { $filtered = new WFArray(); foreach ($this as $entry) { $include = $fn->call($entry); if ($include) { $filtered->append($entry); } } return $filtered; }
/** * Propagate the value from the UI widget to the bound object. * * This method will automatically call the validator for the binding, if it exists. * If the value is valid, it will also push the value onto the bound object with setValueForKeyPath. * * Any validation errors are stored in the widget's error list. * * If the value of the widget is equivalent to an empty string ($value === '') then value is converted into PHP NULL. * Since all values in widgets come from the UI, and there is no distinction in the UI world b/w "" and NULL, we normalize all "" values to NULL. * It is left up to objects to then distinguish between NULL and "" (via normalization in Key-Value Validation). * * @param string The name of the binding to propagate back to the bound object. * @param mixed The value from the UI widget (submitted by the form). * @return mixed The cleaned-up value from validateValueForKeyPath() */ function propagateValueToBinding($bindingName, $value) { // the bindable property may not be bound! The is legitimate, so be graceful about it. $binding = $this->bindingByName($bindingName); if (is_null($binding)) { return $value; } // check OPTION_DO_NOT_PUSH_VALUE_SEMAPHORE if ($binding->hasCoalescedOption(WFBinding::OPTION_DO_NOT_PUSH_VALUE_SEMAPHORE) and $binding->coalescedOption(WFBinding::OPTION_DO_NOT_PUSH_VALUE_SEMAPHORE) === $value) { $func = WFFunction::create('return "propagateValueToBinding() skipping push for {$bindingName} since value matched OPTION_DO_NOT_PUSH_VALUE_SEMAPHORE for for widget id \'{$id}\'";')->withArguments('bindingName', 'id')->curry($bindingName, $this->id); WFLog::log($func, WFLog::TRACE_LOG); return $value; } // assert for r/o bindings. if ($binding->bindingSetup()->readOnly()) { throw new Exception("Attempt to propagateValueToBinding for a read-only binding: {$this->id} / {$bindingName}."); } if (!$binding->canWriteBoundValue()) { throw new Exception("Attempt to propagateValueToBinding for a binding with that doesn't allow writing."); } // normalize "" string values to NULL. Do this pre-validation; that function can do normalization etc. // we simply cover the case of TOTALLY EMPTY STRING is equivalent to NULL here. if ($value === '') { $value = NULL; } $edited = false; $func = WFFunction::create('return "propagateValueToBinding() validating value \'$value\' for bound object \'" . $class . "\' for widget id \'{$id}\' binding: \'{$bindingName}\'";')->withArguments('value', 'class', 'id', 'bindingName')->curry($value, get_class($this), $this->id, $bindingName); WFLog::log($func, WFLog::TRACE_LOG); $errors = array(); $valid = $binding->bindToObject()->validateValueForKeyPath($value, $binding->bindToKeyPath(), $edited, $errors); if ($valid) { $func = WFFunction::create('return "propagateValueToBinding() Pushing value \'$value\' for bound object \'" . $class . "\' for widget id \'{$id}\' binding: \'{$bindingName}\'";')->withArguments('value', 'class', 'id', 'bindingName')->curry($value, get_class($this), $this->id, $bindingName); WFLog::log($func, WFLog::TRACE_LOG); $binding->bindToObject()->setValueForKeyPath($value, $binding->bindToKeyPath()); } else { $func = WFFunction::create('return "propagateValueToBinding() WILL NOT (did not validate) push value \'$value\' for bound object \'" . $class . "\' for widget id \'{$id}\' binding: \'{$bindingName}\'";')->withArguments('value', 'class', 'id', 'bindingName')->curry($value, get_class($this), $this->id, $bindingName); WFLog::log($func, WFLog::TRACE_LOG); // keep all returned errors $this->addErrors($errors); } // return cleaned-up value so UI can update return $value; }