1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-27 00:25:05 +02:00

Upgrade Inputfield and InputfieldWrapper base classes to support new getting, setting, and traversal methods. Plus a new $inputfields->new() method that lets you create a new Inputfield and add it to the wrapper in one step.

This commit is contained in:
Ryan Cramer
2018-08-02 12:17:59 -04:00
parent c01289edb7
commit 5e389ff65a
2 changed files with 231 additions and 5 deletions

View File

@@ -45,6 +45,10 @@
* @property mixed $value HTML 'value' attribute for the Inputfield. #pw-group-attribute-properties
* @property string $class HTML 'class' attribute for the Inputfield. #pw-group-attribute-properties
*
* @method string|Inputfield name($name = null) Get or set the name attribute. @since 3.0.110 #pw-group-attribute-methods
* @method string|Inputfield id($id = null) Get or set the id attribute. @since 3.0.110 #pw-group-attribute-methods
* @method string|Inputfield class($class = null) Get class attribute or add a class to the class attribute. @since 3.0.110 #pw-group-attribute-methods
*
* LABELS & CONTENT
* ================
* @property string $label Primary label text that appears above the input. #pw-group-labels
@@ -56,6 +60,15 @@
* @property string|null $prependMarkup Optional markup to prepend to the Inputfield content container. #pw-group-other
* @property string|null $appendMarkup Optional markup to append to the Inputfield content container. #pw-group-other
*
* @method string|Inputfield label($label = null) Get or set the 'label' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield description($description = null) Get or set the 'description' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield notes($notes = null) Get or set the 'notes' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield icon($icon = null) Get or set the 'icon' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield requiredLabel($requiredLabel = null) Get or set the 'requiredLabel' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield head($head = null) Get or set the 'head' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield prependMarkup($markup = null) Get or set the 'prependMarkup' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield appendMarkup($markup = null) Get or set the 'appendMarkup' property via method. @since 3.0.110 #pw-group-labels
*
* APPEARANCE
* ==========
* @property int $collapsed Whether the field is collapsed or visible, using one of the "collapsed" constants. #pw-group-appearance
@@ -63,6 +76,12 @@
* @property int $columnWidth Width of column for this Inputfield 10-100 percent. 0 is assumed to be 100 (default). #pw-group-appearance
* @property int $skipLabel Skip display of the label? See the "skipLabel" constants for options. #pw-group-appearance
*
* @method int|Inputfield collapsed($collapsed = null) Get or set collapsed property via method. @since 3.0.110 #pw-group-appearance
* @method string|Inputfield showIf($showIf = null) Get or set showIf selector property via method. @since 3.0.110 #pw-group-appearance
* @method int|Inputfield columnWidth($columnWidth = null) Get or set columnWidth property via method. @since 3.0.110 #pw-group-appearance
* @method int|Inputfield skipLabel($skipLabel = null) Get or set the skipLabel constant property via method. @since 3.0.110 #pw-group-appearance
*
*
* SETTINGS & BEHAVIOR
* ===================
* @property int|bool $required Set to true (or 1) to make input required, or false (or 0) to make not required (default=0). #pw-group-behavior
@@ -78,6 +97,14 @@
* @property string $wrapClass Optional class name (CSS) to apply to the HTML element wrapping the Inputfield. #pw-group-other
* @property string $headerClass Optional class name (CSS) to apply to the InputfieldHeader element #pw-group-other
* @property string $contentClass Optional class name (CSS) to apply to the InputfieldContent element #pw-group-other
*
* @method string|Inputfield required($required = null) Get or set required state. @since 3.0.110 #pw-group-behavior
* @method string|Inputfield requiredIf($requiredIf = null) Get or set required-if selector. @since 3.0.110 #pw-group-behavior
*
* @method string|Inputfield wrapClass($class = null) Get wrapper class attribute or add a class to it. @since 3.0.110 #pw-group-other
* @method string|Inputfield headerClass($class = null) Get header class attribute or add a class to it. @since 3.0.110 #pw-group-other
* @method string|Inputfield contentClass($class = null) Get content class attribute or add a class to it. @since 3.0.110 #pw-group-other
*
*
* HOOKABLE METHODS
* ================
@@ -305,7 +332,7 @@ abstract class Inputfield extends WireData implements Module {
self::$numInstances++;
$this->set('label', ''); // primary clikable label
$this->set('label', ''); // primary clickable label
$this->set('description', ''); // descriptive copy, below label
$this->set('icon', ''); // optional icon name to accompany label
$this->set('notes', ''); // highlighted descriptive copy, below output of input field
@@ -321,6 +348,8 @@ abstract class Inputfield extends WireData implements Module {
$this->set('contentClass', ''); // optional class to apply to InputfieldContent wrapper
$this->set('textFormat', self::textFormatBasic); // format applied to description and notes
$this->set('renderValueFlags', 0); // see renderValue* constants, applicable to renderValue mode only
$this->set('prependMarkup', '');
$this->set('appendMarkup', '');
// default ID attribute if no 'id' attribute set
$this->defaultID = $this->className() . self::$numInstances;
@@ -463,6 +492,9 @@ abstract class Inputfield extends WireData implements Module {
*
*/
public function setParent(InputfieldWrapper $parent) {
if($this->parent && $this->parent instanceof InputfieldWrapper && $this->parent !== $parent) {
$this->parent->remove($this);
}
$this->parent = $parent;
return $this;
}
@@ -497,6 +529,47 @@ abstract class Inputfield extends WireData implements Module {
return $parents;
}
/**
* Get or set parent of Inputfield
*
* This convenience method performs the same thing as getParent() and setParent().
*
* To get parent, specify no arguments. It will return null if no parent assigned, or an
* InputfieldWrapper instance of the parent.
*
* To set parent, specify an InputfieldWrapper for the $parent argument. The return value
* is the current Inputfield for fluent interface.
*
* #pw-group-traversal
*
* @param null|InputfieldWrapper $parent
* @return null|Inputfield|InputfieldWrapper
* @since 3.0.110
*
*/
public function parent($parent = null) {
if($parent === null) {
return $this->getParent();
} else {
return $this->setParent($parent);
}
}
/**
* Get array of all parents of this Inputfield
*
* This is identical to and an alias of the getParents() method.
*
* #pw-group-traversal
*
* @return array
* @since 3.0.110
*
*/
public function parents() {
return $this->getParents();
}
/**
* Get the root parent InputfieldWrapper element (farthest parent, commonly InputfieldForm)
*
@@ -715,6 +788,39 @@ abstract class Inputfield extends WireData implements Module {
return $this->setAttribute('value', $value);
}
/**
* If method call resulted in no handler, this hookable method is called.
*
* We use this to allow for attributes and properties to be set via method, useful primarily
* for fluent interface calls.
*
* #pw-internal
*
* @param string $method Requested method name
* @param array $arguments Arguments provided
* @return null|mixed Return value of method (if applicable)
* @throws WireException
* @since 3.0.110
*
*/
protected function ___callUnknown($method, $arguments) {
$arg = isset($arguments[0]) ? $arguments[0] : null;
if(isset($this->attributes[$method])) {
// get or set an attribute
return $arg === null ? $this->getAttribute($method) : $this->setAttribute($method, $arg);
} else if(($value = $this->getSetting($method)) !== null) {
// get or set a setting
if($arg === null) return $value;
if(stripos($method, 'class') !== false) {
// i.e. class, wrapClass, contentClass, etc.
return $this->addClass($arg, $method);
} else {
return $this->set($method, $arg);
}
}
return parent::___callUnknown($method, $arguments);
}
/**
* Get all attributes specified for this Inputfield
*

View File

@@ -24,6 +24,7 @@
* @property InputfieldsArray|null $children Inputfield instances that are direct children of this InputfieldWrapper. #pw-group-properties
*
* @method string renderInputfield(Inputfield $inputfield, $renderValueMode = false) #pw-group-output
* @method Inputfield new($typeName, $name = '', $label = '', array $settings = array()) #pw-group-manipulation
*
*/
@@ -185,23 +186,87 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
/**
* Add an Inputfield item as a child (also accepts array definition)
*
* Since 3.0.110: If given a string value, it is assumed to be an Inputfield type that you
* want to add. In that case, it will create the Inputfield and return it instead of $this.
*
* #pw-group-manipulation
*
* @param Inputfield|array $item
* @return $this
* @param Inputfield|array|string $item
* @return Inputfield|InputfieldWrapper|$this
* @see InputfieldWrapper::import()
*
*/
public function add($item) {
if(is_array($item)) {
if(is_string($item)) {
return $this->___new($item);
} else if(is_array($item)) {
$this->importArray($item);
} else {
$item->setParent($this);
$this->children->add($item);
$item->setParent($this);
}
return $this;
}
/**
* Create a new Inputfield, add it to this InputfieldWrapper, and return the new Inputfield
*
* - Only the $typeName argument is required.
* - You may optionally substitute the $settings argument for the $name or $label arguments.
* - You may optionally substitute Inputfield “description” property for $settings argument.
*
* #pw-group-manipulation
*
* @param string $typeName Inputfield type, i.e. “InputfieldCheckbox” or just “checkbox” for short.
* @param string|array $name Name of input (or substitute $settings here).
* @param string|array $label Label for input (or substitute $settings here).
* @param array|string $settings Settings to add to Inputfield (optional). Or if string, assumed to be “description”.
* @return Inputfield|InputfieldSelect|InputfieldWrapper An Inputfield instance ready to populate with additional properties/attributes.
* @throws WireException If you request an unknown Inputfield type
* @since 3.0.110
*
*/
public function ___new($typeName, $name = '', $label = '', $settings = array()) {
if(is_array($name)) {
$settings = $name;
$name = '';
} else if(is_array($label)) {
$settings = $label;
$label = '';
}
if(strpos($typeName, 'Inputfield') !== 0) {
$typeName = "Inputfield" . ucfirst($typeName);
}
/** @var Inputfield|InputfieldSelect|InputfieldWrapper $inputfield */
$inputfield = $this->wire('modules')->getModule($typeName);
if(!$inputfield && wireClassExists($typeName)) {
$inputfield = $this->wire(new $typeName());
}
if(!$inputfield || !$inputfield instanceof Inputfield) {
throw new WireException("Unknown Inputfield type: $typeName");
}
if(strlen($name)) $inputfield->attr('name', $name);
if(strlen($label)) $inputfield->label = $label;
if(is_array($settings)) {
foreach($settings as $key => $value) {
$inputfield->set($key, $value);
}
} else if(is_string($settings)) {
$inputfield->description = $settings;
}
$this->add($inputfield);
return $inputfield;
}
/**
* Import the given Inputfield items as children
*
@@ -941,6 +1006,61 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
}
}
/**
* Find an Inputfield below this one that has the given name
*
* This is an alternative to the `getChildByName()` method, with more options for when you need it.
* For instance, it can also accept a selector string or numeric index for the $name argument, and you
* can optionally disable the $recursive behavior.
*
* #pw-group-retrieval-and-traversal
*
* @param string|int $name Name or selector string of child to find, omit for first child, or specify zero-based index of child to return.
* @param bool $recursive Find child recursively? Looks for child in this wrapper, and all other wrappers below it. (default=true)
* @return Inputfield|null Returns Inputfield instance if found, or null if not.
* @since 3.0.110
*
*/
public function child($name = '', $recursive = true) {
$child = null;
if(!$this->children->count()) {
// no child possible
} else if(empty($name)) {
// first child
$child = $this->children->first();
} else if(is_int($name)) {
// number index
$child = $this->children->eq($name);
} else if($this->wire('sanitizer')->name($name) === $name) {
// child by name
$wrappers = array();
foreach($this->children as $f) {
if($f->getAttribute('name') === $name) {
$child = $f;
break;
} else if($recursive && $f instanceof InputfieldWrapper) {
$wrappers[] = $f;
}
}
if(!$child && $recursive && count($wrappers)) {
foreach($wrappers as $wrapper) {
$child = $wrapper->child($name, $recursive);
if($child) break;
}
}
} else if(Selectors::stringHasSelector($name)) {
// first child matching selector string
$child = $this->children("$name, limit=1")->first();
}
return $child;
}
/**
* Return all children Inputfields (alias of children method)
*