-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
simple signup form with mongodb #433
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
How to implement simple Sign up Form with MongoDB | ||
================================================= | ||
|
||
Some forms have extra fields which value are not needed to be stored into database. So in this example we create sign up form with some extra fields and embed the form for storing the account information. We use MongoDB for storing data. This explains how to integrate two types of domain model into the form. | ||
|
||
.. tip:: | ||
|
||
If you are not familiar with Doctrine MongoDB Bundle, you should read this :doc:`file</cookbook/doctrine/mongodb>` recipe first to learn how to setup the MongoDB Bundle to be able to work with MongoDB. | ||
|
||
The simple Account model | ||
------------------------ | ||
|
||
So, in this tutorial we begin with the model for the ``Account``:: | ||
|
||
// src/Acme/AccountBundle/Document/Account.php | ||
namespace Acme\AccountBundle\Document; | ||
use Doctrine\ODM\MongoDB\Mapping\Annotations as mongodb; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK! This looks better! But how should the alias be called for this Symfony\Bundle\DoctrineMongoDBBundle\Validator\Constraints? MongoDBBundleAssert? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you don't need any alias for the namespace as there is only one annotation. Import the class directly. |
||
use Symfony\Component\Validator\Constraints as Assert; | ||
|
||
/** | ||
* @mongodb\Document(collection="accounts") | ||
* @Symfony\Bundle\DoctrineMongoDBBundle\Validator\Constraints\Unique(path="email") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you should add a use statement for the annotation. It would be more readable IMO |
||
*/ | ||
class Account | ||
{ | ||
/** | ||
* @mongodb\Id | ||
*/ | ||
protected $id; | ||
|
||
/** | ||
* @mongodb\Field(type="string") | ||
* @Assert\NotBlank() | ||
* @Assert\Email() | ||
*/ | ||
protected $email; | ||
|
||
/** | ||
* @mongodb\Field(type="string") | ||
* @Assert\NotBlank() | ||
*/ | ||
protected $password; | ||
|
||
public function getId() | ||
{ | ||
return $this->id; | ||
} | ||
|
||
public function getEmail() | ||
{ | ||
return $this->email; | ||
} | ||
|
||
public function setEmail($email) | ||
{ | ||
$this->email = $email; | ||
} | ||
|
||
public function getPassword() | ||
{ | ||
return $this->password; | ||
} | ||
|
||
// stupid simple encryption (please don't copy it!) | ||
public function setPassword($password) | ||
{ | ||
$this->password = sha1($password); | ||
} | ||
} | ||
|
||
This ``Account`` document contains three fields and two of them (email and password) should should display on the form. The email property must be unique on the database, so we've added this validation at the top of the class. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
.. tip:: | ||
|
||
The validation is done with the annotations. If you don't know how it works - please read the documentation :doc:`file</book/validation>`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm really not sure this is needed here |
||
|
||
Create form for the model | ||
------------------------- | ||
|
||
Now, you need to create form for this ``Account`` model:: | ||
|
||
// src/Acme/AccountBundle/Form/Account.php | ||
namespace Acme\AccountBundle\Form; | ||
|
||
use Symfony\Component\Form\AbstractType; | ||
use Symfony\Component\Form\Extension\Core\Type\RepeatedType; | ||
use Symfony\Component\Form\FormBuilder; | ||
|
||
class AccountType extends AbstractType | ||
{ | ||
public function buildForm(FormBuilder $builder, array $options) | ||
{ | ||
$builder->add('email', 'email'); | ||
$builder->add('password', 'repeated', array( | ||
'first_name' => 'password', | ||
'second_name' => 'confirm', | ||
'type' => 'password' | ||
)); | ||
} | ||
|
||
public function getDefaultOptions(array $options) | ||
{ | ||
return array('data_class' => 'Acme\AccountBundle\Document\Account'); | ||
} | ||
} | ||
|
||
We just added two fields: email and password (repeated to confirm the entered password). The ``data_class`` option tells the form the name of data class and this is your ``Account`` document and the form is able to create the data model. | ||
|
||
.. tip:: | ||
|
||
To explore more things about form component, read this documentation :doc:`file</book/forms>`. | ||
|
||
Embedding Account form into Signup form | ||
--------------------------------------- | ||
|
||
The form for sign up is not the same as the form for Account. It contains further fields like accepting the terms which value is not needed to be stored into database. So, now we need to create own form for this purpose and embed the existing ``Account`` form. For validation and creation of Account data we need simple domain model for the sign up form:: | ||
|
||
// src/Acme/AccountBundle/Entity/Signup.php | ||
namespace Acme\AccountBundle\Entity; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see why you put it in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know what is the default convention for such domain models? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a really good question. Most typically, these non-persisted domain models tend to something you're creating just so that a form can bind to it (as you've done in this case), so I tend to put them in a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also put them in the |
||
|
||
use Symfony\Component\Validator\Constraints as Assert; | ||
|
||
use Acme\AccountBundle\Document\Account; | ||
|
||
class Signup | ||
{ | ||
/** | ||
* @Assert\Type(type="Acme\AccountBundle\Document\Account") | ||
*/ | ||
protected $account; | ||
|
||
/** | ||
* @Assert\NotBlank() | ||
* @Assert\True() | ||
*/ | ||
protected $termsAccepted; | ||
|
||
public function setAccount(Account $account) | ||
{ | ||
$this->account = $account; | ||
} | ||
|
||
public function getAccount() | ||
{ | ||
return $this->account; | ||
} | ||
|
||
public function getTermsAccepted() | ||
{ | ||
return (boolean)$this->termsAccepted; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. casting as boolean should be done in the setter, not in the getter |
||
} | ||
|
||
public function setTermsAccepted($termsAccepted) | ||
{ | ||
$this->termsAccepted = $termsAccepted; | ||
} | ||
} | ||
|
||
And the form for this ``Signup`` model:: | ||
|
||
// src/Acme/AccountBundle/Form/Signup.php | ||
namespace Acme\AccountBundle\Form; | ||
|
||
use Symfony\Component\Form\AbstractType; | ||
use Symfony\Component\Form\Extension\Core\Type\RepeatedType; | ||
use Symfony\Component\Form\FormBuilder; | ||
|
||
class SignupType extends AbstractType | ||
{ | ||
public function buildForm(FormBuilder $builder, array $options) | ||
{ | ||
$builder->add('account', new AccountType()); | ||
$builder->add('terms', 'checkbox', array('property_path' => 'termsAccepted')); | ||
} | ||
} | ||
|
||
We added two fields into the form. You don't need to use special method for embedding form. A form is a field, too - so you can add this like the fields, with the expectation that you need to instance the class ``AccountType``. | ||
|
||
Handle the form submission | ||
-------------------------- | ||
|
||
Now we need controller to handle the form actions, first we create simple controller for displaying the sign up form:: | ||
|
||
namespace Acme\AccountBundle\Controller; | ||
|
||
use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||
use Symfony\Component\HttpFoundation\Response; | ||
|
||
use Acme\AccountBundle\Form; | ||
use Acme\AccountBundle\Entity; | ||
|
||
class AccountController extends Controller | ||
{ | ||
public function signupAction() | ||
{ | ||
$form = $this->createForm(new Form\SignupType(), new Entity\Signup()); | ||
|
||
return $this->render('AcmeAccountBundle:Account:signup.html.twig', array('form' => $form->createView())); | ||
} | ||
} | ||
|
||
and it's template:: | ||
|
||
<form action="{{ path('create')}}" method="post" {{ form_enctype(form) }}> | ||
{{ form_widget(form) }} | ||
|
||
<input type="submit" /> | ||
</form> | ||
|
||
At least we need the controller which handles the form submission. This performs the validation and saves the data into the database:: | ||
|
||
public function createAction() | ||
{ | ||
$dm = $this->get('doctrine.odm.mongodb.default_document_manager'); | ||
|
||
$form = $this->createForm(new Form\SignupType(), new Entity\Signup()); | ||
|
||
$form->bindRequest($this->get('request')); | ||
|
||
if ($form->isValid()) | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not follow the Sf2 CS. The curly brace should be on the previous line |
||
$signup = $form->getData(); | ||
|
||
$dm->persist($signup->getAccount()); | ||
$dm->flush(); | ||
|
||
return $this->redirect($this->generateUrl('welcome', array('id' => $signup->getAccount()->getId()))); | ||
} | ||
|
||
return $this->render('AcmeAccountBundle:Account:signup.html.twig', array('form' => $form->createView())); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to wrap the lines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, every line should wrap approximately after the first word that crosses the 72nd character. So, line lengths tend to be between 73 and 78 characters, depending on how long the last word on the line is. This needs to be done throughout this entry.