I have following entity:
class Employee {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $employeeId;
/**
* #ORM\Column(type="string", length=45, unique=true)
*/
protected $username;
/**
* #ORM\Column(type="string", length=255, nullable=false)
*/
protected $email;
and I'm running following code:
$employee = new Employee();
$employee->setUsername('test');
$em = $this->getDoctrine()->getManager();
$em->persist($employee);
$em->flush();
As you can see I didn't set value for email column.
But on persists I get:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'email' cannot be null
because Doctrine adds all entity columns to the INSERT query and set null value for email column.
Is there a way to skip not set columns on insert? Or to make Doctrine insert '' (empty string) as default value for non null, string columns?
You may either allow your column to be null, setting nullable=true:
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
protected $email;
This won't raise the SQL error. But if you want to be consistent, use validation so you can deal with empty fields before persistance:
use Symfony\Component\Validator\Constraints as Assert;
...
/**
* #Assert\NotBlank()
* #ORM\Column(type="string", length=255)
*/
protected $email;
This way you can handle validation errors in a more specific way, like stated in docs for example:
$author = new Author();
// ... do something to the $author object
$validator = $this->get('validator');
$errors = $validator->validate($author);
if (count($errors) > 0) {
return new Response(print_r($errors, true));
} else {
return new Response('The author is valid! Yes!');
}
If you want just plain default values for columns, have a look at this question.
I seems that I just have to use entity __construct to set default values:
__construct() {
$this->email = '';
}
It's rather problem with your data model not with doctrine. You explicit declare that every record should have some value in email column. So either you remove NOT NULL constrain from entity or you just set some value on email column. In this case doctrine is only doing what you've told it to do.
Related
Hello i have an entity with two primary keys .
class UsefulnessEvaluation
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="App\EvaluationBundle\Entity\Evaluation", cascade={"persist","remove"})
*/
private $evaluation;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="App\UserBundle\Entity\User", cascade={"persist","remove"})
*/
private $user;
/**
*
* #ORM\Column(type="string", nullable=false)
*/
private $type;
/**
* #ORM\Column(type="datetime", nullable=false)
*/
private $createdAt;
//etc
}
I want , in repository , count the number of an evaluation:
class UsefulnessEvaluationRepository extends EntityRepository
{
public function countEvaluationLikes($evaluation_id)
{
$query = $this->getEntityManager()->createQuery(
'SELECT count(p.evaluation) as nbre
FROM AppEvaluationBundle:UsefulnessEvaluation p
WHERE p.evaluation = :id
)->setParameter('id', $evaluation_id);
return $query->getSingleScalarResult();
}
}
this is the error :
Binding an entity with a composite primary key to a query is not
supported. You should split the parameter into the explicit fields and
bind them separately.
I think the issue is that you're selecting count(p.evaluation) but since you're already specifying the id of p.evaluation it seems unnecessary because you're guaranteed to get non-null values for p.evaluation.
Try this
$query = $this->getEntityManager()->createQuery(
'SELECT count(p) as nbre
FROM AppEvaluationBundle:UsefulnessEvaluation p
WHERE IDENTITY(p.evaluation) = :id'
)->setParameter('id', $evaluation_id);
Here's what I'm having trouble with.
I've a Table which contains a column called shown_on_homepage and only one row should be set to 1, the rest should all be set to 0. I'm trying to add a new row to the database and this one should be set to 1, setting the one that previously had a 1 to 0.
In MySQL I know this can be achieved by issuing an update before the insert:
UPDATE table_name SET shown_on_homepage = 0
Here's my Entity:
class FeaturedPerson {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="content", type="string", length=2500, nullable=false)
*/
private $content;
/**
* #var \DateTime
*
* #ORM\Column(name="date_updated", type="datetime")
*/
private $dateUpdated;
/**
* #var bool
*
* #ORM\Column(name="shown_on_homepage", type="boolean", nullable=false)
*/
private $isShownOnHomepage;
//...
public function getIsShownOnHomepage() {
return $this->isShownOnHomepage;
}
public function setIsShownOnHomepage($isShownOnHomepage) {
$this->isShownOnHomepage = $isShownOnHomepage;
return $this;
}
}
And for the Controller I've:
$featured = new FeaturedPerson();
$featured->setContent('Test content.');
$featured->setDateUpdated('01/02/2013.');
$featured->setIsShownOnHomepage(TRUE);
$em = $this->getDoctrine()->getManager();
$em->persist($featured);
$em->flush();
It does add the new row, but the one that had a shown_on_homepage set to 1 still has it. I've researched but I couldn't find a way to achieve this, I hope you can help me.
You could execute a query prior to your existing code in your controller:
$queryBuilder = $this->getDoctrine()->getRepository('YourBundleName:FeaturedPerson')->createQueryBuilder('qb');
$result = $queryBuilder->update('YourBundleName:FeaturedPerson', 'd')
->set('d.isShownOnHomepage', $queryBuilder->expr()->literal(0))
->where('d.isShownOnHomepage = :shown')
->setParameter('shown', 1)
->getQuery()
->execute();
Change 'YourBundleName' to your bundle name.
How can I create a new object when a cell is related to another table? In my case there exist a table with states, like id=1,state=active;id=2,state=inactive.
My Entity/States.php
class States
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
....
Entity/User.php
....
/**
* Set state
*
* #param \Frontend\AccountBundle\Entity\States $state
* #return User
*/
public function setState(\Frontend\AccountBundle\Entity\States $state = null)
{
$this->state = $state;
return $this;
}
My AccountController:
....
$user = new User();
$em = $this->get('doctrine')->getEntityManager();
$state = $this->getDoctrine()->getRepository('FrontendAccountBundle:States')->find(1);
$user->setEmail($formData->getEmail());
$user->setStateId(1);
$em->persist($user);
$em->flush();
This is not working and way to complicated: http://symfony.com/doc/current/book/doctrine.html#relationship-mapping-metadata. It was so freaking easy in symfony1.4.
Your User entity has a method setState(), which takes a single parameter of $state. That parameter must be of type \Frontend\AccountBundle\Entity\States.
In your controller, you obtain that object through this call:
$state = $this->getDoctrine()->getRepository('FrontendAccountBundle:States')->find(1);
So when you go to set the State of the User, you don't need to bother with IDs. Rather, just set the State directly, and Doctrine will take care of the rest:
$user->setState($state);
This solution works for me:
https://stackoverflow.com/a/14131067/2400373
But in Symfony 4 change the line of getRepository:
$role = $this->getDoctrine()
->getRepository(Role::class)
->find(1);
$usuario->setRole($role);
I have a User entity that implements UserInterface to use with a RBAC system. I have not implemented the whole system yet. However, when I try to remove a user with the following code, the action removes all the users and other associated objects in other tables and then throws me an error. I am able to remove objects from other entities without issues.
User entity
class User implements UserInterface
{
**
* #var integer $id
*
* #ORM\Column(name="id", type="smallint")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
protected $id;
**
* #var string $username
*
* #ORM\Column(name="username", type="string", length=20, unique=TRUE)
*
protected $username;
**
* #var string $password
*
* #ORM\Column(name="password", type="string", length=255)
*
protected $password;
**
* #var string $salt
*
* #ORM\Column(name="salt", type="string", length=255)
*
protected $salt;
**
* #var string $fullName
*
* #ORM\Column(name="full_name", type="string", length=60, unique=TRUE)
*
protected $fullName;
**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users", cascade={"persist", "remove"})
* #ORM\JoinTable(name="users_roles")
*
* #var ArrayCollection $userRoles
*
protected $userRoles;
public function __construct()
{
$this->userRoles = new ArrayCollection();
}
}
Delete action
public function deleteUserAction($id) {
$user = $em->getRepository('ACMECompanyBundle:User')->find($id);
$currentUser = $this->get('security.context')->getToken()->getUser();
if ($id == $currentUser->getId()) {
return new Response("You cannot delete the current user");
}
if (!$user) {
throw $this->createNotFoundException('No user found for id '.$id);
}
try {
$em->remove($user);
$em->flush();
$msg = "User deleted!";
$code = "OK";
} catch (DBALException $e) {
return new Response($e);
$msg = "User cannot be deleted!";
$code = "ERR";
}
$response = new Response(json_encode(array('code' => $code, 'msg' => $msg)));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
The error returned after all users are removed is
InvalidArgumentException: You cannot refresh a user from the EntityUserProvider that does not contain an identifier. The user object has to be serialized with its own identifier mapped by Doctrine.
You left out the definition for em in your action... define it with
$em = $this->getDoctrine()->getEntityManager();
and it should work. Unless you set it on the class itself, then you would need $this->...
When doctrine removes the user, it also removes all rolles assigned to this user and all users assigned to these roles. So according your annotation schema this is the correct behavior because of cascade={"remove"} in $userRoles annotation and cascade={"remove"} in $users annotation in Role entity.
If you want to prevent cascade removing and want to keep cascade persistent remove "remove" argument from both user and role relations
I want to remove the need for a username in the FOSUserBundle. My users will login using an email address only and I've added real name fields as part of the user entity.
I realised that I needed to redo the entire mapping as described here.
I think I've done it correctly but when I try to submit the registration form I get the error:
"Only field names mapped by Doctrine can be validated for uniqueness."
The strange thing is that I haven't tried to assert a unique constraint to anything in the user entity.
Here is my full user entity file:
<?php
// src/MyApp/UserBundle/Entity/User.php
namespace MyApp\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="depbook_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*
* #Assert\NotBlank(message="Please enter your first name.", groups={"Registration", "Profile"})
* #Assert\MaxLength(limit="255", message="The name is too long.", groups={"Registration", "Profile"})
*/
protected $firstName;
/**
* #ORM\Column(type="string", length=255)
*
* #Assert\NotBlank(message="Please enter your last name.", groups={"Registration", "Profile"})
* #Assert\MaxLength(limit="255", message="The name is too long.", groups={"Registration", "Profile"})
*/
protected $lastName;
/**
* #ORM\Column(type="string", length=255)
*
* #Assert\NotBlank(message="Please enter your email address.", groups={"Registration", "Profile"})
* #Assert\MaxLength(limit="255", message="The name is too long.", groups={"Registration", "Profile"})
* #Assert\Email(groups={"Registration"})
*/
protected $email;
/**
* #ORM\Column(type="string", length=255, name="email_canonical", unique=true)
*/
protected $emailCanonical;
/**
* #ORM\Column(type="boolean")
*/
protected $enabled;
/**
* #ORM\Column(type="string")
*/
protected $salt;
/**
* #ORM\Column(type="string")
*/
protected $password;
/**
* #ORM\Column(type="datetime", nullable=true, name="last_login")
*/
protected $lastLogin;
/**
* #ORM\Column(type="boolean")
*/
protected $locked;
/**
* #ORM\Column(type="boolean")
*/
protected $expired;
/**
* #ORM\Column(type="datetime", nullable=true, name="expires_at")
*/
protected $expiresAt;
/**
* #ORM\Column(type="string", nullable=true, name="confirmation_token")
*/
protected $confirmationToken;
/**
* #ORM\Column(type="datetime", nullable=true, name="password_requested_at")
*/
protected $passwordRequestedAt;
/**
* #ORM\Column(type="array")
*/
protected $roles;
/**
* #ORM\Column(type="boolean", name="credentials_expired")
*/
protected $credentialsExpired;
/**
* #ORM\Column(type="datetime", nullable=true, name="credentials_expired_at")
*/
protected $credentialsExpiredAt;
public function __construct()
{
parent::__construct();
// your own logic
}
/**
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* #return string
*/
public function getLastName()
{
return $this->lastName;
}
/**
* Sets the first name.
*
* #param string $firstname
*
* #return User
*/
public function setFirstName($firstname)
{
$this->firstName = $firstname;
return $this;
}
/**
* Sets the last name.
*
* #param string $lastname
*
* #return User
*/
public function setLastName($lastname)
{
$this->lastName = $lastname;
return $this;
}
}
I've seen various suggestions about this but none of the suggestions seem to work for me. The FOSUserBundle docs are very sparse about what must be a very common request.
I think the easiest way to go about this is to leave the bundle as is and rather setup your user class to have a username equal to the email address.
Do this by overriding the setEmail() method to also set the $username property to the $email parameter and the setEmailCanonical() to also set the $usernameCanonical to the $emailCanonical.
public function setEmail($email){
$this->email = $email;
$this->username = $email;
}
public function setEmailCanonical($emailCanonical){
$this->emailCanonical = $emailCanonical;
$this->usernameCanonical = $emailCanonical;
}
All you will have to do other than this is semantics related. Like having your form label read E-mail instead of the default Username label. You can do this by overriding the translations files. I'll leave this up to you (or someone else) since it might not even be necessary for you.
With this strategy you will have redundant data in your database but it will save you a lot of remapping headache.
If you are using doctrine 2, you can use Life Cycle Events to put your logic inside a callback.
http://docs.doctrine-project.org/en/2.0.x/reference/events.html
/**
* #ORM\PreUpdate()
* #ORM\PrePersist()
*/
public function setUsernameToEmail()
{
$this->username = $this->email;
$this->usernameCanonical = $this->emailCanonical;
}
When I didn't want to require users to enter emails (thus making emails optional in FOSUserBundle), I use Symfony 2.7 + FOSUser+SonataUser+SonataAdmin.
At the same time I needed entered emails to be unique in the system. So Users have 2 options when registering:
Leave email empty
Provide a unique email, that is not yet in the system
Below is my solution that works as expected (I don't claim it to be the cleanest one, but hopefully it will show you a way how to accomplish a similar task)
1) Changes to Entity/User.php
namespace AppBundle\Entity;
use Sonata\UserBundle\Entity\BaseUser as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*
*
* #ORM\AttributeOverrides({
* #ORM\AttributeOverride(name="email",
* column=#ORM\Column(
* type = "string",
* name = "email",
* nullable = true,
* unique = true
* )
* ),
* #ORM\AttributeOverride(name="emailCanonical",
* column=#ORM\Column(
* type = "string",
* name = "email_canonical",
* nullable = true,
* unique = true
* )
* )
* })
*
\*/
class User extends BaseUser
{
2) Executed app/console doctrine:migrations:diff & migrate, database tables were changed as expected adding "DEFAULT NULL" to email and email_canonical fields
3) No matter what I tried, email was being set to NULL, but email_canonical wasn't, it was returning "". I tried manually setting it to NULL in my RegistrationFormHandler, var_dump there confirmed that it was indeed set to NULL when email wasn't entered. But to the database FOSUser would submit "" empty string, which violated UNIQUE constraint I had set for emails, so the solution was to override method in Entity/User.php (as is discussed in previous answers to this question)
// src/AppBundle/Entity/User.php
// ...
public function setEmailCanonical($emailCanonical)
{
// when email is empty, force canonical to NULL
// for some reason by default "" empty string is inserted
$this->emailCanonical = $this->getEmail();
}
4) Change Validation for FOSUserBundle (or SonataUserBundle) in my case , so that it doesn't require email to be set. (I simply removed .. from validation.xml as non of those applied to email anymore)
Copy these 2 files into your config/validation/ directory (for SonataUser+FOSUser it is: Application/Sonata/UserBundle/Resources)
vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Resources/config/storage-validation/orm.xml
above path, config/validation/orm.xml
Rename "Registration" group in those files to your own name, like "myRegistration".
Bind your new validation_group to fos_user in config.yml. If using Sonata User, it is:
sonata_user:
profile:
register:
form:
...
validation_groups:
- myRegistration
- Default
Have fun.