I'm trying to build a form gathering information about new player in a game.
To start a game one need to provide nickname, email and a code.
Codes are stored in another table connected with player table with one-to-one relation
What I need to do during validation is to check if provided token exists and if so store Player id in Code record.
To do that I'm trying to build a form:
class PlayerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('code','text')
->add('email', 'email')
->add('nick', 'text')
;
}
...
}
but in that way during validation (or probably during creating instance of Code)
Argument 1 passed to Player::setCode() must be an instance of Code, string given
which is obvious since string has been provided.
What to do to perform a lookup during form validation and pass not token string but token instance?
Look into data transformers. This will let you to create a form with a scalar type field that gets transformed into an entity when populating the model on a form submit.
Regarding the validation, you'll need to create a custom validation constraint that checks if the given code exists in the database.
Related
I have a user class that has a onetomany self-referencing relationship (ancestor and decendents). And I have an invoice class, which references two users based on the ancestor-decendents relationship. Meaning that, a user creates an invoice, so the form's "from" property will have the logged in user, while the form's "to" property should be a selection from the logged in user's decendets.
This is the invoice's buildForm method
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('createdDate')
->add('from')
->add('to', 'entity', array('class' => 'Disty\SystemBundle\Entity\User'))
;
}
To keep it short, I want to somehow only show options of decendent users. Right now it shows all users registered.
You should take a look at the query_builder parameter that should do the trick.
If you look at the documentation you can find query_builder option of the entity form type. So you just need to modify query builder to retrieve only users which belong to the current logged in user.
When form is submitted, object's set method (League#setInformation) is called with corresponding data. All is working correctly. (See code below as an example)
I need to pass additional parameters to setInformation, namely current user id which is stored in session data.
That trick would help keeping session and model separate. Maybe useful in different situations too.
Do you know a way to deal with it?
class LeagueFormType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('name');
$builder->add('information', 'collection', [
'type' => new LeagueInformationFormType(),
]);
}
public function setDefaultOptions(\Symfony\Component\OptionsResolver\OptionsResolverInterface $resolver) {
$resolver->setDefaults([
'data_class' => 'xxx\Models\League',
]);
}
public function getName() {
return 'league';
}
}
class League {
public function getInformation() {
//...
}
public function setInformation($data) {
...
}
}
What I would do is declare form as a service, and inject the data from session. If you can, try to refactor your setInformation() function to two functions for example, so you dont have to provide all information through that one. However I think form events will help you set everything as you like.
If you are using Doctrine2 and the League class is actually a Doctrine2 Entity, I would recommend using a Doctrine2 subscriber/listener.
You can configure the subscriber/listener to do something either just before sending the data to the databse (onFlush), just after telling doctrine about a new entity (persist) or just before updating an existing record (update), whichever is the most appropiate in your case.
Inject the SecurityContext (#security.context in your DIC) into the subscriber/listener to pull out the current user information. (Make sure you check there is a user, because the subscriber wil also be run when nobody is logged in and a League object is saved)
The main advantage of this is that is does not pollute your form or controller. And if for some reason you create a League entity some other way the current user wil also be set.
Some docs:
http://doctrine-orm.readthedocs.org/en/latest/reference/events.html
It's a different story if you are not using Doctrine2 though.
I want to test Form types from Symfony2. I have a custom form type and my test looks like this:
/**
* #param \Acme\UserBundle\Entity\User $user
*/
function let(\Acme\UserBundle\Entity\User $user)
{
$this->beConstructedWith($user);
}
function it_is_initializable()
{
$this->shouldHaveType('Acme\UserBundle\Form\Type\RegistrationFormType');
}
/**
* #param \Symfony\Component\Form\FormBuilderInterface $builder
*/
function it_builds_form(\Symfony\Component\Form\FormBuilderInterface $builder)
{
$this->buildForm($builder, []);
}
And I get: Fatal error: Call to a member function add() on a non-object In buildForm method I invoke $this->add method from FormBuilderInterface how can I solve this ?
You didn't post your form code, but I suspect that the problem is the fluent interface that the builder's add() method uses. If you have multiple calls to add() like this:
$builder
->add('username')
->add('email')
->add(...)
->add(...)
->add('save', 'submit');
Then the problem will occur after the first add(), because that isn't returning an object (hence the "Call to a member function add() on a non-object" error message).
If you are using the fluent style, you need to "train" the $builder collaborator, so that phpspec/mockery can return the same builder object for successive calls to add():
$builder->add(Argument::any(), Argument::any())->willReturn($builder);
$this->buildForm($builder, []);
I think that Symfony 2 forms are may not be the best candidate for phpspec testing, as you really want to test only the public API for your classes and not to test code that you don't own (i.e. framework/3rd-party libraries).
The form type that you are testing isn't the actual form that is produced, it's more like the "blueprint" used to build the form when it is needed, so I think it's harder to test that a form has certain fields or options, etc. as this isn't called by your code, it happens automatically when the forms framework processes the form type.
The work to create the real form happens inside the builder, which in the context of this form type spec is a collaborator rather than a real builder object (and also is not your code to test).
Imagine three entities - Account, Address, Purchase. Account has a OneToMany relationship with Address. A Purchase is related to an Account, but not with an Address - it does however have a text field for the address (this is because addresses can change, I don't want the address directly related). On the users' account page they can add addresses. Whilst logged into the site, a Purchase id is stored in the session and used to retrieve the Purchase details when required.
What I want to do on the checkout page is display a list of all the addresses a user currently has in a <select>, allow them to pick one, and update the address in the current Purchase. $account->getAddresses() exists and will show the addresses relevant to the user.
I have read http://symfony.com/doc/current/reference/forms/types/collection.html and http://symfony.com/doc/current/cookbook/form/form_collections.html and can't see how to apply it to this situation, although an embedded form isn't really necessary - I don't want to change other details of the Purchase at that stage.
So my question is (at least one of): how do I pass the results of $account->getAddresses() to a form type? Or in the form type, should I use an entity field type, and if so, how do I get the custom query_builder to contain the current user in a form type? Or how else should I do this?
You need to pass the entity in to the Type's constructor and then use it to get the parameter.
Class YourType extends AbstractType
{
private $account;
public function __construct($account)
{
$this->account = $account;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$accountId = $account->getAccountId();
$builder->add('addressId',
'entity',
array('class' => 'YourBundle:Address',
'query_builder' => function(EntityRepository $er) use ($accountId) {
return $er->createQueryBuilder('a')
->where('a.accountId = ?1')
->setParameter(1, $accountId)));
}
}
I am creating an application where visitors can upload some stuff (it will be for invited people only). At the end, if they are not logged in, they are asked to log in or create a user. If they create a new user, I only want to ask them to fill in their name and email.
a password will be generated, and a mail will be sent to the user with the links to change their password if they want to (only to make the procedure as low level as possible).
I can't seem to remove the password fields from the registration form. Can someone help me out. I create a custom form type, service and registered it. I also put custom templates in the app/Resource folder etc. Although my custom Form type AND the templates are being used, the password still appears ...
class RegistrationFormType extends BaseType {
public function buildForm(FormBuilderInterface $builder, array $options) {
parent::buildForm($builder, $options);
// add your custom field
$builder->add('username');
$builder->add('email');
}
public function getName() {
return 'val_user_registration';
}
}
You don't need to extend your parent form ... and you're better off not doing it in this case.
just create a Username/Email form and create the new User entity yourself in a custom registration method then persisting it into database ( don't forget to set the usernameCanonical property on your newly created User ).
FOSUserBundle calls several password-related things during the registration process which you don't need and can't easily circumvent in this case.
You can pass the entity ( with newly created password ) to the update method of FOSUserBundle's UserManager service after you have completed the password/email step then.