Doctrine2 after entity update still generate old database table - symfony

Im learning doctrine2. Problem is: I have just updated my entity class. Old version of entity consisted of $id, $name and $username fields. After this update below, I run command doctrine:generate:entities Acme, doctrine:update:schema and etc., but result is still old table with only 3 fields. It looks like old meta-data is saved somewhere. Can someone provide me with information what Im doing wrong ? And why I get old database table instead of new one ? And even how to solve my problem ?
namespace Acme\DemoBundle\Entity;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="user")
* #ORM\Entity
*/
class User implements UserInterface, EquatableInterface
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=50)
*/
private $username;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=50)
*/
private $password;
/**
* #var string
*
* #ORM\Column(name="roles", type="string", length=50)
*/
private $roles;
/**
* #var array
*
* #ORM\Column(name="apikey", type="array")
*/
private $apiKey;
/**
* #var string
*
* #ORM\Column(name="salt", type="string", length=10)
*/
private $salt;
function __construct($apiKey, $id, $password ,$roles , $salt, $username)
{
$this->apiKey = $apiKey;
$this->id = $id;
$this->password = $password;
$this->roles = $roles;
$this->salt = $salt;
$this->username = $username;
}
/**
* The equality comparison should neither be done by referential equality
* nor by comparing identities (i.e. getId() === getId()).
*
* However, you do not need to compare every attribute, but only those that
* are relevant for assessing whether re-authentication is required.
*
* Also implementation should consider that $user instance may implement
* the extended user interface `AdvancedUserInterface`.
*
* #param UserInterface $user
*
* #return bool
*/
public function isEqualTo(UserInterface $user)
{
if (!$user instanceof User) {
return false;
}
if ($this->password !== $user->getPassword()) {
return false;
}
if ($this->salt !== $user->getSalt()) {
return false;
}
if ($this->username !== $user->getUsername()) {
return false;
}
return true;
}
/**
* 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 Role[] The user roles
*/
public function getRoles()
{
return $this->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()
{
return $this->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()
{
return $this->salt;
}
/**
* Returns the username used to authenticate the user.
*
* #return string The username
*/
public function getUsername()
{
return $this->username;
}
/**
* #return string
*/
public function getApiKey()
{
return $this->apiKey;
}
/**
* #param string $apiKey
*/
public function setApiKey($apiKey)
{
$this->apiKey = $apiKey;
}
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* 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()
{
// TODO: Implement eraseCredentials() method.
}
}

If the old metadata is saved You have to clear cache
doctrine:cache:clear-metadata Clears all metadata cache for an entity manager
doctrine:cache:clear-query Clears all query cache for an entity manager
doctrine:cache:clear-result Clears result cache for an entity manager

I think you did not executed the update command correctly.
You'll need to force the changes:
php app/console doctrine:schema:update --force
Or dump the SQL and execute it manually:
php app/console doctrine:schema:update --dump-sql

Related

Generate Slug in Prepersist Lifecycle for Symfony2 with StofDoctrineExtensionsBundle

I have two entities in my code: Session and SessionPicture.
There is a OneToOne relationship between Session and SessionPicture. SessionPicture is the mapping entity and Session is the inverse one.
Session has an attribute called "Slug", which is generated thanks to the StofDoctrineExtensionsBundle https://github.com/stof/StofDoctrineExtensionsBundle, and using the Gedmo\Sluggable annotation.
I have a form to create a Session (so a SessionType), and this SessionType embeds a SessionPictureType. The SessionPicture class contains a file attribute (UploadedFile class) and I have used LifecycleEvents (PostPersist/PostUpdate) to upload the file of the SessionPicture, and give it the "slug" property of the related Session when I save it.
My issue is that when I try to change another property of my SessionPicture (let's say "alt") and give it the value of the "slug" of the session , I cannot do it in the PostPersist Lifecycle and then save it to my database, because this event takes place after the onFlush, which is too late. I have to do it in the PrePersist Lifecyle. But then the "slug" of my session is not available yet, because it is generated during the onFlush by the extension.
So how can I get the slug of the related session in the prePersist lifecyle?
(PS: I have a found a solution, which is to persist the session in my controller, flush with the Manager, and then to persist again and redo a flush() but it is not very elegant).
Please find my code below:
/**
* Session
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="MyBundle\Entity\SessionRepository")
* #ORM\HasLifecycleCallbacks()
*/
class Session
{
/**
* #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, nullable=false)
*/
private $name;
/**
* #ORM\OneToOne(targetEntity="MyBundle\Entity\SessionPicture", cascade={"persist", "remove"}, mappedBy="session")
* #ORM\JoinColumn(name="session_picture_id", referencedColumnName="id", nullable=true)
*/
private $sessionPicture;
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set sessionPicture
*
* #param \MyBundle\Entity\SessionPicture $sessionPicture
*
* #return Session
*/
public function setSessionPicture(MyBundle\Entity\SessionPicture $sessionPicture = null)
{
$this->sessionPicture = $sessionPicture;
return $this;
}
/**
* Get sessionPicture
*
* #return \MyBundle\Session\SessionPicture
*/
public function getSessionPicture()
{
return $this->sessionPicture;
}
And my SessionPicture code:
/**
* #var string
*
* #ORM\Column(name="alt", type="string", length=255, nullable=true)
*/
private $alt;
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255, nullable=true)
*/
private $path;
/**
* #ORM\OneToOne(targetEntity="MyBundle\Entity\Session", inversedBy="sessionPicture")
*/
private $session;
public $file;
private $fileNameForRemove;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set extension
*
* #param string $extension
*
* #return SessionPicture
*/
public function setExtension($extension)
{
$this->extension = $extension;
return $this;
}
/**
* Get extension
*
* #return string
*/
public function getExtension()
{
return $this->extension;
}
/**
* Set alt
*
* #param string $alt
*
* #return SessionPicture
*/
public function setAlt($alt)
{
$this->alt = $alt;
return $this;
}
/**
* Get alt
*
* #return string
*/
public function getAlt()
{
return $this->alt;
}
/**
* Set path
*
* #param string $path
*
* #return SessionPicture
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
/**
* Set session
*
* #param \MyBundle\Entity\Session $session
*
* #return SessionPicture
*/
public function setSession(\NyBundle\Entity\Session $session = null)
{
$this->session = $session;
return $this;
}
/**
* Get session
*
* #return \MyBundle\Entity\Session
*/
public function getSession()
{
return $this->session;
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if($this->file === null)
return ;
$extension = $this->file->guessExtension();
$this->extension = (string)$extension;
My issue is below, I cannot get the slug before the flush of the session
$this->alt = $this->getSession()->getSlug();
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
But here, I can get the slug because my session has already been flushed and the slug generated
$this->alt = $this->getSession()->getSlug();
$this->path = $this->alt.'.'.$this->extension;
if ($this->file === null)
return ;
$this->file->move($this->getUploadRootDir(), $this->path);
unset($this->file);
}
Finally, here is an extract my controller code:
$form->handlerequest($request);
$validator = $this->get('validator');
$errorList = $validator->validate($session);
if (count($errorList) == 0)
{
$em = $this->getDoctrine()->getManager();
$em->persist($session);
$em->flush();
$em->persist($session);
$em->flush();
As you can see, I had to persist the $session and to flush it once, so that the slug is generated at the flush. Then persist again so that the slug is now available for the "alt" property of the SessionPicture at the prePersist n2, and flush again to save the alt property. But it is not very clean.
Thanks for any hel or advice. Thanks.

how to update automatically doctrine entity from database

I create a database and I had the entity file, I added a new column to my table, now how can I update the Entity class and add getter ad setter to this new element ?
the table contains ; userid, username, firstname, password
I added a column "admin" (boolean)
here is my class : Users:
<?php
namespace Login\LoginBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Redirect
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Login\LoginBundle\Entity\Users")
*/
class Users
{
/**
* #var string
*/
private $userName;
/**
* #var string
*/
private $firstName;
/**
* #var string
*/
private $password;
/**
* #var integer
*/
private $userid;
/**
* Set userName
*
* #param string $userName
* #return Users
*/
public function setUserName($userName)
{
$this->userName = $userName;
return $this;
}
/**
* Get userName
*
* #return string
*/
public function getUserName()
{
return $this->userName;
}
/**
* Set firstName
*
* #param string $firstName
* #return Users
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
return $this;
}
/**
* Get firstName
*
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set password
*
* #param string $password
* #return Users
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Get userid
*
* #return integer
*/
public function getUserid()
{
return $this->userid;
}
}
The generator bundle gives you a command to generate an entity from a set of fields via the app/console doctrine:generate:entity command.
Internally, this works by using template files to create your new entities, based on your inputs. Unfortunately, the tool does not yet have the ability to modify existing classes.
If you'd like, you can request that feature here: https://github.com/sensiolabs/SensioGeneratorBundle
But in the meantime, your best bet is one of the following:
If you haven't modified anything, you could erase the file and re-create it using app/console doctrine:generate:entity.
You can just add the field yourself, along with the getters, setters, and Doctrine configuration, using the formats you see used for the other fields in that class (this is probably the easier way, to be honest).

Create relationnal entitiy on Symfony 2

I would like to save my users search on my website. Thaht's why i have a Class User and i would like to create Search Class.
I have done that :
class Search
{
public function __construct()
{
$this->searched_date = new \Datetime();
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="test\UserBundle\Entity\User")
*/
private $user;
/**
* #ORM\Column(name="termsearch", type="string", length=255, nullable="true")
*/
private $termsearch;
/**
* #ORM\Column(name="goodtitle", type="string", length=255, nullable="true")
*/
private $goodtitle;
/**
* #ORM\Column(name="searched_date", type="datetime")
*/
private $searched_date;
/**
* Set termsearch
*
* #param text $termsearch
*/
public function setTermsearch($termsearch)
{
$this->termsearch = $termsearch;
}
/**
* Get termsearch
*
* #return text
*/
public function getTermsearch()
{
return $this->termsearch;
}
/**
* Set searched_date
*
* #param datetime $searchedDate
*/
public function setSearchedDate($searchedDate)
{
$this->searched_date = $searchedDate;
}
/**
* Get searched_date
*
* #return datetime
*/
public function getSearchedDate()
{
return $this->searched_date;
}
/**
* Set user
*
* #param test\UserBundle\Entity\User $user
*/
public function setUser(\test\UserBundle\Entity\User $user)
{
$this->user = $user;
}
/**
* Get user
*
* #return test\UserBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Set goodtitle
*
* #param text $goodtitle
*/
public function setGoodtitle($goodtitle)
{
$this->goodtitle = $goodtitle;
}
/**
* Get goodtitle
*
* #return text
*/
public function getGoodtitle()
{
return $this->goodtitle;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
And i would like to insert like that :
$em = $this->getDoctrine()->getEntityManager();
$user = $em->getRepository('TestUserBundle:User')->find($currentuser->getID());
$search = new Search();
$search->setUser($user);
$search->setTermsearch($termsearch);
$search->setGoodtitle($goodtitle);
$em->persist($search);
$em->flush();
Unfortunately i have this error :
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens (500 Internal Server Error)
And in the stack we can found that :
INSERT INTO s_search (user_id, termsearch, goodtitle, searched_date) VALUES (?, ?, ?, ?) ({"1":"c2c","2":"C2C Down The Road","3":{"date":"2012-10-31 00:18:47","timezone_type":3,"timezone":"Europe\/Paris"}})
I don't know how i can create this class Search...
Thank for for you help !
Remove #ORM\Id from the $user as #ManyToOne mapping reference does not require a type. See Doctrine's Annotation Reference for details. Doctrine takes care of the correct column type to hold the reference to another entity.
Make also sure that your User query really returns a valid $user. If it's possible that Search does not have $user, use #JoinColumn annotation to claim the column nullable. See another SO question Doctrine 2 can't use nullable=false in manyToOne relation?

Symfony 2 - Retrieve Entity with Json, return another Entity

I have a problem while json_encodeing a Entity.
public function jsonvoteAction($id) {
$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository('KorumAGBundle:AGVote')->findOneById($id);
$response = new Response(json_encode($entity, 200));
$response->headers->set('Content-Type',' application/json');
return $response;
}
This code returns me a the users entity
{"users":{"__isInitialized__":false,"id":null,"nickname":null,"pwd":null,"email":null,"firstname":null,"lastname":null,"poste":null,"addr1":null,"addr2":null,"pc":null,"country":null,"phone":null,"province":null,"acess":null,"site":null,"crew":null,"utilisateur":null}}
And when I var dymp my $entity, it returns both my AGVote and USers entity.
Here is my AGVote Entity
<?php
namespace Korum\AGBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Korum\AGBundle\Entity\AGVote
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class AGVote
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $question;
/**
* #ORM\Column(type="smallint")
*/
private $actif;
/**
* #ORM\ManyToOne(targetEntity="\Korum\KBundle\Entity\Users", cascade={"all"})
*/
public $users;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set question
* Nb : Only AG admin can set a question
* #param text $question
*/
public function setQuestion($question)
{
$this->question = $question;
}
/**
* Get question
*
* #return text
*/
public function getquestion()
{
return $this->question;
}
/**
* Set actif
*
* #param smallint $actif
*/
public function setActif($actif)
{
$this->actif = $actif;
}
/**
* Get actif
*
* #return smallint
*/
public function getActif()
{
return $this->actif;
}
/**
* Set Users
*
* #param Korum\KBundle\Entity\Province $Users
*/
public function setUsers(\Korum\KBundle\Entity\Users $users)
{
$this->users = $users;
}
/**
* Get Users
*
* #return Korum\KBundle\Entity\Users
*/
public function getUsers()
{
return $this->users;
}
}
Does anyone have an idea of what happened ?
I tried to install the JSMSerializerBundle but event with Metadata library at version 1.1.
When I want to clear my cache, it failed with error :
See :
JMSSerializerBundle Installation : Catchable Fatal Error: Argument 1 passed to JMSSerializerBundle\Twig\SerializerExtension::__construct()
By default, json_encode only uses public properties.
So it serialized the only public property of AGVote: $users. The content of $users was an instance of User; which public fields were serialized.
You could work around these by adding a toArray() method to your entities, and then doing json_encode($entity->toArray()), but i highly recommend you to have a look and use the JMSSerializedBundle.

Symfony 2 security: account activation

I'm using Symfony 2 security system.
When some user trying to login, I want to additionally check whether user's field "activated" is true. If not, error message appears: "You have to activate your account first".
How can i implement this feature?
If you're using Doctrine as user provider you can implement the AdvancedUserInterface. This interface (definition visible below) provide the isEnabled() method which is equal to account activation status method. If this method return false, the user will have an error message like Your account is not enabled when he attempt to login.
I use it to provide e-mail validation on subscription.
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien#symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\User;
/**
* AdvancedUserInterface adds status flags to a regular account.
*
* #author Fabien Potencier <fabien#symfony.com>
*/
interface AdvancedUserInterface extends UserInterface
{
/**
* Checks whether the user's account has expired.
*
* #return Boolean true if the user's account is non expired, false otherwise
*/
function isAccountNonExpired();
/**
* Checks whether the user is locked.
*
* #return Boolean true if the user is not locked, false otherwise
*/
function isAccountNonLocked();
/**
* Checks whether the user's credentials (password) has expired.
*
* #return Boolean true if the user's credentials are non expired, false otherwise
*/
function isCredentialsNonExpired();
/**
* Checks whether the user is enabled.
*
* #return Boolean true if the user is enabled, false otherwise
*/
function isEnabled();
}
i have tried for the first time. but got an error message
"User is locked"
it try to figure it out how to implement form AdvanceUserInterface.
I want to share code how to implement it, here you go :
class User implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=25, unique=true)
*/
private $username;
/**
* #ORM\Column(type="string", length=255)
* #Assert\Length(min=8, max=255)
*/
private $password;
/**
* #ORM\Column(type="string", length=60, unique=true)
* #Assert\Email(message = "The email '{{ value }}' is not a valid email.", checkMX = true, checkHost = true)
*/
private $email;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
*
*/
private $roles;
private $salt;
public function __construct()
{
$this->isActive = true;
$this->roles = new ArrayCollection();
// may not be needed, see section on salt below
// $this->salt = md5(uniqid(null, true));
}
/**
* #inheritDoc
*/
public function getUsername()
{
return $this->username;
}
/**
* #inheritDoc
*/
public function getSalt()
{
// you *may* need a real salt depending on your encoder
// see section on salt below
return null;
}
/**
* #inheritDoc
*/
public function getPassword()
{
return $this->password;
}
/**
* #inheritDoc
*/
public function getRoles()
{
//return array('ROLE_USER');
return $this->roles->toArray();
}
/**
* #inheritDoc
*/
public function eraseCredentials()
{
}
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt,
));
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt
) = unserialize($serialized);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* #param string $username
* #return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Set password
*
* #param string $password
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Set salt
*
* #param string $salt
* #return User
*/
public function setSalt($salt)
{
$this->salt = $salt;
return $this;
}
/**
* Set email
*
* #param string $email
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set isActive
*
* #param boolean $isActive
* #return User
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
/**
* Add roles
*
* #param \Bsi\BkpmBundle\Entity\Role $roles
* #return User
*/
public function addRole(\Bsi\BkpmBundle\Entity\Role $roles)
{
$this->roles[] = $roles;
return $this;
}
/**
* Remove roles
*
* #param \Bsi\BkpmBundle\Entity\Role $roles
*/
public function removeRole(\Bsi\BkpmBundle\Entity\Role $roles)
{
$this->roles->removeElement($roles);
}
public function isEnabled()
{
return $this->getIsActive();
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
}
You can use the same procedure outlined here for handling expired passwords:
How to use the credentials expired property in Symfony AdvancedUserInterface?
Except you will be handling the Locked exception:
http://api.symfony.com/2.5/Symfony/Component/Security/Core/Exception/LockedException.html
Ignore this section. It doesn't apply when using the symfony security code
Just follow http://symfony.com/doc/current/cookbook/service_container/event_listener.html and handle the various security exceptions there:
LockedException
AccountExpiredException
CredentialsExpiredException
DisabledException
When using symfony's security code
You can't listen to kernel.exception events when dealing with form login. The security code handle all of the exceptions internally.
In order to handle security exceptions during login, you need to implement:
AuthenticationFailureHandlerInterface
AuthenticationSuccessHandlerInterface
I am implementing in my code now. There are several articles on the subject, but were difficult to find. I'll post them when I validate them.
The authentication success/failure handlers can't be used to redirect to a change password form since the user is all ready denied when they execute.
Although they turned out to be great places to dispatch events, like 'account_disabled', 'account_locked', 'account_expired', or 'credentials_expired'.
As long as the destination page allows anonymous users, you can redirect a user where ever you want. But if the destination page requires login, you will have a redirect loop.
A custom authentication listener will propably be needed for this. Another possibility would be to implement a doctrine onload listener and auth voter that sets a 'provisional user' flag when the account/credentials are expired and allows access to change user attributes.
I will eventually need to implement some similar solution, but I don't have time currently.
There are probably other ways.

Resources