323 lines
8.8 KiB
ReStructuredText
323 lines
8.8 KiB
ReStructuredText
FOSUserBundle Invitation
|
|
========================
|
|
|
|
Require an invitation to create a new user is a pattern mostly used for
|
|
early stage of a project. User enter their invitation code in order to
|
|
register.
|
|
|
|
Invitation model
|
|
----------------
|
|
|
|
First we need to add the invitation entity. An invitation is represented
|
|
by a unique code/identifier generated in the constructor::
|
|
|
|
<?php
|
|
// src/AppBundle/Entity/Invitation.php
|
|
|
|
namespace AppBundle\Entity;
|
|
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
|
|
/** @ORM\Entity */
|
|
class Invitation
|
|
{
|
|
/** @ORM\Id @ORM\Column(type="string", length=6) */
|
|
protected $code;
|
|
|
|
/** @ORM\Column(type="string", length=256) */
|
|
protected $email;
|
|
|
|
/**
|
|
* When sending invitation be sure to set this value to `true`
|
|
*
|
|
* It can prevent invitations from being sent twice
|
|
*
|
|
* @ORM\Column(type="boolean")
|
|
*/
|
|
protected $sent = false;
|
|
|
|
public function __construct()
|
|
{
|
|
// generate identifier only once, here a 6 characters length code
|
|
$this->code = substr(md5(uniqid(rand(), true)), 0, 6);
|
|
}
|
|
|
|
public function getCode()
|
|
{
|
|
return $this->code;
|
|
}
|
|
|
|
public function getEmail()
|
|
{
|
|
return $this->email;
|
|
}
|
|
|
|
public function setEmail($email)
|
|
{
|
|
$this->email = $email;
|
|
}
|
|
|
|
public function isSent()
|
|
{
|
|
return $this->sent;
|
|
}
|
|
|
|
public function send()
|
|
{
|
|
$this->sent = true;
|
|
}
|
|
}
|
|
|
|
Next we map our ``Invitation`` entity to our ``User`` with a one-to-one association::
|
|
|
|
<?php
|
|
// src/AppBundel/Entity/User.php
|
|
|
|
namespace AppBundle\Entity;
|
|
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
use Symfony\Component\Validator\Constraints as Assert;
|
|
|
|
/** @ORM\Entity */
|
|
class User extends \FOS\UserBundle\Model\User
|
|
{
|
|
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue(strategy="AUTO") */
|
|
protected $id;
|
|
|
|
/**
|
|
* @ORM\OneToOne(targetEntity="Invitation")
|
|
* @ORM\JoinColumn(referencedColumnName="code")
|
|
* @Assert\NotNull(message="Your invitation is wrong", groups={"Registration"})
|
|
*/
|
|
protected $invitation;
|
|
|
|
public function setInvitation(Invitation $invitation)
|
|
{
|
|
$this->invitation = $invitation;
|
|
}
|
|
|
|
public function getInvitation()
|
|
{
|
|
return $this->invitation;
|
|
}
|
|
}
|
|
|
|
Add invitation to RegistrationFormType
|
|
--------------------------------------
|
|
|
|
Override the default registration form with your own::
|
|
|
|
<?php
|
|
// src/AppBundle/Form/RegistrationFormType.php
|
|
|
|
namespace AppBundle\Form;
|
|
|
|
use Symfony\Component\Form\AbstractType;
|
|
use Symfony\Component\Form\FormBuilderInterface;
|
|
|
|
class RegistrationFormType extends AbstractType
|
|
{
|
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
|
{
|
|
$builder->add('invitation', 'AppBundle\Form\InvitationFormType');
|
|
}
|
|
|
|
public function getParent()
|
|
{
|
|
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
|
|
}
|
|
|
|
public function getBlockPrefix()
|
|
{
|
|
return 'app_user_registration';
|
|
}
|
|
|
|
// Not necessary on Symfony 3+
|
|
public function getName()
|
|
{
|
|
return 'app_user_registration';
|
|
}
|
|
}
|
|
|
|
Create the invitation field::
|
|
|
|
<?php
|
|
// src/AppBundle/Form/InvitationFormType.php
|
|
|
|
namespace AppBundle\Form;
|
|
|
|
use Symfony\Component\Form\AbstractType;
|
|
use Symfony\Component\Form\FormBuilderInterface;
|
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
|
use Doctrine\ORM\EntityRepository;
|
|
use AppBundle\Form\DataTransformer\InvitationToCodeTransformer;
|
|
|
|
class InvitationFormType extends AbstractType
|
|
{
|
|
private $invitationTransformer;
|
|
|
|
public function __construct(InvitationToCodeTransformer $invitationTransformer)
|
|
{
|
|
$this->invitationTransformer = $invitationTransformer;
|
|
}
|
|
|
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
|
{
|
|
$builder->addModelTransformer($this->invitationTransformer);
|
|
}
|
|
|
|
public function configureOptions(OptionsResolver $resolver)
|
|
{
|
|
$resolver->setDefaults(array(
|
|
'class' => 'AppBundle\Entity\Invitation',
|
|
'required' => true,
|
|
));
|
|
}
|
|
|
|
public function getParent()
|
|
{
|
|
return 'Symfony\Component\Form\Extension\Core\Type\TextType';
|
|
}
|
|
|
|
public function getBlockPrefix()
|
|
{
|
|
return 'app_invitation_type';
|
|
}
|
|
|
|
// Not necessary on Symfony 3+
|
|
public function getName()
|
|
{
|
|
return 'app_invitation_type';
|
|
}
|
|
}
|
|
|
|
Create the custom data transformer::
|
|
|
|
<?php
|
|
// src/AppBundle/Form/DataTransformer/InvitationToCodeTransformer.php
|
|
|
|
namespace AppBundle\Form\DataTransformer;
|
|
|
|
use AppBundle\Entity\Invitation;
|
|
use Doctrine\ORM\EntityManager;
|
|
use Symfony\Component\Form\DataTransformerInterface;
|
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
|
|
|
/**
|
|
* Transforms an Invitation to an invitation code.
|
|
*/
|
|
class InvitationToCodeTransformer implements DataTransformerInterface
|
|
{
|
|
private $entityManager;
|
|
|
|
public function __construct(EntityManager $entityManager)
|
|
{
|
|
$this->entityManager = $entityManager;
|
|
}
|
|
|
|
public function transform($value)
|
|
{
|
|
if (null === $value) {
|
|
return null;
|
|
}
|
|
|
|
if (!$value instanceof Invitation) {
|
|
throw new UnexpectedTypeException($value, 'AppBundle\Entity\Invitation');
|
|
}
|
|
|
|
return $value->getCode();
|
|
}
|
|
|
|
public function reverseTransform($value)
|
|
{
|
|
if (null === $value || '' === $value) {
|
|
return null;
|
|
}
|
|
|
|
if (!is_string($value)) {
|
|
throw new UnexpectedTypeException($value, 'string');
|
|
}
|
|
|
|
$dql = <<<DQL
|
|
SELECT i
|
|
FROM AppBundle:Invitation i
|
|
WHERE i.code = :code
|
|
AND NOT EXISTS(SELECT 1 FROM AppBundle:User u WHERE u.invitation = i)
|
|
DQL;
|
|
|
|
return $this->entityManager
|
|
->createQuery($dql)
|
|
->setParameter('code', $value)
|
|
->setMaxResults(1)
|
|
->getOneOrNullResult();
|
|
}
|
|
}
|
|
|
|
Register your custom form type in the container:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/services.yml
|
|
services:
|
|
app.form.registration:
|
|
class: AppBundle\Form\RegistrationFormType
|
|
tags:
|
|
- { name: "form.type", alias: "app_user_registration" }
|
|
|
|
app.form.invitation:
|
|
class: AppBundle\Form\InvitationFormType
|
|
arguments: ['@app.form.data_transformer.invitation']
|
|
tags:
|
|
- { name: "form.type", alias: "app_invitation_type" }
|
|
|
|
app.form.data_transformer.invitation:
|
|
class: AppBundle\Form\DataTransformer\InvitationToCodeTransformer
|
|
arguments: ['@doctrine.orm.entity_manager']
|
|
public: false
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- app/config/services.xml -->
|
|
|
|
<?xml version="1.0" ?>
|
|
|
|
<container xmlns="http://symfony.com/schema/dic/services"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
|
|
|
<services>
|
|
|
|
<service id="app.form.registration" class="AppBundle\Form\RegistrationFormType">
|
|
<tag name="form.type" alias="app_user_registration" />
|
|
</service>
|
|
|
|
<service id="app.form.invitation" class="AppBundle\Form\InvitationFormType">
|
|
<argument type="service" id="app.form.data_transformer.invitation"/>
|
|
<tag name="form.type" alias="app_invitation_type" />
|
|
</service>
|
|
|
|
<service id="app.form.data_transformer.invitation"
|
|
class="AppBundle\Form\DataTransformer\InvitationToCodeTransformer"
|
|
public="false
|
|
>
|
|
<argument type="service" id="doctrine.orm.entity_manager"/>
|
|
</service>
|
|
|
|
</services>
|
|
</container>
|
|
|
|
Next overwrite the default ``RegistrationFormType`` with the one just created :
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/config.yml
|
|
|
|
fos_user:
|
|
registration:
|
|
form:
|
|
type: AppBundle\Form\RegistrationFormType
|
|
|
|
You are done, go to your registration form to see the result.
|