I would like to use a class level annotation constraint. However I cannot get the inner constraints to validate automatically. I'd like to help with one part, to incorporate validation groups into this technique.
#ConstraintA({
#ConstraintB(stuff),
#ConstraintB(stuff, groups=SomeGroup.class)
})
public class Form{
}
I currentily trigger the constraints like so.
if(constraint instanceof ConstraintB){
new ConstraintBValidator().isValid(target, context);
}
However this sucks obviously.I will eventually refactor to trigger the isValid methods through a call to the AnnotationInvocationHandler.invoke() method, but im a little way from that still.
My issue is that all ConstraintB instances are passed into my ConstraintA. I wish only the ones with the appropriate groups to be passed to ConstraintA. I doubt this ability exists, so how can i identify which groups need to be triggered and which dont?
I dont see in my debug, any objects which specify which groups should be triggered?
Any ideas?
I've found a pattern in the JSR 303 spec that allows this type of validation. Its a recursive pattern that wont execute the parent validation rule, only fulfill the nested validations. This is VERY handy. My nested validation rules are conditionally based on other properties values so it allows conditional validation with nested jsr303 annotations.
#Documented
#Constraint(validatedBy = ZipCodeValidator.class)
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
public #interface ZipCode {
String countryCode();
String message() default "{com.acme.constraint.ZipCode.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Defines several #ZipCode annotations on the same element
* #see (#link ZipCode}
*/
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Documented
#interface List {
ZipCode[] value();
}
my own validation is more like this:
#RequiredIf ({
#RequiredIf(ifField="field1", matches={"true","somethingElse"}, requiredField="anotherField", andDisplay="error.code.msg"),
#RequiredIf(ifField="field2", matches={"true","somethingElse"}, requiredField="anotherField", andDisplay="error.code.msg")
})
Related
I have a hierarchy of classes: VehicleDTO is a base abstract class.
CarDTO, TruckDTO, VanDTO extend from it.
I have the same hierarchy on the other side of a mapper:
VehicleBO <- CarBO, TruckBO, VanBO.
I want to have all the mapping logic consolidated in one mapper. Period.
I have defined mappings for common attributes, but here is when it becomes interesting, I get this exception during compilation:
The return type ... is an abstract class or interface.
Provide a non abstract / non interface result type or a factory method.
So, how do I specify a factory method, that based on a value of a particular attribute or a class of the pojo, would create a target object for me? I would appreciate a good code snippet that actually does the trick.
Thanks!
You can use a method annotated with #ObjectFactory receiving a source parameter for what you need.
Let's assume that you have a mapper that looks like:
#Mapper
public interface VehicleMapper {
VehicleDTO map(VehicleBO vehicle);
// more
}
If you add a method looking like:
#ObjectFactory
default VehicleDTO createVehicleDto(VehicleBO vehicle) {
// your creation logic
}
Then MapStruct will use the createVehicleDto to create the VehicleDTO object.
NOTE when mapping hierarchies and when the mapping looks like the one in the answer then MapStruct will only map the properties which are in the VehicleDTO class and not in possible implementations of the class. The reason for that is that MapStruct generates the mapping code during compilation and not during runtime.
For mapping hierarchies like what you explained you can do something like the following:
public interface VehicleMapper {
default VehicleDTO map(VehicleBO vehicle) {
if (vehicle instanceOf CarBO) {
return map((CarBO) vehicle);
} else if (vehicle instanceOf TruckBO) {
return map((TruckBO) vehicle);
} else if (vehicle instanceOf VanBO) {
return map((VanBO) vehicle);
} else {
//TODO decide what you want to do
}
}
#Named("car")
CarDTO map(CarBO car);
#Named("truck")
TruckDTO map(TruckBO truck);
#Named("car")
VanDTO map(VanBO van);
// more
}
There is mapstruct/mapstruct#131 requesting for generating code like my example out of the box
Nowadays, maybe using Visitor pattern could be better choice instead of the instanceOf way, check below:
https://techlab.bol.com/en/blog/mapstruct-object-hierarchies
You need to set the subclassExhaustiveStrategy property in your #Mapper annotation to RUNTIME_EXCEPTION.
See Mapstruct documentation:
...
To allow mappings for abstract classes or interfaces you need to set the subclassExhaustiveStrategy to RUNTIME_EXCEPTION, you can do this at the #MapperConfig, #Mapper or #BeanMapping annotations. If you then pass a GrapeDto an IllegalArgumentException will be thrown because it is unknown how to map a GrapeDto. Adding the missing (#SubclassMapping) for it will fix that.
...
Just a quick question. I'm building some API. I was thinking about creating simple parent class that would deal with form requests.
So for example if you would like to easily handle form request you just extend this class and you get access to request object, request data extracted from that object and bunch of methods that do some things for you out of the box. It doesn't matter what and why exactly.
The problem is:
I send request through postman.
I try to use request object in class that extends parent class but instead of request I get null.
How do I set up the whole thing?:
Now in Symfony every controller is by default registered as a service so I override this definition like this:
#generic api form controller
App\Controller\Api\ApiFormController:
calls:
- [setDependencies, ['#request_stack', '#App\Service\Serialization\Serializer']]
So as you can see I am using setter injection.
I extend above class in my other class. Let's call it PostController. So:
<?php
namespace App\Controller\Api;
use Symfony\Component\HttpFoundation\RequestStack;
class ApiFormController
{
/**
* #var Request
*/
public $request;
/**
* #param RequestStack $requestStack
*/
public function setDependencies(
RequestStack $requestStack
) {
$this->request = $requestStack;
}
}
And now PostController:
public function get(int $post = null)
{
dump($this->request); exit;
}
I was expecting to get access like this and I think I understand why I don't have access to this object. I'm looking for some ideas how I could achieve this goal in cleanest possible way. I'm not expecting ready answers but hints.
I was thinking about using events to set it up in the background?
I also think it has something to do with the way I'm hooking up my controller as a service.
The core of it all: Symfony does not pick up service definition for subclasses. So if you define dependencies for a class and extend it in another class, you have to define the dependencies for this second class too.
The easiest way is to use the parent keyword for this, so your example would work in the following way:
App\Controller\Api\ApiFormController:
calls:
- [setDependencies, ['#request_stack', '#App\Service\Serialization\Serializer']]
PostController:
parent: App\Controller\Api\ApiFormController
If you are using autowiring, you can use #required to make Symfony call the setter automatically. https://symfony.com/doc/current/service_container/autowiring.html#autowiring-other-methods-e-g-setters
/**
* #param RequestStack $requestStack
* #required
*/
public function setDependencies(
RequestStack $requestStack
) {
$this->request = $requestStack;
}
This should do the trick.
I see several problems here.
If you want to inject dependencies in such a way you should define controller as service. You can read more here.
Routing should be something like this:
# config/routes.yaml
get_something:
path: /
defaults: { _controller: App\Controller\Api\PostController:get }
Also, you should define PostController as service, not ApiFormController.
You injected RequestStack but type hint for the attribute is Request.
Instead of:
$this->request = $requestStack;
You need to use:
$this->request = $requestStack->getMasterRequest();
I need to change the validation of some field in a form. The validator is configured via a quite large yml file. I wonder if there is any way to do validation on two fields at once.
In my case I have two fields that cannot be both empty. At least one has to be filled.
Unfortunately till now I just could see that the validation are defined on a per-field basis, not on multiple fields together.
The question is: is it possible in the standard yml configurations to perform the aforementioned validation?
thanks!
I suggest you to look at Custom validator, especially Class Constraint Validator.
I won't copy paste the whole code, just the parts which you will have to change.
Extends the Constraint class.
src/Acme/DemoBundle/Validator/Constraints/CheckTwoFields.php
<?php
namespace Acme\DemoBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class CheckTwoFields extends Constraint
{
public $message = 'You must fill the foo or bar field.';
public function validatedBy()
{
return 'CheckTwoFieldsValidator';
}
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
}
Define the validator by extending the ConstraintValidator class, foo and bar are the 2 fields you want to check:
src/Acme/DemoBundle/Validator/Constraints/CheckTwoFieldsValidator.php
namespace Acme\DemoBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class CheckTwoFieldsValidator extends ConstraintValidator
{
public function validate($protocol, Constraint $constraint)
{
if ((empty($protocol->getFoo())) && (empty($protocol->getBar()))) {
$this->context->addViolationAt('foo', $constraint->message, array(), null);
}
}
}
Use the validator:
src/Acme/DemoBundle/Resources/config/validation.yml
Acme\DemoBundle\Entity\AcmeEntity:
constraints:
- Acme\DemoBundle\Validator\Constraints\CheckTwoFields: ~
I have a model called Field which has id and label.
I have defined PropertyAcess as below and it works. I would like to change it in such a way that I can show label based on condition ie if field.getLabel() is null, use field.getId() as label. How can I acheieve that
interface FieldProperties extends PropertyAccess<Field> {
ModelKeyProvider<Field> id();
LabelProvider<Field> label();
#Path("label")
ValueProvider<Field, String> labelProp();
}
Thanks
The PropertyAccess tool is meant to make it easy to quickly build ValueProvider, ModelKeyProvider, and LabelProvider instances that are based on a specific getter/setter on a bean-like object. If you don't want just access to a single property, then implement the interface directly.
In your case, since you want a LabelProvider that returns getLabel() unless it is null, then getId(), you might do something like this:
public LabelOrIdLabelProvider implements LabelProvider<Field> {
#Override
public String getLabel(Object item) {
return item.getLabel() == null ? item.getId() : item.getLabel();
}
}
If you want custom behavior, build it out yourself to do exactly what you need. If you just want the simple behavior of reading a single getter, the PropertyAccess is there to help save you a few lines of code.
My application uses the MVVM pattern. My TextBox is bound to a property of my ViewModel (type string).
When ever the content of the TextBox changed via the user typing, I want to perform some validation.
So, currently, my code is
<TextBox Text="{Binding XmlContentAsString, UpdateSourceTrigger=PropertyChanged}" />
and my ViewModel has this property and field:
private string _xmlContentAsString;
public string XmlContentAsString
{
get { return _xmlContentAsString; }
set
{
if (_xmlContentAsString == value)
return;
_xmlContentAsString = value;
PerformValidiationLogic(value);//This is where I am unsure
}
}
Now, this works but, and I don't know why, I don't like this! It some how feels 'hacked' to include the method in the property.
Can some one please tell me if this is the correct approach when using the MVVM pattern?
There's different type of validations.
For simplistic validating string lengths or allowed characters etc you can use DataAnnotations and put the validation in attributes on your properties. You'll need to include
using System.ComponentModel.DataAnnotations;
then for example to keep string to 9 characters:
[StringLength(9)]
public string StringValue
{
get
{
return stringValue;
}
set
{
this.stringValue = value;
}
}
Then there is validation that is a bit more complex and is effectively enforcing your business logic.
There seem to be many views on how to do this. Ideally it should belong on the model, so that the validation can be reused, but obviously called via the viewmodel.
Personally I will put method calls in the property setters occasionally, to me thats the whole reason for having the ability to create setters and getters - otherwise there's very little point in having anything other than auto properties.
But if it's complex or asynchronous then you can hit issues.
I'd be very careful doing it with UpdateSourceTrigger=PropertyChanged, as that means you'll be firing it every character.
In your example, you perform validation logic on the value, but what would be the result of the validation if it fails? Typically you would want to notify the user of a validation failure. If that is the case, then I suggest IDataErrorInfo (examples can be found here:
http://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/).
If you plan on overriding the value without notifying the user, then validating in the setter is acceptable (though still not a fan for more personal reasons).
In my opinion thats the correct approach. I would write a base class for your ViewModel that contains a method that sets the property, call PropertyChanged and validate if some validation rule is attached to that property.
For example:
public abstract class ValidableViewModel
{
private List<ValidationRule> _validationRules;
public ValidableViewModel()
{
_validationRules = new List<ValidationRule>();
}
protected virtual void SetValue<T, T2>(Expression<Func<T>> expression,
ref T2 backend, T2 value)
{
if (EqualityComparer<T2>.Default.Equals(backend, value))
return;
backend = value;
OnPropertyChanged(expression);
Validate(expression.Name, value);
}
protected void Validate(string propertyName, object value)
{
foreach(var validationRule in _validationRules)
{
if(validationRule.PropertyName == propertyName)
validationRule.Execute(value);
}
}
}
The code is not complete, there is missing a lot. But it could be a start ;-)
I personally don't advise putting so much logic in your property. I would use a command bound to an event, ie the lostfocus event of the textbox, and perform your validation there.
I would use something like this:
<TextBox Text="{Binding XmlContentAsString, UpdateSourceTrigger=PropertyChanged}">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="LostFocus">
<interactivity:InvokeCommandAction Command="{Binding LostFocusCommand, Mode=OneWay}"/>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
</TextBox>
then have a command in your view model that is LostFocusCommand wiht your validation logic.
I use mvvm-light and can give a more detailed example for that. (you will need to include the blend interactivity declaration at the top of your xaml)