void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft )
{
  bool toEmit( false );
  QString errStr( tr( "predicate is True" ) );
  QString expression = layer()->editFormConfig().constraintExpression( mFieldIdx );
  QString description;
  QVariant value = ft.attribute( mFieldIdx );

  if ( ! expression.isEmpty() )
  {
    description = layer()->editFormConfig().constraintDescription( mFieldIdx );

    QgsExpressionContext context = layer()->createExpressionContext();
    context.setFeature( ft );

    QgsExpression expr( expression );

    mValidConstraint = expr.evaluate( &context ).toBool();

    if ( expr.hasParserError() )
      errStr = expr.parserErrorString();
    else if ( expr.hasEvalError() )
      errStr = expr.evalErrorString();
    else if ( ! mValidConstraint )
      errStr = tr( "predicate is False" );

    toEmit = true;
  }
  else
    mValidConstraint = true;

  if ( layer()->editFormConfig().notNull( mFieldIdx ) )
  {
    if ( !expression.isEmpty() )
    {
      QString fieldName = ft.fields().field( mFieldIdx ).name();
      expression = "( " + expression + " ) AND ( " + fieldName + " IS NOT NULL)";
      description = "( " + description + " ) AND NotNull";
    }
    else
    {
      description = QStringLiteral( "NotNull" );
      expression = QStringLiteral( "NotNull" );
    }

    mValidConstraint = mValidConstraint && !value.isNull();

    if ( value.isNull() )
      errStr = tr( "predicate is False" );

    toEmit = true;
  }

  if ( toEmit )
  {
    updateConstraintWidgetStatus( mValidConstraint );
    emit constraintStatusChanged( expression, description, errStr, mValidConstraint );
  }
}
void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft, QgsFieldConstraints::ConstraintOrigin constraintOrigin )
{
  bool toEmit( false );
  QgsField field = layer()->fields().at( mFieldIdx );

  QString expression = field.constraints().constraintExpression();
  QStringList expressions, descriptions;

  if ( ! expression.isEmpty() )
  {
    expressions << expression;
    descriptions << field.constraints().constraintDescription();
    toEmit = true;
  }

  if ( field.constraints().constraints() & QgsFieldConstraints::ConstraintNotNull )
  {
    descriptions << tr( "Not NULL" );
    if ( !expression.isEmpty() )
    {
      expressions << field.name() + QStringLiteral( " IS NOT NULL" );
    }
    else
    {
      expressions << QStringLiteral( "IS NOT NULL" );
    }
    toEmit = true;
  }

  if ( field.constraints().constraints() & QgsFieldConstraints::ConstraintUnique )
  {
    descriptions << tr( "Unique" );
    if ( !expression.isEmpty() )
    {
      expressions << field.name() + QStringLiteral( " IS UNIQUE" );
    }
    else
    {
      expressions << QStringLiteral( "IS UNIQUE" );
    }
    toEmit = true;
  }

  QStringList errors;
  bool hardConstraintsOk = QgsVectorLayerUtils::validateAttribute( layer(), ft, mFieldIdx, errors, QgsFieldConstraints::ConstraintStrengthHard, constraintOrigin );

  QStringList softErrors;
  bool softConstraintsOk = QgsVectorLayerUtils::validateAttribute( layer(), ft, mFieldIdx, softErrors, QgsFieldConstraints::ConstraintStrengthSoft, constraintOrigin );
  errors << softErrors;

  mValidConstraint = hardConstraintsOk && softConstraintsOk;
  mIsBlockingCommit = !hardConstraintsOk;

  mConstraintFailureReason = errors.join( ", " );

  if ( toEmit )
  {
    QString errStr = errors.isEmpty() ? tr( "Constraint checks passed" ) : mConstraintFailureReason;

    QString description = descriptions.join( ", " );
    QString expressionDesc;
    if ( expressions.size() > 1 )
      expressionDesc = "( " + expressions.join( " ) AND ( " ) + " )";
    else if ( !expressions.isEmpty() )
      expressionDesc = expressions.at( 0 );

    ConstraintResult result = !hardConstraintsOk ? ConstraintResultFailHard
                              : ( !softConstraintsOk ? ConstraintResultFailSoft : ConstraintResultPass );
    updateConstraintWidgetStatus( result );
    emit constraintStatusChanged( expressionDesc, description, errStr, result );
  }
}