Symfony2.1 FOSUserBundle overriding validation - symfony-2.1

i have problems overriding the form validation.
I have overridden the user class and the RegisterFormType class. In the RegisterFormType class i used the setDefaultOptions function to use an other validation group
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Clickstorm\Geruest\UserBundle\Entity\User',
'intention' => 'register',
'validation_groups' => array('register')
));
}
But when i submit the form the default validation is executed. But what confuses me the most is that when i tried to override the validation by create an own validation.xml file the validation was triggered twice (the error messages where schon twice).
Any idea about that?

I don't know if this is the right solution. But it works when overriding the validation_groups in the config file.

Related

How to decorate the final class DocumentGenerator

I am having problems to decorate the final class "DocumentGenerator" (in vendor/shopware/core/Checkout/Document/Service/DocumentGenerator.php) and overwrite the "generate" function inside of it.
I tried to decorate it the usual way, but an error is thrown because the "DocumentController" class excepts the original class and not my decorated one?
Argument 2 passed to Shopware\Core\Checkout\Document\DocumentGeneratorController::__construct() must be an instance of Shopware\Core\Checkout\Document\Service\DocumentGenerator
Its also not possible to extend from the class in my decorated class, because the "DocumentGenerator" is a final class.
My goal is to execute additional code, after an order document is generated. Previously I successfully used to decorate the "DocumentService" Class, but its marked as deprecated and shouldnt be used anymore. Also the "DocumentGenerator" class is used for the new "bulkedit" function for documents as of Version 6.4.14.0
I'm grateful for every tip.
As #j_elfering already wrote it's by design that you should not extend that class and therefore also shouldn't decorate it.
To offer a potential alternative:
Depending on what you want to do after a document has been generated it might be enough to add a subscriber to listen to document.written, check if it was a new document created and then work with the data from the payload for fetching/persisting data depending on that.
public static function getSubscribedEvents()
{
return [
'document.written' => 'onDocumentWritten',
];
}
public function onDocumentWritten(EntityWrittenEvent $event): void
{
foreach ($event->getWriteResults() as $result) {
if ($result->getOperation() !== EntityWriteResult::OPERATION_INSERT) {
// skip if the it's not a new document created
continue;
}
$payload = $result->getPayload();
// do something with the payload
}
}
Probably not what you want to hear but: The service is final in purpose as it is not intended to be decorated.
So the simple answer is you can't. Depending on your use case there may be other ways that don't rely on decoration.

Intercepting Fluent Validation

We are using fluentvalidation (with service stack) to validate our request DTO's. We have recently extended our framework to accept "PATCH" requests, which means we now have a requirement to apply validation ONLY when the patch contained the field being validated.
We have done this using an extension method such as this:
RuleFor(dto => dto.FirstName).Length(1,30)).WhenFieldInPatch((MyRequest dto)=>dto.FirstName);
RuleFor(dto => dto.MiddleName).Length(1,30)).WhenFieldInPatch((MyRequest dto)=>dto.MiddleName);
RuleFor(dto => dto.LastName).Length(1,30)).WhenFieldInPatch((MyRequest dto)=>dto.LastName);
This means we can run the same validation for a POST/PUT or a PATCH.
I have been looking for a way of hooking in to the fluent validation framework in such as way that we do not need to duplicate the .WhenFieldInPatch() rule on EVERY line in our validations, but have not yet found a nice way to do this.
I have tried the following:
Creating a helper method (in a in a base class) to intercept the initial "RuleFor" which adds the .When() clause up front, but the this does not work as fluent validation requires the .When() to be last
Intercepting the calls in PreValidation, but I can only intercept based on the whole class, and not on a rule by rule basis
Adding an extension method to apply to the end of every rule (as per example), but I cannot access the initial expression in order to check whether the field should be mapped - so I need to pass it in again.
Am I missing something, or am I attempting the impossible?
Thanks
When I need to share Fluent Validation Logic I'd use extension methods, here's an example of shared Extension methods for TechStacks, e.g:
public static class ValidatorUtils
{
public static bool IsValidUrl(string arg) => Uri.TryCreate(arg, UriKind.Absolute, out _);
public static string InvalidUrlMessage = "Invalid URL";
public static IRuleBuilderOptions<T, string> OptionalUrl<T>(
this IRuleBuilderInitial<T, string> propertyRule)
{
return propertyRule
.Length(0, UrlMaxLength)
.Must(IsValidUrl)
.When(x => !string.IsNullOrEmpty(x as string))
.WithMessage(InvalidUrlMessage);
}
}
And some examples where they're shared:
public class CreatePostValidator : AbstractValidator<CreatePost>
{
public CreatePostValidator()
{
RuleSet(ApplyTo.Post, () =>
{
RuleFor(x => x.Url).OptionalUrl();
});
}
}
public class UpdatePostValidator : AbstractValidator<UpdatePost>
{
public UpdatePostValidator()
{
RuleSet(ApplyTo.Put, () =>
{
RuleFor(x => x.Url).OptionalUrl();
});
}
}

How to execute servicestack request validation code only

I'm wondering is there a way to flag to servicestack that you only want to execute the fluent validation filter for a request, and not go on to run the real code?
Ideally you could add a property to any request, something like
`ExecuteValidationOnly = true`
This would be really useful for tests, and for validating forms client side.
Thanks.
There's no such feature built into ServiceStack, but you can easily add a Request Filter that short-circuits the request based on a QueryString, e.g:
public class SkipRequestFeature : IPlugin
{
public void Register(IAppHost appHost)
{
appHost.GlobalRequestFilters.Add((req, res, dto) => {
if (req.QueryString["SkipRequest"] == "true")
res.EndRequestWithNoContent();
});
}
}
And register the plugin just after the ValidationFeature so it's executed after validation, e.g:
Plugins.Add(new ValidationFeature());
Plugins.Add(new SkipRequestFeature());
For this case, I usually unit test my actual AbstractValidator<T> class then when I need to test the service I create a mock on the validator and setup validation calls.
So there is a MyValidatorTests class and a MyServiceTests class.

Is there a way to ignore some entity properties when calling EdmxWriter.WriteEdmx

I am specifically using breezejs and the server code for breeze js converts the dbcontext into a form which is useable on the clientside using EdmxWriter.WriteEdmx. There are many properties which I have added JsonIgnore attributes to so that they don't get passed to the client side. However, the metadata that is generated (and passed to the clientside) from EdmxWriter.WriteEdmx still has those properties. Is there any additional attribute that I can add to those properties that I want ignored so that they are ignored by EdmxWriter.WriteEdmx? Or, would I need to make a separate method so as not to have any other unintended side effects.
You can sub-class your DbContext with a more restrictive variant that you use solely for metadata generation. You can continue to use your base context for persistence purposes.
The DocCode sample illustrates this technique with its NorthwindMetadataContext which hides the UserSessionId property from the metadata.
It's just a few extra lines of code that do the trick.
public class NorthwindMetadataContext : NorthwindContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Hide from clients
modelBuilder.Entity<Customer>().Ignore(t => t.CustomerID_OLD);
// Ignore UserSessionId in metadata (but keep it in base DbContext)
modelBuilder.Entity<Customer>().Ignore(t => t.UserSessionId);
modelBuilder.Entity<Employee>().Ignore(t => t.UserSessionId);
modelBuilder.Entity<Order>().Ignore(t => t.UserSessionId);
// ... more of the same ...
}
}
The Web API controller delegates to the NorthwindRepository where you'll see that the Metadata property gets metadata from the NorthwindMetadataContext while the other repository members reference an EFContextProvider for the full NorthwindContext.
public class NorthwindRepository
{
public NorthwindRepository()
{
_contextProvider = new EFContextProvider<NorthwindContext>();
}
public string Metadata
{
get
{
// Returns metadata from a dedicated DbContext that is different from
// the DbContext used for other operations
// See NorthwindMetadataContext for more about the scenario behind this.
var metaContextProvider = new EFContextProvider<NorthwindMetadataContext>();
return metaContextProvider.Metadata();
}
}
public SaveResult SaveChanges(JObject saveBundle)
{
PrepareSaveGuard();
return _contextProvider.SaveChanges(saveBundle);
}
public IQueryable<Category> Categories {
get { return Context.Categories; }
}
// ... more members ...
}
Pretty clever, eh?
Just remember that the UserSessionId is still on the server-side class model and could be set by a rogue client's saveChanges requests. DocCode guards against that risk in its SaveChanges validation processing.
You can sub-class your DbContext with a more restrictive variant that you use solely for metadata generation. You can continue to use your base context for persistence purposes.
The DocCode sample illustrates this technique with its NorthwindMetadataContext which hides the UserSessionId property from the metadata.
It's just a few extra lines of code that do the trick.
The Web API controller delegates to the NorthwindRepository where you'll see that the Metadata property gets metadata from the NorthwindMetadataContext while the other repository members reference an EFContextProvider for the full NorthwindContext.
Pretty clever, eh?
If you use the [NotMapped] attribute on a property, then it should be ignored by the EDMX process.

Black-hole error when route pointed to a class that extends plugin and uses extended class

In routes I have
Router::connect('/opauth-complete/*', array('controller' => 'app_users', 'action' => 'opauth_complete'));
If I change pointer to controller app_users with anything else and create controller everything works with no error. But I need it to work with AppUsersController.
AppUsersController looks like this
App::uses('UsersController', 'Users.Controller');
class AppUsersController extends UsersController {
public function beforeFilter() {
parent::beforeFilter();
$this->User = ClassRegistry::init('AppUser');
}
// ...
// ...
public function opauth_complete() {
die(1);
}
// ...
// ...
}
So, plugin is CakeDC Users and another plugin that goes to /example/callback after /example/auth/facebook is Opauth plugin.
Error message looks like this
The request has been black-holed
Error: The requested address '/example/opauth-complete' was not found on this server.
This is perfectly possible to make these two plugins work together; when browser points to /example/auth/facebook, it redirects to /example/auth/callback and somehow it needs opauth-complete route to link to specific method.
All works if not pointed to app_users that extends plugin, uses plugin. Does not work only with this case. How can users of these two plugins get around such situation.
I solved it by disabling Security component on Opauth action in my AppUsersController. Thing is that Opauth transfers data using POST and you should either change a method of it (ie: use Sessions, GET) or disable Security component.
For a method change use this in your bootstrap.php or core.php
Configure::write('Opauth.callback_transport', 'session'); // you can try 'get' too
To follow my approach add this to a controller where error occurs and where you place your opauth_complete method
public function beforeFilter() {
// ...
if (isset($this->Security) && $this->action == 'opauth_complete') {
$this->Security->validatePost = false;
$this->Security->csrfCheck = false;
}
// ...
}
P.S. Changing method to Sessions has its drawbacks, you can take a look at comments here at Github Opauth issue #16

Resources