Let the platform do the work

Validation Constraints

Overview

This article will cover how to add validation constraints to your custom code.

Constraints

Validation constraints allow developers to validate user input

Symfony Validation Constraints

The Symfony's Validation library, located in ./vendor/symfony/validator/, contains many open source constraints. You can find the full list of constraints documented in Symphony's Validation Constraints. They are listed below for your reference.

String Constraints

Number Constraints

Comparison Constraints

Collection Constraints

File Constraints

Financial and other Number Constraints

Sugar Constraints

Sugar contains its own set of validation constraints. These constraints extend from Symfony's validation constraints and are located in ./src/Security/Validator/Constraints of your Sugar installation.

  • Bean/ModuleName
  • Bean/ModuleNameValidator
  • ComponentName
  • ComponentNameValidator
  • Delimited
  • DelimitedValidator
  • DropdownList
  • DropdownListValidator
  • File
  • FileValidator
  • Guid
  • GuidValidator
  • InputParameters
  • InputParametersValidator
  • JSON
  • JSONValidator
  • Language
  • LanguageValidator
  • LegacyCleanString
  • LegacyCleanStringValidator
  • Mvc/ModuleName
  • Mvc/ModuleNameValidator
  • PhpSerialized
  • PhpSerializedValidator
  • Sql/OrderBy
  • Sql/OrderByValidator
  • Sql/OrderDirection
  • Sql/OrderDirectionValidator
  • SugarLogic/FunctionName
  • SugarLogic/FunctionNameValidator

Custom Constraints

If you find that the existing constraints do not meet your needs, you can also create your own. These constraints exist under ./custom/src/Security/Validator/Constraints/. The following section will outline the details. The example below will demonstrate how to create a phone number validation constraint. 

The constraint file will contain your validations error message.

./custom/src/Security/Validator/Constraints/Phone.php

<?php

namespace Sugarcrm\Sugarcrm\custom\Security\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 *
 * @see PhoneValidator
 *
 */
class Phone extends Constraint 
{
    public $message = 'Phone number violation: %msg%';
}

The validator file will contain the logic to determine whether the value meets the requirements.

./custom/src/Security/Validator/Constraints/PhoneValidator.php

<?php

namespace Sugarcrm\Sugarcrm\custom\Security\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

/**
 *
 * Phone validator
 *
 */
class PhoneValidator extends ConstraintValidator
{
    /*
     * Phone Pattern Examples;
     * +1 12 3456789
     * +1.12.3456789
     */
    const PHONE_PATTERN = '/^([+]?\d+(?:[ \.]\d+)*)$/';

    /**
     * {@inheritdoc}
     */
    public function validate($value, Constraint $constraint)
    {
        if (!$constraint instanceof Phone) {
            throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\Phone');
        }

        if (null === $value || '' === $value) {
            return;
        }

        if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) {
            throw new UnexpectedTypeException($value, 'string');
        }

        $value = (string) $value;

        // check for allowed characters
        if (!preg_match(self::PHONE_PATTERN, $value)) {
            $this->context->buildViolation($constraint->message)
                ->setParameter('%msg%', 'invalid format')
                ->setInvalidValue($value)
                ->addViolation();
            return;
        }
    }
}

Once the files are in place, navigate to Admin > Repairs and run a Quick Repair and Rebuild. Once completed, your constraint will be ready for use.

Using Constraints in API End Points

In this section, we will create a custom endpoint and trigger the custom phone constraint we created above. The code below will create a REST API endpoint path of /phoneCheck:

./custom/clients/base/api/MyEndpointsApi.php

<?php

if (!defined('sugarEntry') || !sugarEntry) {
    die('Not A Valid Entry Point');
}

use Sugarcrm\Sugarcrm\Security\Validator\ConstraintBuilder;
use Sugarcrm\Sugarcrm\Security\Validator\Validator;

class MyEndpointsApi extends SugarApi
{
    public function registerApiRest()
    {
        return array(
            //POST
            'MyEndpointsApi' => array(
                //request type
                'reqType' => 'POST',
//endpoint path 'path' => array('phoneCheck'),
//endpoint variables 'pathVars' => array(''),
//method to call 'method' => 'phoneCheck',

//minimum api version
'minVersion' => 10,
//short help string to be displayed in the help documentation 'shortHelp' => 'An example of a POST endpoint to validate phone numbers',
//long help to be displayed in the help documentation 'longHelp' => 'custom/clients/base/api/help/MyEndPoint_phoneCheck_help.html', ), ); } /** * Method to be used for my MyEndpointsApi/phoneCheck endpoint */ public function phoneCheck($api, $args) { $validator = Validator::getService(); /** * Validating Phone Number */ $phoneContraintBuilder = new ConstraintBuilder(); $phoneConstraints = $phoneContraintBuilder->build( array( 'Assert\Phone', ) ); $errors = $validator->validate($args['phone'], $phoneConstraints); if (count($errors) > 0) { /* * Uses a __toString method on the $errors variable which is a * ConstraintViolationList object. This gives us a nice string * for debugging. */ $errorsString = (string) $errors; // include/api/SugarApiException.php throw new SugarApiExceptionInvalidParameter($errorsString); } //custom logic return $args; } }

After creating the endpoint, navigate to Admin > Repairs and run a Quick Repair and Rebuild. The API will then be ready to use.

Valid Payload - POST to /phoneCheck

The example below demonstrates a valid phone number post the /phoneCheck endpoint.

{
phone: "+1.23.456.789"
}
Result
{
phone: "+1.23.456.789"
}

Invalid Payload - POST to /phoneCheck

The example below demonstrates an invalid phone number post the /phoneCheck endpoint.

{
phone: "Invalid+1.23.456.789"
}
Result
{
"error":"invalid_parameter",
"error_message":"Invalid+1.23.456.789:\n Phone number violation: invalid format\n"
}

Topics