3.4 3.3
Jump to table of contents

Validation

Data can be validated using the entity schemas or the ValidatorFactory. In both cases, we use Laravel’s Validation library.

All of Laravel’s validation rules are supported except the exist rule.

ValidatorFactory #

The ValidatorFactory class is a wrapper for Laravel’s Validator. It mimics Laravel’s make method to create a validator.

use PKP\validation\ValidatorFactory;

$validator = ValidatorFactory::make($props, $rules);

Run the validator and retrieve errors.

if ($validator->fails()) {
  $errors = $validator->errors();
}

Validate a single value.

use PKP\validation\ValidatorFactory;

$props = ['contactEmail' => $userEmail];
$rules = ['contactEmail' => ['email_or_localhost']];
$validator = ValidatorFactory::make($props, $rules);

Or validate more than one value at a time.

use PKP\validation\ValidatorFactory;

$props = [
    'contactEmail' => $userEmail,
    'contactUsername' => $userName,
];
$rules = [
    'contactEmail' => ['email_or_localhost'],
    'contactUsername' => ['alpha_num'],
];
$validator = ValidatorFactory::make($props, $rules);

The validator will return helpful errors when a value does not validate. If you want, you can customize these messages by passing an additional argument.

use PKP\validation\ValidatorFactory;

$props = ['contactUsername' => $userName];
$rules = ['contactUsername' => ['min:6', 'alpha_num']];
$messages = ['contactUsername.min' => 'The journal contact username must be at least 6 characters.'];
$validator = ValidatorFactory::make($props, $rules, $messages);

Schema Validation #

Validation rules can be defined in an entity’s schema. In this example, the numAnnouncementsHomepage must be an integer that is not negative.

{
  "numAnnouncementsHomepage": {
    "type": "integer",
    "validation": [
      "nullable",
      "min:0"
    ]
  }
}

These rules will be applied when the entity’s Repository validates user input that contains a numAnnouncementsHomepage prop. See the Repository Validation section below.

Every property that can be empty or null must have the nullable validation rule assigned, or it will throw an error when it is empty.

{
  "mailingAddress": {
    "type": "string",
    "validation": [
      "nullable"
    ]
  }
}

You never need to add the array, boolean, integer or string validation rules. These will be applied automatically based on the type property.

Repository Validation #

An entity’s Repository should implement a validate method which validates props against the schema. Use the SchemaService to access helper methods for working with the schema.

namespace PKP\context;

use PKP\context\Context;
use PKP\services\PKPSchemaService;
use PKP\validation\ValidatorFactory;

class Repository
{
    protected PKPSchemaService $schemaService;

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

    public function validate(?Context $context, array $props, array $allowedLocales, string $primaryLocale): array
    {
        $errors = [];

        // Validate the $props against the entity's
        // schema file
        $validator = ValidatorFactory::make(
            $props,
            $this->schemaService->getValidationRules(PKPSchemaService::SCHEMA_CONTEXT, $allowedLocales)
        );

        // Validate the $props against the required fields
        // in the entity's schema file
        ValidatorFactory::required(
            $validator,
            $submission,
            $this->schemaService->getRequiredProps(PKPSchemaService::SCHEMA_CONTEXT),
            $this->schemaService->getMultilingualProps(PKPSchemaService::SCHEMA_CONTEXT),
            $primaryLocale,
            $allowedLocales
        );

        // Validate the $props against the locales supported
        // by this context.
        ValidatorFactory::allowedLocales(
            $validator,
            $this->schemaService->getMultilingualProps(SCHEMA_CONTEXT),
            $allowedLocales
        );
    }
}

Some validation rules can not be described in the schema. This is the case when validation requires checking the database. For example, a context can not have a urlPath if another context exists with that urlPath.

In such cases, the Repository’s validate method should be used to extend the validation check.

namespace PKP\context;

use PKP\context\Context;
use PKP\services\PKPSchemaService;
use PKP\validation\ValidatorFactory;

class Repository
{
    protected PKPSchemaService $schemaService;

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

    public function validate(?Context $context, array $props, array $allowedLocales, string $primaryLocale): array
    {
        $errors = [];

        $validator = ValidatorFactory::make(
            $props,
            $this->schemaService->getValidationRules(PKPSchemaService::SCHEMA_CONTEXT, $allowedLocales)
        );

        ...

        // Ensure that a urlPath, if provided, does not already exist
        $validator->after(function($validator) use ($action, $props) {
            if (isset($props['urlPath']) && !$validator->errors()->get('urlPath')) {
                if (/* urlPath is duplicate */) {
                    $validator->errors()->add('urlPath', __('admin.contexts.form.pathExists'));
                }
            }
        });

        ...

        if ($validator->fails()) {
            $errors = $this->schemaService->formatValidationErrors($validator->errors());
        }

        return $errors;
    }
}

Custom Rules #

The following custom validation rules can be used.

Rule Description
email_or_localhost Extends Laravel’s email validation to accept emails @localhost.
issn Require a valid ISSN.
orcid Require a valid ORCID.
currency Require a valid currency code.

These rules can be applied in the schema.

{
  "onlineIssn": {
    "type": "string",
    "validation": [
      "nullable",
      "issn"
    ]
  }
}