ComponentAttributeBag.php
TLDR
This file, ComponentAttributeBag.php
, is a class file that defines a class called ComponentAttributeBag
. This class is used to manage and manipulate component attributes. It provides methods for getting, setting, filtering, merging, and manipulating attributes. The class implements several interfaces, including ArrayAccess
and JsonSerializable
, and uses traits like Conditionable
and Macroable
to add additional functionality.
Methods
__construct
The __construct
method is the constructor of the ComponentAttributeBag
class. It initializes the class by accepting an optional array of attributes and setting them as the $attributes
property.
first
The first
method returns the value of the first attribute in the bag. It accepts an optional default value, which is returned if there are no attributes in the bag.
get
The get
method returns the value of a given attribute from the attribute array. It accepts a key and an optional default value, which is returned if the attribute does not exist in the bag.
has
The has
method determines if a given attribute exists in the attribute array. It accepts a key or an array of keys and returns true
if all the keys exist in the bag.
hasAny
The hasAny
method determines if any of the given keys exist in the attribute array. It accepts a key or an array of keys and returns true
if any of the keys exist in the bag.
missing
The missing
method determines if a given attribute is missing from the attribute array. It accepts a key and returns true
if the attribute does not exist in the bag.
only
The only
method creates a new ComponentAttributeBag
instance that includes only the given attribute keys from the attribute array. It accepts a key or an array of keys and returns a new instance of ComponentAttributeBag
.
except
The except
method creates a new ComponentAttributeBag
instance that excludes the given attribute keys from the attribute array. It accepts a key or an array of keys and returns a new instance of ComponentAttributeBag
.
filter
The filter
method creates a new ComponentAttributeBag
instance that includes only the attributes that pass a given callback filter. It accepts a callable callback and returns a new instance of ComponentAttributeBag
.
whereStartsWith
The whereStartsWith
method creates a new ComponentAttributeBag
instance that includes only the attributes with keys that start with the given value or pattern. It accepts a string or an array of strings and returns a new instance of ComponentAttributeBag
.
whereDoesntStartWith
The whereDoesntStartWith
method creates a new ComponentAttributeBag
instance that excludes the attributes with keys that start with the given value or pattern. It accepts a string or an array of strings and returns a new instance of ComponentAttributeBag
.
thatStartWith
The thatStartWith
method is an alias for the whereStartsWith
method.
onlyProps
The onlyProps
method creates a new ComponentAttributeBag
instance that includes only the attributes with keys that match the given prop names. It accepts a key or an array of keys and returns a new instance of ComponentAttributeBag
.
exceptProps
The exceptProps
method creates a new ComponentAttributeBag
instance that excludes the attributes with keys that match the given prop names. It accepts a key or an array of keys and returns a new instance of ComponentAttributeBag
.
class
The class
method applies conditional merging of classes into the attribute bag. It accepts a class list and returns a new instance of ComponentAttributeBag
with the merged classes.
style
The style
method applies conditional merging of styles into the attribute bag. It accepts a style list and returns a new instance of ComponentAttributeBag
with the merged styles.
merge
The merge
method merges additional attributes and values into the attribute bag. It accepts an array of attribute defaults and an optional escape flag. It returns a new instance of ComponentAttributeBag
with the merged attributes.
shouldEscapeAttributeValue
The shouldEscapeAttributeValue
method determines if a specific attribute value should be escaped. It accepts a boolean escape flag and a value and returns true
if the value should be escaped.
prepends
The prepends
method creates a new appendable attribute value. It accepts a value and returns a new instance of AppendableAttributeValue
.
resolveAppendableAttributeDefault
The resolveAppendableAttributeDefault
method resolves an appendable attribute value's default value. It accepts an array of attribute defaults, a key, and an escape flag. It returns the resolved value.
isEmpty
The isEmpty
method determines if the attribute bag is empty. It returns true
if the attribute bag contains no attributes.
isNotEmpty
The isNotEmpty
method determines if the attribute bag is not empty. It returns true
if the attribute bag contains attributes.
getAttributes
The getAttributes
method returns all of the raw attributes as an array.
setAttributes
The setAttributes
method sets the underlying attributes. It accepts an array of attributes and handles the merging of attribute bags if one of the attributes is an instance of ComponentAttributeBag
.
toHtml
The toHtml
method converts the attribute bag into a string of HTML. It returns the string representation of the attribute bag.
__invoke
The __invoke
method is an alternative way to merge additional attributes/values into the attribute bag. It accepts an array of attribute defaults and returns a new instance of HtmlString
.
offsetExists
The offsetExists
method determines if the given offset exists. It accepts a string offset and returns true
if the offset exists in the attribute array.
offsetGet
The offsetGet
method gets the value at the given offset. It accepts a string offset and returns the value of the attribute.
offsetSet
The offsetSet
method set the value at a given offset. It accepts a string offset and a value and sets the value in the attribute array.
offsetUnset
The offsetUnset
method removes the value at the given offset. It accepts a string offset and removes the value from the attribute array.
getIterator
The getIterator
method returns an iterator for the attribute array. It returns an ArrayIterator
instance.
jsonSerialize
The jsonSerialize
method converts the object into a JSON serializable form. It returns the attributes as they are.
__toString
The __toString
method converts the attributes into a single HTML-ready string. It overrides the default __toString
method and returns the string representation of the attributes.
END
<?php
namespace Illuminate\View;
use ArrayAccess;
use ArrayIterator;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Support\Arr;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Conditionable;
use Illuminate\Support\Traits\Macroable;
use IteratorAggregate;
use JsonSerializable;
use Stringable;
use Traversable;
class ComponentAttributeBag implements ArrayAccess, Htmlable, JsonSerializable, IteratorAggregate, Stringable
{
use Conditionable, Macroable;
/**
* The raw array of attributes.
*
* @var array
*/
protected $attributes = [];
/**
* Create a new component attribute bag instance.
*
* @param array $attributes
* @return void
*/
public function __construct(array $attributes = [])
{
$this->attributes = $attributes;
}
/**
* Get the first attribute's value.
*
* @param mixed $default
* @return mixed
*/
public function first($default = null)
{
return $this->getIterator()->current() ?? value($default);
}
/**
* Get a given attribute from the attribute array.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null)
{
return $this->attributes[$key] ?? value($default);
}
/**
* Determine if a given attribute exists in the attribute array.
*
* @param array|string $key
* @return bool
*/
public function has($key)
{
$keys = is_array($key) ? $key : func_get_args();
foreach ($keys as $value) {
if (! array_key_exists($value, $this->attributes)) {
return false;
}
}
return true;
}
/**
* Determine if any of the keys exist in the attribute array.
*
* @param array|string $key
* @return bool
*/
public function hasAny($key)
{
if (! count($this->attributes)) {
return false;
}
$keys = is_array($key) ? $key : func_get_args();
foreach ($keys as $value) {
if ($this->has($value)) {
return true;
}
}
return false;
}
/**
* Determine if a given attribute is missing from the attribute array.
*
* @param string $key
* @return bool
*/
public function missing($key)
{
return ! $this->has($key);
}
/**
* Only include the given attribute from the attribute array.
*
* @param mixed $keys
* @return static
*/
public function only($keys)
{
if (is_null($keys)) {
$values = $this->attributes;
} else {
$keys = Arr::wrap($keys);
$values = Arr::only($this->attributes, $keys);
}
return new static($values);
}
/**
* Exclude the given attribute from the attribute array.
*
* @param mixed|array $keys
* @return static
*/
public function except($keys)
{
if (is_null($keys)) {
$values = $this->attributes;
} else {
$keys = Arr::wrap($keys);
$values = Arr::except($this->attributes, $keys);
}
return new static($values);
}
/**
* Filter the attributes, returning a bag of attributes that pass the filter.
*
* @param callable $callback
* @return static
*/
public function filter($callback)
{
return new static(collect($this->attributes)->filter($callback)->all());
}
/**
* Return a bag of attributes that have keys starting with the given value / pattern.
*
* @param string|string[] $needles
* @return static
*/
public function whereStartsWith($needles)
{
return $this->filter(function ($value, $key) use ($needles) {
return Str::startsWith($key, $needles);
});
}
/**
* Return a bag of attributes with keys that do not start with the given value / pattern.
*
* @param string|string[] $needles
* @return static
*/
public function whereDoesntStartWith($needles)
{
return $this->filter(function ($value, $key) use ($needles) {
return ! Str::startsWith($key, $needles);
});
}
/**
* Return a bag of attributes that have keys starting with the given value / pattern.
*
* @param string|string[] $needles
* @return static
*/
public function thatStartWith($needles)
{
return $this->whereStartsWith($needles);
}
/**
* Only include the given attribute from the attribute array.
*
* @param mixed|array $keys
* @return static
*/
public function onlyProps($keys)
{
return $this->only($this->extractPropNames($keys));
}
/**
* Exclude the given attribute from the attribute array.
*
* @param mixed|array $keys
* @return static
*/
public function exceptProps($keys)
{
return $this->except($this->extractPropNames($keys));
}
/**
* Extract prop names from given keys.
*
* @param mixed|array $keys
* @return array
*/
protected function extractPropNames($keys)
{
$props = [];
foreach ($keys as $key => $defaultValue) {
$key = is_numeric($key) ? $defaultValue : $key;
$props[] = $key;
$props[] = Str::kebab($key);
}
return $props;
}
/**
* Conditionally merge classes into the attribute bag.
*
* @param mixed|array $classList
* @return static
*/
public function class($classList)
{
$classList = Arr::wrap($classList);
return $this->merge(['class' => Arr::toCssClasses($classList)]);
}
/**
* Conditionally merge styles into the attribute bag.
*
* @param mixed|array $styleList
* @return static
*/
public function style($styleList)
{
$styleList = Arr::wrap($styleList);
return $this->merge(['style' => Arr::toCssStyles($styleList)]);
}
/**
* Merge additional attributes / values into the attribute bag.
*
* @param array $attributeDefaults
* @param bool $escape
* @return static
*/
public function merge(array $attributeDefaults = [], $escape = true)
{
$attributeDefaults = array_map(function ($value) use ($escape) {
return $this->shouldEscapeAttributeValue($escape, $value)
? e($value)
: $value;
}, $attributeDefaults);
[$appendableAttributes, $nonAppendableAttributes] = collect($this->attributes)
->partition(function ($value, $key) use ($attributeDefaults) {
return $key === 'class' || $key === 'style' || (
isset($attributeDefaults[$key]) &&
$attributeDefaults[$key] instanceof AppendableAttributeValue
);
});
$attributes = $appendableAttributes->mapWithKeys(function ($value, $key) use ($attributeDefaults, $escape) {
$defaultsValue = isset($attributeDefaults[$key]) && $attributeDefaults[$key] instanceof AppendableAttributeValue
? $this->resolveAppendableAttributeDefault($attributeDefaults, $key, $escape)
: ($attributeDefaults[$key] ?? '');
if ($key === 'style') {
$value = Str::finish($value, ';');
}
return [$key => implode(' ', array_unique(array_filter([$defaultsValue, $value])))];
})->merge($nonAppendableAttributes)->all();
return new static(array_merge($attributeDefaults, $attributes));
}
/**
* Determine if the specific attribute value should be escaped.
*
* @param bool $escape
* @param mixed $value
* @return bool
*/
protected function shouldEscapeAttributeValue($escape, $value)
{
if (! $escape) {
return false;
}
return ! is_object($value) &&
! is_null($value) &&
! is_bool($value);
}
/**
* Create a new appendable attribute value.
*
* @param mixed $value
* @return \Illuminate\View\AppendableAttributeValue
*/
public function prepends($value)
{
return new AppendableAttributeValue($value);
}
/**
* Resolve an appendable attribute value default value.
*
* @param array $attributeDefaults
* @param string $key
* @param bool $escape
* @return mixed
*/
protected function resolveAppendableAttributeDefault($attributeDefaults, $key, $escape)
{
if ($this->shouldEscapeAttributeValue($escape, $value = $attributeDefaults[$key]->value)) {
$value = e($value);
}
return $value;
}
/**
* Determine if the attribute bag is empty.
*
* @return bool
*/
public function isEmpty()
{
return trim((string) $this) === '';
}
/**
* Determine if the attribute bag is not empty.
*
* @return bool
*/
public function isNotEmpty()
{
return ! $this->isEmpty();
}
/**
* Get all of the raw attributes.
*
* @return array
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* Set the underlying attributes.
*
* @param array $attributes
* @return void
*/
public function setAttributes(array $attributes)
{
if (isset($attributes['attributes']) &&
$attributes['attributes'] instanceof self) {
$parentBag = $attributes['attributes'];
unset($attributes['attributes']);
$attributes = $parentBag->merge($attributes, $escape = false)->getAttributes();
}
$this->attributes = $attributes;
}
/**
* Get content as a string of HTML.
*
* @return string
*/
public function toHtml()
{
return (string) $this;
}
/**
* Merge additional attributes / values into the attribute bag.
*
* @param array $attributeDefaults
* @return \Illuminate\Support\HtmlString
*/
public function __invoke(array $attributeDefaults = [])
{
return new HtmlString((string) $this->merge($attributeDefaults));
}
/**
* Determine if the given offset exists.
*
* @param string $offset
* @return bool
*/
public function offsetExists($offset): bool
{
return isset($this->attributes[$offset]);
}
/**
* Get the value at the given offset.
*
* @param string $offset
* @return mixed
*/
public function offsetGet($offset): mixed
{
return $this->get($offset);
}
/**
* Set the value at a given offset.
*
* @param string $offset
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value): void
{
$this->attributes[$offset] = $value;
}
/**
* Remove the value at the given offset.
*
* @param string $offset
* @return void
*/
public function offsetUnset($offset): void
{
unset($this->attributes[$offset]);
}
/**
* Get an iterator for the items.
*
* @return \ArrayIterator
*/
public function getIterator(): Traversable
{
return new ArrayIterator($this->attributes);
}
/**
* Convert the object into a JSON serializable form.
*
* @return mixed
*/
public function jsonSerialize(): mixed
{
return $this->attributes;
}
/**
* Implode the attributes into a single HTML ready string.
*
* @return string
*/
public function __toString()
{
$string = '';
foreach ($this->attributes as $key => $value) {
if ($value === false || is_null($value)) {
continue;
}
if ($value === true) {
// Exception for Alpine...
$value = $key === 'x-data' ? '' : $key;
}
$string .= ' '.$key.'="'.str_replace('"', '\\"', trim($value)).'"';
}
return trim($string);
}
}