Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] Validation rules stopped working, Laravel maintainers suggest you provide new ones? #427

Closed
ls-dac-chartrand opened this issue Jan 21, 2020 · 4 comments

Comments

@ls-dac-chartrand
Copy link

ls-dac-chartrand commented Jan 21, 2020

Package version, Laravel version

laravel-doctrine/orm: 1.5.4
laravel/framework: 6.11.1

Expected behaviour

Where My\Entities\Something is a valid @ORM\Entity with a constructor that has more than zero parameters:

$x = \Illuminate\Validation\Rule::unique(My\Entities\Something::class);

Should return \Illuminate\Validation\Rules\Unique

Actual behaviour

We upgraded laravel/framework from 6.5.1 to 6.11.0 and we now get:

Too few arguments to function My\Entities\Something::__construct(), 0 passed in src/Illuminate/Validation/Rules/DatabaseRule.php on line 65 and at least 4 expected

Steps to reproduce the behaviour

Upgrade laravel/framework to anything above 6.6+ and try to instantiate a new \Illuminate\Validation\Rules\Unique with a a valid @ORM\Entity that has a constructor with more than zero parameters. Same thing will happen with Exists, and anything else that uses trait \Illuminate\Validation\Rules\DatabaseRule.

Here's the commit that broke things: laravel/framework#30653

Here's a bug report about this issue that was closed upstream: laravel/framework#31180

Maintainer suggested:

We only support Eloquent models for this rule. Because you're not using an Eloquent model, this rule won't work. You'll have to ask the maintainers of laravel doctrine to provide a custom database rule.

This is why I opened this ticket.

Thank you for your time.

@ls-dac-chartrand ls-dac-chartrand changed the title [QUESTION] [QUESTION] Validation rules stopped working, Laravel maintainers suggest you provide new ones? Jan 21, 2020
@eigan
Copy link
Member

eigan commented Jan 21, 2020

Extending the Illuminate\Validation\Unique and overriding resolveTableName might be a good start.
https://laravel.com/docs/6.x/validation#custom-validation-rules

$tableName = EntityManagerInterface::getClassMetadata($entityClass)->getTableName();

Will review and merge a PR if anyone have the time to write it :)

@jaimyborgman
Copy link

I'd have encountered the same issue and made the following solution for it to work. Probably there have been made some changes on the validator level and by changing it's logic a little you can make it work again with Doctrine.

Like @eigan suggested you'll need to make a custom validation rule:

<?php

namespace Your\Namespace\Here;

use Illuminate\Validation\Rules\Unique as UniqueRule;

final class Unique extends UniqueRule
{
    /**
     * @param string $table
     *
     * @return string
     */
    public function resolveTableName($table)
    {
        return $table;
    }
}

one side note here: I'm using full class names while passing it into the Rule like so: Rule::unique(User::class, 'email')
so if the above doesn't work for you, try to replace the line return $table; with the following:

return \LaravelDoctrine\ORM\Facades\EntityManager::getClassMetadata($table)
            ->getTableName();

as followed I've created a new Rule object which I'll call in my controllers, please note that this probably also could be overwritten on ServiceProvider level to overwrite the original 'unique' rule of Laravel itself.

<?php

namespace Your\Namespace\Here;

use Illuminate\Validation\Rule as BaseRule;

final class Rule extends BaseRule
{
    /**
     * Get a unique constraint builder instance.
     *
     * @param string $table
     * @param string $column
     *
     * @return \Remindr\Support\Validations\Rules\Unique
     */
    public static function unique($table, $column = 'NULL')
    {
        return new Unique($table, $column);
    }
}

now that we have created the rule itself and extended the helper we'll need to create our own custom validator instance like so:

<?php

namespace Your\Namespace\Here;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Illuminate\Validation\Validator as BaseValidator;

class Validator extends BaseValidator
{
    /**
     * Parse the connection / table for the unique / exists rules.
     *
     * @param  string  $table
     * @return array
     */
    public function parseTable($table)
    {
        [$connection, $table] = Str::contains($table, '.') ? explode('.', $table, 2) : [null, $table];

        if (Str::contains($table, '\\') && class_exists($table) && is_a($table, Model::class)) {
            $model = new $table;

            $table = $model->getTable();

            $connection = $connection ?? $model->getConnectionName();
        }

        return [$connection, $table];
    }
}

the problem lays in is_a($table, Model::class) which Laravel originally has specified like is_a($table, Model::class, true)

last but not least we'll should add the following lines in for instance your AppServiceProvider under register():

$this->app->resolving('validator', function (Factory $factory) {
    $factory->resolver(function ($translator, $data, $rules, $messages) {
        return new CustomValidator($translator, $data, $rules, $messages);
    });
});

hope this helps.

Best,
Jaimy

@3ech7oul
Copy link

3ech7oul commented Aug 6, 2020

<?php

namespace Your\Namespace\Here;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Illuminate\Validation\Validator as BaseValidator;

class Validator extends BaseValidator
{
    /**
     * Parse the connection / table for the unique / exists rules.
     *
     * @param  string  $table
     * @return array
     */
    public function parseTable($table)
    {
        [$connection, $table] = Str::contains($table, '.') ? explode('.', $table, 2) : [null, $table];

        if (Str::contains($table, '\\') && class_exists($table) && is_a($table, Model::class)) {
            $model = new $table;

            $table = $model->getTable();

            $connection = $connection ?? $model->getConnectionName();
        }

        return [$connection, $table];
    }
}
$this->app->resolving('validator', function (Factory $factory) {
    $factory->resolver(function ($translator, $data, $rules, $messages) {
        return new CustomValidator($translator, $data, $rules, $messages);
    });
});

Best,
Jaimy

Thank you, it was helpful for me.

@vv12131415
Copy link
Contributor

This was fixed by me here laravel/framework#33481

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants