Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/app/Models/Model.php
10261 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(): \Illuminate\Validation\Validator
85
{
86
$rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules();
87
88
// @phpstan-ignore-next-line return.type
89
return static::$validatorFactory->make([], $rules);
90
}
91
92
/**
93
* Returns the rules associated with this model.
94
*/
95
public static function getRules(): array
96
{
97
$rules = static::$validationRules;
98
foreach ($rules as $key => &$rule) {
99
$rule = is_array($rule) ? $rule : explode('|', $rule);
100
}
101
102
return $rules;
103
}
104
105
/**
106
* Returns the rules for a specific field. If the field is not found an empty
107
* array is returned.
108
*/
109
public static function getRulesForField(string $field): array
110
{
111
return Arr::get(static::getRules(), $field) ?? [];
112
}
113
114
/**
115
* Returns the rules associated with the model, specifically for updating the given model
116
* rather than just creating it.
117
*/
118
public static function getRulesForUpdate($model, string $column = 'id'): array
119
{
120
if ($model instanceof Model) {
121
[$id, $column] = [$model->getKey(), $model->getKeyName()];
122
}
123
124
$rules = static::getRules();
125
foreach ($rules as $key => &$data) {
126
// For each rule in a given field, iterate over it and confirm if the rule
127
// is one for a unique field. If that is the case, append the ID of the current
128
// working model, so we don't run into errors due to the way that field validation
129
// works.
130
foreach ($data as &$datum) {
131
if (!is_string($datum) || !Str::startsWith($datum, 'unique')) {
132
continue;
133
}
134
135
[, $args] = explode(':', $datum);
136
$args = explode(',', $args);
137
138
$datum = Rule::unique($args[0], $args[1] ?? $key)->ignore($id ?? $model, $column);
139
}
140
}
141
142
return $rules;
143
}
144
145
/**
146
* Determines if the model is in a valid state or not.
147
*
148
* @throws ValidationException
149
*/
150
public function validate(): void
151
{
152
if ($this->skipValidation) {
153
return;
154
}
155
156
$validator = $this->getValidator();
157
$validator->setData(
158
// Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist
159
// for that model. Doing this will return all the attributes in a format that can
160
// properly be validated.
161
$this->addCastAttributesToArray(
162
$this->getAttributes(),
163
$this->getMutatedAttributes()
164
)
165
);
166
167
if (!$validator->passes()) {
168
throw new ValidationException($validator);
169
}
170
}
171
172
/**
173
* Return a timestamp as DateTime object.
174
*/
175
protected function asDateTime($value): Carbon|CarbonImmutable
176
{
177
if (!$this->immutableDates) {
178
return parent::asDateTime($value);
179
}
180
181
return parent::asDateTime($value)->toImmutable();
182
}
183
}
184
185