this methodi use it to add a new job but when i add a job the password of the that current user get its password set to empty cus the user object that i retrieve has no password and
symfony behaves like so for to secure the password any help would be much appreciated
` public function addJobAction(){
if(false === $this->get('security.context')
->isGranted('ROLE_ANNOUNCER')
)
{
throw new AccessDeniedException();
}
$job = new Job() ;
$jobForm = $this->createForm( new JobType() ,$job) ;
$request = $this->getRequest();
if( $request->getMethod() == 'GET'){
return
$this->render('MyJobBundle:Job:addJob.html.twig' ,
array('form'=> $jobForm->createView() )
) ;
}
if( $request->getMethod() == 'POST'){
$jobForm->bindRequest($request);
if( $jobForm->isValid() ){
$user = $this->get('security.context')->getToken()
->getUser();
$job->setAnnouncer($user);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($job) ;
$em->flush() ;
return
$this->redirect($this->generateUrl('show_job' ,
array('id'=> $job->getId() ) )
);
}else{
return
new Response('no');
}
}
}
heres my job entity
namespace My\JobBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use My\UserBundle\Entity\User ;
use Symfony\Component\Validator\Constraints as Assert;
/**
* My\JobBundle\Entity\Job
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="My\JobBundle\Entity\JobRepository")
*/
class Job
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $title
*
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string $content
*
*
* #ORM\Column(name="content", type="text")
*/
private $content;
/**
* #var string $city
*
* #ORM\Column(name="city", type="string", length=255)
*
*/
private $city;
/**
* #var datetime $created_at
*
* #ORM\Column(name="created_at", type="datetime")
*/
private $created_at;
/**
* #var string $salary
*
* #ORM\Column(name="salary", type="string", length=255)
*
*
*/
private $salary;
/**
* #ORM\ManyToOne(targetEntity="My\UserBundle\Entity\User")
*/
private $announcer ;
/**
* link a job to a user
*/
public function setAnnouncer(User $a)
{
$this->announcer = $a;
}
/**
* return a user from a job object
*/
public function getAnnouncer()
{
return $this->announcer;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
*/
public function setContent($content)
{
$this->content = $content;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set created_at
*
* #param datetime $createdAt
*/
public function setCreatedAt($createdAt)
{
$this->created_at = $createdAt;
}
/**
* Get created_at
*
* #return datetime
*/
public function getCreatedAt()
{
return $this->created_at;
}
/**
* Set salary
*
* #param string $salary
*/
public function setSalary($salary)
{
$this->salary = $salary;
}
/**
* Get salary
*
* #return string
*/
public function getSalary()
{
return $this->salary;
}
public function setCity($c)
{
$this->city = $c;
}
public function getCity()
{
return $this->city ;
}
public function __construct(){
$this->created_at = new \DateTime() ;
}
}
heres my jobType
namespace My\JobBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class JobType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('title')
->add('content','textarea' )
//->add('created_at')
->add('salary')
->add('city')
//->add('announcer')
;
}
public function getName()
{
return 'my_jobbundle_jobtype';
}
}
and heres my log where i see the password updated
INSERT INTO Job (title, content, city, created_at, salary, announcer_id) VALUES (?, ?, ?, ?, ?, ?) ({"1":"lfdgdfl;","2":";lkl;fdlgkdfl;","3":"lklkl;;l","4":{"date":"2012-02-05 23:39:16","timezone_type":3,"timezone":"Europe\/Paris"},"5":"333","6":1})
UPDATE User SET password = ? WHERE id = ? ([null,1])
well i found the issue it was caused by that eraseCredential method of the UserInterface in my User entity
<?php
public function eraseCredential(){
$this->password = null ;
}
i just had to empty it as it was doin to my password by commenting that line ; ]
2 kosaidpo
Your solution works because eraseCredentials() method is used to clear user sensitive data (means NOT secret, but the one that can be restored, the sense is like __sleep()) when serializing user object or saving it to database (that is what manual says). So when you attach user to job object and call #flush(), doctrine will check for changes in all objects connected with job and find that user object has changed because eraseCredentials() has erased password. That is why your user gets updated.
There is one more solution which could help you:
The Solution:
Change Tracking Policies from Doctrine documentation.
In short, you can add #ChangeTrackingPolicy("DEFERRED_EXPLICIT") annotation (as I did, because I'm using annotations. Captain Obvious =) ) to UserInterface implementation (in my case I'm using User class) and this will tell Doctrine not to check all 'connected' to job objects.
In this case doctrine will not check user object and save it with erased password, unless you will force it to do it with calling #persist(User object) manually.
But anyway, you should not do $this->password = null in your eraseCredentials() method.
It does seem strange, but you could always retrieve User object prior to binding it to a newly created job:
$token = $this->get('security.context')->getToken();
$user_repo = $this->getDoctrine()->getRepository('**NAMESPACE**:User');
$user = $user_repo->find($token->getUser()->getId());
$job->setAnnouncer($user);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($job) ;
$em->flush();
Also, I'm not really sure but I read somewhere that token isn't supposed to carry password due to it's security nature.... maybe that is your problem...
Related
I am retrieving the code of an old application that have not been updated for a few years now. Our sysadmin have put the code in a php7 server (it was working correctly on a php5 previously). The code worked quite good. I wanted to make some updates and the first one I did was to upgrade symfony from 2.3 to 2.7.*. And of course, now the problems arise.
I have a form that is correctly rendered (all the fields are OK even the ones from the database). Here is my builer:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('object','text',array(
"required"=>false,
"attr"=>array(
"placeholder"=>"Object"
)
))
->add('date','date',array(
'widget'=>'single_text',
))
->add('contact', 'entity', array(
'label'=>'Contact',
'class'=>'MyApp\AppliBundle\Entity\Contact',
'choice_translation_domain' => true,
'placeholder'=>'initials',
'choice_label' => 'initials',
'multiple'=>true
))
->add('text','redactor',array(
"required"=>false,
"redactor"=>"default"
))
;
}
Here is my controller:
public function editMeetingAction($id,Request $request)
{
$em = $this->getDoctrine()->getManager();
$meeting = $em->getRepository('MyAPPAppliBundle:Meeting')-
>findOneById($id);
$form = $this->createForm(new MeetingType, $meeting);
$form->handleRequest($request);
if ($form->isValid()) {
$em->persist($meeting);
$em->flush();
$this->get('session')->getFlashBag()->add('success', 'Meeting
edited successfully');
return $this->redirect($this-
>generateUrl('myapp_appli_manage_editmeeting', array("id" => $id)));
}
return array(
"form" => $form->createView(),
"id" => $id,
);
}
Now when I try to save the form, I have the following error:
[Syntax Error] line 0, col -1: Error: Expected Literal, got end of string.
[1/2] QueryException: SELECT e FROM MyApp\AppliBundle\Entity\Contact e WHERE
It seems that the app is not able to retrieve the Contact being selected in the form.
I have no idea what is wrong here as it worked correctly in the previous version. I followed the steps in this website to help me with the migration and modified already some fields in the form (placeholder, choices_as_values etc)
https://gist.github.com/mickaelandrieu/5211d0047e7a6fbff925
It would be much appreciated if you could help me.
[EDIT1]: the form was working properly before I updated symfony from 2.3 to 2.7
[EDIT2]: Entity Contact:
<?php
namespace MyApp\AppliBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\EntityRepository;
/**
* Contact
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="MyApp\AppliBundle\Entity\ContactRepository")
*/
class Contact
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Name", type="string", length=255)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="Email", type="string", length=255)
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="Initials", type="string", length=255)
*/
private $initials;
/**
* #var integer
*
* #ORM\Column(name="id_binome", type="integer")
*/
private $id_binome;
/**
* #var string
*
* #ORM\Column(name="JobTitles", type="string", length=255)
*/
private $jobtitle;
/**
* Tostring method
*
*/
public function __toString()
{
return $this->name;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Contact
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set email
*
* #param string $email
* #return Contact
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set initials
*
* #param string $initials
* #return Contact
*/
public function setInitials($initials)
{
$this->initials = $initials;
return $this;
}
/**
* Get initials
*
* #return string
*/
public function getInitials()
{
return $this->initials;
}
/**
* Get id_binome
*
* #return integer
*/
public function getIdBinome()
{
return $this->id_binome;
}
/**
* Set id_binome
*
* #param integer $id
* #return Contact
*/
public function setIdBinome($id)
{
$this->id_binome = $id;
return $this;
}
/**
* Get jobtitle
*
* #return string
*/
public function getjobtitle()
{
return $this->jobtitle;
}
/**
* Set jobtitle
*
* #param string $jobtitle
* #return Contact
*/
public function setjobtitle($jobtitle)
{
$this->jobtitle = $jobtitle;
return $this;
}
}
class ContactRepository extends EntityRepository
{
public function findEmailBinome($id_binome)
{
$querybuilder = $this->createQueryBuilder("Contact")
->select("Contact.email")
->where("Contact.id = :idbinome")
->setParameter('idbinome',$id_binome)
;
return $querybuilder
->getQuery()
->getSingleResult()
;
}
}
[EDIT getter setter]
/**
* Add contacts
*
* #param \MyApp\AppliBundle\Entity\Contact $contacts
* #return Meeting
*/
public function addContact(\MyApp\AppliBundle\Entity\Contact $contacts)
{
$this->contacts[] = $contacts;
return $this;
}
/**
* Remove contacts
*
* #param \MyApp\AppliBundle\Entity\Contact $contacts
*/
public function removeContact(\MyApp\AppliBundle\Entity\Contact $contacts)
{
$this->contacts->removeElement($contacts);
}
/**
* Get contacts
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getContacts()
{
return $this->contacts;
}
I think I found the solution. As it was a problem related to the db connection I suspected dotrine/orm to be guilty! I update the composer.json by changing :
"doctrine/orm": "=2.2.*"
to
"doctrine/orm": ">=2.2.3"
When I tried composer update doctrine/orm it did not solve the problem. However when I simply tried composer update then my app worked again.
Thanks a lot for your help
Currently I'm trying to modify my classes and looking for idea to save dynamic relations between users and roles.
I want to create associations when loading fixtures and also to have such a functionality in controller when I need to create an user with relation, example:
...
$user = new User();
$user->setName($_POST['name']);
$user->setPassword($_POST['password']);
...
$user->setRole('ROLE_USER');//Role for everyone
...
$role = new Role();
$role->setName('ROLE_' . strtoupper($_POST['name']) );//Role for personal use
...
//Here need to implement user+role association (I'm looking for recommendations)
...
$entityManager->persist($user);
$entityManager->persist($role);
//Persist role+user assosiacion
$entityManager->flush();
$entityManager->clear();
My User.php :
<?php
namespace App\Entity;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* User
*
* #ORM\Table(name="user", uniqueConstraints={#ORM\UniqueConstraint(name="user_name", columns={"user_name"}), #ORM\UniqueConstraint(name="email", columns={"email"})})
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ORM\Cache(usage="NONSTRICT_READ_WRITE", region="fast_cache")
* #UniqueEntity(fields="email", message="Email already taken")
* #UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface, \Serializable
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="App\Entity\Role", inversedBy="users", cascade={"remove"})
* #ORM\JoinTable(name="users_roles",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="role_id", referencedColumnName="id")}
* )
*/
protected $roles;
/**
* #var int
*
* #ORM\Column(name="id", type="smallint", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="user_name", type="string", length=255, nullable=false)
*/
private $username;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255, nullable=false)
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255, nullable=false)
*/
private $password;
/**
* #var bool
*
* #ORM\Column(name="is_enabled", type="boolean", nullable=false)
*/
private $isEnabled;
/**
* #var bool
*
* #ORM\Column(name="is_verified", type="boolean", nullable=false)
*/
private $isVerified;
/**
* #var DateTime
*
* #ORM\Column(name="created_at", type="datetime", nullable=false)
*/
private $createdAt;
/**
* #var DateTime
*
* #ORM\Column(name="updated_at", type="datetime", nullable=false)
*/
private $updatedAt;
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #return string
*/
public function getEmail(): string
{
return $this->email;
}
/**
* #param string $email
*/
public function setEmail(string $email): void
{
$this->email = $email;
}
/**
* #return bool
*/
public function isEnabled(): bool
{
return $this->isEnabled;
}
/**
* #param bool $isEnabled
*/
public function setIsEnabled(bool $isEnabled): void
{
$this->isEnabled = $isEnabled;
}
/**
* #return bool
*/
public function isVerified(): bool
{
return $this->isVerified;
}
/**
* #param bool $isVerified
*/
public function setIsVerified(bool $isVerified): void
{
$this->isVerified = $isVerified;
}
/**
* #return DateTime
*/
public function getCreatedAt(): DateTime
{
return $this->createdAt;
}
/**
* #param DateTime $createdAt
*/
public function setCreatedAt(DateTime $createdAt): void
{
$this->createdAt = $createdAt;
}
/**
* #return DateTime
*/
public function getUpdatedAt(): DateTime
{
return $this->updatedAt;
}
/**
* #param DateTime $updatedAt
*/
public function setUpdatedAt(DateTime $updatedAt): void
{
$this->updatedAt = $updatedAt;
}
/**
* String representation of object
* #link http://php.net/manual/en/serializable.serialize.php
* #return string the string representation of the object or null
* #since 5.1.0
* NOTE: SYNFONY BUG 3.4 -> 4.1; https://github.com/symfony/symfony-docs/pull/9914
*/
public function serialize(): string
{
// add $this->salt too if you don't use Bcrypt or Argon2i
return serialize([$this->id, $this->username, $this->password]);
}
/**
* Constructs the object
* #link http://php.net/manual/en/serializable.unserialize.php
* #param string $serialized <p>
* The string representation of the object.
* </p>
* #return void
* #since 5.1.0
*/
public function unserialize($serialized): void
{
// add $this->salt too if you don't use Bcrypt or Argon2i
[$this->id, $this->username, $this->password] = unserialize($serialized, ['allowed_classes' => false]);
}
/**
* Returns the roles granted to the user.
*
* <code>
* public function getRoles()
* {
* return array('ROLE_USER');
* }
* </code>
*
* Alternatively, the roles might be stored on a ``roles`` property,
* and populated in any number of different ways when the user object
* is created.
*
* #return array The user roles
*/
public function getRoles(): array
{
$roles = [];
foreach ($this->roles->toArray() AS $role) {
$roles[] = $role->getName();
}
return $roles;
}
/**
* Returns the password used to authenticate the user.
*
* This should be the encoded password. On authentication, a plain-text
* password will be salted, encoded, and then compared to this value.
*
* #return string The password
*/
public function getPassword(): string
{
return $this->password;
}
/**
* #param string $password
*/
public function setPassword(string $password): void
{
$this->password = $password;
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* #return string|null The salt
*/
public function getSalt()
{
// See "Do you need to use a Salt?" at https://symfony.com/doc/current/cookbook/security/entity_provider.html
// we're using bcrypt in security.yml to encode the password, so
// the salt value is built-in and you don't have to generate one
return null;
}
/**
* Returns the username used to authenticate the user.
*
* #return string The username
*/
public function getUsername()
{
return $this->username;
}
/**
* #param string $username
*/
public function setUsername(string $username): void
{
$this->username = $username;
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
// if you had a plainPassword property, you'd nullify it here
$this->plainPassword = null;
}
}
Role.php file:
<?php
namespace App\Entity;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Role
*
* #ORM\Table(name="role", uniqueConstraints={#ORM\UniqueConstraint(name="name", columns={"name"})})
* #ORM\Entity(repositoryClass="App\Repository\RoleRepository")
*/
class Role
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="App\Entity\User", mappedBy="roles", cascade={"remove"})
* #ORM\JoinTable(name="users_roles",
* joinColumns={#ORM\JoinColumn(name="role_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")}
* )
*/
protected $users;
/**
* #var int
*
* #ORM\Column(name="id", type="smallint", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var DateTime
*
* #ORM\Column(name="created_at", type="datetime", nullable=false)
*/
private $createdAt;
/**
* #var DateTime
*
* #ORM\Column(name="updated_at", type="datetime", nullable=false)
*/
private $updatedAt;
/**
* Role constructor.
*/
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* #return array
*/
public function getUsers(): array
{
return $this->users->toArray();
}
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* #return string
*/
public function getName(): string
{
return $this->name;
}
/**
* #param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
/**
* #return DateTime
*/
public function getCreatedAt(): DateTime
{
return $this->createdAt;
}
/**
* #param DateTime $createdAt
*/
public function setCreatedAt(DateTime $createdAt): void
{
$this->createdAt = $createdAt;
}
/**
* #return DateTime
*/
public function getUpdatedAt(): DateTime
{
return $this->updatedAt;
}
/**
* #param DateTime $updatedAt
*/
public function setUpdatedAt(DateTime $updatedAt): void
{
$this->updatedAt = $updatedAt;
}
}
My data fixtures AppFixtures.php:
<?php
namespace App\DataFixtures;
use App\Entity\Role;
use App\Entity\User;
use DateTime;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
/**
* Class AppFixtures
* #package App\DataFixtures
*/
class AppFixtures extends Fixture
{
/**
* #var UserPasswordEncoderInterface
*/
private $encoder;
/**
* #var EntityManagerInterface
*/
private $entityManager;
/**
* AppFixtures constructor.
* #param UserPasswordEncoderInterface $userPasswordEncoder
* #param EntityManagerInterface $entityManager
*/
public function __construct(UserPasswordEncoderInterface $userPasswordEncoder, EntityManagerInterface $entityManager)
{
$this->encoder = $userPasswordEncoder;
$this->entityManager = $entityManager;
}
/**
* #param ObjectManager $manager
*/
public function load(ObjectManager $manager)
{
//Creating default roles
$role = new Role();
$role->setName('ROLE_USER');
$role->setCreatedAt(new DateTime());
$role->setUpdatedAt(new DateTime());
$manager->persist($role);
$role = new Role();
$role->setName('ROLE_MODERATOR');
$role->setCreatedAt(new DateTime());
$role->setUpdatedAt(new DateTime());
$manager->persist($role);
$role = new Role();
$role->setName('ROLE_ADMIN');
$role->setCreatedAt(new DateTime());
$role->setUpdatedAt(new DateTime());
$manager->persist($role);
$manager->flush();
$manager->clear();
//Creating users
$user = new User();
$user->setUserName('john');
$user->setEmail('john#localhost');
//$user->setRoles(['ROLE_USER', 'ROLE_JOHN']);
//$roleAssociation = null;
$user->setPassword($this->encoder->encodePassword($user, 'test'));
$user->setCreatedAt(new DateTime());
$user->setUpdatedAt(new DateTime());
$user->setIsVerified(true);
$user->setIsEnabled(true);
//$manager->persist($roleAssociation);
$manager->persist($user);
$user = new User();
$user->setUserName('tom');
$user->setEmail('tom#localhost');
//$user->setRoles(['ROLE_USER', 'ROLE_TOM', 'ROLE_MODERATOR']);
//$roleAssociation = null;
$user->setPassword($this->encoder->encodePassword($user, 'test'));
$user->setCreatedAt(new DateTime());
$user->setUpdatedAt(new DateTime());
$user->setIsVerified(true);
$user->setIsEnabled(true);
//$manager->persist($roleAssociation);
$manager->persist($user);
$user = new User();
$user->setUserName('jimmy');
$user->setEmail('jimmy#localhost');
//$user->setRoles(['ROLE_USER', 'ROLE_JIMMY', 'ROLE_ADMIN']);
//$roleAssociation = null;
$user->setPassword($this->encoder->encodePassword($user, 'test'));
$user->setCreatedAt(new DateTime());
$user->setUpdatedAt(new DateTime());
$user->setIsVerified(true);
$user->setIsEnabled(true);
//$manager->persist($roleAssociation);
$manager->persist($user);
$manager->flush();
$manager->clear();
}
}
I'm looking for advises for:
User entity is cached in annotation, because symfony on each request loads it. config part:
orm:
metadata_cache_driver:
type: redis
result_cache_driver:
type: redis
query_cache_driver:
type: redis
I'm caching all the data for 1min in redis. Any better solution?
DB schema creates Foreign Keys (FK) and extra indexes on users_roles
table and I would love to not to have FK, because IMHO it's "heavy" thing. I would prefer to have primary key on (user_id,role_id) only. Any recommendations of this?
The solution for having flexible add/remove for user + role + roles_users
Big thanks!
I'll try to answer this but as I mentioned in my comment, this question is a bit large so some of this may be generalized.
User entity is cached in annotation, because symfony on each request
loads it. config part:
orm:
metadata_cache_driver:
type: redis
result_cache_driver:
type: redis
query_cache_driver:
type: redis
I'm caching all the data for 1min in redis. Any better solution?
When you say "better" here, it's subjective. Each application is different. Caches, and the length at which caches stay alive, is specific to each application's requirements. I don't know if there's anything inherently wrong with this, but it's largely dependent on your app's requirements.
DB schema creates Foreign Keys (FK) and extra indexes on users_roles table and I would love to not to have FK, because IMHO
it's "heavy" thing. I would prefer to have primary key on
(user_id,role_id) only. Any recommendations of this?
First, FKs are important, and they're intended to keep integrity in your data. They ensure that if someone attempted to delete a user or role that a user_roles row linked to, the operation would fail. You usually want this behavior so that you don't lose data or create orphaned data.
Secondly, I'm not sure what version of Doctrine you're using but my similar ManyToMany tables do create a PK and unique index on (user_id, role_id).
The solution for having flexible add/remove for user + role + roles_users
You do this by using Doctrine's ArrayCollections (click Collections in the menu to make sure the anchor link worked, they're broken sometimes).
There is one caveat when doing this with the the default Symfony User entity though. It implements the Symfony\Component\Security\Core\User\UserInterface interface which defines a getRoles() method that is meant to return an array of roles as strings. This is so that certain Symfony security features work as expected. This means that if you have a private $roles property that you will have to rename its standard Doctrine getter to something else so that you can leave getRoles() functioning as expected.
So, for the User entity, I typically just rename my getter, setter, adder, and remover to something like getUserRoles(), setUserRoles(), addUserRole(), and removeUserRole() and then I leave getRoles() to implement the expected interface.
Here is an incomplete example of a user class with no Role class example.
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection/*Interface*/;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use \InvalidArgumentException;
use function array_unique;
class User implements UserInterface
{
/* ... */
/**
* #var Role[]|array User's roles
*
* Note that the getter, setter, adder, and remover for this method are renamed so that the getRoles() method that
* we implement from Symfony\Component\Security\Core\User\UserInterface can function as expected.
*
* #see UserInterface
* #see User::getRoles()
* #see User::getUserRoles()
* #see User::setUserRoles()
* #see User::addUserRole()
* #see User::removeUserRole()
*
* #ORM\ManyToMany(targetEntity="App\Entity\Role", inversedBy="users", cascade={"persist"})
*/
private $roles;
/* ... */
/**
* Constructor
*/
public function __construct()
{
$this->roles = new ArrayCollection();
}
/* ... */
/**
* #return Collection|Role[]
*/
public function getUserRoles(): Collection
{
return $this->roles;
}
/**
* #param Collection|Role[] $roles
*
* #return self
*/
public function setUserRoles(Collection/*Interface*/ $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #param Role $role
*
* #return self
*/
public function addUserRole(Role $role): self
{
$this->roles->add($role);
return $this;
}
/**
* #param Role $role
*
* #return self
*/
public function removeUserRole(Role $role): self
{
$this->roles->removeElement($role);
return $this;
}
/**
* Get array of roles as strings
*
* This method is an implementation of UserInterface::getRoles(). The getter for self::$roles is
* self::getUserRoles().
*
* #return string[]|array
*
* #see UserInterface
*/
public function getRoles()
{
$roleStrings = [];
foreach ($this->roles as $role) {
$roleStrings[] = $role->getName();
}
// guarantee every user at least has ROLE_USER
$roleStrings[] = 'ROLE_USER';
return array_unique($roleStrings);
}
/* ... */
}
You can also do the reverse for the Role object if you wanted to, but this depends on if you wanted to add users to roles that way and it's often best to choose the owning side of the relation.
Here is an example of how this can be used anywhere where you're working with entities, fixtures or otherwise.
<?php
use Doctrine\Common\Collections\ArrayCollection;
use App\Entity\User;
use App\Entity\Role;
$entityManager = (get entity manager);
$user = new User();
// You can also add the name as an argument to Role::__construct()
// then call from setName() from that if you wanted to simplify this.
$roleFoo = (new Role())->setName('ROLE_FOO');
$roleBar = (new Role())->setName('ROLE_BAR');
// set roles using ArrayCollection of roles.
$user->setUserRoles(new ArrayCollection($roleFoo, $roleBar));
// add new role, see User::addUserRole() for
$user->addUserRole((new Role()->setName('ROLE_WIN'));
// remove ROLE_BAR
// You can also do this with entities that you find with Doctrine
// if you want to remove them from a persisted collection.
$user->removeUserRole($roleBar);
// get roles as a collection of Role objects.
// This will return either ArrayCollection or PersistentCollection
// depending on context. These are objects that act like arrays
// and each element will be a Role object.
$roles = $user->getUserRoles();
// get roles as strings... mostly used by Symfony's security system
// but could be used by your app too.
$roleStrings = $user->getRoles();
I would like to check if I am logged in when I login with a form, but I can't find my anwser and I don't understand the tutorial on symfony website... I try to follow this : http://symfony.com/doc/current/security.html , but I don't want to do with a "HTTP basic authentication", but with my symfony form.
Here is my user class :
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Player
*
* #ORM\Table(name="player")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PlayerRepository")
*/
class Player implements UserInterface, \Serializable
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="pseudo", type="string", length=255, unique=true)
*/
private $pseudo;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255, unique=true)
*/
protected $email;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255)
*/
protected $password;
/**
* #var \DateTime
*
* #ORM\Column(name="date_log", type="datetime", nullable=true)
*/
private $dateLog;
/**
* #ORM\OneToMany(targetEntity="Characters", mappedBy="player")
*/
private $characters;
public function __construct(){
$this->characters = new ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set pseudo
*
* #param string $pseudo
*
* #return Player
*/
public function setPseudo($pseudo)
{
$this->pseudo = $pseudo;
return $this;
}
/**
* Get pseudo
*
* #return string
*/
public function getPseudo()
{
return $this->pseudo;
}
/**
* Set email
*
* #param string $email
*
* #return Player
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set password
*
* #param string $password
*
* #return Player
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set dateLog
*
* #param \DateTime $dateLog
*
* #return Player
*/
public function setDateLog($date_log)
{
$this->dateLog = $date_log;
return $this;
}
/**
* Get dateLog
*
* #return \DateTime
*/
public function getDateLog()
{
return $this->dateLog;
}
/**
* Set Characters
*
* #param array $characters
*
* #return Characters
*/
public function setCharacters($characters)
{
$this->characters = $characters;
return $this;
}
/**
* Get characters
*
* #return array
*/
public function getCharacters()
{
return $this->characters;
}
/**
* Add character
*
* #param \AppBundle\Entity\Characters $character
*
* #return Player
*/
public function addCharacter(\AppBundle\Entity\Characters $character)
{
$this->characters[] = $character;
return $this;
}
/**
* Remove character
*
* #param \AppBundle\Entity\Characters $character
*/
public function removeCharacter(\AppBundle\Entity\Characters $character)
{
$this->characters->removeElement($character);
}
public function getUsername()
{
return $this->pseudo;
}
public function getSalt()
{
// you *may* need a real salt depending on your encoder
// see section on salt below
return null;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function eraseCredentials()
{
}
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->pseudo,
$this->password,
// see section on salt below
// $this->salt,
));
}
/** #see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->pseudo,
$this->password,
// see section on salt below
// $this->salt
) = unserialize($serialized);
}
}
Then my login form (it work well, I am just not able to check if anybody is connected):
public function indexAction(Request $request)
{
$player = new Player;
$form = $this->createFormBuilder($player)
->add('email', TextType::class, array('label' => 'Email :'))
->add('password', PasswordType::class, array('label' => 'Mot de passe :'))
->add('login', SubmitType::class, array('label' => 'Login'))
->getForm();
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid())
{
$password = $form['password']->getData();
$email = $form['email']->getData();
$encoded_pass = sha1($form['password']->getData());
$date = date_create();
$player = $this->getDoctrine()
->getRepository('AppBundle:Player')
->findOneByEmail($email);
$pass_check = $this->getDoctrine()
->getRepository('AppBundle:Player')
->findByPassword($encoded_pass);
if(!$player)
{
//return $this->redirectToRoute('registration');
}
else
{
$pseudo = $this->getDoctrine()
->getRepository('AppBundle:Player')
->findOneByEmail($email)->getPseudo();
$player->setDateLog($date);
$em = $this->getDoctrine()->getManager();
$em->persist($player);
$em->flush(); // insère dans la BD
return $this->redirectToRoute('accueil', array('pseudo' => $pseudo));
}
}
return $this->render('Sko/menu.html.twig', array('form' => $form->createView()));
}
EDIT : I don't want to encode my password because I already do it (even if it is not 100% securised)
EDIT2 : Well I think I can't do that, when I saw the tutoriel, I saw that on their symfony toolbar, we can seethere is the role of the user instead of an anonymous user, but they didn't do this dynamically. Maybe I can't do it dynamically so I need to send user information each time I change page
EDIT3 : I will clarify my question : How to change the token class when I login?
You dont need to create your token, but encoder like described in this answer
After that you can log in programmatically with this code
$token = new UsernamePasswordToken($player, $player->getPassword(), "main");
$event = new InteractiveLoginEvent(new Request(), $token);
$this->container->get("event_dispatcher")->dispatch("security.interactive_login", $event);
$this->container->get("security.token_storage")->setToken($token);
Now you can use standard symfony security functionality to check if user logged in etc
I have made on my Symfony 3.0 project a User Entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="AppBundle\Entity\UserRepository")
*/
class Users
{
/**
* #ORM\Column(type="string", length=60)
* #ORM\Id
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="AppBundle\Doctrine\AutoIdGenerate")
*/
private $id;
/**
* #ORM\Column(type="string", length=15)
*/
private $username;
/**
* #ORM\Column(type="string", length=30)
*/
private $password;
/**
* #ORM\Column(type="string", length=60)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="AppBundle\Doctrine\AutoIdGenerate")
*/
private $token;
/**
* #ORM\Column(type="boolean")
*/
private $activated;
/**
* Get id
*
* #return string
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* #param string $username
*
* #return Users
*/
public function setUsername($username)
{
$this->username = strip_tags($username);
return $this;
}
/**
* Get username
*
* #return string
*/
public function getUsername()
{
return $this->username;
}
/**
* Set password
*
* #param string $password
*
* #return Users
*/
public function setPassword($password)
{
$options = ['cost' => 42];
$this->password = password_hash ($password,CRYPT_BLOWFISH,$options);
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set token
*
* #param string $token
*
* #return Users
*/
public function setToken($token)
{
$this->token = $token;
return $this;
}
/**
* Get token
*
* #return string
*/
public function getToken()
{
return $this->token;
}
/**
* Set activated
*
* #param boolean $activated
*
* #return Users
*/
public function setActivated($activated)
{
$this->activated = $activated;
return $this;
}
/**
* Get activated
*
* #return boolean
*/
public function getActivated()
{
return $this->activated;
}
}
And a Repository that Iplements My logic:
<?php
namespace AppBundle\Entity;
use AppBundle\Entity\Product;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
public function register($username,$password,$email)
{
//A Generic return Object
$status=array('status'=>'true','data'=>"");
if(empty($username))
{
$status['status']=-1;
$status['data']='Username not given';
return $status;
}
if(empty($password))
{
$status['status']=-1;
$status['data']='Password not given';
return $status;
}
if(empty($email))
{
$status['status']=-1;
$status['data']='Email not given';
return $status;
}
$user=new User();
$user->setUsername($username)->setPassword($password);
$em=$this->getEntityManager();
$em->persist($user);
$em->flush();
return $status;
}
}
What I wanna know is when an Entry Sucessfully Inserted to send the email to verify the registration. How I will know That the User Has Sucessfully Inserted In the DB; Also How I will take any Sort of AutoGenerated Fields such as id and token in order to verify the mail address.
I Found that In order to Use Email I can Use the Swiftmailer so the Question
Is NOT hoiw to send an Email, but how I will know that the user successfully inserted.
If your entry is correctly inserted, you'll not have any error.
Otherwise, you'll have one (unless you have a mistake that is not coming from the insertion).
To catch a possible error, you can do this :
// ...
$em->persist($user);
try {
$em->flush();
// Success
} catch (\Exception $e) {
$result['data'] = $e->getMessage(); // The exact message of the exception thrown during INSERT
// Error
}
Otherwise, you can consider the entry has been stored.
For the auto-generated fields, they are immediately available through getters after calling $em->flush().
You can register Event Listener for Doctrine postFlush event.
Example listener:
<?php
use Doctrine\ORM\Event\PostFlushEventArgs;
class PostFlushExampleListener
{
public function postFlush(PostFlushEventArgs $args)
{
// ...
}
}
Service registration in Symfony:
services:
my.listener:
class: PostFlushExampleListener
tags:
- { name: doctrine.event_listener, event: postFlush }
Symfony documentation: http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html
Doctrine documentation: http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/events.html#listening-and-subscribing-to-lifecycle-events
You should have all autogenerated fields available just after calling flush action there where you call it. Doctrine events are non mandatory in your case. I would just assume that after flush i will have all needed data and data will be set in database.
I have the following model, or as you call them entity, and I also have a controller, everything works in this action, but when I check the database there is no user. So I am curious as what I am missing. So lets start at the beginning as to what I have:
bootstrap.php contains the following code, among other things.
...
/** ---------------------------------------------------------------- **/
// Lets Setup Doctrine.
/** ---------------------------------------------------------------- **/
require_once 'vendor/autoload.php';
$loader = require 'vendor/autoload.php';
\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
/**
* Set up Doctrine.
*/
class DoctrineSetup {
/**
* #var array $paths - where the entities live.
*/
protected $paths = array(APP_MODELS);
/**
* #var bool $isDevMode - Are we considered "in development."
*/
protected $isDevMode = false;
/**
* #var array $dbParams - The database paramters.
*/
protected $dbParams = null;
/**
* Constructor to set some core values.
*/
public function __construct(){
if (!file_exists('db_config.ini')) {
throw new \Exception(
'Missing db_config.ini. You can create this from the db_config_sample.ini'
);
}
$this->dbParams = array(
'driver' => 'pdo_mysql',
'user' => parse_ini_file('db_config.ini')['DB_USER'],
'password' => parse_ini_file('db_config.ini')['DB_PASSWORD'],
'dbname' => parse_ini_file('db_config.ini')['DB_NAME']
);
}
/**
* Get the entity manager for use through out the app.
*
* #return EntityManager
*/
public function getEntityManager() {
$config = Setup::createAnnotationMetadataConfiguration($this->paths, $this->isDevMode, null, null, false);
return EntityManager::create($this->dbParams, $config);
}
}
/**
* Function that can be called through out the app.
*
* #return EntityManager
*/
function getEntityManager() {
$ds = new DoctrineSetup();
return $ds->getEntityManager();
}
/**
* Function that returns the conection to the database.
*/
function getConnection() {
$ds = new DoctrineSetup();
return $ds->getEntityManager()->getConnection();
}
...
So now that we have doctrine set up its time to create a model (entity) and set which fields can and cannot be blank and so on and so forth.
Note At this point, you should know that I am not using Symfony other then its components on top of Doctrine. I am using Slim Framework. So if any suggestion is to use x or y from symfony, please make sure its a component.
Models/User.php
<?php
namespace ImageUploader\Models;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="users", uniqueConstraints={
* #ORM\UniqueConstraint(name="user", columns={"userName", "email"})}
* )
*/
class User {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #ORM\Column(type="string", length=32, nullable=false)
* #Assert\NotBlank()
*/
protected $firstName;
/**
* #ORM\Column(type="string", length=32, nullable=false)
* #Assert\NotBlank()
*/
protected $lastName;
/**
* #ORM\Column(type="string", length=100, unique=true, nullable=false)
* #Assert\NotBlank(
* message = "Username cannot be blank"
* )
*/
protected $userName;
/**
* #ORM\Column(type="string", length=100, unique=true, nullable=false)
* #Assert\NotBlank(
* message = "Email field cannot be blank."
* )
* #Assert\Email(
* message = "The email you entered is invalid.",
* checkMX = true
* )
*/
protected $email;
/**
* #ORM\Column(type="string", length=500, nullable=false)
* #Assert\NotBlank(
* message = "The password field cannot be empty."
* )
*/
protected $password;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
protected $created_at;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
protected $updated_at;
/**
* Get the value of Created At
*
* #return mixed
*/
public function getCreatedAt()
{
return $this->created_at;
}
/**
* Set the value of Created At
*
* #param mixed created_at
*
* #return self
*/
public function setCreatedAt(\DateTime $created_at = null)
{
$this->created_at = $created_at;
return $this;
}
/**
* Get the value of Updated At
*
* #return mixed
*/
public function getUpdatedAt()
{
return $this->updated_at;
}
/**
* Set the value of Updated At
*
* #param mixed updated_at
*
* #return self
*/
public function setUpdatedAt(\DateTime $updated_at = null)
{
$this->updated_at = $updated_at;
return $this;
}
/**
* Get the value of First Name
*
* #return mixed
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set the value of First Name
*
* #param mixed firstName
*
* #return self
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
return $this;
}
/**
* Get the value of Last Name
*
* #return mixed
*/
public function getLastName()
{
return $this->lastName;
}
/**
* Set the value of Last Name
*
* #param mixed lastName
*
* #return self
*/
public function setLastName($lastName)
{
$this->lastName = $lastName;
return $this;
}
/**
* Get the value of User Name
*
* #return mixed
*/
public function getUserName()
{
return $this->userName;
}
/**
* Set the value of User Name
*
* #param mixed userName
*
* #return self
*/
public function setUserName($userName)
{
$this->userName = $userName;
return $this;
}
/**
* Get the value of Email
*
* #return mixed
*/
public function getEmail()
{
return $this->email;
}
/**
* Set the value of Email
*
* #param mixed email
*
* #return self
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Set ths password.
*
* #param string password
*
* #return self
*/
public function setPassword($password) {
$this->password = password_hash($password, PASSWORD_DEFAULT);
return $this;
}
/**
* Check the users password against that which is enterd.
*
* #param string password
*
* #return bool
*/
public function checkPassword($password) {
if (password_hash($password, PASSWORD_DEFAULT) === $this->getPassword()) {
return true;
}
return false;
}
/**
* Return the password value.
*
* #return hash
*/
private function getPassword(){
return $this->password;
}
/**
* #ORM\PrePersist
*/
public function setCreatedAtTimeStamp() {
if (is_null($this->getCreatedAt())) {
$this->setCreatedAt(new \DateTime());
}
}
/**
* #ORM\PreUpdate
*/
public function setUpdatedAtTimeStamp() {
if (is_null($this->getUpdatedAt())) {
$this->setUpdatedAt(new \DateTime());
}
}
}
The above model is correct, as far as I know, I mean when I run "vendor/bin/doctrine migrations:migrate" a database table is created.
Now, where is all this used? it's used in a controller called SignupController under an action called createAction($params)
**createAction($params)**
public static function createAction($params){
$postParams = $params->request()->post();
$flash = new Flash();
if ($postParams['password'] !== $postParams['repassword']) {
$flash->createFlash('error', 'Your passwords do not match.');
self::$createEncryptedPostParams($postParams);
$params->redirect('/signup/error');
}
$user = new User();
$user->setFirstName($postParams['firstname'])
->setLastName($postParams['lastname'])
->setUserName($postParams['username'])
->setEmail($postParams['email'])
->setPassword($postParams['password'])
->setCreatedAtTimeStamp();
$validator = Validator::createValidatorBuilder();
$validator->enableAnnotationMapping();
$errors = $validator->getValidator()->validate($user);
if (count($errors) > 0) {
foreach($errors as $error) {
$flash->createFlash(
$error->getPropertyPath() . 'error',
$error->getMessage()
);
}
self::createEncryptedPostParams($postParams);
$params->redirect('/signup/error');
}
$anyEncryptedErors = self::getEncryptedPostParams();
if ($anyEncryptedErors !== null) {
$anyEncryptedErors->destroy('error');
}
getEntityManager()->flush();
getEntityManager()->persist($user);
$flash->createFlash('success', ' You have signed up successfully! Please sign in!');
$params->redirect('/signin');
}
Now should you enter everything in correctly I show a flash of success and redirect you. THIS WORKS it redirects, it shows a flash message. But its the:
getEntityManager()->flush();
getEntityManager()->persist($user);
That I don't think is working. Why? Because doing a select * from users on the database in question comes back with no records.
Why?
Flush statement should be execute after persist. So Code should be:
getEntityManager()->persist($user);
getEntityManager()->flush();
I had a similar issue and thought I would post it here. I was creating an entity and everything was responding correctly, but when I checked the database no record had been created.
Just wrapped the flush in a try-catch and logged the error.
$this->em->persist($insectLifeCycle);
try {
$this->em->flush();
} catch (\Exception $error) {
$this->logger->debug($error);
}
It turns out that one of properties was exceeding its character limit and the database was throwing an error. Also found out I need to improve my error handling....
As Samiul Amin Shanto said:
getEntityManager()->persist($user);
getEntityManager()->flush();
will be correct way, because persist action prepare the data to be stored in DB and flush "Flushes all changes to now to the database."
If you have the object id and then, the database is not showing it, you might have a "START TRANSACTION" and then you have your insert, after this insert you will have a "COMMIT". If any error appears between your persist and the COMMIT, object won't be stored in your database.
Check your Symfony request profiler information.
You can find it using the developer tool and checking your response for it.