GuardsAttributes.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. <?php
  2. namespace Illuminate\Database\Eloquent\Concerns;
  3. trait GuardsAttributes
  4. {
  5. /**
  6. * The attributes that are mass assignable.
  7. *
  8. * @var array<int, string>
  9. */
  10. protected $fillable = [];
  11. /**
  12. * The attributes that aren't mass assignable.
  13. *
  14. * @var array<string>|bool
  15. */
  16. protected $guarded = ['*'];
  17. /**
  18. * Indicates if all mass assignment is enabled.
  19. *
  20. * @var bool
  21. */
  22. protected static $unguarded = false;
  23. /**
  24. * The actual columns that exist on the database and can be guarded.
  25. *
  26. * @var array<string>
  27. */
  28. protected static $guardableColumns = [];
  29. /**
  30. * Get the fillable attributes for the model.
  31. *
  32. * @return array<string>
  33. */
  34. public function getFillable()
  35. {
  36. return $this->fillable;
  37. }
  38. /**
  39. * Set the fillable attributes for the model.
  40. *
  41. * @param array<string> $fillable
  42. * @return $this
  43. */
  44. public function fillable(array $fillable)
  45. {
  46. $this->fillable = $fillable;
  47. return $this;
  48. }
  49. /**
  50. * Merge new fillable attributes with existing fillable attributes on the model.
  51. *
  52. * @param array<string> $fillable
  53. * @return $this
  54. */
  55. public function mergeFillable(array $fillable)
  56. {
  57. $this->fillable = array_values(array_unique(array_merge($this->fillable, $fillable)));
  58. return $this;
  59. }
  60. /**
  61. * Get the guarded attributes for the model.
  62. *
  63. * @return array<string>
  64. */
  65. public function getGuarded()
  66. {
  67. return $this->guarded === false
  68. ? []
  69. : $this->guarded;
  70. }
  71. /**
  72. * Set the guarded attributes for the model.
  73. *
  74. * @param array<string> $guarded
  75. * @return $this
  76. */
  77. public function guard(array $guarded)
  78. {
  79. $this->guarded = $guarded;
  80. return $this;
  81. }
  82. /**
  83. * Merge new guarded attributes with existing guarded attributes on the model.
  84. *
  85. * @param array<string> $guarded
  86. * @return $this
  87. */
  88. public function mergeGuarded(array $guarded)
  89. {
  90. $this->guarded = array_values(array_unique(array_merge($this->guarded, $guarded)));
  91. return $this;
  92. }
  93. /**
  94. * Disable all mass assignable restrictions.
  95. *
  96. * @param bool $state
  97. * @return void
  98. */
  99. public static function unguard($state = true)
  100. {
  101. static::$unguarded = $state;
  102. }
  103. /**
  104. * Enable the mass assignment restrictions.
  105. *
  106. * @return void
  107. */
  108. public static function reguard()
  109. {
  110. static::$unguarded = false;
  111. }
  112. /**
  113. * Determine if the current state is "unguarded".
  114. *
  115. * @return bool
  116. */
  117. public static function isUnguarded()
  118. {
  119. return static::$unguarded;
  120. }
  121. /**
  122. * Run the given callable while being unguarded.
  123. *
  124. * @param callable $callback
  125. * @return mixed
  126. */
  127. public static function unguarded(callable $callback)
  128. {
  129. if (static::$unguarded) {
  130. return $callback();
  131. }
  132. static::unguard();
  133. try {
  134. return $callback();
  135. } finally {
  136. static::reguard();
  137. }
  138. }
  139. /**
  140. * Determine if the given attribute may be mass assigned.
  141. *
  142. * @param string $key
  143. * @return bool
  144. */
  145. public function isFillable($key)
  146. {
  147. if (static::$unguarded) {
  148. return true;
  149. }
  150. // If the key is in the "fillable" array, we can of course assume that it's
  151. // a fillable attribute. Otherwise, we will check the guarded array when
  152. // we need to determine if the attribute is black-listed on the model.
  153. if (in_array($key, $this->getFillable())) {
  154. return true;
  155. }
  156. // If the attribute is explicitly listed in the "guarded" array then we can
  157. // return false immediately. This means this attribute is definitely not
  158. // fillable and there is no point in going any further in this method.
  159. if ($this->isGuarded($key)) {
  160. return false;
  161. }
  162. return empty($this->getFillable()) &&
  163. ! str_contains($key, '.') &&
  164. ! str_starts_with($key, '_');
  165. }
  166. /**
  167. * Determine if the given key is guarded.
  168. *
  169. * @param string $key
  170. * @return bool
  171. */
  172. public function isGuarded($key)
  173. {
  174. if (empty($this->getGuarded())) {
  175. return false;
  176. }
  177. return $this->getGuarded() == ['*'] ||
  178. ! empty(preg_grep('/^'.preg_quote($key, '/').'$/i', $this->getGuarded())) ||
  179. ! $this->isGuardableColumn($key);
  180. }
  181. /**
  182. * Determine if the given column is a valid, guardable column.
  183. *
  184. * @param string $key
  185. * @return bool
  186. */
  187. protected function isGuardableColumn($key)
  188. {
  189. if (! isset(static::$guardableColumns[get_class($this)])) {
  190. $columns = $this->getConnection()
  191. ->getSchemaBuilder()
  192. ->getColumnListing($this->getTable());
  193. if (empty($columns)) {
  194. return true;
  195. }
  196. static::$guardableColumns[get_class($this)] = $columns;
  197. }
  198. return in_array($key, static::$guardableColumns[get_class($this)]);
  199. }
  200. /**
  201. * Determine if the model is totally guarded.
  202. *
  203. * @return bool
  204. */
  205. public function totallyGuarded()
  206. {
  207. return count($this->getFillable()) === 0 && $this->getGuarded() == ['*'];
  208. }
  209. /**
  210. * Get the fillable attributes of a given array.
  211. *
  212. * @param array $attributes
  213. * @return array
  214. */
  215. protected function fillableFromArray(array $attributes)
  216. {
  217. if (count($this->getFillable()) > 0 && ! static::$unguarded) {
  218. return array_intersect_key($attributes, array_flip($this->getFillable()));
  219. }
  220. return $attributes;
  221. }
  222. }