Laravel unique validation rule and Spatie Multitenancy cover image

Laravel unique validation rule and Spatie Multitenancy

Tom Oehlrich

Laravel Validation Multitenancy

Data privacy laws have become increasingly important in the digital age, as companies store and process more user data than ever before. These laws often require organizations to separate customer data from one another.

Laravel provides developers with several multitenancy packages that make it easier to comply with these regulations.

In a multi DB setup an application usually has a "landlord" database that consists - at a bare minimum - of an "accounts" or "tenants" table.

Then for each customer there is a separate database holding all customer specific data tables.

When using Spatie's Laravel-multitenancy package with multi DB setup you might encounter the following error:

Base table or view not found: 1146 Table [landlord.TABLE_NAME] doesn't exist

The source of this error usually stems from the package searching for the table with the unique value in the "landlord" database instead of the "tenant" database, despite the use of the "UsesTenantConnection" concern in the model class.

If you encounter this error, it is recommended to verify if a validation with the "unique" rule is being used.

In Laravel there are a couple of ways to write validation rules:

'email' => ['email', Rule::unique('users')],
'email' => 'email|unique:users',

If you do get the mentioned error try using the model class name instead of the table name.

'email' => ['email', Rule::unique(User::class)],

In the background Laravel takes care of resolving the class name to a table string.

/**
    * Resolves the name of the table from the given string.
    *
    * @param  string  $table
    * @return string
    */
public function resolveTableName($table)
{
    if (! str_contains($table, '\\') || ! class_exists($table)) {
        return $table;
    }

    if (is_subclass_of($table, Model::class)) {
        $model = new $table;

        if (str_contains($model->getTable(), '.')) {
            return $table;
        }

        return implode('.', array_map(function (string $part) {
            return trim($part, '.');
        }, array_filter([$model->getConnectionName(), $model->getTable()])));
    }

    return $table;
}