tuscan88
tuscan88

Reputation: 5829

Laravel custom validation rule with dynamic message

I have a request for adding a show with a custom validation method in it that checks whether a show already exists that clashes with the date and time. If the method fails I want to say which show it clashes with in the validation error message.

class ShowStoreRequest extends Request {

    public function rules()
    {
        Validator::extend('time_clash_check', function($attribute, $value, $parameters)
        {
            list($day, $month, $year) = explode('/', $value);
            $date = $year.'-'.$month.'-'.$day;
            $start_time = $parameters[0];
            $end_time = $parameters[1];
            $show_id = $parameters[2];

            $query = Show::where('date', '=', $date)->where(function($q) use ($start_time, $end_time) {
                // show overlaps beginning of another show
                $q->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '>=', $start_time)
                      ->where('start_time', '<=', $end_time)
                      ->where('end_time', '>=', $end_time);

                // show is in the middle of the another show
                })->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '<=', $start_time)
                      ->where('end_time', '>=', $end_time);

                // show overlaps the end of the another show
                })->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '<=', $start_time)
                      ->where('end_time', '>=', $start_time)
                      ->where('end_time', '<=', $end_time);

                // show completely overlaps another show
                })->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '>=', $start_time)
                      ->where('end_time', '<=', $end_time);
                });
            });

            if(!is_null($show_id)) {
                $query->where('id', '!=', $show_id);
            }

            $existing_show = $query->first();

            if(is_null($existing_show)) {
                return true;
            } else {
                return false;
            }
        });

        $rules = [
            'name' => 'required|max:255',
            'show_code' => 'required|alpha_num|min:10|max:11',
            'url_slug' => ['required', 'max:255', 'regex:/^[a-z0-9-_]+$/'],
            'youtube_url' => 'url',
            'name' => 'required|max:255',
            'date' => ['regex:/[0-9]{2}\/[0-9]{2}\/[0-9]{2}/', 'time_clash_check:'.$this->start_time.','.$this->end_time.','.$this->route('id')],
            'start_time' => ['regex:/[0-9]{1,2}\:[0-9]{2}\:[0-9]{2}/'],
            'end_time' => ['regex:/[0-9]{1,2}\:[0-9]{2}\:[0-9]{2}/']
        ];
    }
}

So if time_clash_check fails I want to output the value of $existing_show->name in the error message to tell them which show it clashes with. How would I do this?

Upvotes: 4

Views: 1931

Answers (1)

IllegalPigeon
IllegalPigeon

Reputation: 1769

Add this into your request file:

class ShowStoreRequest extends Request {

    public $exists;

    public function rules()
    {
        Validator::extend('time_clash_check', function($attribute, $value, $parameters)
        {
            list($day, $month, $year) = explode('/', $value);
            $date = $year.'-'.$month.'-'.$day;
            $start_time = $parameters[0];
            $end_time = $parameters[1];
            $show_id = $parameters[2];

            $query = Show::where('date', '=', $date)->where(function($q) use ($start_time, $end_time) {
                // show overlaps beginning of another show
                $q->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '>=', $start_time)
                      ->where('start_time', '<=', $end_time)
                      ->where('end_time', '>=', $end_time);

                // show is in the middle of the another show
                })->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '<=', $start_time)
                      ->where('end_time', '>=', $end_time);

                // show overlaps the end of the another show
                })->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '<=', $start_time)
                      ->where('end_time', '>=', $start_time)
                      ->where('end_time', '<=', $end_time);

                // show completely overlaps another show
                })->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '>=', $start_time)
                      ->where('end_time', '<=', $end_time);
                });
            });

            if(!is_null($show_id)) {
                $query->where('id', '!=', $show_id);
            }

            $existing_show = $query->first();

            if(is_null($existing_show)) {
                return true;
            } else {
                $this->exists = $existing_show->name;
                return false;
            }
        });

        $rules = [
            'name' => 'required|max:255',
            'show_code' => 'required|alpha_num|min:10|max:11',
            'url_slug' => ['required', 'max:255', 'regex:/^[a-z0-9-_]+$/'],
            'youtube_url' => 'url',
            'name' => 'required|max:255',
            'date' => ['regex:/[0-9]{2}\/[0-9]{2}\/[0-9]{2}/', 'time_clash_check:'.$this->start_time.','.$this->end_time.','.$this->route('id')],
            'start_time' => ['regex:/[0-9]{1,2}\:[0-9]{2}\:[0-9]{2}/'],
            'end_time' => ['regex:/[0-9]{1,2}\:[0-9]{2}\:[0-9]{2}/']
        ];
    }
}

public function messages()
{
    return [
        'time_clash_check ' => $this->exists . ' has clashed'
    ];
}

Edit:

Okay, so, I worked out what the problem was. Here's the solution:

class ShowStoreRequest extends Request {

    public $exists;

    public function rules()
    {
        Validator::extend('time_clash_check', function($attribute, $value, $parameters)
        {
            list($day, $month, $year) = explode('/', $value);
            $date = $year.'-'.$month.'-'.$day;
            $start_time = $parameters[0];
            $end_time = $parameters[1];
            $show_id = $parameters[2];

            $query = Show::where('date', '=', $date)->where(function($q) use ($start_time, $end_time) {
                // show overlaps beginning of another show
                $q->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '>=', $start_time)
                      ->where('start_time', '<=', $end_time)
                      ->where('end_time', '>=', $end_time);

                // show is in the middle of the another show
                })->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '<=', $start_time)
                      ->where('end_time', '>=', $end_time);

                // show overlaps the end of the another show
                })->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '<=', $start_time)
                      ->where('end_time', '>=', $start_time)
                      ->where('end_time', '<=', $end_time);

                // show completely overlaps another show
                })->orWhere(function($q) use ($start_time, $end_time) {
                    $q->where('start_time', '>=', $start_time)
                      ->where('end_time', '<=', $end_time);
                });
            });

            if(!is_null($show_id)) {
                $query->where('id', '!=', $show_id);
            }

            $existing_show = $query->first();

            if(is_null($existing_show)) {
                return true;
            } else {
                $this->exists = $existing_show->name;
                return false;
            }
        });

        Validator::replacer('time_clash_check', function($message, $attribute, $rule, $parameters) {
            return str_replace(':variable', $this->exists, $message);
        });    

        $rules = [
            'name' => 'required|max:255',
            'show_code' => 'required|alpha_num|min:10|max:11',
            'url_slug' => ['required', 'max:255', 'regex:/^[a-z0-9-_]+$/'],
            'youtube_url' => 'url',
            'name' => 'required|max:255',
            'date' => ['regex:/[0-9]{2}\/[0-9]{2}\/[0-9]{2}/', 'time_clash_check:'.$this->start_time.','.$this->end_time.','.$this->route('id')],
            'start_time' => ['regex:/[0-9]{1,2}\:[0-9]{2}\:[0-9]{2}/'],
            'end_time' => ['regex:/[0-9]{1,2}\:[0-9]{2}\:[0-9]{2}/']
        ];
    }
}

public function messages()
{
    return [
        'time_clash_check ' => ':variable has clashed'
    ];
}

That should fix the problem.

Upvotes: 3

Related Questions