8000 introduce freezing model · laravel/framework@3b80435 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3b80435

Browse files
committed
introduce freezing model
1 parent 4cd413b commit 3b80435

File tree

4 files changed

+136
-35
lines changed

4 files changed

+136
-35
lines changed

src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Illuminate\Database\Eloquent\Casts\AsEnumCollection;
2222
use Illuminate\Database\Eloquent\Casts\Attribute;
2323
use Illuminate\Database\Eloquent\Casts\Json;
24+
use Illuminate\Database\Eloquent\FrozenModelException;
2425
use Illuminate\Database\Eloquent\InvalidCastException;
2526
use Illuminate\Database\Eloquent\JsonEncodingException;
2627
use Illuminate\Database\Eloquent\MissingAttributeException;
@@ -1006,10 +1007,16 @@ protected function isDecimalCast($cast)
10061007
* @param string $key
10071008
* @param mixed $value
10081009
* @return mixed
1010+
*
1011+
* @throws \Illuminate\Database\Eloquent\FrozenModelException
10091012
*/
10101013
public function setAttribute($key, $value)
10111014
{
1012-
// First we will check for the presence of a mutator for the set operation
1015+
if ($this->frozen) {
1016+
throw FrozenModelException::forSetAttribute($this, $key);
1017+
}
1018+
1019+
// Next we will check for the presence of a mutator for the set operation
10131020
// which simply lets the developers tweak the attribute as it is set on
10141021
// this model, such as "json_encoding" a listing of data for storage.
10151022
if ($this->hasSetMutator($key)) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Eloquent;
4+
5+
class FrozenModelException extends \RuntimeException
6+
{
7+
/**
8+
* @param Model $model
9+
* @return self
10+
*/
11+
public static function forFill(Model $model)
12+
{
13+
return new self(sprintf("Cannot fill properties on Model [%s] because it is frozen.", $model::class));
14+
}
15+
16+
public static function forSetAttribute(Model $model, $attribute)
17+
{
18+
return new self(sprintf("Cannot set property [%s] on Model [%s] because it is frozen.", $attribute, $model::class));
19+
}
20+
}

src/Illuminate/Database/Eloquent/Model.php

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ abstract class Model implements Arrayable, ArrayAccess, CanBeEscapedWhenCastToSt
3737
Concerns\GuardsAttributes,
3838
Concerns\PreventsCircularRecursion,
3939
ForwardsCalls;
40+
4041
/** @use HasCollection<\Illuminate\Database\Eloquent\Collection<array-key, static>> */
4142
use HasCollection;
4243

@@ -124,6 +125,13 @@ abstract class Model implements Arrayable, ArrayAccess, CanBeEscapedWhenCastToSt
124125
*/
125126
protected $escapeWhenCastingToString = false;
126127

128+
/**
129+
* Indicates that the model is frozen and cannot modify properties or load relations.
130+
*
131+
* @var bool
132+
*/
133+
protected $frozen = false;
134+
127135
/**
128136
* The connection resolver instance.
129137
*
@@ -267,7 +275,7 @@ public function __construct(array $attributes = [])
267275
*/
268276
protected function bootIfNotBooted()
269277
{
270-
if (! isset(static::$booted[static::class])) {
278+
if (!isset(static::$booted[static::class])) {
271279
static::$booted[static::class] = true;
272280

273281
$this->fireModelEvent('booting', false);
@@ -316,7 +324,7 @@ protected static function bootTraits()
316324
foreach (class_uses_recursive($class) as $trait) {
317325
$method = 'boot'.class_basename($trait);
318326

319-
if (method_exists($class, $method) && ! in_array($method, $booted)) {
327+
if (method_exists($class, $method) && !in_array($method, $booted)) {
320328
forward_static_call([$class, $method]);
321329

322330
$booted[] = $method;
@@ -405,7 +413,7 @@ public static function isIgnoringTouch($class = null)
405413
{
406414
$class = $class ?: static::class;
407415

408-
if (! get_class_vars($class)['timestamps'] || ! $class::UPDATED_AT) {
416+
if (!get_class_vars($class)['timestamps'] || !$class::UPDATED_AT) {
409417
return true;
410418
}
411419

@@ -526,6 +534,10 @@ public static function withoutBroadcasting(callable $callback)
526534
*/
527535
public function fill(array $attributes)
528536
{
537+
if ($this->frozen) {
538+
throw FrozenModelException::forFill($this);
539+
}
540+
529541
$totallyGuarded = $this->totallyGuarded();
530542

531543
$fillable = $this->fillableFromArray($attributes);
@@ -574,7 +586,7 @@ public function fill(array $attributes)
574586
*/
575587
public function forceFill(array $attributes)
576588
{
577-
return static::unguarded(fn () => $this->fill($attributes));
589+
return static::unguarded(fn() => $this->fill($attributes));
578590
}
579591

580592
/**
@@ -734,7 +746,7 @@ public function load($relations)
734746
*/
735747
public function loadMorph($relation, $relations)
736748
{
737-
if (! $this->{$relation}) {
749+
if (!$this->{$relation}) {
738750
return $this;
739751
}
740752

@@ -858,7 +870,7 @@ public function loadExists($relations)
858870
*/
859871
public function loadMorphAggregate($relation, $relations, $column, $function = null)
860872
{
861-
if (! $this->{$relation}) {
873+
if (!$this->{$relation}) {
862874
return $this;
863875
}
864876

@@ -970,7 +982,7 @@ protected function decrement($column, $amount = 1, array $extra = [])
970982
*/
971983
protected function incrementOrDecrement($column, $amount, $extra, $method)
972984
{
973-
if (! $this->exists) {
985+
if (!$this->exists) {
974986
return $this->newQueryWithoutRelationships()->{$method}($column, $amount, $extra);
975987
}
976988

@@ -988,13 +1000,14 @@ protected function incrementOrDecrement($column, $amount, $extra, $method)
9881000
$amount = (clone $this)->setAttribute($column, $amount)->getAttributeFromArray($column);
9891001
}
9901002

991-
return tap($this->setKeysForSaveQuery($this->newQueryWithoutScopes())->{$method}($column, $amount, $extra), function () use ($column) {
992-
$this->syncChanges();
1003+
return tap($this->setKeysForSaveQuery($this->newQueryWithoutScopes())->{$method}($column, $amount, $extra),
1004+
function () use ($column) {
1005+
$this->syncChanges();
9931006

994-
$this->fireModelEvent('updated', false);
1007+
$this->fireModelEvent('updated', false);
9951008

996-
$this->syncOriginalAttribute($column);
997-
});
1009+
$this->syncOriginalAttribute($column);
1010+
});
9981011
}
9991012

10001013
/**
@@ -1006,7 +1019,7 @@ protected function incrementOrDecrement($column, $amount, $extra, $method)
10061019
*/
10071020
public function update(array $attributes = [], array $options = [])
10081021
{
1009-
if (! $this->exists) {
1022+
if (!$this->exists) {
10101023
return false;
10111024
}
10121025

@@ -1024,7 +1037,7 @@ public function update(array $attributes = [], array $options = [])
10241037
*/
10251038
public function updateOrFail(array $attributes = [], array $options = [])
10261039
{
1027-
if (! $this->exists) {
1040+
if (!$this->exists) {
10281041
return false;
10291042
}
10301043

@@ -1040,7 +1053,7 @@ public function updateOrFail(array $attributes = [], array $options = [])
10401053
*/
10411054
public function updateQuietly(array $attributes = [], array $options = [])
10421055
{
1043-
if (! $this->exists) {
1056+
if (!$this->exists) {
10441057
return false;
10451058
}
10461059

@@ -1085,7 +1098,7 @@ protected function decrementQuietly($column, $amount = 1, array $extra = [])
10851098
public function push()
10861099
{
10871100
return $this->withoutRecursion(function () {
1088-
if (! $this->save()) {
1101+
if (!$this->save()) {
10891102
return false;
10901103
}
10911104

@@ -1097,7 +1110,7 @@ public function push()
10971110
? $models->all() : [$models];
10981111

10991112
foreach (array_filter($models) as $model) {
1100-
if (! $model->push()) {
1113+
if (!$model->push()) {
11011114
return false;
11021115
}
11031116
}
@@ -1114,7 +1127,7 @@ public function push()
11141127
*/
11151128
public function pushQuietly()
11161129
{
1117-
return static::withoutEvents(fn () => $this->push());
1130+
return static::withoutEvents(fn() => $this->push());
11181131
}
11191132

11201133
/**
@@ -1125,7 +1138,7 @@ public function pushQuietly()
11251138
*/
11261139
public function saveQuietly(array $options = [])
11271140
{
1128-
return static::withoutEvents(fn () => $this->save($options));
1141+
return static::withoutEvents(fn() => $this->save($options));
11291142
}
11301143

11311144
/**
@@ -1161,7 +1174,7 @@ public function save(array $options = [])
11611174
else {
11621175
$saved = $this->performInsert($query);
11631176

1164-
if (! $this->getConnectionName() &&
1177+
if (!$this->getConnectionName() &&
11651178
$connection = $query->getConnection()) {
11661179
$this->setConnection($connection->getName());
11671180
}
@@ -1187,7 +1200,7 @@ public function save(array $options = [])
11871200
*/
11881201
public function saveOrFail(array $options = [])
11891202
{
1190-
return $this->getConnection()->transaction(fn () => $this->save($options));
1203+
return $this->getConnection()->transaction(fn() => $this->save($options));
11911204
}
11921205

11931206
/**
@@ -1416,7 +1429,7 @@ public function delete()
14161429
// If the model doesn't exist, there is nothing to delete so we'll just return
14171430
// immediately and not do anything else. Otherwise, we will continue with a
14181431
// deletion process on the model, firing the proper events, and so forth.
1419-
if (! $this->exists) {
1432+
if (!$this->exists) {
14201433
return;
14211434
}
14221435

@@ -1446,7 +1459,7 @@ public function delete()
14461459
*/
14471460
public function deleteQuietly()
14481461
{
1449-
return static::withoutEvents(fn () => $this->delete());
1462+
return static::withoutEvents(fn() => $this->delete());
14501463
}
14511464

14521465
/**
@@ -1458,11 +1471,11 @@ public function deleteQuietly()
14581471
*/
14591472
public function deleteOrFail()
14601473
{
1461-
if (! $this->exists) {
1474+
if (!$this->exists) {
14621475
return false;
14631476
}
14641477

1465-
return $this->getConnection()->transaction(fn () => $this->delete());
1478+
return $this->getConnection()->transaction(fn() => $this->delete());
14661479
}
14671480

14681481
/**
@@ -1661,8 +1674,8 @@ public function callNamedScope($scope, array $parameters = [])
16611674
public function toArray()
16621675
{
16631676
return $this->withoutRecursion(
1664-
fn () => array_merge($this->attributesToArray(), $this->relationsToArray()),
1665-
fn () => $this->attributesToArray(),
1677+
fn() => array_merge($this->attributesToArray(), $this->relationsToArray()),
1678+
fn() => $this->attributesToArray(),
16661679
);
16671680
}
16681681

@@ -1703,7 +1716,7 @@ public function jsonSerialize(): mixed
17031716
*/
17041717
public function fresh($with = [])
17051718
{
1706-
if (! $this->exists) {
1719+
if (!$this->exists) {
17071720
return;
17081721
}
17091722

@@ -1720,7 +1733,7 @@ public function fresh($with = [])
17201733
*/
17211734
public function refresh()
17221735
{
1723-
if (! $this->exists) {
1736+
if (!$this->exists) {
17241737
return $this;
17251738
}
17261739

@@ -1778,7 +1791,7 @@ public function replicate(?array $except = null)
17781791
*/
17791792
public function replicateQuietly(?array $except = null)
17801793
{
1781-
return static::withoutEvents(fn () => $this->replicate($except));
1794+
return static::withoutEvents(fn() => $this->replicate($except));
17821795
}
17831796

17841797
/**
@@ -1789,7 +1802,7 @@ public function replicateQuietly(?array $except = null)
17891802
*/
17901803
public function is($model)
17911804
{
1792-
return ! is_null($model) &&
1805+
return !is_null($model) &&
17931806
$this->getKey() === $model->getKey() &&
17941807
$this->getTable() === $model->getTable() &&
17951808
$this->getConnectionName() === $model->getConnectionName();
@@ -1803,7 +1816,7 @@ public function is($model)
18031816
*/
18041817
public function isNot($model)
18051818
{
1806-
return ! $this->is($model);
1819+
return !$this->is($model);
18071820
}
18081821

18091822
/**
@@ -2014,7 +2027,7 @@ public function getQueueableRelations()
20142027
$relations = [];
20152028

20162029
foreach ($this->getRelations() as $key => $relation) {
2017-
if (! method_exists($this, $key)) {
2030+
if (!method_exists($this, $key)) {
20182031
continue;
20192032
}
20202033

@@ -2117,6 +2130,25 @@ public function resolveSoftDeletableChildRouteBinding($childType, $value, $field
21172130
return $this->resolveChildRouteBindingQuery($childType, $value, $field)->withTrashed()->first();
21182131
}
21192132

2133+
/**
2134+
* @param $frozen
2135+
* @return $this
2136+
*/
2137+
public function freeze($frozen = true)
2138+
{
2139+
$this->frozen = $frozen;
2140+
2141+
return $this;
2142+
}
2143+
2144+
/**
2145+
* @return bool
2146+
*/
2147+
public function isFrozen()
2148+
{
2149+
return $this->frozen;
2150+
}
2151+
21202152
/**
21212153
* Retrieve the child model query for a bound value.
21222154
*
@@ -2280,7 +2312,7 @@ public function __set($key, $value)
22802312
public function offsetExists($offset): bool
22812313
{
22822314
try {
2283-
return ! is_null($this->getAttribute($offset));
2315+
return !is_null($this->getAttribute($offset));
22842316
} catch (MissingAttributeException) {
22852317
return false;
22862318
}

0 commit comments

Comments
 (0)
0