PHP 10 - Laravel Blog (CMS-Validation)

By Sheldon L Published at 2020-09-10 Updated at 2020-09-10


Make Requests

php artisan make:request CategoryRequest
php artisan make:request AuthorRequest
php artisan make:request PostRequest
php artisan make:request PageRequest
php artisan make:request NewsRequest
php artisan make:request PhotoRequest
php artisan make:request TagRequest

Add Costomed Validation Rules

// app/Providers/AppServiceProvider

public function boot()
{
    // Other boots

    // Extends the validator
    Validator::extendImplicit(
        'empty_if',
        function ($attribute, $value, $parameters, $validator) {
            $data = request()->input($parameters[0]);
            $parameters_values = array_slice($parameters, 1);
            foreach ($parameters_values as $parameter_value) {
                if ($data == $parameter_value && !empty($value)) {
                    return false;
                }
            }
            return true;
        }
    );

    // (optional) Display error replacement
    Validator::replacer(
        'empty_if',
        function ($message, $attribute, $rule, $parameters) {
            return str_replace(
                [':other', ':value'],
                [$parameters[0], request()->input($parameters[0])],
                $message
            );
        }
    );
}

Request and Controller

Category

protected $rules = [
    'thumbnail' => 'nullable|url|max:255',

    'name_cn' => 'required|max:15|unique:categories,name->cn',
    'name_en' => 'required|max:63|unique:categories,name->en',
    'slug' => 'unique:categories,slug',

    'category_id' => 'required_if:is_column,==,"0"|empty_if:is_column,==,"1"',
];

protected function prepareForValidation()
{
    $this->name_cn = trim($this->name_cn);
    $this->name_en = trim($this->name_en);

    $this->merge([
        'name_cn' => str_replace(' ', '', $this->name_cn),
        'name_en' => ucwords($this->name_en),
        'slug' => Str::slug($this->name_en),
    ]);
}

public function authorize()
{
    return true;
}

public function rules()
{
    $rules = $this->rules;

    switch ($this->method()) {
        case 'GET':
        case 'DELETE': {
            return [];
        }
        case 'POST': {
            return $rules;
        }
        case 'PUT':
        case 'PATCH': {
            $rules['name_cn'] = 'required|max:15|unique:categories,name->cn,' . $this->category->id;
            $rules['name_en'] = 'required|max:63|unique:categories,name->en,' . $this->category->id;
            $rules['slug'] = 'unique:categories,slug,' . $this->category->id;
            return $rules;
        }
    }
}

public function messages()
{
    return [
        'thumbnail.url' => trans('admin_CRUD.must_url'),
        'thumbnail.max' => trans('admin_CRUD.max_limit_255'),

        'name_cn.required' => trans('admin_CRUD.is_must'),
        'name_en.required' => trans('admin_CRUD.is_must'),
        'name_cn.max' => trans('admin_CRUD.max_limit_15_cn'),
        'name_en.max' => trans('admin_CRUD.max_limit_63_en'),
        'name_cn.unique' => trans('admin_CRUD.already_exist'),
        'name_en.unique' => trans('admin_CRUD.already_exist'),
        'slug.unique' => trans('admin_CRUD.already_exist'),

        'category_id.required_if' => trans('admin_CRUD.required_if_is_sub_category'),
        'category_id.empty_if' => trans('admin_CRUD.empty_if_is_column'),
    ];
}

Author

// TODO: need more validation such as regex rules for all Request
protected $rules = [
    'thumbnail' => 'nullable|url|max:255',

    'name_cn' => 'required|max:15|unique:tags,name->cn',
    'name_en' => "required|max:63|unique:tags,name->en",
    'slug' => "unique:tags,slug",

    'intro_cn' => 'max:250',
    'intro_en' => 'max:1500',
];

// TODO: need more cearfull prepareForValidation for all Request
protected function prepareForValidation()
{
    $this->name_cn = trim($this->name_cn);
    $this->name_en = trim($this->name_en);

    $this->merge([
        'name_cn' => str_replace(' ', '', $this->title_cn),
        'name_en' => ucwords($this->name_en),
        'slug' => Str::slug($this->name_en),
    ]);
}

public function authorize()
{
    return true;
}

public function rules()
{
    $rules = $this->rules;

    switch ($this->method()) {
        case 'GET':
        case 'DELETE': {
            return [];
        }
        case 'POST': {
            return $rules;
        }
        case 'PUT':
        case 'PATCH': {
            $rules['name_cn'] = 'required|max:15|unique:authors,name->cn,' . $this->author->id;
            $rules['name_en'] = 'required|max:63|unique:authors,name->en,' . $this->author->id;
            $rules['slug'] = 'unique:authors,slug,' . $this->author->id;
            return $rules;
        }
    }
}

public function messages()
{
    return [
        'thumbnail.url' => trans('admin_CRUD.must_url'),
        'thumbnail.max' => trans('admin_CRUD.max_limit_255'),

        'name_cn.required' => trans('admin_CRUD.is_must'),
        'name_en.required' => trans('admin_CRUD.is_must'),
        'name_cn.max' => trans('admin_CRUD.max_limit_15_cn'),
        'name_en.max' => trans('admin_CRUD.max_limit_63_en'),
        'name_cn.unique' => trans('admin_CRUD.already_exist'),
        'name_en.unique' => trans('admin_CRUD.already_exist'),
        // 'name_en.regex' => trans('admin_CRUD.legal_english_name'),
        'slug.unique' => trans('admin_CRUD.already_exist'),

        'intro_cn.max' => trans('admin_CRUD.max_limit_250_cn'),
        'intro_en.max' => trans('admin_CRUD.max_limit_1500_en'),
    ];
}

Post

protected $rules = [
    'thumbnail' => 'nullable|url|max:255',

    'title_cn' => 'required|max:31|unique:posts,title->cn',
    'title_en' => 'required|max:127|unique:posts,title->en',
    'slug' => 'unique:posts,slug',

    'sub_title_cn' => 'max:31',
    'sub_title_en' => 'max:127',

    'source' => 'required_if:is_reproduced,==,"1"|max:31',
    'source_url' => 'nullable|url|max:255',

    'editor' => 'max:31',

    'intro_cn' => 'max:250',
    'intro_en' => 'max:1500',

    'details_cn' => 'max:3000',
    'details_en' => 'max:15000',

    'author_id' => 'required',
    'category_id' => 'required',
];

protected function prepareForValidation()
{
    $this->title_cn = trim($this->title_cn);
    $this->title_en = trim($this->title_en);

    $this->merge([
        'title_cn' => str_replace(' ', '', $this->title_cn),
        'title_en' => ucwords($this->title_en),
        'slug' => Str::slug($this->title_en),
        'sub_title_en' => ucwords($this->sub_title_en),
    ]);
}

public function authorize()
{
    return true;
}

public function rules()
{
    $rules = $this->rules;

    switch ($this->method()) {
        case 'GET':
        case 'DELETE': {
            return [];
        }
        case 'POST': {
            return $rules;
        }
        case 'PUT':
        case 'PATCH': {
            $rules['title_cn'] = 'required|max:31|unique:posts,title->cn,' . $this->post->id;
            $rules['title_en'] = 'required|max:127|unique:posts,title->en,' . $this->post->id;
            $rules['slug'] = 'unique:posts,slug,' . $this->post->id;
            return $rules;
        }
    }
}

public function messages()
{
    return [
        'thumbnail.url' => trans('admin_CRUD.must_url'),
        'thumbnail.max' => trans('admin_CRUD.max_limit_255'),

        'title_cn.required' => trans('admin_CRUD.is_must'),
        'title_en.required' => trans('admin_CRUD.is_must'),
        'title_cn.max' => trans('admin_CRUD.max_limit_31_cn'),
        'title_en.max' => trans('admin_CRUD.max_limit_127_en'),
        'title_cn.unique' => trans('admin_CRUD.already_exist'),
        'title_en.unique' => trans('admin_CRUD.already_exist'),
        'slug.unique' => trans('admin_CRUD.already_exist'),

        'sub_title_cn.max' => trans('admin_CRUD.max_limit_31_cn'),
        'sub_title_en.max' => trans('admin_CRUD.max_limit_127_en'),

        'source.required' => trans('admin_CRUD.is_must_for_reproduced'),
        'source.max' => trans('admin_CRUD.max_limit_31'),
        'source_url.url' => trans('admin_CRUD.must_url'),
        'source_url.max' => trans('admin_CRUD.max_limit_255'),

        'editor.max' => trans('admin_CRUD.max_limit_31_cn'),

        'intro_cn.max' => trans('admin_CRUD.max_limit_250_cn'),
        'intro_en.max' => trans('admin_CRUD.max_limit_1500_en'),

        'details_cn.max' => trans('admin_CRUD.max_limit_2500_cn'),
        'details_en.max' => trans('admin_CRUD.max_limit_15000_en'),

        'author_id.required' => trans('admin_CRUD.is_must'),
        'category_id.required' => trans('admin_CRUD.is_must'),
    ];
}

Page

protected $rules = [
    'thumbnail' => 'nullable|url|max:255',

    'title_cn' => 'required|max:31|unique:posts,title->cn',
    'title_en' => 'required|max:127|unique:posts,title->en',
    'slug' => 'unique:posts,slug',

    'sub_title_cn' => 'max:31',
    'sub_title_en' => 'max:127',

    'details_cn' => 'max:2500',
    'details_en' => 'max:15000',
];

protected function prepareForValidation()
{
    $this->title_cn = trim($this->title_cn);
    $this->title_en = trim($this->title_en);

    $this->merge([
        'title_cn' => str_replace(' ', '', $this->title_cn),
        'title_en' => ucwords($this->title_en),
        'slug' => Str::slug($this->title_en),
        'sub_title_en' => ucwords($this->sub_title_en),
    ]);
}

public function authorize()
{
    return true;
}

public function rules()
{
    $rules = $this->rules;

    switch ($this->method()) {
        case 'GET':
        case 'DELETE': {
            return [];
        }
        case 'POST': {
            return $rules;
        }
        case 'PUT':
        case 'PATCH': {
            $rules['title_cn'] = 'required|max:31|unique:posts,title->cn,' . $this->page->id;
            $rules['title_en'] = 'required|max:127|unique:posts,title->en,' . $this->page->id;
            $rules['slug'] = 'unique:posts,slug,' . $this->page->id;
            return $rules;
        }
    }
}

public function messages()
{
    return [
        'thumbnail.url' => trans('admin_CRUD.must_url'),
        'thumbnail.max' => trans('admin_CRUD.max_limit_255'),

        'title_cn.required' => trans('admin_CRUD.is_must'),
        'title_en.required' => trans('admin_CRUD.is_must'),
        'title_cn.max' => trans('admin_CRUD.max_limit_31_cn'),
        'title_en.max' => trans('admin_CRUD.max_limit_127_en'),
        'title_cn.unique' => trans('admin_CRUD.already_exist'),
        'title_en.unique' => trans('admin_CRUD.already_exist'),
        'slug.unique' => trans('admin_CRUD.already_exist'),

        'sub_title_cn.max' => trans('admin_CRUD.max_limit_31_cn'),
        'sub_title_en.max' => trans('admin_CRUD.max_limit_127_en'),

        'details_cn.max' => trans('admin_CRUD.max_limit_2500_cn'),
        'details_en.max' => trans('admin_CRUD.max_limit_15000_en'),
    ];
}

News

protected $rules = [
    'thumbnail' => 'nullable|url|max:255',

    'title_cn' => 'required|max:31|unique:posts,title->cn',
    'title_en' => 'required|max:127|unique:posts,title->en',
    'slug' => 'unique:posts,slug',

    'sub_title_cn' => 'max:31',
    'sub_title_en' => 'max:127',

    'details_cn' => 'max:2500',
    'details_en' => 'max:15000',
];

protected function prepareForValidation()
{
    $this->title_cn = trim($this->title_cn);
    $this->title_en = trim($this->title_en);

    $this->merge([
        'title_cn' => str_replace(' ', '', $this->title_cn),
        'title_en' => ucwords($this->title_en),
        'slug' => Str::slug($this->title_en),
        'sub_title_en' => ucwords($this->sub_title_en),
    ]);
}

public function authorize()
{
    return true;
}

public function rules()
{
    $rules = $this->rules;

    switch ($this->method()) {
        case 'GET':
        case 'DELETE': {
            return [];
        }
        case 'POST': {
            return $rules;
        }
        case 'PUT':
        case 'PATCH': {
            $rules['title_cn'] = 'required|max:31|unique:posts,title->cn,' . $this->news->id;
            $rules['title_en'] = 'required|max:127|unique:posts,title->en,' . $this->news->id;
            $rules['slug'] = 'unique:posts,slug,' . $this->news->id;
            return $rules;
        }
    }
}

public function messages()
{
    return [
        'thumbnail.url' => trans('admin_CRUD.must_url'),
        'thumbnail.max' => trans('admin_CRUD.max_limit_255'),

        'title_cn.required' => trans('admin_CRUD.is_must'),
        'title_en.required' => trans('admin_CRUD.is_must'),
        'title_cn.max' => trans('admin_CRUD.max_limit_31_cn'),
        'title_en.max' => trans('admin_CRUD.max_limit_127_en'),
        'title_cn.unique' => trans('admin_CRUD.already_exist'),
        'title_en.unique' => trans('admin_CRUD.already_exist'),
        'slug.unique' => trans('admin_CRUD.already_exist'),

        'sub_title_cn.max' => trans('admin_CRUD.max_limit_31_cn'),
        'sub_title_en.max' => trans('admin_CRUD.max_limit_127_en'),

        'details_cn.max' => trans('admin_CRUD.max_limit_2500_cn'),
        'details_en.max' => trans('admin_CRUD.max_limit_15000_en'),
    ];
}

Photo

protected $rules = [
    'image_url' => 'required|max:31',

    'intro_cn' => 'max:250',
    'intro_en' => 'max:1500',

    'tag_id' => 'required',
];

public function authorize()
{
    return true;
}

public function rules()
{
    $rules = $this->rules;

    switch ($this->method()) {
        case 'GET':
        case 'DELETE': {
            return [];
        }
        case 'POST': {
            return $rules;
        }
        case 'PUT':
        case 'PATCH': {
            $rules['image_url'] = 'is_empty';
            return $rules;
        }
    }
}

public function messages()
{
    return [

        'img_url.required' => trans('admin_CRUD.is_must'),
        'img_url.max' => trans('admin_CRUD.max_limit_31'),
        // 'image.size',

        'intro_cn.max' => trans('admin_CRUD.max_limit_250_cn'),
        'intro_en.max' => trans('admin_CRUD.max_limit_1500_en'),

        'tag_id.required' => trans('admin_CRUD.is_must'),
    ];
}

Tag

protected $rules = [
    'name_cn' => 'required|max:15|unique:tags,name->cn',
    'name_en' => "required|max:63|unique:tags,name->en",
    'slug' => 'unique:tags,slug',
];

protected function prepareForValidation()
{
    $this->name_cn = trim($this->name_cn);
    $this->name_cn = str_replace(' ', '', $this->name_cn);

    $this->name_en = trim($this->name_en);
    $this->name_en = Str::lower($this->name_en);
    $this->name_en = str_replace(' ', '_', $this->name_en);

    $this->merge([
        'name_cn' => preg_replace('/[^\p{L}\p{N}\-]/u', '', $this->name_cn),
        'name_en' => preg_replace('/[^\p{L}\p{N}\-\'\_]/u', '', $this->name_en),
        'slug' => Str::slug($this->name_en),
    ]);
}

public function authorize()
{
    return true;
}

public function rules()
{
    $rules = $this->rules;

    switch ($this->method()) {
        case 'GET':
        case 'DELETE': {
            return [];
        }
        case 'POST': {
            return $rules;
        }
        case 'PUT':
        case 'PATCH': {
            $rules['name_cn'] = 'required|max:15|unique:tags,name->cn,' . $this->tag->id;
            $rules['name_en'] = 'required|max:63|unique:tags,name->en,' . $this->tag->id;
            $rules['slug'] = 'unique:tags,slug,' . $this->tag->id;
            return $rules;
        }
    }
}

public function messages()
{
    return [
        'name_cn.required' => trans('admin_CRUD.is_must'),
        'name_en.required' => trans('admin_CRUD.is_must'),
        'name_cn.max' => trans('admin_CRUD.max_limit_15_cn'),
        'name_en.max' => trans('admin_CRUD.max_limit_63_en'),
        'name_cn.unique' => trans('admin_CRUD.already_exist'),
        'name_en.unique' => trans('admin_CRUD.already_exist'),
        // 'name_en.regex' => trans('admin_CRUD.legal_english_name')
        'slug.unique' => trans('admin_CRUD.already_exist'),
    ];
}