| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141 |
- <?php
- namespace Illuminate\Database\Eloquent\Concerns;
- use LogicException;
- use DateTimeInterface;
- use Illuminate\Support\Arr;
- use Illuminate\Support\Str;
- use Illuminate\Support\Carbon;
- use Illuminate\Contracts\Support\Arrayable;
- use Illuminate\Database\Eloquent\Relations\Relation;
- use Illuminate\Support\Collection as BaseCollection;
- use Illuminate\Database\Eloquent\JsonEncodingException;
- trait HasAttributes
- {
- /**
- * The model's attributes.
- *
- * @var array
- */
- protected $attributes = [];
- /**
- * The model attribute's original state.
- *
- * @var array
- */
- protected $original = [];
- /**
- * The changed model attributes.
- *
- * @var array
- */
- protected $changes = [];
- /**
- * The attributes that should be cast to native types.
- *
- * @var array
- */
- protected $casts = [];
- /**
- * The attributes that should be mutated to dates.
- *
- * @var array
- */
- protected $dates = [];
- /**
- * The storage format of the model's date columns.
- *
- * @var string
- */
- protected $dateFormat;
- /**
- * The accessors to append to the model's array form.
- *
- * @var array
- */
- protected $appends = [];
- /**
- * Indicates whether attributes are snake cased on arrays.
- *
- * @var bool
- */
- public static $snakeAttributes = true;
- /**
- * The cache of the mutated attributes for each class.
- *
- * @var array
- */
- protected static $mutatorCache = [];
- /**
- * Convert the model's attributes to an array.
- *
- * @return array
- */
- public function attributesToArray()
- {
- // If an attribute is a date, we will cast it to a string after converting it
- // to a DateTime / Carbon instance. This is so we will get some consistent
- // formatting while accessing attributes vs. arraying / JSONing a model.
- $attributes = $this->addDateAttributesToArray(
- $attributes = $this->getArrayableAttributes()
- );
- $attributes = $this->addMutatedAttributesToArray(
- $attributes, $mutatedAttributes = $this->getMutatedAttributes()
- );
- // Next we will handle any casts that have been setup for this model and cast
- // the values to their appropriate type. If the attribute has a mutator we
- // will not perform the cast on those attributes to avoid any confusion.
- $attributes = $this->addCastAttributesToArray(
- $attributes, $mutatedAttributes
- );
- // Here we will grab all of the appended, calculated attributes to this model
- // as these attributes are not really in the attributes array, but are run
- // when we need to array or JSON the model for convenience to the coder.
- foreach ($this->getArrayableAppends() as $key) {
- $attributes[$key] = $this->mutateAttributeForArray($key, null);
- }
- return $attributes;
- }
- /**
- * Add the date attributes to the attributes array.
- *
- * @param array $attributes
- * @return array
- */
- protected function addDateAttributesToArray(array $attributes)
- {
- foreach ($this->getDates() as $key) {
- if (! isset($attributes[$key])) {
- continue;
- }
- $attributes[$key] = $this->serializeDate(
- $this->asDateTime($attributes[$key])
- );
- }
- return $attributes;
- }
- /**
- * Add the mutated attributes to the attributes array.
- *
- * @param array $attributes
- * @param array $mutatedAttributes
- * @return array
- */
- protected function addMutatedAttributesToArray(array $attributes, array $mutatedAttributes)
- {
- foreach ($mutatedAttributes as $key) {
- // We want to spin through all the mutated attributes for this model and call
- // the mutator for the attribute. We cache off every mutated attributes so
- // we don't have to constantly check on attributes that actually change.
- if (! array_key_exists($key, $attributes)) {
- continue;
- }
- // Next, we will call the mutator for this attribute so that we can get these
- // mutated attribute's actual values. After we finish mutating each of the
- // attributes we will return this final array of the mutated attributes.
- $attributes[$key] = $this->mutateAttributeForArray(
- $key, $attributes[$key]
- );
- }
- return $attributes;
- }
- /**
- * Add the casted attributes to the attributes array.
- *
- * @param array $attributes
- * @param array $mutatedAttributes
- * @return array
- */
- protected function addCastAttributesToArray(array $attributes, array $mutatedAttributes)
- {
- foreach ($this->getCasts() as $key => $value) {
- if (! array_key_exists($key, $attributes) || in_array($key, $mutatedAttributes)) {
- continue;
- }
- // Here we will cast the attribute. Then, if the cast is a date or datetime cast
- // then we will serialize the date for the array. This will convert the dates
- // to strings based on the date format specified for these Eloquent models.
- $attributes[$key] = $this->castAttribute(
- $key, $attributes[$key]
- );
- // If the attribute cast was a date or a datetime, we will serialize the date as
- // a string. This allows the developers to customize how dates are serialized
- // into an array without affecting how they are persisted into the storage.
- if ($attributes[$key] &&
- ($value === 'date' || $value === 'datetime')) {
- $attributes[$key] = $this->serializeDate($attributes[$key]);
- }
- }
- return $attributes;
- }
- /**
- * Get an attribute array of all arrayable attributes.
- *
- * @return array
- */
- protected function getArrayableAttributes()
- {
- return $this->getArrayableItems($this->attributes);
- }
- /**
- * Get all of the appendable values that are arrayable.
- *
- * @return array
- */
- protected function getArrayableAppends()
- {
- if (! count($this->appends)) {
- return [];
- }
- return $this->getArrayableItems(
- array_combine($this->appends, $this->appends)
- );
- }
- /**
- * Get the model's relationships in array form.
- *
- * @return array
- */
- public function relationsToArray()
- {
- $attributes = [];
- foreach ($this->getArrayableRelations() as $key => $value) {
- // If the values implements the Arrayable interface we can just call this
- // toArray method on the instances which will convert both models and
- // collections to their proper array form and we'll set the values.
- if ($value instanceof Arrayable) {
- $relation = $value->toArray();
- }
- // If the value is null, we'll still go ahead and set it in this list of
- // attributes since null is used to represent empty relationships if
- // if it a has one or belongs to type relationships on the models.
- elseif (is_null($value)) {
- $relation = $value;
- }
- // If the relationships snake-casing is enabled, we will snake case this
- // key so that the relation attribute is snake cased in this returned
- // array to the developers, making this consistent with attributes.
- if (static::$snakeAttributes) {
- $key = Str::snake($key);
- }
- // If the relation value has been set, we will set it on this attributes
- // list for returning. If it was not arrayable or null, we'll not set
- // the value on the array because it is some type of invalid value.
- if (isset($relation) || is_null($value)) {
- $attributes[$key] = $relation;
- }
- unset($relation);
- }
- return $attributes;
- }
- /**
- * Get an attribute array of all arrayable relations.
- *
- * @return array
- */
- protected function getArrayableRelations()
- {
- return $this->getArrayableItems($this->relations);
- }
- /**
- * Get an attribute array of all arrayable values.
- *
- * @param array $values
- * @return array
- */
- protected function getArrayableItems(array $values)
- {
- if (count($this->getVisible()) > 0) {
- $values = array_intersect_key($values, array_flip($this->getVisible()));
- }
- if (count($this->getHidden()) > 0) {
- $values = array_diff_key($values, array_flip($this->getHidden()));
- }
- return $values;
- }
- /**
- * Get an attribute from the model.
- *
- * @param string $key
- * @return mixed
- */
- public function getAttribute($key)
- {
- if (! $key) {
- return;
- }
- // If the attribute exists in the attribute array or has a "get" mutator we will
- // get the attribute's value. Otherwise, we will proceed as if the developers
- // are asking for a relationship's value. This covers both types of values.
- if (array_key_exists($key, $this->attributes) ||
- $this->hasGetMutator($key)) {
- return $this->getAttributeValue($key);
- }
- // Here we will determine if the model base class itself contains this given key
- // since we don't want to treat any of those methods as relationships because
- // they are all intended as helper methods and none of these are relations.
- if (method_exists(self::class, $key)) {
- return;
- }
- return $this->getRelationValue($key);
- }
- /**
- * Get a plain attribute (not a relationship).
- *
- * @param string $key
- * @return mixed
- */
- public function getAttributeValue($key)
- {
- $value = $this->getAttributeFromArray($key);
- // If the attribute has a get mutator, we will call that then return what
- // it returns as the value, which is useful for transforming values on
- // retrieval from the model to a form that is more useful for usage.
- if ($this->hasGetMutator($key)) {
- return $this->mutateAttribute($key, $value);
- }
- // If the attribute exists within the cast array, we will convert it to
- // an appropriate native PHP type dependant upon the associated value
- // given with the key in the pair. Dayle made this comment line up.
- if ($this->hasCast($key)) {
- return $this->castAttribute($key, $value);
- }
- // If the attribute is listed as a date, we will convert it to a DateTime
- // instance on retrieval, which makes it quite convenient to work with
- // date fields without having to create a mutator for each property.
- if (in_array($key, $this->getDates()) &&
- ! is_null($value)) {
- return $this->asDateTime($value);
- }
- return $value;
- }
- /**
- * Get an attribute from the $attributes array.
- *
- * @param string $key
- * @return mixed
- */
- protected function getAttributeFromArray($key)
- {
- if (isset($this->attributes[$key])) {
- return $this->attributes[$key];
- }
- }
- /**
- * Get a relationship.
- *
- * @param string $key
- * @return mixed
- */
- public function getRelationValue($key)
- {
- // If the key already exists in the relationships array, it just means the
- // relationship has already been loaded, so we'll just return it out of
- // here because there is no need to query within the relations twice.
- if ($this->relationLoaded($key)) {
- return $this->relations[$key];
- }
- // If the "attribute" exists as a method on the model, we will just assume
- // it is a relationship and will load and return results from the query
- // and hydrate the relationship's value on the "relationships" array.
- if (method_exists($this, $key)) {
- return $this->getRelationshipFromMethod($key);
- }
- }
- /**
- * Get a relationship value from a method.
- *
- * @param string $method
- * @return mixed
- *
- * @throws \LogicException
- */
- protected function getRelationshipFromMethod($method)
- {
- $relation = $this->$method();
- if (! $relation instanceof Relation) {
- throw new LogicException(get_class($this).'::'.$method.' must return a relationship instance.');
- }
- return tap($relation->getResults(), function ($results) use ($method) {
- $this->setRelation($method, $results);
- });
- }
- /**
- * Determine if a get mutator exists for an attribute.
- *
- * @param string $key
- * @return bool
- */
- public function hasGetMutator($key)
- {
- return method_exists($this, 'get'.Str::studly($key).'Attribute');
- }
- /**
- * Get the value of an attribute using its mutator.
- *
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- protected function mutateAttribute($key, $value)
- {
- return $this->{'get'.Str::studly($key).'Attribute'}($value);
- }
- /**
- * Get the value of an attribute using its mutator for array conversion.
- *
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- protected function mutateAttributeForArray($key, $value)
- {
- $value = $this->mutateAttribute($key, $value);
- return $value instanceof Arrayable ? $value->toArray() : $value;
- }
- /**
- * Cast an attribute to a native PHP type.
- *
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- protected function castAttribute($key, $value)
- {
- if (is_null($value)) {
- return $value;
- }
- switch ($this->getCastType($key)) {
- case 'int':
- case 'integer':
- return (int) $value;
- case 'real':
- case 'float':
- case 'double':
- return (float) $value;
- case 'string':
- return (string) $value;
- case 'bool':
- case 'boolean':
- return (bool) $value;
- case 'object':
- return $this->fromJson($value, true);
- case 'array':
- case 'json':
- return $this->fromJson($value);
- case 'collection':
- return new BaseCollection($this->fromJson($value));
- case 'date':
- return $this->asDate($value);
- case 'datetime':
- return $this->asDateTime($value);
- case 'timestamp':
- return $this->asTimestamp($value);
- default:
- return $value;
- }
- }
- /**
- * Get the type of cast for a model attribute.
- *
- * @param string $key
- * @return string
- */
- protected function getCastType($key)
- {
- return trim(strtolower($this->getCasts()[$key]));
- }
- /**
- * Set a given attribute on the model.
- *
- * @param string $key
- * @param mixed $value
- * @return $this
- */
- public function setAttribute($key, $value)
- {
- // First we will check for the presence of a mutator for the set operation
- // which simply lets the developers tweak the attribute as it is set on
- // the model, such as "json_encoding" an listing of data for storage.
- if ($this->hasSetMutator($key)) {
- $method = 'set'.Str::studly($key).'Attribute';
- return $this->{$method}($value);
- }
- // If an attribute is listed as a "date", we'll convert it from a DateTime
- // instance into a form proper for storage on the database tables using
- // the connection grammar's date format. We will auto set the values.
- elseif ($value && $this->isDateAttribute($key)) {
- $value = $this->fromDateTime($value);
- }
- if ($this->isJsonCastable($key) && ! is_null($value)) {
- $value = $this->castAttributeAsJson($key, $value);
- }
- // If this attribute contains a JSON ->, we'll set the proper value in the
- // attribute's underlying array. This takes care of properly nesting an
- // attribute in the array's value in the case of deeply nested items.
- if (Str::contains($key, '->')) {
- return $this->fillJsonAttribute($key, $value);
- }
- $this->attributes[$key] = $value;
- return $this;
- }
- /**
- * Determine if a set mutator exists for an attribute.
- *
- * @param string $key
- * @return bool
- */
- public function hasSetMutator($key)
- {
- return method_exists($this, 'set'.Str::studly($key).'Attribute');
- }
- /**
- * Determine if the given attribute is a date or date castable.
- *
- * @param string $key
- * @return bool
- */
- protected function isDateAttribute($key)
- {
- return in_array($key, $this->getDates()) ||
- $this->isDateCastable($key);
- }
- /**
- * Set a given JSON attribute on the model.
- *
- * @param string $key
- * @param mixed $value
- * @return $this
- */
- public function fillJsonAttribute($key, $value)
- {
- list($key, $path) = explode('->', $key, 2);
- $this->attributes[$key] = $this->asJson($this->getArrayAttributeWithValue(
- $path, $key, $value
- ));
- return $this;
- }
- /**
- * Get an array attribute with the given key and value set.
- *
- * @param string $path
- * @param string $key
- * @param mixed $value
- * @return $this
- */
- protected function getArrayAttributeWithValue($path, $key, $value)
- {
- return tap($this->getArrayAttributeByKey($key), function (&$array) use ($path, $value) {
- Arr::set($array, str_replace('->', '.', $path), $value);
- });
- }
- /**
- * Get an array attribute or return an empty array if it is not set.
- *
- * @param string $key
- * @return array
- */
- protected function getArrayAttributeByKey($key)
- {
- return isset($this->attributes[$key]) ?
- $this->fromJson($this->attributes[$key]) : [];
- }
- /**
- * Cast the given attribute to JSON.
- *
- * @param string $key
- * @param mixed $value
- * @return string
- */
- protected function castAttributeAsJson($key, $value)
- {
- $value = $this->asJson($value);
- if ($value === false) {
- throw JsonEncodingException::forAttribute(
- $this, $key, json_last_error_msg()
- );
- }
- return $value;
- }
- /**
- * Encode the given value as JSON.
- *
- * @param mixed $value
- * @return string
- */
- protected function asJson($value)
- {
- return json_encode($value);
- }
- /**
- * Decode the given JSON back into an array or object.
- *
- * @param string $value
- * @param bool $asObject
- * @return mixed
- */
- public function fromJson($value, $asObject = false)
- {
- return json_decode($value, ! $asObject);
- }
- /**
- * Return a timestamp as DateTime object with time set to 00:00:00.
- *
- * @param mixed $value
- * @return \Illuminate\Support\Carbon
- */
- protected function asDate($value)
- {
- return $this->asDateTime($value)->startOfDay();
- }
- /**
- * Return a timestamp as DateTime object.
- *
- * @param mixed $value
- * @return \Illuminate\Support\Carbon
- */
- protected function asDateTime($value)
- {
- // If this value is already a Carbon instance, we shall just return it as is.
- // This prevents us having to re-instantiate a Carbon instance when we know
- // it already is one, which wouldn't be fulfilled by the DateTime check.
- if ($value instanceof Carbon) {
- return $value;
- }
- // If the value is already a DateTime instance, we will just skip the rest of
- // these checks since they will be a waste of time, and hinder performance
- // when checking the field. We will just return the DateTime right away.
- if ($value instanceof DateTimeInterface) {
- return new Carbon(
- $value->format('Y-m-d H:i:s.u'), $value->getTimezone()
- );
- }
- // If this value is an integer, we will assume it is a UNIX timestamp's value
- // and format a Carbon object from this timestamp. This allows flexibility
- // when defining your date fields as they might be UNIX timestamps here.
- if (is_numeric($value)) {
- return Carbon::createFromTimestamp($value);
- }
- // If the value is in simply year, month, day format, we will instantiate the
- // Carbon instances from that format. Again, this provides for simple date
- // fields on the database, while still supporting Carbonized conversion.
- if ($this->isStandardDateFormat($value)) {
- return Carbon::createFromFormat('Y-m-d', $value)->startOfDay();
- }
- // Finally, we will just assume this date is in the format used by default on
- // the database connection and use that format to create the Carbon object
- // that is returned back out to the developers after we convert it here.
- return Carbon::createFromFormat(
- str_replace('.v', '.u', $this->getDateFormat()), $value
- );
- }
- /**
- * Determine if the given value is a standard date format.
- *
- * @param string $value
- * @return bool
- */
- protected function isStandardDateFormat($value)
- {
- return preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $value);
- }
- /**
- * Convert a DateTime to a storable string.
- *
- * @param \DateTime|int $value
- * @return string
- */
- public function fromDateTime($value)
- {
- return empty($value) ? $value : $this->asDateTime($value)->format(
- $this->getDateFormat()
- );
- }
- /**
- * Return a timestamp as unix timestamp.
- *
- * @param mixed $value
- * @return int
- */
- protected function asTimestamp($value)
- {
- return $this->asDateTime($value)->getTimestamp();
- }
- /**
- * Prepare a date for array / JSON serialization.
- *
- * @param \DateTimeInterface $date
- * @return string
- */
- protected function serializeDate(DateTimeInterface $date)
- {
- return $date->format($this->getDateFormat());
- }
- /**
- * Get the attributes that should be converted to dates.
- *
- * @return array
- */
- public function getDates()
- {
- $defaults = [static::CREATED_AT, static::UPDATED_AT];
- return $this->usesTimestamps()
- ? array_unique(array_merge($this->dates, $defaults))
- : $this->dates;
- }
- /**
- * Get the format for database stored dates.
- *
- * @return string
- */
- protected function getDateFormat()
- {
- return $this->dateFormat ?: $this->getConnection()->getQueryGrammar()->getDateFormat();
- }
- /**
- * Set the date format used by the model.
- *
- * @param string $format
- * @return $this
- */
- public function setDateFormat($format)
- {
- $this->dateFormat = $format;
- return $this;
- }
- /**
- * Determine whether an attribute should be cast to a native type.
- *
- * @param string $key
- * @param array|string|null $types
- * @return bool
- */
- public function hasCast($key, $types = null)
- {
- if (array_key_exists($key, $this->getCasts())) {
- return $types ? in_array($this->getCastType($key), (array) $types, true) : true;
- }
- return false;
- }
- /**
- * Get the casts array.
- *
- * @return array
- */
- public function getCasts()
- {
- if ($this->getIncrementing()) {
- return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts);
- }
- return $this->casts;
- }
- /**
- * Determine whether a value is Date / DateTime castable for inbound manipulation.
- *
- * @param string $key
- * @return bool
- */
- protected function isDateCastable($key)
- {
- return $this->hasCast($key, ['date', 'datetime']);
- }
- /**
- * Determine whether a value is JSON castable for inbound manipulation.
- *
- * @param string $key
- * @return bool
- */
- protected function isJsonCastable($key)
- {
- return $this->hasCast($key, ['array', 'json', 'object', 'collection']);
- }
- /**
- * Get all of the current attributes on the model.
- *
- * @return array
- */
- public function getAttributes()
- {
- return $this->attributes;
- }
- /**
- * Set the array of model attributes. No checking is done.
- *
- * @param array $attributes
- * @param bool $sync
- * @return $this
- */
- public function setRawAttributes(array $attributes, $sync = false)
- {
- $this->attributes = $attributes;
- if ($sync) {
- $this->syncOriginal();
- }
- return $this;
- }
- /**
- * Get the model's original attribute values.
- *
- * @param string|null $key
- * @param mixed $default
- * @return mixed|array
- */
- public function getOriginal($key = null, $default = null)
- {
- return Arr::get($this->original, $key, $default);
- }
- /**
- * Get a subset of the model's attributes.
- *
- * @param array|mixed $attributes
- * @return array
- */
- public function only($attributes)
- {
- $results = [];
- foreach (is_array($attributes) ? $attributes : func_get_args() as $attribute) {
- $results[$attribute] = $this->getAttribute($attribute);
- }
- return $results;
- }
- /**
- * Sync the original attributes with the current.
- *
- * @return $this
- */
- public function syncOriginal()
- {
- $this->original = $this->attributes;
- return $this;
- }
- /**
- * Sync a single original attribute with its current value.
- *
- * @param string $attribute
- * @return $this
- */
- public function syncOriginalAttribute($attribute)
- {
- $this->original[$attribute] = $this->attributes[$attribute];
- return $this;
- }
- /**
- * Sync the changed attributes.
- *
- * @return $this
- */
- public function syncChanges()
- {
- $this->changes = $this->getDirty();
- return $this;
- }
- /**
- * Determine if the model or given attribute(s) have been modified.
- *
- * @param array|string|null $attributes
- * @return bool
- */
- public function isDirty($attributes = null)
- {
- return $this->hasChanges(
- $this->getDirty(), is_array($attributes) ? $attributes : func_get_args()
- );
- }
- /**
- * Determine if the model or given attribute(s) have remained the same.
- *
- * @param array|string|null $attributes
- * @return bool
- */
- public function isClean($attributes = null)
- {
- return ! $this->isDirty(...func_get_args());
- }
- /**
- * Determine if the model or given attribute(s) have been modified.
- *
- * @param array|string|null $attributes
- * @return bool
- */
- public function wasChanged($attributes = null)
- {
- return $this->hasChanges(
- $this->getChanges(), is_array($attributes) ? $attributes : func_get_args()
- );
- }
- /**
- * Determine if the given attributes were changed.
- *
- * @param array $changes
- * @param array|string|null $attributes
- * @return bool
- */
- protected function hasChanges($changes, $attributes = null)
- {
- // If no specific attributes were provided, we will just see if the dirty array
- // already contains any attributes. If it does we will just return that this
- // count is greater than zero. Else, we need to check specific attributes.
- if (empty($attributes)) {
- return count($changes) > 0;
- }
- // Here we will spin through every attribute and see if this is in the array of
- // dirty attributes. If it is, we will return true and if we make it through
- // all of the attributes for the entire array we will return false at end.
- foreach (Arr::wrap($attributes) as $attribute) {
- if (array_key_exists($attribute, $changes)) {
- return true;
- }
- }
- return false;
- }
- /**
- * Get the attributes that have been changed since last sync.
- *
- * @return array
- */
- public function getDirty()
- {
- $dirty = [];
- foreach ($this->getAttributes() as $key => $value) {
- if (! $this->originalIsEquivalent($key, $value)) {
- $dirty[$key] = $value;
- }
- }
- return $dirty;
- }
- /**
- * Get the attributes that were changed.
- *
- * @return array
- */
- public function getChanges()
- {
- return $this->changes;
- }
- /**
- * Determine if the new and old values for a given key are equivalent.
- *
- * @param string $key
- * @param mixed $current
- * @return bool
- */
- protected function originalIsEquivalent($key, $current)
- {
- if (! array_key_exists($key, $this->original)) {
- return false;
- }
- $original = $this->getOriginal($key);
- if ($current === $original) {
- return true;
- } elseif (is_null($current)) {
- return false;
- } elseif ($this->isDateAttribute($key)) {
- return $this->fromDateTime($current) ===
- $this->fromDateTime($original);
- } elseif ($this->hasCast($key)) {
- return $this->castAttribute($key, $current) ===
- $this->castAttribute($key, $original);
- }
- return is_numeric($current) && is_numeric($original)
- && strcmp((string) $current, (string) $original) === 0;
- }
- /**
- * Append attributes to query when building a query.
- *
- * @param array|string $attributes
- * @return $this
- */
- public function append($attributes)
- {
- $this->appends = array_unique(
- array_merge($this->appends, is_string($attributes) ? func_get_args() : $attributes)
- );
- return $this;
- }
- /**
- * Set the accessors to append to model arrays.
- *
- * @param array $appends
- * @return $this
- */
- public function setAppends(array $appends)
- {
- $this->appends = $appends;
- return $this;
- }
- /**
- * Get the mutated attributes for a given instance.
- *
- * @return array
- */
- public function getMutatedAttributes()
- {
- $class = static::class;
- if (! isset(static::$mutatorCache[$class])) {
- static::cacheMutatedAttributes($class);
- }
- return static::$mutatorCache[$class];
- }
- /**
- * Extract and cache all the mutated attributes of a class.
- *
- * @param string $class
- * @return void
- */
- public static function cacheMutatedAttributes($class)
- {
- static::$mutatorCache[$class] = collect(static::getMutatorMethods($class))->map(function ($match) {
- return lcfirst(static::$snakeAttributes ? Str::snake($match) : $match);
- })->all();
- }
- /**
- * Get all of the attribute mutator methods.
- *
- * @param mixed $class
- * @return array
- */
- protected static function getMutatorMethods($class)
- {
- preg_match_all('/(?<=^|;)get([^;]+?)Attribute(;|$)/', implode(';', get_class_methods($class)), $matches);
- return $matches[1];
- }
- }
|