407 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			407 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?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\Macroable;
 | |
| use IteratorAggregate;
 | |
| 
 | |
| class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
 | |
| {
 | |
|     use 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  string  $key
 | |
|      * @return bool
 | |
|      */
 | |
|     public function has($key)
 | |
|     {
 | |
|         return array_key_exists($key, $this->attributes);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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
 | |
|      * @return static
 | |
|      */
 | |
|     public function whereStartsWith($string)
 | |
|     {
 | |
|         return $this->filter(function ($value, $key) use ($string) {
 | |
|             return Str::startsWith($key, $string);
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return a bag of attributes with keys that do not start with the given value / pattern.
 | |
|      *
 | |
|      * @param  string  $string
 | |
|      * @return static
 | |
|      */
 | |
|     public function whereDoesntStartWith($string)
 | |
|     {
 | |
|         return $this->filter(function ($value, $key) use ($string) {
 | |
|             return ! Str::startsWith($key, $string);
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return a bag of attributes that have keys starting with the given value / pattern.
 | |
|      *
 | |
|      * @param  string  $string
 | |
|      * @return static
 | |
|      */
 | |
|     public function thatStartWith($string)
 | |
|     {
 | |
|         return $this->whereStartsWith($string);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Exclude the given attribute from the attribute array.
 | |
|      *
 | |
|      * @param  mixed|array  $keys
 | |
|      * @return static
 | |
|      */
 | |
|     public function exceptProps($keys)
 | |
|     {
 | |
|         $props = [];
 | |
| 
 | |
|         foreach ($keys as $key => $defaultValue) {
 | |
|             $key = is_numeric($key) ? $defaultValue : $key;
 | |
| 
 | |
|             $props[] = $key;
 | |
|             $props[] = Str::kebab($key);
 | |
|         }
 | |
| 
 | |
|         return $this->except($props);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Conditionally merge classes into the attribute bag.
 | |
|      *
 | |
|      * @param  mixed|array  $classList
 | |
|      * @return static
 | |
|      */
 | |
|     public function class($classList)
 | |
|     {
 | |
|         $classList = Arr::wrap($classList);
 | |
| 
 | |
|         $classes = [];
 | |
| 
 | |
|         foreach ($classList as $class => $constraint) {
 | |
|             if (is_numeric($class)) {
 | |
|                 $classes[] = $constraint;
 | |
|             } elseif ($constraint) {
 | |
|                 $classes[] = $class;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $this->merge(['class' => implode(' ', $classes)]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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' ||
 | |
|                                (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] ?? '');
 | |
| 
 | |
|             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;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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)
 | |
|     {
 | |
|         return isset($this->attributes[$offset]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the value at the given offset.
 | |
|      *
 | |
|      * @param  string  $offset
 | |
|      * @return mixed
 | |
|      */
 | |
|     public function offsetGet($offset)
 | |
|     {
 | |
|         return $this->get($offset);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Set the value at a given offset.
 | |
|      *
 | |
|      * @param  string  $offset
 | |
|      * @param  mixed  $value
 | |
|      * @return void
 | |
|      */
 | |
|     public function offsetSet($offset, $value)
 | |
|     {
 | |
|         $this->attributes[$offset] = $value;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Remove the value at the given offset.
 | |
|      *
 | |
|      * @param  string  $offset
 | |
|      * @return void
 | |
|      */
 | |
|     public function offsetUnset($offset)
 | |
|     {
 | |
|         unset($this->attributes[$offset]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get an iterator for the items.
 | |
|      *
 | |
|      * @return \ArrayIterator
 | |
|      */
 | |
|     public function getIterator()
 | |
|     {
 | |
|         return new ArrayIterator($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) {
 | |
|                 $value = $key;
 | |
|             }
 | |
| 
 | |
|             $string .= ' '.$key.'="'.str_replace('"', '\\"', trim($value)).'"';
 | |
|         }
 | |
| 
 | |
|         return trim($string);
 | |
|     }
 | |
| }
 |