PUGXMultiUserBundle came by the need to use different types of users using only one fos_user service. In practice it is an hack that forces FOSUser bundle through custom UserManager, controllers, and forms handlers.
It's just a lazy way to use for free most of the functionality of FOSUserBundle.
This bundle has been realized as a part of a real application that uses doctrine orm, so for now it only supports the ORM db driver.
This version of the bundle requires Symfony dev-master and FOSUserBundle dev-master
[FOSUserBundle] (https://github.com/FriendsOfSymfony/FOSUserBundle)
- Download PUGXMultiUserBundle
- Enable the Bundle
- Create your Entities
- Configure the FOSUserBundle (PUGXMultiUserBundle params)
- Configure parameters for UserDiscriminator
- Create your controllers
- Using the User Manager
Using composer
Add the following lines in your composer.json:
{
"require": {
"friendsofsymfony/user-bundle": "2.0.*@dev",
"rvntone/multi-user-bundle": "2.0.*@dev"
}
}
Now, run the composer to download the bundle:
$ php composer.phar update rvntone/multi-user-bundle
Enable the bundles in the kernel:
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new Rvntone\MultiUserBundle\PUGXMultiUserBundle(),
new FOS\UserBundle\FOSUserBundle(),
);
}
Create entities using Doctrine2 inheritance.
Abstract User that directly extends the model FOS\UserBundle\Model\User
<?php
namespace Acme\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
/**
* @ORM\Entity
* @ORM\Table(name="user")
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({"user_one" = "UserOne", "user_two" = "UserTwo"})
*
*/
abstract class User extends BaseUser
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}
UserOne
<?php
namespace Acme\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Rvntone\MultiUserBundle\Validator\Constraints\UniqueEntity;
/**
* @ORM\Entity
* @ORM\Table(name="user_one")
* @UniqueEntity(fields = "username", targetClass = "Acme\UserBundle\Entity\User", message="fos_user.username.already_used")
* @UniqueEntity(fields = "email", targetClass = "Acme\UserBundle\Entity\User", message="fos_user.email.already_used")
*/
class UserOne extends User
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}
UserTwo
<?php
namespace Acme\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Rvntone\MultiUserBundle\Validator\Constraints\UniqueEntity;
/**
* @ORM\Entity
* @ORM\Table(name="user_two")
* @UniqueEntity(fields = "username", targetClass = "Acme\UserBundle\Entity\User", message="fos_user.username.already_used")
* @UniqueEntity(fields = "email", targetClass = "Acme\UserBundle\Entity\User", message="fos_user.email.already_used")
*/
class UserTwo extends User
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}
You must also create forms for your entities: see [Overriding Default FOSUserBundle Forms] (https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/overriding_forms.md)
Keep in mind that PUGXMultiUserBundle overwrites user_class via UserDiscriminator but it does it only in controllers and forms handlers; in the other cases (command, sonata integration, etc) it still uses the user_class configured in the config.
# Acme/UserBundle/Resources/config/config.yml
fos_user:
db_driver: orm
firewall_name: main
user_class: Acme\UserBundle\Entity\User
service:
user_manager: pugx_user_manager
Note:
Acme\UserBundle\Entity\User must be an abstract class, because you don't have to use it. In fact is the discriminator that has responsibility to get the user class depending on context.
# Acme/UserBundle/Resources/config/config.yml
pugx_multi_user:
users:
user_one:
entity:
class: Acme\UserBundle\Entity\UserOne
# factory:
registration:
form:
type: Acme\UserBundle\Form\Type\RegistrationUserOneFormType
name: fos_user_registration_form
validation_groups: [Registration, Default]
template: AcmeUserBundle:Registration:user_one.form.html.twig
profile:
form:
type: Acme\UserBundle\Form\Type\ProfileUserOneFormType
name: fos_user_profile_form
validation_groups: [Profile, Default]
user_two:
entity:
class: Acme\UserBundle\Entity\UserTwo
registration:
form:
type: Acme\UserBundle\Form\Type\RegistrationUserTwoFormType
template: AcmeUserBundle:Registration:user_two.form.html.twig
profile:
form:
type: Acme\UserBundle\Form\Type\ProfileUserTwoFormType
# Acme/UserBundle/Resources/config/routing.yml
user_one_registration:
pattern: /register/user-one
defaults: { _controller: AcmeUserBundle:RegistrationUserOne:register }
user_two_registration:
pattern: /register/user-two
defaults: { _controller: AcmeUserBundle:RegistrationUserTwo:register }
Note:
You have to disable the default route registration coming from FOSUser or you have to manage it for prevent incorrect registration
RegistrationUserOneController
<?php
namespace Acme\UserBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class RegistrationUserOneController extends Controller
{
public function registerAction()
{
return $this->container
->get('pugx_multi_user.registration_manager')
->register('Acme\UserBundle\Entity\UserOne');
}
}
RegistrationUserTwoController
<?php
namespace Acme\UserBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class RegistrationUserTwoController extends Controller
{
public function registerAction()
{
return $this->container
->get('pugx_multi_user.registration_manager')
->register('Acme\UserBundle\Entity\UserTwo');
}
}
Note:
Remember to create the templates for registration form with correct routes
something like this, if you are extending fosub
{% extends "FOSUserBundle::layout.html.twig" %}
{% block fos_user_content %}
{% trans_default_domain 'FOSUserBundle' %}
<form action="{{ path('user_one_registration') }}" {{ form_enctype(form) }} method="POST">
{{ form_widget(form) }}
<div>
<input type="submit" value="{{ 'registration.submit'|trans }}" />
</div>
</form>
{% endblock fos_user_content %}
Note:
For now only registration and profile form factories are wrapped; if you need creat a custom FormType you have to inject the discriminator.
Creating a new UserOne:
$discriminator = $this->container->get('pugx_user.manager.user_discriminator');
$discriminator->setClass('Acme\UserBundle\Entity\UserOne');
$userManager = $this->container->get('pugx_user_manager');
$userOne = $userManager->createUser();
$userOne->setUsername('admin');
$userOne->setEmail('admin@mail.com');
$userOne->setPlainPassword('123456');
$userOne->setEnabled(true);
$userManager->updateUser($userOne, true);