Symfony2 + FOSUserbundle + dynamic change validation groups - symfony

I am new with Symfony2 and so I am with FOSUserbundle, the registration form I wrote got different fields mandatory based on if it is a company or private person, I set it up etc and if I hardcode the valdiation group in the fos config it will work, but what I need is to set it up dynamic based on the form submit data, I tried to follow:
http://symfony.com/doc/current/book/forms.html#book-forms-validation-groups
Seeing that FOSUserbundle use the deprecated OptionsResolverInterface , I even tried it with that one, but whatever I do it is never called.
So my question is kinda, what is the right approach with that bundle to change the valdiation group on the fly based on the submitted data?

Figured it out myself how to do and because I could nowhere on the internet find the solution, answering here my own question for the case someone else need that too:
in the config.yml i set the validationgroup like this
validation_groups: [AppBundle\Form\Type\RegistrationFormType, determineValidationGroups]
and inside that class then:
public static function determineValidationGroups(FormInterface $form){
...
}
And that one returns an array with the validations groups, just dont forget to include for registration for example Registration or it wont validate if the user already exists etc

Related

#Security annotation and user parameter

In a controller, I have an action meant to display a user. The argument is the user to be displayed and is automatically fetched through a parameter converter.
Now I want to secure this action (displaying a user profile) so that only users with the USER_VIEW permission (currently implemented as a custom Voter) have access.
Using an #Security annotation it would look like:
/**
* #Security("is_granted('USER_VIEW', user)")
*/
public function showAction(User $user) {...}
This doesn't work because the user variable in the expression refers to the authenticated user rather than the action argument.
The documentation mentions that, but this is rather unfortunate.
How can I fix this issue and get my $user argument passed to the security expression? I could rename it, but perhaps there's something better to do.
I was also wondering if this should be considered a bug in Symfony, since 'user' is a rather common word, perhaps it should be renamed to something more specific such as 'authenticated_user' or 'security_user' or something. The same goes for the other 3 variables that are passed to the expression by Symfony: 'token', 'request', 'roles'.
Thanks.

Symfony2 best way of removing business logic from controller and correct usage of model

I'm in searching of the best way of removing business logic from controller and correct usage of model(and maybe services).
Some details below.
Actually, my project is more complicated, but as example I will use Simple Blog application.
I have created my application (Simple Blog) in next steps:
created bundle
generated entities(Topic, Post, Comment)
generated controller for each entity, using doctrine:generate:crud
installed FOSUserBundle and generated User entity
So, I have all needed methods and forms in my controllers. But now I have some troubles:
Admin need to be able see all topics and posts, when simple User can only see
topic and posts where he is owner.
Currently there are indexAction, that return findAll common for any user. As solution, I can check in action, if ROLE_USER or ADMIN and return find result for each condition. But this variant keep some logic at action.
I also can generate action for each role, but what happened if roles amount will increase?
What is the best way to solve this problem with result for each role?
I need to edit some parameters before saving.
For example, I have some scheduler, where I create date in some steps, using features of DateTime.
Before saving I need to do some calculations with date.
I can do it in controller using service or simple $request->params edit.
What is the best way to edit some $request parameters before saving?
My questions I have marked with bold.
Thanks a lot for any help!
What I would do is to create a query which fetches the topics. Afterwards I would have a method argument which specifies if the query should select only the topics for a certain user or all topics. Something like this should do the work in your TopicRepository:
public function findTopics($userId = false)
{
$query = $this->createQueryBuilder('topic');
if($userId) {
$query->join('topic.user', 'user')
->where('user.id = :user_id')
->setParameter(':user_id', $userId)
;
}
return $query->getQuery()->getResult();
}
So, whenever you need to get the topics only by a user, you would pass a $userId to the method and it would return the results only for that user. In your controller you'd have something similar to this code (Symfony 2.6+):
$authorizationChecker = $this->get('security.authorization_checker');
if($authorizationChecker->isGranted('ROLE_ADMIN')){
$results = $this->get('doctrine.orm.entity_manager')->getRepository('TopicRepository')->findTopics();
} else {
$results = $this->get('doctrine.orm.entity_manager')->getRepository('TopicRepository')->findTopics($this->getUser()->getId());
}
You can try using Doctrine Events and create a PreUpdate depending on your case. See the documentation for more information. If you have a TopicFormType, you could also try the form events.
You are not supposed to "edit" a $request, which is why you can't directly do that. You can, however, retrieve a value, save it as a $variable and then do whatever you want with it. You can always create a new Request if you really need it. Could you be more specific what you want to do here and why is this necessary?

Create an Admin User with FosUserBundle

I try to create an Admin User with FOsUserBundle from command windows with the following command:
php app/console fos:user:create
In my project the Admin User extends other user with mandatory propriety. So, when I choose my username, mail and password, it tells me:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'latitude' cannot be null
How can I set the value "latitude" in my AdminUser? I also use PUGXMultiUserBundle.
Only possibile way to reach that to me is
1 - override the cli command of FOSUserBundle placed into Command/CreateUserCommand.php
2 - override the user create method of FOSUserBundle placed into Util/UserManipulator.php
// Command/CreateUserCommand.php
protected function execute(InputInterface $input, OutputInterface $output)
{
$username = $input->getArgument('username');
$email = $input->getArgument('email');
$password = $input->getArgument('password');
$inactive = $input->getOption('inactive');
$superadmin = $input->getOption('super-admin');
$latitude = $input->getOption('latitude'); //this will be your own logic add
$manipulator = $this->getContainer()->get('fos_user.util.user_manipulator');
$manipulator->create($username, $password, $email, $latitude, !$inactive, $superadmin);
$output->writeln(sprintf('Created user <comment>%s</comment>', $username));
}
and
// Util/UserManipulator.php
public function create($username, $password, $email, $latitude, $active, $superadmin)
{
$user = $this->userManager->createUser();
$user->setUsername($username);
$user->setEmail($email);
$user->setPlainPassword($password);
$user->setEnabled((Boolean) $active);
$user->setSuperAdmin((Boolean) $superadmin);
$user->setLatitude($latitude);
$this->userManager->updateUser($user);
return $user;
}
Of course when I say override i mean ... override :P So you haven't to modify FOSUserBundle original files (you know, it's dangerous for many reasons) but make your own files by making your bundle extended by FOSUserBundle
Are you wondering how to make your bundle extended by FOSUserBundle?
Into your bundle "mainfile" - is the one you use to register your bundle - just add this lines
public function getParent()
{
return 'FOSUserBundle';
}
Then you simply recreate the tree structure where your ovverride files lives into original bundle, into your custom bundle's Resources/ directory (same position, same file name, same annotations if any) and .... the magic can start :) (this is valid only for views, please pay attention!)
What "override" means?
Override means that you take an existent function, "shadow" it by redefining elsewhere (declare a function with the same name, no matter how many parameters it accept, no matter the type of paramenters since php doesn't support method overloading [except if you do some "hack"]) and then you can use it instead of the original one. This is a common technique for add extra functionalities to a function or to change the function itself.
Say that we have two classes, A and B with B that is a child class of A. Say also that A have a method called myMethod().
In B we can do something like
public function myMethod() {
parent::myMethod();
//add extra functionalities here
}
in that way we're adding extra functionalities as we're calling the parent ("original") method and then execute some extra functionalities
Whereas if in B we make something like
public function myMethod() {
//some code here, but not calling parent method
}
we're redefining the behaviour of myMethod()
How Symfony2 let me override methods?
As I said previously in my answer, you have to make your bundle a child of the bundle that containts the function(s) you're trying to override (in that case FOSUserBundle). Once you did it, use the Resources directory of your bundle to accomplish what you need. reproduce the "tree-folder-structure" of the original bundle (ie.: same names of the folders) until you reach the class that contains the function you need to override.
Follow your real example: you need to override execute() function contained in Command/CreateUserCommand.php. You have to create, into your bundle folder that path:
PathTo/YourCostumBundle/Command/
and place inside the file CreateUserCommand.php with the content I show you above.
If you don't understand where I find that path, please take a look to FOSUserBundle code and it will be absolutely clear!
Why is dangerous to modify the FOSUserBundle code directly?
Well, there's a lot of answer an critic point that I can show you. Choosing the main (not ordered for importance):
What if you need to update FOSUserBundle? You'll use composer and lost every modify that you made to FOSUserBundle code
What if you have more than one bundle into your project that need to use FOSUserBundle? Maybe the custom behaviour makes sense for a bundle but not for the other one. Costumizing the behaviour at local bundle level helps you to keep FOSUserBundle logic intact
What if you're developing a bundle that you want to share with other user? You need to force them to "take" your own costumized FOSUserBundle version and warn them about updating it
Finally: I perfeclty know that your entity isn't into FOSUserBundle, but I can bet that they extend FOSUserBundle base user so what I told above is applicable to your case.
Hope it's less fuzzy now :)
Documentation: http://symfony.com/doc/current/cookbook/bundles/inheritance.html#overriding-controllers
I always follow the pattern I learned in the symfony documentation itself:
php bin/console fos:user:create usertest mail#domain.com password
and sometimes I need change the "roles" on the table "fos_user"
then
a:0:{}
to
a:1:{i:0;s:10:"ROLE_ADMIN";}
After creating a user with:
bash-5.1# bin/console fos:user:create admin admin#mydomain.com password123
Promote the user with the ROLE_ADMIN role:
bash-5.1# bin/console fos:user:promote
Please choose a username:admin
Please choose a role:ROLE_ADMIN
Role "ROLE_ADMIN" has been added to user "admin". This change will not
apply until the user logs out and back in again.

FOSUserBundle: Update entity after registration

In order to register, users have to select a their account name created by my moderators. That means that a moderators have to create an account name before the user registers.
To do so, I made a first entity, let's call it "Member", that has a field "account". Then I added to this entity the boolean field "bound" that is set to false by default.
What I want to do is to set this field "bound" to true when someone registers after he selected his account name and fill the FOSUserBundle required fields (username, passwords, email...).
I tried to follow the documentation of "overriding controllers", but I'm getting an error (You have requested a non-existent service "fos_user.registration.form".) and this is where I'm stucked.
Using controller events can maybe help me, but I do not know which is the best solution.
If anyone has a solution to my problem, I'll be really grateful.
You should used the controller event to hook after the registration process, and more specifically the
REGISTRATION_COMPLETED event (if I remember correctly).

How to update the value of a single field invoking appropriate validation

I'm making a module to allow users to update single fields on in this case, their user entity.
The code below is an example of the method I have initially been using to get it working and test other elements of the module
global $user;
$account = user_load($user->uid);
$edit = (array) $account;
$edit['field_lastname']['und'][0]['value'] = 'test';
user_save($account, $edit);
However this bypasses any field validation defined elsewhere in Drupal. I don't want to reproduce any validation written elsewhere - it's not the Drupal way!
My question is: Is there a function in Drupal 7 that can be called to update the value of a single field. I imagine such a function would clear the appropriate caches, invoke the fields validation etc.
I am aware the solution will be totally different to my current user object based one. I just can't for the life of me find the appropriate function in the API. I wander whether the fact I am looking for a save function alone is the problem - and that there are some other necessary steps that come before.
Any help gratefully appreciated.
Check out the drupal_form_submit function. It lets you submit forms from code. In this case, you could use it to the user edit form, which would then fire the appropriate validation.

Resources