HasAttributes.php 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361
  1. <?php
  2. namespace Illuminate\Database\Eloquent\Concerns;
  3. use BackedEnum;
  4. use Brick\Math\BigDecimal;
  5. use Brick\Math\Exception\MathException as BrickMathException;
  6. use Brick\Math\RoundingMode;
  7. use Carbon\CarbonImmutable;
  8. use Carbon\CarbonInterface;
  9. use DateTimeImmutable;
  10. use DateTimeInterface;
  11. use Illuminate\Contracts\Database\Eloquent\Castable;
  12. use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
  13. use Illuminate\Contracts\Support\Arrayable;
  14. use Illuminate\Database\Eloquent\Casts\AsArrayObject;
  15. use Illuminate\Database\Eloquent\Casts\AsCollection;
  16. use Illuminate\Database\Eloquent\Casts\AsEncryptedArrayObject;
  17. use Illuminate\Database\Eloquent\Casts\AsEncryptedCollection;
  18. use Illuminate\Database\Eloquent\Casts\AsEnumArrayObject;
  19. use Illuminate\Database\Eloquent\Casts\AsEnumCollection;
  20. use Illuminate\Database\Eloquent\Casts\Attribute;
  21. use Illuminate\Database\Eloquent\Casts\Json;
  22. use Illuminate\Database\Eloquent\InvalidCastException;
  23. use Illuminate\Database\Eloquent\JsonEncodingException;
  24. use Illuminate\Database\Eloquent\MissingAttributeException;
  25. use Illuminate\Database\Eloquent\Relations\Relation;
  26. use Illuminate\Database\LazyLoadingViolationException;
  27. use Illuminate\Support\Arr;
  28. use Illuminate\Support\Carbon;
  29. use Illuminate\Support\Collection as BaseCollection;
  30. use Illuminate\Support\Exceptions\MathException;
  31. use Illuminate\Support\Facades\Crypt;
  32. use Illuminate\Support\Facades\Date;
  33. use Illuminate\Support\Facades\Hash;
  34. use Illuminate\Support\Str;
  35. use InvalidArgumentException;
  36. use LogicException;
  37. use ReflectionClass;
  38. use ReflectionMethod;
  39. use ReflectionNamedType;
  40. use RuntimeException;
  41. use ValueError;
  42. trait HasAttributes
  43. {
  44. /**
  45. * The model's attributes.
  46. *
  47. * @var array
  48. */
  49. protected $attributes = [];
  50. /**
  51. * The model attribute's original state.
  52. *
  53. * @var array
  54. */
  55. protected $original = [];
  56. /**
  57. * The changed model attributes.
  58. *
  59. * @var array
  60. */
  61. protected $changes = [];
  62. /**
  63. * The attributes that should be cast.
  64. *
  65. * @var array
  66. */
  67. protected $casts = [];
  68. /**
  69. * The attributes that have been cast using custom classes.
  70. *
  71. * @var array
  72. */
  73. protected $classCastCache = [];
  74. /**
  75. * The attributes that have been cast using "Attribute" return type mutators.
  76. *
  77. * @var array
  78. */
  79. protected $attributeCastCache = [];
  80. /**
  81. * The built-in, primitive cast types supported by Eloquent.
  82. *
  83. * @var string[]
  84. */
  85. protected static $primitiveCastTypes = [
  86. 'array',
  87. 'bool',
  88. 'boolean',
  89. 'collection',
  90. 'custom_datetime',
  91. 'date',
  92. 'datetime',
  93. 'decimal',
  94. 'double',
  95. 'encrypted',
  96. 'encrypted:array',
  97. 'encrypted:collection',
  98. 'encrypted:json',
  99. 'encrypted:object',
  100. 'float',
  101. 'hashed',
  102. 'immutable_date',
  103. 'immutable_datetime',
  104. 'immutable_custom_datetime',
  105. 'int',
  106. 'integer',
  107. 'json',
  108. 'object',
  109. 'real',
  110. 'string',
  111. 'timestamp',
  112. ];
  113. /**
  114. * The storage format of the model's date columns.
  115. *
  116. * @var string
  117. */
  118. protected $dateFormat;
  119. /**
  120. * The accessors to append to the model's array form.
  121. *
  122. * @var array
  123. */
  124. protected $appends = [];
  125. /**
  126. * Indicates whether attributes are snake cased on arrays.
  127. *
  128. * @var bool
  129. */
  130. public static $snakeAttributes = true;
  131. /**
  132. * The cache of the mutated attributes for each class.
  133. *
  134. * @var array
  135. */
  136. protected static $mutatorCache = [];
  137. /**
  138. * The cache of the "Attribute" return type marked mutated attributes for each class.
  139. *
  140. * @var array
  141. */
  142. protected static $attributeMutatorCache = [];
  143. /**
  144. * The cache of the "Attribute" return type marked mutated, gettable attributes for each class.
  145. *
  146. * @var array
  147. */
  148. protected static $getAttributeMutatorCache = [];
  149. /**
  150. * The cache of the "Attribute" return type marked mutated, settable attributes for each class.
  151. *
  152. * @var array
  153. */
  154. protected static $setAttributeMutatorCache = [];
  155. /**
  156. * The cache of the converted cast types.
  157. *
  158. * @var array
  159. */
  160. protected static $castTypeCache = [];
  161. /**
  162. * The encrypter instance that is used to encrypt attributes.
  163. *
  164. * @var \Illuminate\Contracts\Encryption\Encrypter|null
  165. */
  166. public static $encrypter;
  167. /**
  168. * Initialize the trait.
  169. *
  170. * @return void
  171. */
  172. protected function initializeHasAttributes()
  173. {
  174. $this->casts = $this->ensureCastsAreStringValues(
  175. array_merge($this->casts, $this->casts()),
  176. );
  177. }
  178. /**
  179. * Convert the model's attributes to an array.
  180. *
  181. * @return array
  182. */
  183. public function attributesToArray()
  184. {
  185. // If an attribute is a date, we will cast it to a string after converting it
  186. // to a DateTime / Carbon instance. This is so we will get some consistent
  187. // formatting while accessing attributes vs. arraying / JSONing a model.
  188. $attributes = $this->addDateAttributesToArray(
  189. $attributes = $this->getArrayableAttributes()
  190. );
  191. $attributes = $this->addMutatedAttributesToArray(
  192. $attributes, $mutatedAttributes = $this->getMutatedAttributes()
  193. );
  194. // Next we will handle any casts that have been setup for this model and cast
  195. // the values to their appropriate type. If the attribute has a mutator we
  196. // will not perform the cast on those attributes to avoid any confusion.
  197. $attributes = $this->addCastAttributesToArray(
  198. $attributes, $mutatedAttributes
  199. );
  200. // Here we will grab all of the appended, calculated attributes to this model
  201. // as these attributes are not really in the attributes array, but are run
  202. // when we need to array or JSON the model for convenience to the coder.
  203. foreach ($this->getArrayableAppends() as $key) {
  204. $attributes[$key] = $this->mutateAttributeForArray($key, null);
  205. }
  206. return $attributes;
  207. }
  208. /**
  209. * Add the date attributes to the attributes array.
  210. *
  211. * @param array $attributes
  212. * @return array
  213. */
  214. protected function addDateAttributesToArray(array $attributes)
  215. {
  216. foreach ($this->getDates() as $key) {
  217. if (! isset($attributes[$key])) {
  218. continue;
  219. }
  220. $attributes[$key] = $this->serializeDate(
  221. $this->asDateTime($attributes[$key])
  222. );
  223. }
  224. return $attributes;
  225. }
  226. /**
  227. * Add the mutated attributes to the attributes array.
  228. *
  229. * @param array $attributes
  230. * @param array $mutatedAttributes
  231. * @return array
  232. */
  233. protected function addMutatedAttributesToArray(array $attributes, array $mutatedAttributes)
  234. {
  235. foreach ($mutatedAttributes as $key) {
  236. // We want to spin through all the mutated attributes for this model and call
  237. // the mutator for the attribute. We cache off every mutated attributes so
  238. // we don't have to constantly check on attributes that actually change.
  239. if (! array_key_exists($key, $attributes)) {
  240. continue;
  241. }
  242. // Next, we will call the mutator for this attribute so that we can get these
  243. // mutated attribute's actual values. After we finish mutating each of the
  244. // attributes we will return this final array of the mutated attributes.
  245. $attributes[$key] = $this->mutateAttributeForArray(
  246. $key, $attributes[$key]
  247. );
  248. }
  249. return $attributes;
  250. }
  251. /**
  252. * Add the casted attributes to the attributes array.
  253. *
  254. * @param array $attributes
  255. * @param array $mutatedAttributes
  256. * @return array
  257. */
  258. protected function addCastAttributesToArray(array $attributes, array $mutatedAttributes)
  259. {
  260. foreach ($this->getCasts() as $key => $value) {
  261. if (! array_key_exists($key, $attributes) ||
  262. in_array($key, $mutatedAttributes)) {
  263. continue;
  264. }
  265. // Here we will cast the attribute. Then, if the cast is a date or datetime cast
  266. // then we will serialize the date for the array. This will convert the dates
  267. // to strings based on the date format specified for these Eloquent models.
  268. $attributes[$key] = $this->castAttribute(
  269. $key, $attributes[$key]
  270. );
  271. // If the attribute cast was a date or a datetime, we will serialize the date as
  272. // a string. This allows the developers to customize how dates are serialized
  273. // into an array without affecting how they are persisted into the storage.
  274. if (isset($attributes[$key]) && in_array($value, ['date', 'datetime', 'immutable_date', 'immutable_datetime'])) {
  275. $attributes[$key] = $this->serializeDate($attributes[$key]);
  276. }
  277. if (isset($attributes[$key]) && ($this->isCustomDateTimeCast($value) ||
  278. $this->isImmutableCustomDateTimeCast($value))) {
  279. $attributes[$key] = $attributes[$key]->format(explode(':', $value, 2)[1]);
  280. }
  281. if ($attributes[$key] instanceof DateTimeInterface &&
  282. $this->isClassCastable($key)) {
  283. $attributes[$key] = $this->serializeDate($attributes[$key]);
  284. }
  285. if (isset($attributes[$key]) && $this->isClassSerializable($key)) {
  286. $attributes[$key] = $this->serializeClassCastableAttribute($key, $attributes[$key]);
  287. }
  288. if ($this->isEnumCastable($key) && (! ($attributes[$key] ?? null) instanceof Arrayable)) {
  289. $attributes[$key] = isset($attributes[$key]) ? $this->getStorableEnumValue($this->getCasts()[$key], $attributes[$key]) : null;
  290. }
  291. if ($attributes[$key] instanceof Arrayable) {
  292. $attributes[$key] = $attributes[$key]->toArray();
  293. }
  294. }
  295. return $attributes;
  296. }
  297. /**
  298. * Get an attribute array of all arrayable attributes.
  299. *
  300. * @return array
  301. */
  302. protected function getArrayableAttributes()
  303. {
  304. return $this->getArrayableItems($this->getAttributes());
  305. }
  306. /**
  307. * Get all of the appendable values that are arrayable.
  308. *
  309. * @return array
  310. */
  311. protected function getArrayableAppends()
  312. {
  313. if (! count($this->appends)) {
  314. return [];
  315. }
  316. return $this->getArrayableItems(
  317. array_combine($this->appends, $this->appends)
  318. );
  319. }
  320. /**
  321. * Get the model's relationships in array form.
  322. *
  323. * @return array
  324. */
  325. public function relationsToArray()
  326. {
  327. $attributes = [];
  328. foreach ($this->getArrayableRelations() as $key => $value) {
  329. // If the values implement the Arrayable interface we can just call this
  330. // toArray method on the instances which will convert both models and
  331. // collections to their proper array form and we'll set the values.
  332. if ($value instanceof Arrayable) {
  333. $relation = $value->toArray();
  334. }
  335. // If the value is null, we'll still go ahead and set it in this list of
  336. // attributes, since null is used to represent empty relationships if
  337. // it has a has one or belongs to type relationships on the models.
  338. elseif (is_null($value)) {
  339. $relation = $value;
  340. }
  341. // If the relationships snake-casing is enabled, we will snake case this
  342. // key so that the relation attribute is snake cased in this returned
  343. // array to the developers, making this consistent with attributes.
  344. if (static::$snakeAttributes) {
  345. $key = Str::snake($key);
  346. }
  347. // If the relation value has been set, we will set it on this attributes
  348. // list for returning. If it was not arrayable or null, we'll not set
  349. // the value on the array because it is some type of invalid value.
  350. if (isset($relation) || is_null($value)) {
  351. $attributes[$key] = $relation;
  352. }
  353. unset($relation);
  354. }
  355. return $attributes;
  356. }
  357. /**
  358. * Get an attribute array of all arrayable relations.
  359. *
  360. * @return array
  361. */
  362. protected function getArrayableRelations()
  363. {
  364. return $this->getArrayableItems($this->relations);
  365. }
  366. /**
  367. * Get an attribute array of all arrayable values.
  368. *
  369. * @param array $values
  370. * @return array
  371. */
  372. protected function getArrayableItems(array $values)
  373. {
  374. if (count($this->getVisible()) > 0) {
  375. $values = array_intersect_key($values, array_flip($this->getVisible()));
  376. }
  377. if (count($this->getHidden()) > 0) {
  378. $values = array_diff_key($values, array_flip($this->getHidden()));
  379. }
  380. return $values;
  381. }
  382. /**
  383. * Determine whether an attribute exists on the model.
  384. *
  385. * @param string $key
  386. * @return bool
  387. */
  388. public function hasAttribute($key)
  389. {
  390. if (! $key) {
  391. return false;
  392. }
  393. return array_key_exists($key, $this->attributes) ||
  394. array_key_exists($key, $this->casts) ||
  395. $this->hasGetMutator($key) ||
  396. $this->hasAttributeMutator($key) ||
  397. $this->isClassCastable($key);
  398. }
  399. /**
  400. * Get an attribute from the model.
  401. *
  402. * @param string $key
  403. * @return mixed
  404. */
  405. public function getAttribute($key)
  406. {
  407. if (! $key) {
  408. return;
  409. }
  410. // If the attribute exists in the attribute array or has a "get" mutator we will
  411. // get the attribute's value. Otherwise, we will proceed as if the developers
  412. // are asking for a relationship's value. This covers both types of values.
  413. if ($this->hasAttribute($key)) {
  414. return $this->getAttributeValue($key);
  415. }
  416. // Here we will determine if the model base class itself contains this given key
  417. // since we don't want to treat any of those methods as relationships because
  418. // they are all intended as helper methods and none of these are relations.
  419. if (method_exists(self::class, $key)) {
  420. return $this->throwMissingAttributeExceptionIfApplicable($key);
  421. }
  422. return $this->isRelation($key) || $this->relationLoaded($key)
  423. ? $this->getRelationValue($key)
  424. : $this->throwMissingAttributeExceptionIfApplicable($key);
  425. }
  426. /**
  427. * Either throw a missing attribute exception or return null depending on Eloquent's configuration.
  428. *
  429. * @param string $key
  430. * @return null
  431. *
  432. * @throws \Illuminate\Database\Eloquent\MissingAttributeException
  433. */
  434. protected function throwMissingAttributeExceptionIfApplicable($key)
  435. {
  436. if ($this->exists &&
  437. ! $this->wasRecentlyCreated &&
  438. static::preventsAccessingMissingAttributes()) {
  439. if (isset(static::$missingAttributeViolationCallback)) {
  440. return call_user_func(static::$missingAttributeViolationCallback, $this, $key);
  441. }
  442. throw new MissingAttributeException($this, $key);
  443. }
  444. return null;
  445. }
  446. /**
  447. * Get a plain attribute (not a relationship).
  448. *
  449. * @param string $key
  450. * @return mixed
  451. */
  452. public function getAttributeValue($key)
  453. {
  454. return $this->transformModelValue($key, $this->getAttributeFromArray($key));
  455. }
  456. /**
  457. * Get an attribute from the $attributes array.
  458. *
  459. * @param string $key
  460. * @return mixed
  461. */
  462. protected function getAttributeFromArray($key)
  463. {
  464. return $this->getAttributes()[$key] ?? null;
  465. }
  466. /**
  467. * Get a relationship.
  468. *
  469. * @param string $key
  470. * @return mixed
  471. */
  472. public function getRelationValue($key)
  473. {
  474. // If the key already exists in the relationships array, it just means the
  475. // relationship has already been loaded, so we'll just return it out of
  476. // here because there is no need to query within the relations twice.
  477. if ($this->relationLoaded($key)) {
  478. return $this->relations[$key];
  479. }
  480. if (! $this->isRelation($key)) {
  481. return;
  482. }
  483. if ($this->preventsLazyLoading) {
  484. $this->handleLazyLoadingViolation($key);
  485. }
  486. // If the "attribute" exists as a method on the model, we will just assume
  487. // it is a relationship and will load and return results from the query
  488. // and hydrate the relationship's value on the "relationships" array.
  489. return $this->getRelationshipFromMethod($key);
  490. }
  491. /**
  492. * Determine if the given key is a relationship method on the model.
  493. *
  494. * @param string $key
  495. * @return bool
  496. */
  497. public function isRelation($key)
  498. {
  499. if ($this->hasAttributeMutator($key)) {
  500. return false;
  501. }
  502. return method_exists($this, $key) ||
  503. $this->relationResolver(static::class, $key);
  504. }
  505. /**
  506. * Handle a lazy loading violation.
  507. *
  508. * @param string $key
  509. * @return mixed
  510. */
  511. protected function handleLazyLoadingViolation($key)
  512. {
  513. if (isset(static::$lazyLoadingViolationCallback)) {
  514. return call_user_func(static::$lazyLoadingViolationCallback, $this, $key);
  515. }
  516. if (! $this->exists || $this->wasRecentlyCreated) {
  517. return;
  518. }
  519. throw new LazyLoadingViolationException($this, $key);
  520. }
  521. /**
  522. * Get a relationship value from a method.
  523. *
  524. * @param string $method
  525. * @return mixed
  526. *
  527. * @throws \LogicException
  528. */
  529. protected function getRelationshipFromMethod($method)
  530. {
  531. $relation = $this->$method();
  532. if (! $relation instanceof Relation) {
  533. if (is_null($relation)) {
  534. throw new LogicException(sprintf(
  535. '%s::%s must return a relationship instance, but "null" was returned. Was the "return" keyword used?', static::class, $method
  536. ));
  537. }
  538. throw new LogicException(sprintf(
  539. '%s::%s must return a relationship instance.', static::class, $method
  540. ));
  541. }
  542. return tap($relation->getResults(), function ($results) use ($method) {
  543. $this->setRelation($method, $results);
  544. });
  545. }
  546. /**
  547. * Determine if a get mutator exists for an attribute.
  548. *
  549. * @param string $key
  550. * @return bool
  551. */
  552. public function hasGetMutator($key)
  553. {
  554. return method_exists($this, 'get'.Str::studly($key).'Attribute');
  555. }
  556. /**
  557. * Determine if a "Attribute" return type marked mutator exists for an attribute.
  558. *
  559. * @param string $key
  560. * @return bool
  561. */
  562. public function hasAttributeMutator($key)
  563. {
  564. if (isset(static::$attributeMutatorCache[get_class($this)][$key])) {
  565. return static::$attributeMutatorCache[get_class($this)][$key];
  566. }
  567. if (! method_exists($this, $method = Str::camel($key))) {
  568. return static::$attributeMutatorCache[get_class($this)][$key] = false;
  569. }
  570. $returnType = (new ReflectionMethod($this, $method))->getReturnType();
  571. return static::$attributeMutatorCache[get_class($this)][$key] =
  572. $returnType instanceof ReflectionNamedType &&
  573. $returnType->getName() === Attribute::class;
  574. }
  575. /**
  576. * Determine if a "Attribute" return type marked get mutator exists for an attribute.
  577. *
  578. * @param string $key
  579. * @return bool
  580. */
  581. public function hasAttributeGetMutator($key)
  582. {
  583. if (isset(static::$getAttributeMutatorCache[get_class($this)][$key])) {
  584. return static::$getAttributeMutatorCache[get_class($this)][$key];
  585. }
  586. if (! $this->hasAttributeMutator($key)) {
  587. return static::$getAttributeMutatorCache[get_class($this)][$key] = false;
  588. }
  589. return static::$getAttributeMutatorCache[get_class($this)][$key] = is_callable($this->{Str::camel($key)}()->get);
  590. }
  591. /**
  592. * Get the value of an attribute using its mutator.
  593. *
  594. * @param string $key
  595. * @param mixed $value
  596. * @return mixed
  597. */
  598. protected function mutateAttribute($key, $value)
  599. {
  600. return $this->{'get'.Str::studly($key).'Attribute'}($value);
  601. }
  602. /**
  603. * Get the value of an "Attribute" return type marked attribute using its mutator.
  604. *
  605. * @param string $key
  606. * @param mixed $value
  607. * @return mixed
  608. */
  609. protected function mutateAttributeMarkedAttribute($key, $value)
  610. {
  611. if (array_key_exists($key, $this->attributeCastCache)) {
  612. return $this->attributeCastCache[$key];
  613. }
  614. $attribute = $this->{Str::camel($key)}();
  615. $value = call_user_func($attribute->get ?: function ($value) {
  616. return $value;
  617. }, $value, $this->attributes);
  618. if ($attribute->withCaching || (is_object($value) && $attribute->withObjectCaching)) {
  619. $this->attributeCastCache[$key] = $value;
  620. } else {
  621. unset($this->attributeCastCache[$key]);
  622. }
  623. return $value;
  624. }
  625. /**
  626. * Get the value of an attribute using its mutator for array conversion.
  627. *
  628. * @param string $key
  629. * @param mixed $value
  630. * @return mixed
  631. */
  632. protected function mutateAttributeForArray($key, $value)
  633. {
  634. if ($this->isClassCastable($key)) {
  635. $value = $this->getClassCastableAttributeValue($key, $value);
  636. } elseif (isset(static::$getAttributeMutatorCache[get_class($this)][$key]) &&
  637. static::$getAttributeMutatorCache[get_class($this)][$key] === true) {
  638. $value = $this->mutateAttributeMarkedAttribute($key, $value);
  639. $value = $value instanceof DateTimeInterface
  640. ? $this->serializeDate($value)
  641. : $value;
  642. } else {
  643. $value = $this->mutateAttribute($key, $value);
  644. }
  645. return $value instanceof Arrayable ? $value->toArray() : $value;
  646. }
  647. /**
  648. * Merge new casts with existing casts on the model.
  649. *
  650. * @param array $casts
  651. * @return $this
  652. */
  653. public function mergeCasts($casts)
  654. {
  655. $casts = $this->ensureCastsAreStringValues($casts);
  656. $this->casts = array_merge($this->casts, $casts);
  657. return $this;
  658. }
  659. /**
  660. * Ensure that the given casts are strings.
  661. *
  662. * @param array $casts
  663. * @return array
  664. */
  665. protected function ensureCastsAreStringValues($casts)
  666. {
  667. foreach ($casts as $attribute => $cast) {
  668. $casts[$attribute] = match (true) {
  669. is_array($cast) => value(function () use ($cast) {
  670. if (count($cast) === 1) {
  671. return $cast[0];
  672. }
  673. [$cast, $arguments] = [array_shift($cast), $cast];
  674. return $cast.':'.implode(',', $arguments);
  675. }),
  676. default => $cast,
  677. };
  678. }
  679. return $casts;
  680. }
  681. /**
  682. * Cast an attribute to a native PHP type.
  683. *
  684. * @param string $key
  685. * @param mixed $value
  686. * @return mixed
  687. */
  688. protected function castAttribute($key, $value)
  689. {
  690. $castType = $this->getCastType($key);
  691. if (is_null($value) && in_array($castType, static::$primitiveCastTypes)) {
  692. return $value;
  693. }
  694. // If the key is one of the encrypted castable types, we'll first decrypt
  695. // the value and update the cast type so we may leverage the following
  696. // logic for casting this value to any additionally specified types.
  697. if ($this->isEncryptedCastable($key)) {
  698. $value = $this->fromEncryptedString($value);
  699. $castType = Str::after($castType, 'encrypted:');
  700. }
  701. switch ($castType) {
  702. case 'int':
  703. case 'integer':
  704. return (int) $value;
  705. case 'real':
  706. case 'float':
  707. case 'double':
  708. return $this->fromFloat($value);
  709. case 'decimal':
  710. return $this->asDecimal($value, explode(':', $this->getCasts()[$key], 2)[1]);
  711. case 'string':
  712. return (string) $value;
  713. case 'bool':
  714. case 'boolean':
  715. return (bool) $value;
  716. case 'object':
  717. return $this->fromJson($value, true);
  718. case 'array':
  719. case 'json':
  720. return $this->fromJson($value);
  721. case 'collection':
  722. return new BaseCollection($this->fromJson($value));
  723. case 'date':
  724. return $this->asDate($value);
  725. case 'datetime':
  726. case 'custom_datetime':
  727. return $this->asDateTime($value);
  728. case 'immutable_date':
  729. return $this->asDate($value)->toImmutable();
  730. case 'immutable_custom_datetime':
  731. case 'immutable_datetime':
  732. return $this->asDateTime($value)->toImmutable();
  733. case 'timestamp':
  734. return $this->asTimestamp($value);
  735. }
  736. if ($this->isEnumCastable($key)) {
  737. return $this->getEnumCastableAttributeValue($key, $value);
  738. }
  739. if ($this->isClassCastable($key)) {
  740. return $this->getClassCastableAttributeValue($key, $value);
  741. }
  742. return $value;
  743. }
  744. /**
  745. * Cast the given attribute using a custom cast class.
  746. *
  747. * @param string $key
  748. * @param mixed $value
  749. * @return mixed
  750. */
  751. protected function getClassCastableAttributeValue($key, $value)
  752. {
  753. $caster = $this->resolveCasterClass($key);
  754. $objectCachingDisabled = $caster->withoutObjectCaching ?? false;
  755. if (isset($this->classCastCache[$key]) && ! $objectCachingDisabled) {
  756. return $this->classCastCache[$key];
  757. } else {
  758. $value = $caster instanceof CastsInboundAttributes
  759. ? $value
  760. : $caster->get($this, $key, $value, $this->attributes);
  761. if ($caster instanceof CastsInboundAttributes ||
  762. ! is_object($value) ||
  763. $objectCachingDisabled) {
  764. unset($this->classCastCache[$key]);
  765. } else {
  766. $this->classCastCache[$key] = $value;
  767. }
  768. return $value;
  769. }
  770. }
  771. /**
  772. * Cast the given attribute to an enum.
  773. *
  774. * @param string $key
  775. * @param mixed $value
  776. * @return mixed
  777. */
  778. protected function getEnumCastableAttributeValue($key, $value)
  779. {
  780. if (is_null($value)) {
  781. return;
  782. }
  783. $castType = $this->getCasts()[$key];
  784. if ($value instanceof $castType) {
  785. return $value;
  786. }
  787. return $this->getEnumCaseFromValue($castType, $value);
  788. }
  789. /**
  790. * Get the type of cast for a model attribute.
  791. *
  792. * @param string $key
  793. * @return string
  794. */
  795. protected function getCastType($key)
  796. {
  797. $castType = $this->getCasts()[$key];
  798. if (isset(static::$castTypeCache[$castType])) {
  799. return static::$castTypeCache[$castType];
  800. }
  801. if ($this->isCustomDateTimeCast($castType)) {
  802. $convertedCastType = 'custom_datetime';
  803. } elseif ($this->isImmutableCustomDateTimeCast($castType)) {
  804. $convertedCastType = 'immutable_custom_datetime';
  805. } elseif ($this->isDecimalCast($castType)) {
  806. $convertedCastType = 'decimal';
  807. } elseif (class_exists($castType)) {
  808. $convertedCastType = $castType;
  809. } else {
  810. $convertedCastType = trim(strtolower($castType));
  811. }
  812. return static::$castTypeCache[$castType] = $convertedCastType;
  813. }
  814. /**
  815. * Increment or decrement the given attribute using the custom cast class.
  816. *
  817. * @param string $method
  818. * @param string $key
  819. * @param mixed $value
  820. * @return mixed
  821. */
  822. protected function deviateClassCastableAttribute($method, $key, $value)
  823. {
  824. return $this->resolveCasterClass($key)->{$method}(
  825. $this, $key, $value, $this->attributes
  826. );
  827. }
  828. /**
  829. * Serialize the given attribute using the custom cast class.
  830. *
  831. * @param string $key
  832. * @param mixed $value
  833. * @return mixed
  834. */
  835. protected function serializeClassCastableAttribute($key, $value)
  836. {
  837. return $this->resolveCasterClass($key)->serialize(
  838. $this, $key, $value, $this->attributes
  839. );
  840. }
  841. /**
  842. * Determine if the cast type is a custom date time cast.
  843. *
  844. * @param string $cast
  845. * @return bool
  846. */
  847. protected function isCustomDateTimeCast($cast)
  848. {
  849. return str_starts_with($cast, 'date:') ||
  850. str_starts_with($cast, 'datetime:');
  851. }
  852. /**
  853. * Determine if the cast type is an immutable custom date time cast.
  854. *
  855. * @param string $cast
  856. * @return bool
  857. */
  858. protected function isImmutableCustomDateTimeCast($cast)
  859. {
  860. return str_starts_with($cast, 'immutable_date:') ||
  861. str_starts_with($cast, 'immutable_datetime:');
  862. }
  863. /**
  864. * Determine if the cast type is a decimal cast.
  865. *
  866. * @param string $cast
  867. * @return bool
  868. */
  869. protected function isDecimalCast($cast)
  870. {
  871. return str_starts_with($cast, 'decimal:');
  872. }
  873. /**
  874. * Set a given attribute on the model.
  875. *
  876. * @param string $key
  877. * @param mixed $value
  878. * @return mixed
  879. */
  880. public function setAttribute($key, $value)
  881. {
  882. // First we will check for the presence of a mutator for the set operation
  883. // which simply lets the developers tweak the attribute as it is set on
  884. // this model, such as "json_encoding" a listing of data for storage.
  885. if ($this->hasSetMutator($key)) {
  886. return $this->setMutatedAttributeValue($key, $value);
  887. } elseif ($this->hasAttributeSetMutator($key)) {
  888. return $this->setAttributeMarkedMutatedAttributeValue($key, $value);
  889. }
  890. // If an attribute is listed as a "date", we'll convert it from a DateTime
  891. // instance into a form proper for storage on the database tables using
  892. // the connection grammar's date format. We will auto set the values.
  893. elseif (! is_null($value) && $this->isDateAttribute($key)) {
  894. $value = $this->fromDateTime($value);
  895. }
  896. if ($this->isEnumCastable($key)) {
  897. $this->setEnumCastableAttribute($key, $value);
  898. return $this;
  899. }
  900. if ($this->isClassCastable($key)) {
  901. $this->setClassCastableAttribute($key, $value);
  902. return $this;
  903. }
  904. if (! is_null($value) && $this->isJsonCastable($key)) {
  905. $value = $this->castAttributeAsJson($key, $value);
  906. }
  907. // If this attribute contains a JSON ->, we'll set the proper value in the
  908. // attribute's underlying array. This takes care of properly nesting an
  909. // attribute in the array's value in the case of deeply nested items.
  910. if (str_contains($key, '->')) {
  911. return $this->fillJsonAttribute($key, $value);
  912. }
  913. if (! is_null($value) && $this->isEncryptedCastable($key)) {
  914. $value = $this->castAttributeAsEncryptedString($key, $value);
  915. }
  916. if (! is_null($value) && $this->hasCast($key, 'hashed')) {
  917. $value = $this->castAttributeAsHashedString($key, $value);
  918. }
  919. $this->attributes[$key] = $value;
  920. return $this;
  921. }
  922. /**
  923. * Determine if a set mutator exists for an attribute.
  924. *
  925. * @param string $key
  926. * @return bool
  927. */
  928. public function hasSetMutator($key)
  929. {
  930. return method_exists($this, 'set'.Str::studly($key).'Attribute');
  931. }
  932. /**
  933. * Determine if an "Attribute" return type marked set mutator exists for an attribute.
  934. *
  935. * @param string $key
  936. * @return bool
  937. */
  938. public function hasAttributeSetMutator($key)
  939. {
  940. $class = get_class($this);
  941. if (isset(static::$setAttributeMutatorCache[$class][$key])) {
  942. return static::$setAttributeMutatorCache[$class][$key];
  943. }
  944. if (! method_exists($this, $method = Str::camel($key))) {
  945. return static::$setAttributeMutatorCache[$class][$key] = false;
  946. }
  947. $returnType = (new ReflectionMethod($this, $method))->getReturnType();
  948. return static::$setAttributeMutatorCache[$class][$key] =
  949. $returnType instanceof ReflectionNamedType &&
  950. $returnType->getName() === Attribute::class &&
  951. is_callable($this->{$method}()->set);
  952. }
  953. /**
  954. * Set the value of an attribute using its mutator.
  955. *
  956. * @param string $key
  957. * @param mixed $value
  958. * @return mixed
  959. */
  960. protected function setMutatedAttributeValue($key, $value)
  961. {
  962. return $this->{'set'.Str::studly($key).'Attribute'}($value);
  963. }
  964. /**
  965. * Set the value of a "Attribute" return type marked attribute using its mutator.
  966. *
  967. * @param string $key
  968. * @param mixed $value
  969. * @return mixed
  970. */
  971. protected function setAttributeMarkedMutatedAttributeValue($key, $value)
  972. {
  973. $attribute = $this->{Str::camel($key)}();
  974. $callback = $attribute->set ?: function ($value) use ($key) {
  975. $this->attributes[$key] = $value;
  976. };
  977. $this->attributes = array_merge(
  978. $this->attributes,
  979. $this->normalizeCastClassResponse(
  980. $key, $callback($value, $this->attributes)
  981. )
  982. );
  983. if ($attribute->withCaching || (is_object($value) && $attribute->withObjectCaching)) {
  984. $this->attributeCastCache[$key] = $value;
  985. } else {
  986. unset($this->attributeCastCache[$key]);
  987. }
  988. return $this;
  989. }
  990. /**
  991. * Determine if the given attribute is a date or date castable.
  992. *
  993. * @param string $key
  994. * @return bool
  995. */
  996. protected function isDateAttribute($key)
  997. {
  998. return in_array($key, $this->getDates(), true) ||
  999. $this->isDateCastable($key);
  1000. }
  1001. /**
  1002. * Set a given JSON attribute on the model.
  1003. *
  1004. * @param string $key
  1005. * @param mixed $value
  1006. * @return $this
  1007. */
  1008. public function fillJsonAttribute($key, $value)
  1009. {
  1010. [$key, $path] = explode('->', $key, 2);
  1011. $value = $this->asJson($this->getArrayAttributeWithValue(
  1012. $path, $key, $value
  1013. ));
  1014. $this->attributes[$key] = $this->isEncryptedCastable($key)
  1015. ? $this->castAttributeAsEncryptedString($key, $value)
  1016. : $value;
  1017. if ($this->isClassCastable($key)) {
  1018. unset($this->classCastCache[$key]);
  1019. }
  1020. return $this;
  1021. }
  1022. /**
  1023. * Set the value of a class castable attribute.
  1024. *
  1025. * @param string $key
  1026. * @param mixed $value
  1027. * @return void
  1028. */
  1029. protected function setClassCastableAttribute($key, $value)
  1030. {
  1031. $caster = $this->resolveCasterClass($key);
  1032. $this->attributes = array_replace(
  1033. $this->attributes,
  1034. $this->normalizeCastClassResponse($key, $caster->set(
  1035. $this, $key, $value, $this->attributes
  1036. ))
  1037. );
  1038. if ($caster instanceof CastsInboundAttributes ||
  1039. ! is_object($value) ||
  1040. ($caster->withoutObjectCaching ?? false)) {
  1041. unset($this->classCastCache[$key]);
  1042. } else {
  1043. $this->classCastCache[$key] = $value;
  1044. }
  1045. }
  1046. /**
  1047. * Set the value of an enum castable attribute.
  1048. *
  1049. * @param string $key
  1050. * @param \UnitEnum|string|int $value
  1051. * @return void
  1052. */
  1053. protected function setEnumCastableAttribute($key, $value)
  1054. {
  1055. $enumClass = $this->getCasts()[$key];
  1056. if (! isset($value)) {
  1057. $this->attributes[$key] = null;
  1058. } elseif (is_object($value)) {
  1059. $this->attributes[$key] = $this->getStorableEnumValue($enumClass, $value);
  1060. } else {
  1061. $this->attributes[$key] = $this->getStorableEnumValue(
  1062. $enumClass, $this->getEnumCaseFromValue($enumClass, $value)
  1063. );
  1064. }
  1065. }
  1066. /**
  1067. * Get an enum case instance from a given class and value.
  1068. *
  1069. * @param string $enumClass
  1070. * @param string|int $value
  1071. * @return \UnitEnum|\BackedEnum
  1072. */
  1073. protected function getEnumCaseFromValue($enumClass, $value)
  1074. {
  1075. return is_subclass_of($enumClass, BackedEnum::class)
  1076. ? $enumClass::from($value)
  1077. : constant($enumClass.'::'.$value);
  1078. }
  1079. /**
  1080. * Get the storable value from the given enum.
  1081. *
  1082. * @param string $expectedEnum
  1083. * @param \UnitEnum|\BackedEnum $value
  1084. * @return string|int
  1085. */
  1086. protected function getStorableEnumValue($expectedEnum, $value)
  1087. {
  1088. if (! $value instanceof $expectedEnum) {
  1089. throw new ValueError(sprintf('Value [%s] is not of the expected enum type [%s].', var_export($value, true), $expectedEnum));
  1090. }
  1091. return $value instanceof BackedEnum
  1092. ? $value->value
  1093. : $value->name;
  1094. }
  1095. /**
  1096. * Get an array attribute with the given key and value set.
  1097. *
  1098. * @param string $path
  1099. * @param string $key
  1100. * @param mixed $value
  1101. * @return $this
  1102. */
  1103. protected function getArrayAttributeWithValue($path, $key, $value)
  1104. {
  1105. return tap($this->getArrayAttributeByKey($key), function (&$array) use ($path, $value) {
  1106. Arr::set($array, str_replace('->', '.', $path), $value);
  1107. });
  1108. }
  1109. /**
  1110. * Get an array attribute or return an empty array if it is not set.
  1111. *
  1112. * @param string $key
  1113. * @return array
  1114. */
  1115. protected function getArrayAttributeByKey($key)
  1116. {
  1117. if (! isset($this->attributes[$key])) {
  1118. return [];
  1119. }
  1120. return $this->fromJson(
  1121. $this->isEncryptedCastable($key)
  1122. ? $this->fromEncryptedString($this->attributes[$key])
  1123. : $this->attributes[$key]
  1124. );
  1125. }
  1126. /**
  1127. * Cast the given attribute to JSON.
  1128. *
  1129. * @param string $key
  1130. * @param mixed $value
  1131. * @return string
  1132. */
  1133. protected function castAttributeAsJson($key, $value)
  1134. {
  1135. $value = $this->asJson($value);
  1136. if ($value === false) {
  1137. throw JsonEncodingException::forAttribute(
  1138. $this, $key, json_last_error_msg()
  1139. );
  1140. }
  1141. return $value;
  1142. }
  1143. /**
  1144. * Encode the given value as JSON.
  1145. *
  1146. * @param mixed $value
  1147. * @return string
  1148. */
  1149. protected function asJson($value)
  1150. {
  1151. return Json::encode($value);
  1152. }
  1153. /**
  1154. * Decode the given JSON back into an array or object.
  1155. *
  1156. * @param string $value
  1157. * @param bool $asObject
  1158. * @return mixed
  1159. */
  1160. public function fromJson($value, $asObject = false)
  1161. {
  1162. return Json::decode($value ?? '', ! $asObject);
  1163. }
  1164. /**
  1165. * Decrypt the given encrypted string.
  1166. *
  1167. * @param string $value
  1168. * @return mixed
  1169. */
  1170. public function fromEncryptedString($value)
  1171. {
  1172. return static::currentEncrypter()->decrypt($value, false);
  1173. }
  1174. /**
  1175. * Cast the given attribute to an encrypted string.
  1176. *
  1177. * @param string $key
  1178. * @param mixed $value
  1179. * @return string
  1180. */
  1181. protected function castAttributeAsEncryptedString($key, #[\SensitiveParameter] $value)
  1182. {
  1183. return static::currentEncrypter()->encrypt($value, false);
  1184. }
  1185. /**
  1186. * Set the encrypter instance that will be used to encrypt attributes.
  1187. *
  1188. * @param \Illuminate\Contracts\Encryption\Encrypter|null $encrypter
  1189. * @return void
  1190. */
  1191. public static function encryptUsing($encrypter)
  1192. {
  1193. static::$encrypter = $encrypter;
  1194. }
  1195. /**
  1196. * Get the current encrypter being used by the model.
  1197. *
  1198. * @return \Illuminate\Contracts\Encryption\Encrypter
  1199. */
  1200. protected static function currentEncrypter()
  1201. {
  1202. return static::$encrypter ?? Crypt::getFacadeRoot();
  1203. }
  1204. /**
  1205. * Cast the given attribute to a hashed string.
  1206. *
  1207. * @param string $key
  1208. * @param mixed $value
  1209. * @return string
  1210. */
  1211. protected function castAttributeAsHashedString($key, #[\SensitiveParameter] $value)
  1212. {
  1213. if ($value === null) {
  1214. return null;
  1215. }
  1216. if (! Hash::isHashed($value)) {
  1217. return Hash::make($value);
  1218. }
  1219. if (! Hash::verifyConfiguration($value)) {
  1220. throw new RuntimeException("Could not verify the hashed value's configuration.");
  1221. }
  1222. return $value;
  1223. }
  1224. /**
  1225. * Decode the given float.
  1226. *
  1227. * @param mixed $value
  1228. * @return mixed
  1229. */
  1230. public function fromFloat($value)
  1231. {
  1232. return match ((string) $value) {
  1233. 'Infinity' => INF,
  1234. '-Infinity' => -INF,
  1235. 'NaN' => NAN,
  1236. default => (float) $value,
  1237. };
  1238. }
  1239. /**
  1240. * Return a decimal as string.
  1241. *
  1242. * @param float|string $value
  1243. * @param int $decimals
  1244. * @return string
  1245. */
  1246. protected function asDecimal($value, $decimals)
  1247. {
  1248. try {
  1249. return (string) BigDecimal::of($value)->toScale($decimals, RoundingMode::HALF_UP);
  1250. } catch (BrickMathException $e) {
  1251. throw new MathException('Unable to cast value to a decimal.', previous: $e);
  1252. }
  1253. }
  1254. /**
  1255. * Return a timestamp as DateTime object with time set to 00:00:00.
  1256. *
  1257. * @param mixed $value
  1258. * @return \Illuminate\Support\Carbon
  1259. */
  1260. protected function asDate($value)
  1261. {
  1262. return $this->asDateTime($value)->startOfDay();
  1263. }
  1264. /**
  1265. * Return a timestamp as DateTime object.
  1266. *
  1267. * @param mixed $value
  1268. * @return \Illuminate\Support\Carbon
  1269. */
  1270. protected function asDateTime($value)
  1271. {
  1272. // If this value is already a Carbon instance, we shall just return it as is.
  1273. // This prevents us having to re-instantiate a Carbon instance when we know
  1274. // it already is one, which wouldn't be fulfilled by the DateTime check.
  1275. if ($value instanceof CarbonInterface) {
  1276. return Date::instance($value);
  1277. }
  1278. // If the value is already a DateTime instance, we will just skip the rest of
  1279. // these checks since they will be a waste of time, and hinder performance
  1280. // when checking the field. We will just return the DateTime right away.
  1281. if ($value instanceof DateTimeInterface) {
  1282. return Date::parse(
  1283. $value->format('Y-m-d H:i:s.u'), $value->getTimezone()
  1284. );
  1285. }
  1286. // If this value is an integer, we will assume it is a UNIX timestamp's value
  1287. // and format a Carbon object from this timestamp. This allows flexibility
  1288. // when defining your date fields as they might be UNIX timestamps here.
  1289. if (is_numeric($value)) {
  1290. return Date::createFromTimestamp($value, date_default_timezone_get());
  1291. }
  1292. // If the value is in simply year, month, day format, we will instantiate the
  1293. // Carbon instances from that format. Again, this provides for simple date
  1294. // fields on the database, while still supporting Carbonized conversion.
  1295. if ($this->isStandardDateFormat($value)) {
  1296. return Date::instance(Carbon::createFromFormat('Y-m-d', $value)->startOfDay());
  1297. }
  1298. $format = $this->getDateFormat();
  1299. // Finally, we will just assume this date is in the format used by default on
  1300. // the database connection and use that format to create the Carbon object
  1301. // that is returned back out to the developers after we convert it here.
  1302. try {
  1303. $date = Date::createFromFormat($format, $value);
  1304. } catch (InvalidArgumentException) {
  1305. $date = false;
  1306. }
  1307. return $date ?: Date::parse($value);
  1308. }
  1309. /**
  1310. * Determine if the given value is a standard date format.
  1311. *
  1312. * @param string $value
  1313. * @return bool
  1314. */
  1315. protected function isStandardDateFormat($value)
  1316. {
  1317. return preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $value);
  1318. }
  1319. /**
  1320. * Convert a DateTime to a storable string.
  1321. *
  1322. * @param mixed $value
  1323. * @return string|null
  1324. */
  1325. public function fromDateTime($value)
  1326. {
  1327. return empty($value) ? $value : $this->asDateTime($value)->format(
  1328. $this->getDateFormat()
  1329. );
  1330. }
  1331. /**
  1332. * Return a timestamp as unix timestamp.
  1333. *
  1334. * @param mixed $value
  1335. * @return int
  1336. */
  1337. protected function asTimestamp($value)
  1338. {
  1339. return $this->asDateTime($value)->getTimestamp();
  1340. }
  1341. /**
  1342. * Prepare a date for array / JSON serialization.
  1343. *
  1344. * @param \DateTimeInterface $date
  1345. * @return string
  1346. */
  1347. protected function serializeDate(DateTimeInterface $date)
  1348. {
  1349. return $date instanceof DateTimeImmutable ?
  1350. CarbonImmutable::instance($date)->toJSON() :
  1351. Carbon::instance($date)->toJSON();
  1352. }
  1353. /**
  1354. * Get the attributes that should be converted to dates.
  1355. *
  1356. * @return array
  1357. */
  1358. public function getDates()
  1359. {
  1360. return $this->usesTimestamps() ? [
  1361. $this->getCreatedAtColumn(),
  1362. $this->getUpdatedAtColumn(),
  1363. ] : [];
  1364. }
  1365. /**
  1366. * Get the format for database stored dates.
  1367. *
  1368. * @return string
  1369. */
  1370. public function getDateFormat()
  1371. {
  1372. return $this->dateFormat ?: $this->getConnection()->getQueryGrammar()->getDateFormat();
  1373. }
  1374. /**
  1375. * Set the date format used by the model.
  1376. *
  1377. * @param string $format
  1378. * @return $this
  1379. */
  1380. public function setDateFormat($format)
  1381. {
  1382. $this->dateFormat = $format;
  1383. return $this;
  1384. }
  1385. /**
  1386. * Determine whether an attribute should be cast to a native type.
  1387. *
  1388. * @param string $key
  1389. * @param array|string|null $types
  1390. * @return bool
  1391. */
  1392. public function hasCast($key, $types = null)
  1393. {
  1394. if (array_key_exists($key, $this->getCasts())) {
  1395. return $types ? in_array($this->getCastType($key), (array) $types, true) : true;
  1396. }
  1397. return false;
  1398. }
  1399. /**
  1400. * Get the attributes that should be cast.
  1401. *
  1402. * @return array
  1403. */
  1404. public function getCasts()
  1405. {
  1406. if ($this->getIncrementing()) {
  1407. return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts);
  1408. }
  1409. return $this->casts;
  1410. }
  1411. /**
  1412. * Get the attributes that should be cast.
  1413. *
  1414. * @return array
  1415. */
  1416. protected function casts()
  1417. {
  1418. return [];
  1419. }
  1420. /**
  1421. * Determine whether a value is Date / DateTime castable for inbound manipulation.
  1422. *
  1423. * @param string $key
  1424. * @return bool
  1425. */
  1426. protected function isDateCastable($key)
  1427. {
  1428. return $this->hasCast($key, ['date', 'datetime', 'immutable_date', 'immutable_datetime']);
  1429. }
  1430. /**
  1431. * Determine whether a value is Date / DateTime custom-castable for inbound manipulation.
  1432. *
  1433. * @param string $key
  1434. * @return bool
  1435. */
  1436. protected function isDateCastableWithCustomFormat($key)
  1437. {
  1438. return $this->hasCast($key, ['custom_datetime', 'immutable_custom_datetime']);
  1439. }
  1440. /**
  1441. * Determine whether a value is JSON castable for inbound manipulation.
  1442. *
  1443. * @param string $key
  1444. * @return bool
  1445. */
  1446. protected function isJsonCastable($key)
  1447. {
  1448. return $this->hasCast($key, ['array', 'json', 'object', 'collection', 'encrypted:array', 'encrypted:collection', 'encrypted:json', 'encrypted:object']);
  1449. }
  1450. /**
  1451. * Determine whether a value is an encrypted castable for inbound manipulation.
  1452. *
  1453. * @param string $key
  1454. * @return bool
  1455. */
  1456. protected function isEncryptedCastable($key)
  1457. {
  1458. return $this->hasCast($key, ['encrypted', 'encrypted:array', 'encrypted:collection', 'encrypted:json', 'encrypted:object']);
  1459. }
  1460. /**
  1461. * Determine if the given key is cast using a custom class.
  1462. *
  1463. * @param string $key
  1464. * @return bool
  1465. *
  1466. * @throws \Illuminate\Database\Eloquent\InvalidCastException
  1467. */
  1468. protected function isClassCastable($key)
  1469. {
  1470. $casts = $this->getCasts();
  1471. if (! array_key_exists($key, $casts)) {
  1472. return false;
  1473. }
  1474. $castType = $this->parseCasterClass($casts[$key]);
  1475. if (in_array($castType, static::$primitiveCastTypes)) {
  1476. return false;
  1477. }
  1478. if (class_exists($castType)) {
  1479. return true;
  1480. }
  1481. throw new InvalidCastException($this->getModel(), $key, $castType);
  1482. }
  1483. /**
  1484. * Determine if the given key is cast using an enum.
  1485. *
  1486. * @param string $key
  1487. * @return bool
  1488. */
  1489. protected function isEnumCastable($key)
  1490. {
  1491. $casts = $this->getCasts();
  1492. if (! array_key_exists($key, $casts)) {
  1493. return false;
  1494. }
  1495. $castType = $casts[$key];
  1496. if (in_array($castType, static::$primitiveCastTypes)) {
  1497. return false;
  1498. }
  1499. return enum_exists($castType);
  1500. }
  1501. /**
  1502. * Determine if the key is deviable using a custom class.
  1503. *
  1504. * @param string $key
  1505. * @return bool
  1506. *
  1507. * @throws \Illuminate\Database\Eloquent\InvalidCastException
  1508. */
  1509. protected function isClassDeviable($key)
  1510. {
  1511. if (! $this->isClassCastable($key)) {
  1512. return false;
  1513. }
  1514. $castType = $this->resolveCasterClass($key);
  1515. return method_exists($castType::class, 'increment') && method_exists($castType::class, 'decrement');
  1516. }
  1517. /**
  1518. * Determine if the key is serializable using a custom class.
  1519. *
  1520. * @param string $key
  1521. * @return bool
  1522. *
  1523. * @throws \Illuminate\Database\Eloquent\InvalidCastException
  1524. */
  1525. protected function isClassSerializable($key)
  1526. {
  1527. return ! $this->isEnumCastable($key) &&
  1528. $this->isClassCastable($key) &&
  1529. method_exists($this->resolveCasterClass($key), 'serialize');
  1530. }
  1531. /**
  1532. * Resolve the custom caster class for a given key.
  1533. *
  1534. * @param string $key
  1535. * @return mixed
  1536. */
  1537. protected function resolveCasterClass($key)
  1538. {
  1539. $castType = $this->getCasts()[$key];
  1540. $arguments = [];
  1541. if (is_string($castType) && str_contains($castType, ':')) {
  1542. $segments = explode(':', $castType, 2);
  1543. $castType = $segments[0];
  1544. $arguments = explode(',', $segments[1]);
  1545. }
  1546. if (is_subclass_of($castType, Castable::class)) {
  1547. $castType = $castType::castUsing($arguments);
  1548. }
  1549. if (is_object($castType)) {
  1550. return $castType;
  1551. }
  1552. return new $castType(...$arguments);
  1553. }
  1554. /**
  1555. * Parse the given caster class, removing any arguments.
  1556. *
  1557. * @param string $class
  1558. * @return string
  1559. */
  1560. protected function parseCasterClass($class)
  1561. {
  1562. return ! str_contains($class, ':')
  1563. ? $class
  1564. : explode(':', $class, 2)[0];
  1565. }
  1566. /**
  1567. * Merge the cast class and attribute cast attributes back into the model.
  1568. *
  1569. * @return void
  1570. */
  1571. protected function mergeAttributesFromCachedCasts()
  1572. {
  1573. $this->mergeAttributesFromClassCasts();
  1574. $this->mergeAttributesFromAttributeCasts();
  1575. }
  1576. /**
  1577. * Merge the cast class attributes back into the model.
  1578. *
  1579. * @return void
  1580. */
  1581. protected function mergeAttributesFromClassCasts()
  1582. {
  1583. foreach ($this->classCastCache as $key => $value) {
  1584. $caster = $this->resolveCasterClass($key);
  1585. $this->attributes = array_merge(
  1586. $this->attributes,
  1587. $caster instanceof CastsInboundAttributes
  1588. ? [$key => $value]
  1589. : $this->normalizeCastClassResponse($key, $caster->set($this, $key, $value, $this->attributes))
  1590. );
  1591. }
  1592. }
  1593. /**
  1594. * Merge the cast class attributes back into the model.
  1595. *
  1596. * @return void
  1597. */
  1598. protected function mergeAttributesFromAttributeCasts()
  1599. {
  1600. foreach ($this->attributeCastCache as $key => $value) {
  1601. $attribute = $this->{Str::camel($key)}();
  1602. if ($attribute->get && ! $attribute->set) {
  1603. continue;
  1604. }
  1605. $callback = $attribute->set ?: function ($value) use ($key) {
  1606. $this->attributes[$key] = $value;
  1607. };
  1608. $this->attributes = array_merge(
  1609. $this->attributes,
  1610. $this->normalizeCastClassResponse(
  1611. $key, $callback($value, $this->attributes)
  1612. )
  1613. );
  1614. }
  1615. }
  1616. /**
  1617. * Normalize the response from a custom class caster.
  1618. *
  1619. * @param string $key
  1620. * @param mixed $value
  1621. * @return array
  1622. */
  1623. protected function normalizeCastClassResponse($key, $value)
  1624. {
  1625. return is_array($value) ? $value : [$key => $value];
  1626. }
  1627. /**
  1628. * Get all of the current attributes on the model.
  1629. *
  1630. * @return array
  1631. */
  1632. public function getAttributes()
  1633. {
  1634. $this->mergeAttributesFromCachedCasts();
  1635. return $this->attributes;
  1636. }
  1637. /**
  1638. * Get all of the current attributes on the model for an insert operation.
  1639. *
  1640. * @return array
  1641. */
  1642. protected function getAttributesForInsert()
  1643. {
  1644. return $this->getAttributes();
  1645. }
  1646. /**
  1647. * Set the array of model attributes. No checking is done.
  1648. *
  1649. * @param array $attributes
  1650. * @param bool $sync
  1651. * @return $this
  1652. */
  1653. public function setRawAttributes(array $attributes, $sync = false)
  1654. {
  1655. $this->attributes = $attributes;
  1656. if ($sync) {
  1657. $this->syncOriginal();
  1658. }
  1659. $this->classCastCache = [];
  1660. $this->attributeCastCache = [];
  1661. return $this;
  1662. }
  1663. /**
  1664. * Get the model's original attribute values.
  1665. *
  1666. * @param string|null $key
  1667. * @param mixed $default
  1668. * @return mixed|array
  1669. */
  1670. public function getOriginal($key = null, $default = null)
  1671. {
  1672. return (new static)->setRawAttributes(
  1673. $this->original, $sync = true
  1674. )->getOriginalWithoutRewindingModel($key, $default);
  1675. }
  1676. /**
  1677. * Get the model's original attribute values.
  1678. *
  1679. * @param string|null $key
  1680. * @param mixed $default
  1681. * @return mixed|array
  1682. */
  1683. protected function getOriginalWithoutRewindingModel($key = null, $default = null)
  1684. {
  1685. if ($key) {
  1686. return $this->transformModelValue(
  1687. $key, Arr::get($this->original, $key, $default)
  1688. );
  1689. }
  1690. return collect($this->original)->mapWithKeys(function ($value, $key) {
  1691. return [$key => $this->transformModelValue($key, $value)];
  1692. })->all();
  1693. }
  1694. /**
  1695. * Get the model's raw original attribute values.
  1696. *
  1697. * @param string|null $key
  1698. * @param mixed $default
  1699. * @return mixed|array
  1700. */
  1701. public function getRawOriginal($key = null, $default = null)
  1702. {
  1703. return Arr::get($this->original, $key, $default);
  1704. }
  1705. /**
  1706. * Get a subset of the model's attributes.
  1707. *
  1708. * @param array|mixed $attributes
  1709. * @return array
  1710. */
  1711. public function only($attributes)
  1712. {
  1713. $results = [];
  1714. foreach (is_array($attributes) ? $attributes : func_get_args() as $attribute) {
  1715. $results[$attribute] = $this->getAttribute($attribute);
  1716. }
  1717. return $results;
  1718. }
  1719. /**
  1720. * Sync the original attributes with the current.
  1721. *
  1722. * @return $this
  1723. */
  1724. public function syncOriginal()
  1725. {
  1726. $this->original = $this->getAttributes();
  1727. return $this;
  1728. }
  1729. /**
  1730. * Sync a single original attribute with its current value.
  1731. *
  1732. * @param string $attribute
  1733. * @return $this
  1734. */
  1735. public function syncOriginalAttribute($attribute)
  1736. {
  1737. return $this->syncOriginalAttributes($attribute);
  1738. }
  1739. /**
  1740. * Sync multiple original attribute with their current values.
  1741. *
  1742. * @param array|string $attributes
  1743. * @return $this
  1744. */
  1745. public function syncOriginalAttributes($attributes)
  1746. {
  1747. $attributes = is_array($attributes) ? $attributes : func_get_args();
  1748. $modelAttributes = $this->getAttributes();
  1749. foreach ($attributes as $attribute) {
  1750. $this->original[$attribute] = $modelAttributes[$attribute];
  1751. }
  1752. return $this;
  1753. }
  1754. /**
  1755. * Sync the changed attributes.
  1756. *
  1757. * @return $this
  1758. */
  1759. public function syncChanges()
  1760. {
  1761. $this->changes = $this->getDirty();
  1762. return $this;
  1763. }
  1764. /**
  1765. * Determine if the model or any of the given attribute(s) have been modified.
  1766. *
  1767. * @param array|string|null $attributes
  1768. * @return bool
  1769. */
  1770. public function isDirty($attributes = null)
  1771. {
  1772. return $this->hasChanges(
  1773. $this->getDirty(), is_array($attributes) ? $attributes : func_get_args()
  1774. );
  1775. }
  1776. /**
  1777. * Determine if the model or all the given attribute(s) have remained the same.
  1778. *
  1779. * @param array|string|null $attributes
  1780. * @return bool
  1781. */
  1782. public function isClean($attributes = null)
  1783. {
  1784. return ! $this->isDirty(...func_get_args());
  1785. }
  1786. /**
  1787. * Discard attribute changes and reset the attributes to their original state.
  1788. *
  1789. * @return $this
  1790. */
  1791. public function discardChanges()
  1792. {
  1793. [$this->attributes, $this->changes] = [$this->original, []];
  1794. return $this;
  1795. }
  1796. /**
  1797. * Determine if the model or any of the given attribute(s) were changed when the model was last saved.
  1798. *
  1799. * @param array|string|null $attributes
  1800. * @return bool
  1801. */
  1802. public function wasChanged($attributes = null)
  1803. {
  1804. return $this->hasChanges(
  1805. $this->getChanges(), is_array($attributes) ? $attributes : func_get_args()
  1806. );
  1807. }
  1808. /**
  1809. * Determine if any of the given attributes were changed when the model was last saved.
  1810. *
  1811. * @param array $changes
  1812. * @param array|string|null $attributes
  1813. * @return bool
  1814. */
  1815. protected function hasChanges($changes, $attributes = null)
  1816. {
  1817. // If no specific attributes were provided, we will just see if the dirty array
  1818. // already contains any attributes. If it does we will just return that this
  1819. // count is greater than zero. Else, we need to check specific attributes.
  1820. if (empty($attributes)) {
  1821. return count($changes) > 0;
  1822. }
  1823. // Here we will spin through every attribute and see if this is in the array of
  1824. // dirty attributes. If it is, we will return true and if we make it through
  1825. // all of the attributes for the entire array we will return false at end.
  1826. foreach (Arr::wrap($attributes) as $attribute) {
  1827. if (array_key_exists($attribute, $changes)) {
  1828. return true;
  1829. }
  1830. }
  1831. return false;
  1832. }
  1833. /**
  1834. * Get the attributes that have been changed since the last sync.
  1835. *
  1836. * @return array
  1837. */
  1838. public function getDirty()
  1839. {
  1840. $dirty = [];
  1841. foreach ($this->getAttributes() as $key => $value) {
  1842. if (! $this->originalIsEquivalent($key)) {
  1843. $dirty[$key] = $value;
  1844. }
  1845. }
  1846. return $dirty;
  1847. }
  1848. /**
  1849. * Get the attributes that have been changed since the last sync for an update operation.
  1850. *
  1851. * @return array
  1852. */
  1853. protected function getDirtyForUpdate()
  1854. {
  1855. return $this->getDirty();
  1856. }
  1857. /**
  1858. * Get the attributes that were changed when the model was last saved.
  1859. *
  1860. * @return array
  1861. */
  1862. public function getChanges()
  1863. {
  1864. return $this->changes;
  1865. }
  1866. /**
  1867. * Determine if the new and old values for a given key are equivalent.
  1868. *
  1869. * @param string $key
  1870. * @return bool
  1871. */
  1872. public function originalIsEquivalent($key)
  1873. {
  1874. if (! array_key_exists($key, $this->original)) {
  1875. return false;
  1876. }
  1877. $attribute = Arr::get($this->attributes, $key);
  1878. $original = Arr::get($this->original, $key);
  1879. if ($attribute === $original) {
  1880. return true;
  1881. } elseif (is_null($attribute)) {
  1882. return false;
  1883. } elseif ($this->isDateAttribute($key) || $this->isDateCastableWithCustomFormat($key)) {
  1884. return $this->fromDateTime($attribute) ===
  1885. $this->fromDateTime($original);
  1886. } elseif ($this->hasCast($key, ['object', 'collection'])) {
  1887. return $this->fromJson($attribute) ===
  1888. $this->fromJson($original);
  1889. } elseif ($this->hasCast($key, ['real', 'float', 'double'])) {
  1890. if ($original === null) {
  1891. return false;
  1892. }
  1893. return abs($this->castAttribute($key, $attribute) - $this->castAttribute($key, $original)) < PHP_FLOAT_EPSILON * 4;
  1894. } elseif ($this->isEncryptedCastable($key) && ! empty(static::currentEncrypter()->getPreviousKeys())) {
  1895. return false;
  1896. } elseif ($this->hasCast($key, static::$primitiveCastTypes)) {
  1897. return $this->castAttribute($key, $attribute) ===
  1898. $this->castAttribute($key, $original);
  1899. } elseif ($this->isClassCastable($key) && Str::startsWith($this->getCasts()[$key], [AsArrayObject::class, AsCollection::class])) {
  1900. return $this->fromJson($attribute) === $this->fromJson($original);
  1901. } elseif ($this->isClassCastable($key) && Str::startsWith($this->getCasts()[$key], [AsEnumArrayObject::class, AsEnumCollection::class])) {
  1902. return $this->fromJson($attribute) === $this->fromJson($original);
  1903. } elseif ($this->isClassCastable($key) && $original !== null && Str::startsWith($this->getCasts()[$key], [AsEncryptedArrayObject::class, AsEncryptedCollection::class])) {
  1904. if (empty(static::currentEncrypter()->getPreviousKeys())) {
  1905. return $this->fromEncryptedString($attribute) === $this->fromEncryptedString($original);
  1906. }
  1907. return false;
  1908. }
  1909. return is_numeric($attribute) && is_numeric($original)
  1910. && strcmp((string) $attribute, (string) $original) === 0;
  1911. }
  1912. /**
  1913. * Transform a raw model value using mutators, casts, etc.
  1914. *
  1915. * @param string $key
  1916. * @param mixed $value
  1917. * @return mixed
  1918. */
  1919. protected function transformModelValue($key, $value)
  1920. {
  1921. // If the attribute has a get mutator, we will call that then return what
  1922. // it returns as the value, which is useful for transforming values on
  1923. // retrieval from the model to a form that is more useful for usage.
  1924. if ($this->hasGetMutator($key)) {
  1925. return $this->mutateAttribute($key, $value);
  1926. } elseif ($this->hasAttributeGetMutator($key)) {
  1927. return $this->mutateAttributeMarkedAttribute($key, $value);
  1928. }
  1929. // If the attribute exists within the cast array, we will convert it to
  1930. // an appropriate native PHP type dependent upon the associated value
  1931. // given with the key in the pair. Dayle made this comment line up.
  1932. if ($this->hasCast($key)) {
  1933. if (static::preventsAccessingMissingAttributes() &&
  1934. ! array_key_exists($key, $this->attributes) &&
  1935. ($this->isEnumCastable($key) ||
  1936. in_array($this->getCastType($key), static::$primitiveCastTypes))) {
  1937. $this->throwMissingAttributeExceptionIfApplicable($key);
  1938. }
  1939. return $this->castAttribute($key, $value);
  1940. }
  1941. // If the attribute is listed as a date, we will convert it to a DateTime
  1942. // instance on retrieval, which makes it quite convenient to work with
  1943. // date fields without having to create a mutator for each property.
  1944. if ($value !== null
  1945. && \in_array($key, $this->getDates(), false)) {
  1946. return $this->asDateTime($value);
  1947. }
  1948. return $value;
  1949. }
  1950. /**
  1951. * Append attributes to query when building a query.
  1952. *
  1953. * @param array|string $attributes
  1954. * @return $this
  1955. */
  1956. public function append($attributes)
  1957. {
  1958. $this->appends = array_values(array_unique(
  1959. array_merge($this->appends, is_string($attributes) ? func_get_args() : $attributes)
  1960. ));
  1961. return $this;
  1962. }
  1963. /**
  1964. * Get the accessors that are being appended to model arrays.
  1965. *
  1966. * @return array
  1967. */
  1968. public function getAppends()
  1969. {
  1970. return $this->appends;
  1971. }
  1972. /**
  1973. * Set the accessors to append to model arrays.
  1974. *
  1975. * @param array $appends
  1976. * @return $this
  1977. */
  1978. public function setAppends(array $appends)
  1979. {
  1980. $this->appends = $appends;
  1981. return $this;
  1982. }
  1983. /**
  1984. * Return whether the accessor attribute has been appended.
  1985. *
  1986. * @param string $attribute
  1987. * @return bool
  1988. */
  1989. public function hasAppended($attribute)
  1990. {
  1991. return in_array($attribute, $this->appends);
  1992. }
  1993. /**
  1994. * Get the mutated attributes for a given instance.
  1995. *
  1996. * @return array
  1997. */
  1998. public function getMutatedAttributes()
  1999. {
  2000. if (! isset(static::$mutatorCache[static::class])) {
  2001. static::cacheMutatedAttributes($this);
  2002. }
  2003. return static::$mutatorCache[static::class];
  2004. }
  2005. /**
  2006. * Extract and cache all the mutated attributes of a class.
  2007. *
  2008. * @param object|string $classOrInstance
  2009. * @return void
  2010. */
  2011. public static function cacheMutatedAttributes($classOrInstance)
  2012. {
  2013. $reflection = new ReflectionClass($classOrInstance);
  2014. $class = $reflection->getName();
  2015. static::$getAttributeMutatorCache[$class] =
  2016. collect($attributeMutatorMethods = static::getAttributeMarkedMutatorMethods($classOrInstance))
  2017. ->mapWithKeys(function ($match) {
  2018. return [lcfirst(static::$snakeAttributes ? Str::snake($match) : $match) => true];
  2019. })->all();
  2020. static::$mutatorCache[$class] = collect(static::getMutatorMethods($class))
  2021. ->merge($attributeMutatorMethods)
  2022. ->map(function ($match) {
  2023. return lcfirst(static::$snakeAttributes ? Str::snake($match) : $match);
  2024. })->all();
  2025. }
  2026. /**
  2027. * Get all of the attribute mutator methods.
  2028. *
  2029. * @param mixed $class
  2030. * @return array
  2031. */
  2032. protected static function getMutatorMethods($class)
  2033. {
  2034. preg_match_all('/(?<=^|;)get([^;]+?)Attribute(;|$)/', implode(';', get_class_methods($class)), $matches);
  2035. return $matches[1];
  2036. }
  2037. /**
  2038. * Get all of the "Attribute" return typed attribute mutator methods.
  2039. *
  2040. * @param mixed $class
  2041. * @return array
  2042. */
  2043. protected static function getAttributeMarkedMutatorMethods($class)
  2044. {
  2045. $instance = is_object($class) ? $class : new $class;
  2046. return collect((new ReflectionClass($instance))->getMethods())->filter(function ($method) use ($instance) {
  2047. $returnType = $method->getReturnType();
  2048. if ($returnType instanceof ReflectionNamedType &&
  2049. $returnType->getName() === Attribute::class) {
  2050. if (is_callable($method->invoke($instance)->get)) {
  2051. return true;
  2052. }
  2053. }
  2054. return false;
  2055. })->map->name->values()->all();
  2056. }
  2057. }