Skip to content

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

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 231 additions & 0 deletions cookbook/doctrine/simple_signup_form_with_mongodb.rst
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.
Copy link
Member

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

Copy link
Member

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.


.. 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB; in the mongo chapter, so we should probably use that here as well for consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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?

Copy link
Member

Choose a reason for hiding this comment

The 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")
Copy link
Member

Choose a reason for hiding this comment

The 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should is duplicated


.. tip::

The validation is done with the annotations. If you don't know how it works - please read the documentation :doc:`file</book/validation>`.
Copy link
Member

Choose a reason for hiding this comment

The 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why you put it in the Entity subnamespace. The convention is to use it for ORM entities and this is not an entity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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?

Copy link
Member

Choose a reason for hiding this comment

The 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 Form directory. If there's a better convention, hopefully @stof will tell us ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also put them in the Form subnamespace of my bundle. I'm currently wondering how to separate them from other Form resources (I will put types in Form\Type as it is done in the Form component (where they are inside Extension\Core\Type) but I haven't find a good naming for the subnamespace for non-persisted form-specific models


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;
Copy link
Member

Choose a reason for hiding this comment

The 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())
{
Copy link
Member

Choose a reason for hiding this comment

The 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()));
}