mirror of
https://github.com/flarum/framework.git
synced 2024-11-25 09:41:49 +08:00
Extract model validation into a trait
Also use Laravel’s ValidationException rather than our own custom one
This commit is contained in:
parent
04501545e3
commit
53e269fd89
|
@ -1,6 +1,7 @@
|
|||
<?php namespace Flarum\Api\Actions;
|
||||
|
||||
use Flarum\Api\Request;
|
||||
use Illuminate\Contracts\Validation\ValidationException;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Flarum\Core\Exceptions\ValidationFailureException;
|
||||
use Flarum\Core\Exceptions\PermissionDeniedException;
|
||||
|
@ -20,9 +21,9 @@ abstract class JsonApiAction implements ActionInterface
|
|||
// TODO: Move this error handling code to middleware?
|
||||
try {
|
||||
return $this->respond($request);
|
||||
} catch (ValidationFailureException $e) {
|
||||
} catch (ValidationException $e) {
|
||||
$errors = [];
|
||||
foreach ($e->getErrors()->getMessages() as $field => $messages) {
|
||||
foreach ($e->errors()->toArray() as $field => $messages) {
|
||||
$errors[] = [
|
||||
'detail' => implode("\n", $messages),
|
||||
'path' => $field
|
||||
|
|
|
@ -19,8 +19,6 @@ class CoreServiceProvider extends ServiceProvider
|
|||
return get_class($command).'Handler@handle';
|
||||
});
|
||||
|
||||
Model::setValidator($this->app['validator']);
|
||||
|
||||
Forum::allow('*', function (Forum $forum, User $user, $action) {
|
||||
return $user->hasPermission('forum.'.$action) ?: null;
|
||||
});
|
||||
|
|
|
@ -12,19 +12,28 @@ use Flarum\Core\Users\User;
|
|||
use Flarum\Core\Support\EventGenerator;
|
||||
use Flarum\Core\Support\Locked;
|
||||
use Flarum\Core\Support\VisibleScope;
|
||||
use Flarum\Core\Support\ValidatesBeforeSave;
|
||||
|
||||
class Discussion extends Model
|
||||
{
|
||||
use EventGenerator;
|
||||
use Locked;
|
||||
use VisibleScope;
|
||||
use ValidatesBeforeSave;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'discussions';
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $rules = [
|
||||
protected $rules = [
|
||||
'title' => 'required',
|
||||
'start_time' => 'required|date',
|
||||
'comments_count' => 'integer',
|
||||
|
@ -37,13 +46,6 @@ class Discussion extends Model
|
|||
'last_post_number' => 'integer'
|
||||
];
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'discussions';
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
|
|
|
@ -19,6 +19,8 @@ class DiscussionsServiceProvider extends ServiceProvider
|
|||
new Extend\EventSubscriber('Flarum\Core\Discussions\Listeners\DiscussionMetadataUpdater')
|
||||
]);
|
||||
|
||||
Discussion::setValidator($this->app->make('validator'));
|
||||
|
||||
Discussion::allow('*', function (Discussion $discussion, User $user, $action) {
|
||||
return $user->hasPermission('discussion.'.$action) ?: null;
|
||||
});
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
<?php namespace Flarum\Core\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use DomainException;
|
||||
|
||||
class ValidationFailureException extends DomainException
|
||||
{
|
||||
/**
|
||||
* @var MessageBag
|
||||
*/
|
||||
protected $errors;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $input = array();
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Exception $previous
|
||||
*/
|
||||
public function __construct($message = '', $code = 0, Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
|
||||
$this->errors = new MessageBag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MessageBag $errors
|
||||
* @return $this
|
||||
*/
|
||||
public function setErrors(MessageBag $errors)
|
||||
{
|
||||
$this->errors = $errors;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $input
|
||||
* @return $this
|
||||
*/
|
||||
public function setInput(array $input)
|
||||
{
|
||||
$this->input = $input;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getInput()
|
||||
{
|
||||
return $this->input;
|
||||
}
|
||||
}
|
|
@ -35,13 +35,6 @@ abstract class Model extends Eloquent
|
|||
*/
|
||||
protected static $dateAttributes = [];
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $rules = [];
|
||||
|
||||
/**
|
||||
* An array of custom relation methods, grouped by subclass.
|
||||
*
|
||||
|
@ -49,124 +42,6 @@ abstract class Model extends Eloquent
|
|||
*/
|
||||
protected static $relationMethods = [];
|
||||
|
||||
/**
|
||||
* The validation factory instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Validation\Factory
|
||||
*/
|
||||
protected static $validator;
|
||||
|
||||
/**
|
||||
* Boot the model.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
// Before the model is saved, validate it. If validation fails, an
|
||||
// exception will be thrown, preventing the model from saving.
|
||||
static::saving(function ($model) {
|
||||
$model->assertValid();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the validation factory instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Validation\Factory $validator
|
||||
*/
|
||||
public static function setValidator(Factory $validator)
|
||||
{
|
||||
static::$validator = $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the model is valid in its current state.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->makeValidator()->passes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception if the model is not valid in its current state.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Flarum\Core\ValidationFailureException
|
||||
*/
|
||||
public function assertValid()
|
||||
{
|
||||
$validator = $this->makeValidator();
|
||||
if ($validator->fails()) {
|
||||
$this->throwValidationFailureException($validator);
|
||||
}
|
||||
}
|
||||
|
||||
protected function throwValidationFailureException($validator)
|
||||
{
|
||||
throw (new ValidationFailureException)
|
||||
->setErrors($validator->errors())
|
||||
->setInput($validator->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new validator instance for this model.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
protected function makeValidator()
|
||||
{
|
||||
$dirty = $this->getDirty();
|
||||
|
||||
$rules = $this->expandUniqueRules(array_only(static::$rules, array_keys($dirty)));
|
||||
|
||||
// TODO: translation
|
||||
$messages = [
|
||||
'unique' => 'That :attribute has already been taken.',
|
||||
'email' => 'The :attribute must be a valid email address.',
|
||||
'alpha_num' => 'The :attribute may only contain letters and numbers.'
|
||||
];
|
||||
|
||||
return static::$validator->make($dirty, $rules, $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand 'unique' rules in a set of validation rules into a fuller form
|
||||
* that Laravel's validator can understand.
|
||||
*
|
||||
* @param array $rules
|
||||
* @return array
|
||||
*/
|
||||
protected function expandUniqueRules($rules)
|
||||
{
|
||||
foreach ($rules as $column => &$ruleset) {
|
||||
if (is_string($ruleset)) {
|
||||
$ruleset = explode('|', $ruleset);
|
||||
}
|
||||
foreach ($ruleset as &$rule) {
|
||||
if (strpos($rule, 'unique') === 0) {
|
||||
$parts = explode(':', $rule);
|
||||
$key = $this->getKey() ?: 'NULL';
|
||||
$rule = 'unique:'.$this->getTable().','.$column.','.$key.','.$this->getKeyName();
|
||||
if (! empty($parts[1])) {
|
||||
$wheres = explode(',', $parts[1]);
|
||||
foreach ($wheres as &$where) {
|
||||
$where .= ','.$this->$where;
|
||||
}
|
||||
$rule .= ','.implode(',', $wheres);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attributes that should be converted to dates.
|
||||
*
|
||||
|
@ -216,7 +91,7 @@ abstract class Model extends Eloquent
|
|||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function getCustomRelationship($name)
|
||||
protected function getCustomRelationship($name)
|
||||
{
|
||||
$relation = static::$relationMethods[get_called_class()][$name]($this);
|
||||
|
||||
|
@ -225,7 +100,7 @@ abstract class Model extends Eloquent
|
|||
. 'Illuminate\Database\Eloquent\Relations\Relation');
|
||||
}
|
||||
|
||||
return $this->relations[$method] = $relation->getResults();
|
||||
return $this->relations[$name] = $relation->getResults();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,21 +4,27 @@ use DomainException;
|
|||
use Flarum\Core\Posts\Events\PostWasDeleted;
|
||||
use Flarum\Core\Model;
|
||||
use Flarum\Core\Support\Locked;
|
||||
use Flarum\Core\Exceptions\ValidationFailureException;
|
||||
use Flarum\Core\Support\EventGenerator;
|
||||
use Flarum\Core\Support\ValidatesBeforeSave;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class Post extends Model
|
||||
{
|
||||
use EventGenerator;
|
||||
use Locked;
|
||||
use ValidatesBeforeSave;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $table = 'posts';
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $rules = [
|
||||
protected $rules = [
|
||||
'discussion_id' => 'required|integer',
|
||||
'time' => 'required|date',
|
||||
'content' => 'required',
|
||||
|
@ -30,11 +36,6 @@ class Post extends Model
|
|||
'hide_user_id' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $table = 'posts';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -19,6 +19,8 @@ class PostsServiceProvider extends ServiceProvider
|
|||
new Extend\PostType('Flarum\Core\Posts\DiscussionRenamedPost')
|
||||
]);
|
||||
|
||||
Post::setValidator($this->app->make('validator'));
|
||||
|
||||
CommentPost::setFormatter($this->app->make('flarum.formatter'));
|
||||
|
||||
Post::allow('*', function ($post, $user, $action) {
|
||||
|
|
143
src/Core/Support/ValidatesBeforeSave.php
Normal file
143
src/Core/Support/ValidatesBeforeSave.php
Normal file
|
@ -0,0 +1,143 @@
|
|||
<?php namespace Flarum\Core\Support;
|
||||
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Illuminate\Contracts\Validation\ValidationException;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
trait ValidatesBeforeSave
|
||||
{
|
||||
/**
|
||||
* The validation factory instance.
|
||||
*
|
||||
* @var Factory
|
||||
*/
|
||||
protected static $validator;
|
||||
|
||||
/**
|
||||
* Boot the model.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function bootValidatesBeforeSave()
|
||||
{
|
||||
// Before the model is saved, validate it. If validation fails, an
|
||||
// exception will be thrown, preventing the model from saving.
|
||||
static::saving(function ($model) {
|
||||
$model->assertValid();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the validation factory instance.
|
||||
*
|
||||
* @param Factory $validator
|
||||
*/
|
||||
public static function setValidator(Factory $validator)
|
||||
{
|
||||
static::$validator = $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the model is valid in its current state.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->makeValidator()->passes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception if the model is not valid in its current state.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function assertValid()
|
||||
{
|
||||
$validator = $this->makeValidator();
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->throwValidationException($validator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function throwValidationException(Validator $validator)
|
||||
{
|
||||
throw new ValidationException($validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new validator instance for this model.
|
||||
*
|
||||
* @return \Illuminate\Validation\Validator
|
||||
*/
|
||||
protected function makeValidator()
|
||||
{
|
||||
$dirty = $this->getDirty();
|
||||
|
||||
$rules = $this->expandUniqueRules(array_only($this->rules, array_keys($dirty)));
|
||||
|
||||
$validator = static::$validator->make($dirty, $rules);
|
||||
|
||||
// TODO: event
|
||||
|
||||
return $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand 'unique' rules in a set of validation rules into a fuller form
|
||||
* that Laravel's validator can understand.
|
||||
*
|
||||
* @param array $rules
|
||||
* @return array
|
||||
*/
|
||||
protected function expandUniqueRules($rules)
|
||||
{
|
||||
foreach ($rules as $attribute => &$ruleset) {
|
||||
if (is_string($ruleset)) {
|
||||
$ruleset = explode('|', $ruleset);
|
||||
}
|
||||
|
||||
foreach ($ruleset as &$rule) {
|
||||
if (strpos($rule, 'unique') === 0) {
|
||||
$rule = $this->expandUniqueRule($attribute, $rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a 'unique' rule into a fuller form that Laravel's validator can
|
||||
* understand, based on this model's properties.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param string $rule
|
||||
* @return string
|
||||
*/
|
||||
protected function expandUniqueRule($attribute, $rule)
|
||||
{
|
||||
$parts = explode(':', $rule);
|
||||
$key = $this->getKey() ?: 'NULL';
|
||||
$rule = 'unique:'.$this->getTable().','.$attribute.','.$key.','.$this->getKeyName();
|
||||
|
||||
if (! empty($parts[1])) {
|
||||
$wheres = explode(',', $parts[1]);
|
||||
|
||||
foreach ($wheres as &$where) {
|
||||
$where .= ','.$this->$where;
|
||||
}
|
||||
|
||||
$rule .= ','.implode(',', $wheres);
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}
|
||||
}
|
|
@ -17,18 +17,35 @@ use Flarum\Core\Users\Events\UserEmailChangeWasRequested;
|
|||
use Flarum\Core\Support\Locked;
|
||||
use Flarum\Core\Support\VisibleScope;
|
||||
use Flarum\Core\Support\EventGenerator;
|
||||
use Flarum\Core\Support\ValidatesBeforeSave;
|
||||
|
||||
class User extends Model
|
||||
{
|
||||
use EventGenerator;
|
||||
use Locked;
|
||||
use VisibleScope;
|
||||
use ValidatesBeforeSave;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $table = 'users';
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $rules = [
|
||||
'username' => 'required|alpha_dash|unique',
|
||||
'email' => 'required|email|unique',
|
||||
'password' => 'required',
|
||||
'join_time' => 'date',
|
||||
'last_seen_time' => 'date',
|
||||
'discussions_count' => 'integer',
|
||||
'posts_count' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -46,21 +63,6 @@ class User extends Model
|
|||
*/
|
||||
protected static $formatter;
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $rules = [
|
||||
'username' => 'required|alpha_dash|unique',
|
||||
'email' => 'required|email|unique',
|
||||
'password' => 'required',
|
||||
'join_time' => 'date',
|
||||
'last_seen_time' => 'date',
|
||||
'discussions_count' => 'integer',
|
||||
'posts_count' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* The hasher with which to hash passwords.
|
||||
*
|
||||
|
@ -158,13 +160,13 @@ class User extends Model
|
|||
public function requestEmailChange($email)
|
||||
{
|
||||
if ($email !== $this->email) {
|
||||
$validator = static::$validator->make(
|
||||
compact('email'),
|
||||
$this->expandUniqueRules(array_only(static::$rules, 'email'))
|
||||
);
|
||||
$validator = $this->makeValidator();
|
||||
|
||||
$validator->setRules(array_only($validator->getRules(), 'email'));
|
||||
$validator->setData(compact('email'));
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->throwValidationFailureException($validator);
|
||||
$this->throwValidationException($validator);
|
||||
}
|
||||
|
||||
$this->raise(new UserEmailChangeWasRequested($this, $email));
|
||||
|
|
|
@ -21,6 +21,7 @@ class UsersServiceProvider extends ServiceProvider
|
|||
|
||||
User::setHasher($this->app->make('hash'));
|
||||
User::setFormatter($this->app->make('flarum.formatter'));
|
||||
User::setValidator($this->app->make('validator'));
|
||||
|
||||
User::addPreference('discloseOnline', 'boolval', true);
|
||||
User::addPreference('indexProfile', 'boolval', true);
|
||||
|
|
Loading…
Reference in New Issue
Block a user