longestdrive's avatar

Create Custom Validation Rule with additional Parameters - implement in Request

Hi I'm trying to add a custom validation rule to a Laravel 5.5 app.

I've created a rule using php artisan make:rule and am struggling getting it to work so I can build on it. I've looked at the docs here: https://laravel.com/docs/5.6/validation#custom-validation-rules

This goes on to use this in a controller, implies you can use it in a request but doesn't say how.

My rule will check whether the ticket has been used and if it has pass if it is for this id or fail if it is for another id. So it needs the ticket number and the parent record id.

Here's the rule skeleton:

    <?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class TicketNumberIsNotUsed implements Rule
{


    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        dd($value);
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The validation error message.';
    }
}

and here's how I'm trying to implement in my request:

    public function rules()
    {
        return [
            'play_date'=>'required',
            'am_tee'=>'required',
            'howBooked'=>'required',
            'ticket'=>['required|exists:ticket_audits,ticketnumber' , new TicketNumberIsNotUsed()] ,
            'pay_method'=>'required',
            'customer_id'=>'required:exists:customers,id'

        ];
    }

The error I get is Method [validateRequired|exists] does not exist.

so by including the way I have corrupts the rules.

I've tried looking for tutorials which all point towards extending the validator class and using the AppServiceProvider but trying to avoid this route and follow the guidance in the docs.

I've tried this approach for the rules but it doesn't appear to fire:

public function rules()
    {
        return [
            'play_date'=>'required',
            'am_tee'=>'required',
            'howBooked'=>'required',
            'ticket'=>'required|exists:ticket_audits,ticketnumber' , new TicketNumberIsNotUsed() ,
            'pay_method'=>'required',
            'customer_id'=>'required:exists:customers,id'

        ];

Any help appreciated

0 likes
6 replies
longestdrive's avatar

Found the answer = my lack of understanding of the dot syntax :(

Changed the rules to:

public function rules()
    {
        return [
            'play_date'=>'required',
            'am_tee'=>'required',
            'howBooked'=>'required',
            'ticket'=>['required', 'exists:ticket_audits,ticketnumber' , new TicketNumberIsNotUsed] ,
            'pay_method'=>'required',
            'customer_id'=>'required:exists:customers,id'

        ];
    }

And now the rule fires

Snapey's avatar
Snapey
Best Answer
Level 122

You can pass things into your validation rule using the constructor if you need to.

eg

'ticket'=>['required', 'exists:ticket_audits,ticketnumber' , new TicketNumberIsNotUsed($this->id)]

and then in the rule;

    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

and then you can use the parameter $this->id in your rule

12 likes
goran1301's avatar

Sometimes it's better to do it via setters. If you use constructor for a primitive type, you can not to inject this rule to rulesmethod in Request class. Sometimes you'll need a repository or some service in validator and it's better to inject it in __construct. It will be like this

MyRule extends Rule 
{
    private $someServiceINeed;

    private $id;

    public function __construct(SomeServiceINeed $service)
    {
        $this->someServiceINeed = $service;
    {

    public function setId(int $id): self
    {
        $this->id = $id;
        return $this;
    }

    public function passes($attribute, $value)
    {
        // using your service and id
    }
}

MyRequest extends Request
{
     public function rules(MyRule $myRule): array
    {
        return [
             'my_field' => [$myRule->setId($this->id)],
        ]
    }
}
1 like
longestdrive's avatar

Thanks @Snapey . Yes, I did that in the final implementation to pass the id as you've shown. My struggle was trying to mix dot syntax and instantiating the rule class. T

Please or to participate in this conversation.