This post is part of a series of blogpost about foundation bundles of Sylius/Symfony eCommerce Framework.
At Odiseo, we were working with Symfony Framework for a very long time. The foundation of the most of our many web projects are based on Symfony. Our team believe in Symfony ecosystem and community. We have open-sourced many tools to work with Symfony and we use relevant libraries of the community on our projects.
We have made many types of application for every needs of our clients. For eCommerce web applications we used Sylius.
"Sylius is an Open Source eCommerce Framework on top of Symfony. The highest quality of code, strong testing culture, built-in Agile (BDD: https://solidgeargroup.com/bdd-testing-behat) workflow and exceptional flexibility make it the best solution for application tailored to your business requirements" Sylius
And it does very well but Sylius, as a framework designed for developers is not only about selling products. It also has many other features that we can and WE USE in almost every project with Symfony. In order to bring our experience back to the Symfony community we created a series of posts explain how to work with some of them.
Introduction to Sylius non-ecommerce features: SyliusUserBundle
Users are an important part of almost any project that has some level of security or user interaction. Symfony provide an useful interface for the secure your system, but the rest is up to you.
Sylius by its hands has a Component and a Bundle that provide you models, services and events to work with users, including login, registration, oauth, and more. Let's see how to work with it!!!.
Origin and motivation. Why not FOSUserBundle?
Yes, we used to use FOSUserBundle. Actually it is a very good bundle, but it doesn't follow Sylius convention. We started to feels that working with users and customers don't feel like all other entities on our projects. And of course Sylius core team though the same:
"Sylius is built around strong conventions, which means that for all data models have the same structure of services, naming of events, classes and generally things work in the same way for everything, except User model from FOSUserBundle" Sylius Blog
SyliusUserBundle is a very good choice for managing users and security on Symfony project. It provide all needed features to have a security based on users, plus it integrate SyliusResourceBundle configuration for users so you can manage it easily. If you want to know more information about the Resource bundle, Sylius already have a well documentation on their site.
Installation
You can install this bundle with Composer. Having Composer installed globally run:
$ composer require sylius/user-bundle
If you are not using Symfony Flex, you have to add required bundles to the kernel, which are `SyliusUserBundle`, `SyliusMailerBundle` and if you're not using any other Sylius bundles, you will also need to add ``SyliusResourceBundle`` and its dependencies to kernel following its documentation.
Configuration
Basic configuration from the bundle:
# config/packages/sylius_user.yaml
security:
encoders:
argon2i: argon2i
sylius_user:
encoder: argon2i
jms_serializer:
metadata:
directories:
sylius-user:
namespace_prefix: "Sylius\\Component\\User"
path: "@SyliusUserBundle/Resources/config/serializer"
Configure doctrine extensions which are used by the bundle.
# config/packages/stof_doctrine_extensions.yaml
stof_doctrine_extensions:
orm:
default:
timestampable: true
Models
User
Sylius Framework as the concept of “Customers” and “Users” by separated. That means that you could have a customer (for other projects could be Client, Supplier, Student, etc) containing personal information. And the User entity which represent a registered user should have everything concerning application user preferences and a association with a customer (If it's needed).
OAuth
The OAuth entity has all data concerning OAuth account. A user can has many OAuth accounts.
Dividing users on AdminUser, AppUser (ShopUser on Sylius).
About users, we follow Sylius convention, creating 2 types of users:
AdminUser: Created to enable administrator accounts that have access to the administration panel.
AppUser: Designed for customers that have registered in the system - they have an account with both e-mail and password. They can visit and modify their account.
AdminUser
The AdminUser entity simple extends the User entity.
namespace App\Entity;
use Sylius\Component\User\Model\User;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="app_admin_user")
*/
class AdminUser extends User
{
public function __construct()
{
parent::__construct();
$this->roles = ['ROLE_ADMIN'];
}
}
Configure it has an user type:
sylius_user:
driver: doctrine/orm
resources:
admin:
user:
classes:
model: App\Entity\AdminUser
repository: Sylius\Bundle\UserBundle\Doctrine\ORM\UserRepository
form: Sylius\Bundle\UserBundle\Form\Type\UserType
templates: 'admin/user'
In order to update your database schema just run the following command:
$ php bin/console doctrine:schema:update --force
How to create a AdminUser programmatically?
Creating an AdminUser programmatically is just like every other entity, it has its own factory. By default it will have an administration role (ROLE_ADMIN) assigned like the example above.
$admin = $this->container->get('sylius.factory.admin_user')->createNew();
$admin->setEmail('administrator@test.com');
$admin->setPlainPassword('pswd');
$this->container->get('sylius.repository.admin_user')->add($admin);
AppUser (or ShopUser)
You probably want to add some custom fields and functions to your User entity so let’s create a custom one. You could call it FrontendUser, ShopUser, AppUser or just User:
namespace App\Entity;
use Sylius\Component\User\Model\User as BaseUser;
use Sylius\Component\User\Model\UserOAuthInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="app_user")
*/
class AppUser extends BaseUser
{
/** @var string|null */
protected $name;
/**
* Override method to set username as email.
*
* @param string|null $email
*/
public function setEmail(?string $email): void
{
parent::setEmail($email);
$this->setUsername($email);
}
/**
* Custom method to retrieve Facebook OAuth account.
*
* @return AppUserOAuth|UserOAuthInterface|null
*/
public function getFacebook(): ?UserOAuthInterface
{
return $this->getOAuthAccount('facebook');
}
}
Configure it has an user type:
sylius_user:
driver: doctrine/orm
resources:
# ... admin_user configuration
app:
user:
classes:
model: App\Entity\AppUser
oauth:
user:
classes:
model: Sylius\Component\User\Model\UserOAuth
interface: Sylius\Component\User\Model\UserOAuthInterface
controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController
repository: Sylius\Bundle\UserBundle\Doctrine\ORM\UserRepository
form: Sylius\Bundle\UserBundle\Form\Type\UserType
templates: 'admin/user'
So, update your database schema again:
$ php bin/console doctrine:schema:update --force
How to create a AppUser programmatically?
We can create a Customer (or Guess) entity to collect data about non-registered guests of the system (ones that has been doing something without having an account or that have somehow provided us their e-mail).
Assuming that we already have a Guess entity configured as `app.guess` (either retrieved from the repository or a newly created one) use a factory to create a new AppUser, assign the existing Guess and a password via the ``setPlainPassword()`` method.
$user = $this->container->get('sylius.factory.app_user')->createNew();
$customer = $this->container->get('app.repository.guess')->findOneBy(['email' => 'guess@test.com']);
$user->setGuess($customer);
$user->setPlainPassword('pswd');
$this->container->get('sylius.repository.app_user')->add($user);
Changing the AppUser password
The already set password of a AppUser can be easily changed via the ``setPlainPassword()`` method.
$user->getPassword(); // returns encrypted password - 'pswd'
$user->setPlainPassword('resu1'); // the password will now be 'resu1' and will become encrypted while saving the user in the database
That's it! In the next post we will continue and talk about how configure the user security access.