The following example shows how to register a custom constraint using the custom_constraint
property.
Register a custom constraint
The custom_constraint
property is an object
in the schema and is reserved for custom defined constraints using above outlined invocation and hook. The interpretation of what custom_constraint
should contain and how those values of the schema are interpret are in the solely hand of the implementor.
{
"type": "PROPERTY_CONSTRAINT_SCHEMA",
"constraints": {
"custom_constraint": {
"foo_constraint": true
}
},
"tags": [
"property constraint",
"custom constraint"
]
}
The validation schema only checks whether custom_constraint
is an object or not, any further validation of what and how the object is structured is not part of the schema validation, the assigned Constraint
class should handle any inconsistencies with an invalid constraint definition.
Hook registration
use Hooks;
use Foo\FooConstraint;
Hooks::register( 'SMW::Constraint::initConstraints', function ( $constraintRegistry ) {
$constraintRegistry->registerConstraint( 'foo_constraint', FooConstraint::class );
return true;
} );
Assigning a non Constraint
class or a callable that doesn't return a Constraint
instance will throw an exception.
Constraint representation
class FooConstraint implements Constraint {
private $hasViolation = false;
public function hasViolation() {
return $this->hasViolation;
}
public function getType() {
return Constraint::TYPE_INSTANT;
}
public function checkConstraint( array $constraint, $dataValue ) {
$this->hasViolation = false;
...
$this->reportError( $dataValue );
}
private function reportError( $dataValue ) {
$this->hasViolation = true;
$error = [
'foo-violation-message-key'
];
$dataValue->addError(
new ConstraintError( $error )
);
}
}
Other examples
For example, when implementing custom_constraint
with a start_end_constraint
that contains greater_than
condition, the greater_than
property may expect an array and the registered class for start_end_constraint
has to interpret what greater_than
means in the context of the given array. It could be defined as "the first element (e.g. property `Event end`) needs to be greater than the second element (e.g. property `Event start`)" but that is up to the implementing class to decide and has nothing to do with Semantic MediaWiki.
{
"type": "CLASS_CONSTRAINT_SCHEMA",
"constraints": {
"mandatory_properties": [
"Event start",
"Event end"
],
"custom_constraint": {
"start_end_constraint": {
"greater_than": ["Event end", "Event start"]
}
}
},
"tags": [
"class constraint",
"custom constraint"
]
}
use Hooks;
use Custom\StartEndConstraint;
Hooks::register( 'SMW::Constraint::initConstraints', function ( $constraintRegistry ) {
$constraintRegistry->registerConstraint( 'start_end_constraint', StartEndConstraint::class );
return true;
} );
class StartEndConstraint implements Constraint {
private $hasViolation;
public function hasViolation() {
return $this->hasViolation;
}
public function getType() {
return Constraint::TYPE_INSTANT;
}
public function checkConstraint( array $constraint, $dataValue ) {
$this->hasViolation = false;
}
foreach ( $constraint as $key => $v ) {
if ( $key === 'start_end_constraint' ) {
$this->start_end_constraint( $v, $dataValue );
}
}
}
private function start_end_constraint( $constraint, $dataValue ) {
if ( !isset( $constraint['greater_than'] ) ) {
return;
}
$end = $constraint['greater_than'][0];
$start = $constraint['greater_than'][1];
$semanticData = $dataValue->getCallable( SemanticData::class )();
$s = $semanticData->getPropertyValues(
DIProperty::newFromUserLabel( $start )
);
$e = $semanticData->getPropertyValues(
DIProperty::newFromUserLabel( $end )
);
...
}
See also
- https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/technical/hooks/hook.constraint.initconstraints.md "hook.constraint.initconstraints.md"