Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jrdavidson's avatar

Refactoring A Long Custom Rule

I'm looking for ways that I can refactor this custom rule. I ran PHP Insights and this is definitely a class that needs refactored as the passes method has 40 lines compared to the 20 that I'm looking to make methods less than.

The reason for how this rule is constructed is based off of the Password custom rule that was introduced into Laravel last year.

https://github.com/laravel/framework/blob/b9203fca96960ef9cd8860cb4ec99d1279353a8d/src/Illuminate/Validation/Rules/Password.php

<?php

namespace App\Rules\Players;

use App\Models\Team;
use App\Models\Player;
use Carbon\Carbon;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;

class CanJoinUpdatedTeam implements Rule, DataAwareRule, ValidatorAwareRule
{
    /**
     * The data under validation.
     *
     * @var array
     */
    protected $data;

    /**
     * The validator instance.
     *
     * @var \Illuminate\Validation\Validator
     */
    protected $validator;

    /**
     * The failure messages, if any.
     *
     * @var array
     */
    protected $messages = [];

    /**
     * @var \App\Models\Team
     */
    protected $team;

    /**
     * Create a new rule instance.
     *
     * @param \App\Models\Team $team
     */
    public function __construct(Team $team)
    {
        $this->team = $team;
    }

    /**
     * Set the data under validation.
     *
     * @param  array  $data
     * @return $this
     */
    public function setData($data)
    {
        $this->data = $data;

        return $this;
    }

    /**
     * Set the current validator.
     *
     * @param  \Illuminate\Validation\Validator  $validator
     * @return $this
     */
    public function setValidator($validator)
    {
        $this->validator = $validator;

        return $this;
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        $this->messages = [];

        $validator = Validator::make(
            $this->data,
            [$attribute => []],
            $this->validator->customMessages,
            $this->validator->customAttributes
        )->after(function ($validator) use ($attribute, $value) {
            $player = Player::query()
                ->whereKey($value)
                ->sole();

            if ($this->team->currentPlayers->contains($player)) {
                return true;
            }

            if ($player->isSuspended()) {
                $validator->errors()->add($attribute, "{$player->name} is suspended and cannot join team.");
            }

            if ($player->isInjured()) {
                $validator->errors()->add($attribute, "{$player->name} is injured and cannot join team.");
            }

            if ($player->isCurrentlyEmployed()
                && $this->data['started_at']
                && ! $user->employedBefore(Carbon::parse($this->data['started_at']))
            ) {
                $validator->errors()->add(
                    $attribute,
                    "{$player->name} cannot have a start date after team's start date."
                );
            }
        });

        if ($validator->fails()) {
            return $this->fail($validator->messages()->all());
        }

        return true;
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return $this->messages;
    }

    /**
     * Adds the given failures, and return false.
     *
     * @param  array|string  $messages
     * @return bool
     */
    protected function fail($messages)
    {
        $messages = collect(Arr::wrap($messages))->map(function ($message) {
            return $this->validator->getTranslator()->get($message);
        })->all();

        $this->messages = array_merge($this->messages, $messages);

        return false;
    }
}

0 likes
0 replies

Please or to participate in this conversation.