Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/app/Models/Model.php
7432 views
1
<?php
2
3
namespace Pterodactyl\Models;
4
5
use Carbon\CarbonImmutable;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\Str;
8
use Illuminate\Support\Carbon;
9
use Illuminate\Validation\Rule;
10
use Illuminate\Container\Container;
11
use Illuminate\Contracts\Validation\Validator;
12
use Illuminate\Validation\ValidationException;
13
use Pterodactyl\Exceptions\Model\DataValidationException;
14
use Illuminate\Database\Eloquent\Model as IlluminateModel;
15
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
16
17
abstract class Model extends IlluminateModel
18
{
19
/**
20
* Set to true to return immutable Carbon date instances from the model.
21
*/
22
protected bool $immutableDates = false;
23
24
/**
25
* Determines if the model should undergo data validation before it is saved
26
* to the database.
27
*/
28
protected bool $skipValidation = false;
29
30
protected static ValidationFactory $validatorFactory;
31
32
public static array $validationRules = [];
33
34
/**
35
* Listen for the model saving event and fire off the validation
36
* function before it is saved.
37
*
38
* @throws \Illuminate\Contracts\Container\BindingResolutionException
39
*/
40
protected static function boot()
41
{
42
parent::boot();
43
44
static::$validatorFactory = Container::getInstance()->make(ValidationFactory::class);
45
46
static::saving(function (Model $model) {
47
try {
48
$model->validate();
49
} catch (ValidationException $exception) {
50
throw new DataValidationException($exception->validator, $model);
51
}
52
53
return true;
54
});
55
}
56
57
/**
58
* Returns the model key to use for route model binding. By default, we'll
59
* assume every model uses a UUID field for this. If the model does not have
60
* a UUID and is using a different key it should be specified on the model
61
* itself.
62
*
63
* You may also optionally override this on a per-route basis by declaring
64
* the key name in the URL definition, like "{user:id}".
65
*/
66
public function getRouteKeyName(): string
67
{
68
return 'uuid';
69
}
70
71
/**
72
* Set the model to skip validation when saving.
73
*/
74
public function skipValidation(): self
75
{
76
$this->skipValidation = true;
77
78
return $this;
79
}
80
81
/**
82
* Returns the validator instance used by this model.
83
*/
84
public function getValidator(): Validator
85
{
86
$rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules();
87
88
return static::$validatorFactory->make([], $rules, [], []);
89
}
90
91
/**
92
* Returns the rules associated with this model.
93
*/
94
public static function getRules(): array
95
{
96
$rules = static::$validationRules;
97
foreach ($rules as $key => &$rule) {
98
$rule = is_array($rule) ? $rule : explode('|', $rule);
99
}
100
101
return $rules;
102
}
103
104
/**
105
* Returns the rules for a specific field. If the field is not found an empty
106
* array is returned.
107
*/
108
public static function getRulesForField(string $field): array
109
{
110
return Arr::get(static::getRules(), $field) ?? [];
111
}
112
113
/**
114
* Returns the rules associated with the model, specifically for updating the given model
115
* rather than just creating it.
116
*/
117
public static function getRulesForUpdate($model, string $column = 'id'): array
118
{
119
if ($model instanceof Model) {
120
[$id, $column] = [$model->getKey(), $model->getKeyName()];
121
}
122
123
$rules = static::getRules();
124
foreach ($rules as $key => &$data) {
125
// For each rule in a given field, iterate over it and confirm if the rule
126
// is one for a unique field. If that is the case, append the ID of the current
127
// working model, so we don't run into errors due to the way that field validation
128
// works.
129
foreach ($data as &$datum) {
130
if (!is_string($datum) || !Str::startsWith($datum, 'unique')) {
131
continue;
132
}
133
134
[, $args] = explode(':', $datum);
135
$args = explode(',', $args);
136
137
$datum = Rule::unique($args[0], $args[1] ?? $key)->ignore($id ?? $model, $column);
138
}
139
}
140
141
return $rules;
142
}
143
144
/**
145
* Determines if the model is in a valid state or not.
146
*
147
* @throws ValidationException
148
*/
149
public function validate(): void
150
{
151
if ($this->skipValidation) {
152
return;
153
}
154
155
$validator = $this->getValidator();
156
$validator->setData(
157
// Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist
158
// for that model. Doing this will return all the attributes in a format that can
159
// properly be validated.
160
$this->addCastAttributesToArray(
161
$this->getAttributes(),
162
$this->getMutatedAttributes()
163
)
164
);
165
166
if (!$validator->passes()) {
167
throw new ValidationException($validator);
168
}
169
}
170
171
/**
172
* Return a timestamp as DateTime object.
173
*/
174
protected function asDateTime($value): Carbon|CarbonImmutable
175
{
176
if (!$this->immutableDates) {
177
return parent::asDateTime($value);
178
}
179
180
return parent::asDateTime($value)->toImmutable();
181
}
182
}
183
184