I try this code: Symfony: ManyToMany table extra columns
This new field is because the user have a diferent role for diferent events.
In the fist time i can add add the new fields on table user.evento, but now i cant. And i dont find the problem.
I get this fail:
An exception occurred while executing 'INSERT INTO user_evento (user_id, evento_id) VALUES (?, ?)' with params [3, 18]: SQLSTATE[HY000]: General error: 1364 Field 'tipoinvitado' doesn't have a default value
why not detecting the other 2 fields ?
capture of my sql update with the new fields
my entity UserHasEvento:
/**
* #ORM\Entity
* #ORM\Table(name="user_evento")
*/
class UserHasEvento
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Evento", cascade={"persist"}, fetch="LAZY")
* #ORM\JoinColumn(name="evento_id", referencedColumnName="id")
*/
private $eventos;
/**
* #ORM\ManyToOne(targetEntity="User", cascade={"persist","remove"}, fetch="LAZY" )
* #ORM\JoinColumn(name="user_id", referencedColumnName="id",nullable=true)
*/
private $users;
/**
* #ORM\Column(name="tipoinvitado", type="string", length=255, nullable=true)
*/
private $tipoinvitado;
/**
* #var \DateTime|null
*
* #ORM\Column(name="fechainscripcion", type="datetime", nullable=true)
*/
private $fechainscripcion;
public function setTipoinvitado(string $tipoinvitado): self
{
$this->tipoinvitado = $tipoinvitado;
return $this;
}
public function getTipoinvitado(): string
{
return $this->tipoinvitado;
}
public function getFechainscripcion()
{
return $this->fechainscripcion;
}
public function setFechainscripcion($fechainscripcion): self
{
$this->fechainscripcion = $fechainscripcion;
return $this;
}
public function __construct()
{
$this->createdAt= new \DateTime('now');
}
}
My controller update.
public function asignarUsuario(Request $request, Evento $evento, Userhasevento $userhasevento){
$user_repo = $this->getDoctrine()->getRepository(User::class);
$users = $user_repo->findAll();
$evento = $this->getDoctrine()->getRepository(Evento::class)->findOneById($evento);
$form = $this->createForm(AsignarEventoFormType::class, $evento);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$users = $evento->GetUsers();
foreach($users as $user){
$user->addEvento($evento);
$evento->addUser($user);
$userhasevento->SetTipoinvitado('normal');
$userhasevento->setFechainscripcion(new \DateTime('now'));
$em = $this->getDoctrine()->getManager();
$em->persist($evento);
$em->persist($userhasevento);
$em->flush();
}
return $this->redirect($this->generateUrl('evento_detalle', ['id' => $evento->getId()]));
}
return $this->render('evento/AsignarEvento.html.twig',[
'form' => $form->createView()
]);
}
Is your field $tipoinvitado disappear?
You might have changed your relation and the null boolean of $tipoinvitado at some point. You made your migration after changing any relation?
This can happen when you change your relations and had already data stored in you DB in a field that disappear because of you relation change.
I might be able to help you, don't hesitate giving more info
You have the answer in the error, the $tipoinvitado field has the annotation nullable=false which is a not null constraint (prevent storing null value), so it should absolutely have a value.
So you either give it a default value, give a value or change it to nullable=true.
Related
A Company can have multiple emails and all emails have to be unique.
This my Entites for Company and CompanyEmail
CompanyEmail Entity:
/**
* #ORM\Entity(repositoryClass="App\Repository\CompanyEmailRepository")
* #UniqueEntity("name")
*/
class CompanyEmail
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=128, unique=true)
* #Assert\Email()
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Company", inversedBy="emails")
* #ORM\JoinColumn(nullable=false)
*/
private $company;
// ...
}
Company Entity:
/**
* #ORM\Entity(repositoryClass="App\Repository\CompanyRepository")
*/
class Company
{
// ...
/**
* #ORM\OneToMany(targetEntity="App\Entity\CompanyEmail", mappedBy="company", orphanRemoval=true, cascade={"persist"})
* #Assert\Valid
*/
private $emails;
// ...
}
and I'm using an custom EmailsInputType that use this DataTransformer
class EmailArrayToStringTransformer implements DataTransformerInterface
{
public function transform($emails): string
{
return implode(', ', $emails);
}
public function reverseTransform($string): array
{
if ($string === '' || $string === null) {
return [];
}
$inputEmails = array_filter(array_unique(array_map('trim', explode(',', $string))));
$cEmails = [];
foreach($inputEmails as $email){
$cEmail = new CompanyEmail();
$cEmail->setName($email);
$cEmails[] = $cEmail;
}
return $cEmails;
}
}
and in the Controller a use this edit method
/**
* #Route("/edit/{id}", name="admin_company_edit", requirements={"id": "\d+"}, methods={"GET", "POST"})
*/
public function edit(Request $request, $id): Response
{
$entityManager = $this->getDoctrine()->getManager();
$company = $entityManager->getRepository(Company::class)->find($id);
$form = $this->createForm(CompanyType::class, $company);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->flush();
}
}
There is two problems with this code
1 - In the edit form when i try to keep an already saved email Symfony generate a validation error that tells that this email is already exits.
2 - When I remove the validation restriction from the code, Symfony thrown the database error "*Integrity constraint violation: 1062 Duplicate entry ... *"
What i should do to make my code work as expected !
The problem is right here
public function reverseTransform($string): array
{
[...]
foreach($inputEmails as $email){
$cEmail = new CompanyEmail();
[...]
}
[...]
}
You need to retrieve the email instead of creating new one.
So basically, inject a CompanyEmailRepository, try to find if email already exists (findOneBy(['name'])), if it does not exists, create a new one but if exists, use what you've retrieved.
Just few notes
Pay attention to email owner (so the retrieve should be do per user I guess as no one can share the same mail UNLESS you can specify some aliases or shared address)
Maybe you don't need an extra entity like CompanyEmail as you can use a json field where you can store them in a comma separated fashion (unless you need some extra parameters or unless you need to perform some indexing/querying operation on the emails)
I am building a project management tool for my team in Symfony 3. I am using ramsey/uuid-doctrine for the IDs in the system.
So far, this hasn't been a problem with One-to-Many or Many-to-One associations, but when I try to persist a One-to-One association, Doctrine is not converting the associated entity to its UUID, and instead leaving a null value in the SQL.
In this example, I have a WikiPage which can have multiple WikiPageVersions. The WikiPage has a One-to-Many association with WikiPageVersion (the versions property: for all the versions of the page), but also a One-to-One (Unidirectional) association with WikiPageVersion (the currentVersion property: for the, well, current version).
The WikiPage also has a Many-to-One associations with Project (to track which project the wiki page is for) and that property is populated correctly.
The WikiPage Entity
/**
* #ORM\Table(name="wiki_page")
* #ORM\Entity(repositoryClass="AppBundle\Repository\Project\WikiPageRepository")
*/
class WikiPage
{
/**
* #var Uuid
* #ORM\Id
* #ORM\Column(type="uuid")
*/
protected $id;
/**
* #var Project
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Project\Project", inversedBy="wikiPages")
* #ORM\JoinColumn(name="project_id", referencedColumnName="id")
*/
protected $project;
/**
* #var string
* #ORM\Column(name="title", type="text")
* #Assert\NotBlank()
*/
protected $title;
/**
* #var HiveWikiPageVersion
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Project\WikiPageVersion", fetch="EAGER")
*/
protected $currentVersion;
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Project\WikiPageVersion", mappedBy="wikiPage", cascade={"persist", "remove"})
*/
protected $versions;
// -- Class Methods
}
The WikiPageVersion Entity
/**
* #ORM\Table(name="wiki_page_version")
* #ORM\Entity(repositoryClass="AppBundle\Repository\Project\WikiPageVersionRepository")
*/
class WikiPageVersion
{
/**
* #var Uuid
* #ORM\Id
* #ORM\Column(type="uuid")
*/
protected $id;
/**
* #var WikiPage
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Project\WikiPage", inversedBy="versions")
* #ORM\JoinColumn(name="wiki_page_id", referencedColumnName="id")
* #Assert\NotBlank()
*/
protected $wikiPage;
/**
* #var string
* #ORM\Column(name="content", type="text")
* #Assert\NotBlank()
*/
protected $content;
/**
* #var string
* #ORM\Column(name="version_comment", type="string", length=255)
* #Assert\NotNull()
*/
protected $versionComment;
/**
* #var HiveWikiPageVersion
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Project\WikiPageVersion")
* #ORM\JoinColumn(name="previous_version", referencedColumnName="id")
* #Assert\Type(type="Odev\Hive\Model\Entity\Project\WikiPageVersion")
*/
protected $previousVersion;
/**
* #var \DateTimeInterface
* #ORM\Column(name="created", type="datetime")
* #Assert\NotBlank()
*/
protected $created;
/**
* #var User
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* #ORM\JoinColumn(name="created_by", referencedColumnName="id")
* #Assert\NotBlank()
*/
protected $createdBy;
}
// -- Methods
Troubleshooting So Far
I can confirm that before persisting the WikiPage, that the WikiPageVersion has been associated.
I can duplicate this same behaviour with my Project and WikiPageVersion entites (Project has a One-to-One association with WikiPage for the wiki homepage and WikiPageVersion has a One-to-One association with itself for the previous version). Only the One-to-One associations are not converting the UUID.
I have the same problem when trying to persist from Symfony Controller or from when loading Doctrine fixtures.
I have tried to trace down where the conversion occurs using xdebug, but I am not that versed in using a debugger and after 20 minutes of stepping through the debugger, I can't find where the conversion for that field happens. I'm either skipping past the loop it happens in or just missing it. After wasting an hour skipping through runs trying to find the problem, I had to give up.
Here is the error I get from Doctrine when I try to perist the WikiPage:
[Doctrine\DBAL\Exception\NotNullConstraintViolationException]
An exception occurred while executing 'INSERT INTO wiki_page (id, project_home, title, project_id, current_version_id) VALUES (?, ?, ?, ?, ?)' with params ["ddc1f51a-f5d9-489f-89bb-cd79f3393af0", 1
, "Technical Reviews Wiki", "5138b185-b10b-48ac-a102-bdea1139c911", null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'current_version_id' cannot be null
and the exception trace (this exception is from saving during fixtures loading):
Exception trace:
() at /home/vagrant/hive/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php:112
Doctrine\DBAL\Driver\AbstractMySQLDriver->convertException() at /home/vagrant/hive/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php:128
Doctrine\DBAL\DBALException::driverExceptionDuringQuery() at /home/vagrant/hive/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:177
Doctrine\DBAL\Statement->execute() at /home/vagrant/hive/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:281
Doctrine\ORM\Persisters\Entity\BasicEntityPersister->executeInserts() at /home/vagrant/hive/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1014
Doctrine\ORM\UnitOfWork->executeInserts() at /home/vagrant/hive/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:378
Doctrine\ORM\UnitOfWork->commit() at /home/vagrant/hive/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:356
Doctrine\ORM\EntityManager->flush() at /home/vagrant/hive/src/Odev/Hive/Infrastructure/AppBundle/DataFixtures/ORM/LoadProjectData.php:89
Odev\Hive\Infrastructure\AppBundle\DataFixtures\ORM\LoadProjectData->load() at /home/vagrant/hive/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/AbstractExecutor.php:121
Doctrine\Common\DataFixtures\Executor\AbstractExecutor->load() at /home/vagrant/hive/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/ORMExecutor.php:88
Doctrine\Common\DataFixtures\Executor\ORMExecutor->Doctrine\Common\DataFixtures\Executor\{closure}() at n/a:n/a
call_user_func() at /home/vagrant/hive/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:233
Doctrine\ORM\EntityManager->transactional() at /dev/shm/app/cache/dev/appDevDebugProjectContainer.php:5645
DoctrineORMEntityManager_00000000015ce1f5000000002a2c79364ae5d79093a662a969d1540330e84087->transactional() at /home/vagrant/hive/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/ORMExecutor.php:90
Doctrine\Common\DataFixtures\Executor\ORMExecutor->execute() at /home/vagrant/hive/vendor/doctrine/doctrine-fixtures-bundle/Command/LoadDataFixturesDoctrineCommand.php:118
Doctrine\Bundle\FixturesBundle\Command\LoadDataFixturesDoctrineCommand->execute() at /home/vagrant/hive/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php:262
Symfony\Component\Console\Command\Command->run() at /home/vagrant/hive/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:848
Symfony\Component\Console\Application->doRunCommand() at /home/vagrant/hive/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:189
Symfony\Component\Console\Application->doRun() at /home/vagrant/hive/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:80
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /home/vagrant/hive/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:120
Symfony\Component\Console\Application->run() at /home/vagrant/hive/bin/console:29
At this point I am lost - if there is any suggestions of where I should be looking or if anyone else has run into this problem, I would love and guidance that could be provided.
Update
As requested by matteo:
Here is the load method of the fixture that creates the records and throws the error.
/**
* {#inheritDoc}
* #param ObjectManager $manager
*/
public function load(ObjectManager $manager)
{
// Create ODEV Project Section
$section = new ProjectSection(
Uuid::uuid4(),
'ODEV',
'ODEV specific projects'
);
$manager->persist($section);
$this->addReference('section-odev', $section);
// Create Technical Review Project -> public
$project = new Project(
Uuid::uuid4(),
'techreview',
$section,
'Technical Reviews',
'Technical Reviews for Work Requests',
false,
false,
$this->getReference('user-system'),
$this->getReference('user-system'),
'',
$this->getReference('user-system')
);
// VarDumper::dump($project->getWikiHome()->getId());
// VarDumper::dump($project->getCreatedBy()->getId());
$manager->persist($project);
$this->addReference('project-tech-review', $project);
$manager->flush();
}
The two VarDumper::dump() commands were to confirm that the associations were getting created.
The actual WikiPage gets generated in the Project's constructor and the WikiPageVersion is generated in the WikiPages's constructor.
Here is Project's constructor:
public function __construct(
string $id,
string $identifier,
ProjectSection $section,
string $name,
string $description,
bool $private,
bool $sensitive,
User $owner,
User $contact,
string $homepage,
User $createdBy
) {
$this->id = Uuid::fromString($id);
$this->identifier = $identifier;
$this->projectSection = $section;
$this->name = $name;
$this->description = $description;
$this->private = $private;
$this->sensitive = $sensitive;
$this->owner = $owner;
$this->contact = $contact;
$this->homepage = $homepage;
$this->createdBy = $createdBy;
$this->created = new \DateTimeImmutable();
$this->updatedBy = $createdBy;
$this->updated = new \DateTimeImmutable();
$this->archived = false;
$this->workRequest = null;
// set up collections
$this->teamMembers = new ArrayCollection();
$this->issues = new ArrayCollection();
$this->folders = new ArrayCollection($this->defaultFolders());
$this->wikiHome = $this->defaultWikiPage();
$this->wikiPages = new ArrayCollection([$this->wikiHome]);
$this->labels = new ArrayCollection($this->defaultLabels());
$this->milestones = new ArrayCollection($this->defaultMilestones());
}
protected function defaultWikiPage(): WikiPage
{
return new WikiPage(Uuid::uuid4(), $this, $this->name.' Wiki', '', $this->createdBy);
}
And the constructor of WikiPage:
public function __construct(string $id, Project $project, string $title, string $content, User $createdBy)
{
$this->id = Uuid::fromString($id);
$this->project = $project;
$this->title = $title;
$this->content = $content;
$this->created = new \DateTimeImmutable();
$this->createdBy = $createdBy;
$this->currentVersion = $this->createFirstVersion($content, $createdBy);
$this->versions = new ArrayCollection([$this->currentVersion]);
}
protected function createFirstVersion(string $content, User $createdBy)
{
return new WikiPageVersion(Uuid::uuid4(), $this, $content, $createdBy, 'Page Created');
}
Hope that helps.
When WikiPage Entity tries to INSERT it is trying to insert all its properties. Do a check for version and if === null unset the key index. then when the
INSERT fires the parm array is only 4 keys.
I keep getting this error with Doctrine:
PHP Catchable fatal error: Object of class User could not be converted to string in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 1337
In my system users can have many permissions in a One to Many relationship. I have set up a User and Permission entity. They look like this (I removed some annotations, getters and setters to reduce clutter):
class User {
/**
* #ORM\Column(name="user_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
public function getId()
{
return $this->id;
}
/**
* #ORM\OneToMany(targetEntity="Permission", mappedBy="user", cascade={"persist"})
*/
protected $permissions;
public function getPermissions()
{
return $this->permissions;
}
}
class Permission {
/**
* #ORM\Column(name="user_id", type="integer")
* #ORM\ManyToOne(targetEntity="User", inversedBy="permissions")
*/
protected $user;
public function getUser()
{
return $this->user;
}
public function setUser( $user )
{
$this->user = $user;
return $this;
}
}
The problem occurs when I add a new Permission to a User:
$permission = new Permission();
$user->getPermissions()->add( $permission );
$em->persist( $user );
$em->flush();
This is the last bit of my stack trace:
PHP 11. Doctrine\ORM\UnitOfWork->persist() vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:565
PHP 12. Doctrine\ORM\UnitOfWork->doPersist() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1555
PHP 13. Doctrine\ORM\UnitOfWork->cascadePersist() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1615
PHP 14. Doctrine\ORM\UnitOfWork->doPersist() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:2169
PHP 15. Doctrine\ORM\UnitOfWork->persistNew() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1597
PHP 16. Doctrine\ORM\UnitOfWork->scheduleForInsert() doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:836
PHP 17. Doctrine\ORM\UnitOfWork->addToIdentityMap() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1157
PHP 18. implode() vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1337
Any insight would be greatly appreciated.
OK. I've got it working.
I haven't fully worked out the reason yet but when I add the following to my User entity it works:
class User {
public function __toString()
{
return strval( $this->getId() );
}
}
If I find out more I will post here.
Your solution gave me a clue of what is happening.
Even though you have the entities and the anotations, Doctrine is not being able to understand the relation between entities. When doctrine understands the relation between entities, it knows what methods to call (ie User::getId()) but otherwise, it tries to transform whatever you are sending to a scalar value that it can use to query the database. Thats why it is calling the __toString function of the User, and thats why if you return the id in toString, everything works from here.
This is ok, but its a patch, and probably you dont want to keep it if we can find a better solution, since it could be harder to maintain as your application grows.
What i can see, is that in Permissions you have:
/**
* #ORM\Column(name="user_id", type="integer")
* #ORM\ManyToOne(targetEntity="User", inversedBy="permissions")
*/
protected $user;
You should remove the #ORM\Column(type="integer")
About the join columns, it is not mandatory, but you have to be sure that the defauts, are what you want. As we can read here
Before we introduce all the association mappings in detail, you should
note that the #JoinColumn and #JoinTable definitions are usually
optional and have sensible default values. The defaults for a join
column in a one-to-one/many-to-one association is as follows:
name: "<fieldname>_id"
referencedColumnName: "id"
so, they will be the same as an explicit:
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="permissions", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
protected $user;
So it is supposed to look for a column user_id in the Permissions table, and join it with the id column of the User table. We suppose that this is ok.
If this is true, then in your User, the id shouldnt be user_id, but id:
/**
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
Or if the column name is actually user_id, then the User class is ok, but you have to change the join column to #ORM\JoinColumn(name="user_id", referencedColumnName="user_id")
That much i can say. I cannot try it know, but i will be glad if you can give it a second.
I think there's a problem with the mapping of user property in permission entity. Try this one:
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="permissions")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
are you initializing the collection in your OneToMany side?
and also, the methods to add and remove from the collection?
class User {
/**
* #ORM\OneToMany(targetEntity="Permission", mappedBy="user", cascade={"persist"})
*/
protected $permissions;
public function getPermissions()
{
return $this->permissions;
}
public function __construct()
{
$this->permissions = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addPermissions (Permission $permissions)
{
$this->permissions[] = $permissions;
return $this;
}
public function removePermissions(Permission $permissions)
{
$this->permissions->removeElement($permissions);
}
//...
I do not understad why with some Entity objects I can set the Id and for others objects I get an error and says me that the Id can't be null and I have to pass an object instead.
e.g.:
$log = new Log();
$log->setTypeId(1);
$log->setUserId(1);
$entityManager->persist($log);
$entityManager->flush();
If I try the code above I get error that says: Integrity constraint violation: 1048 Column 'user_id' cannot be null. And I have to first create the Type Object and de User object and the pass them:
$log->setType($TypeObject)
$log->setUser($UserObject)
But for other entity objects I have no problem assigning the value directly, why is that?
This is my Entity Log:
<?php
/**
* #Entity
* #Table(name="log")
* #HasLifecycleCallbacks
*/
class Log
{
/**
* #var type
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
*
* #var type
* #Column(type="integer")
*/
protected $user_id;
/**
*
* #var type
* #Column(type="integer")
*/
protected $type_id;
/**
*
* #var type
* #Column(type="datetime")
*/
protected $created;
/**
*
* #var type
* #ManyToOne(targetEntity="User", inversedBy="logs")
*/
protected $user;
/**
*
* #ManyToOne(targetEntity="Type", inversedBy="logs")
*/
protected $type;
public function getId()
{
return $this->id;
}
public function getUserId()
{
return $this->user_id;
}
public function getTypeId()
{
return $this->type_id;
}
public function getCreated()
{
return $this->created;
}
public function setUserId($userId)
{
$this->user_id = $userId;
}
public function setTypeId($typeId)
{
$this->type_id = $typeId;
}
public function setCreated($created)
{
$this->created = $created;
}
public function setUser($user)
{
$this->user = $user;
}
public function setType($type)
{
$this->type = $type;
}
/**
* #PrePersist
*/
public function prePersist()
{
$this->setCreated(new DateTime());
}
}
?>
The existing answer never did sit well with me. There are many valid scenarios where loading an object just to define the relationship while already having the FK handy just does not make any sense at all.
A better solution is to use Doctrine's EntityManager's getRefrence method.
Reference Proxies...
The method EntityManager#getReference($entityName, $identifier) lets
you obtain a reference to an entity for which the identifier is known,
without loading that entity from the database. This is useful, for
example, as a performance enhancement, when you want to establish an
association to an entity for which you have the identifier. You could
simply do this:
<?php
// $em instanceof EntityManager, $cart instanceof MyProject\Model\Cart
// $itemId comes from somewhere, probably a request parameter
$item = $em->getReference(\MyProject\Model\Item::class, $itemId);
$cart->addItem($item);
Maybe this was not available when this question was first posted - I don't know.
EDIT
I found this statement on the website of Doctrine2. It's a best practice that you might want to follow when coding your models.
Doctrine2 Best Practices
25.9. Don’t map foreign keys to fields in an entity
Foreign keys have no meaning whatsoever in an object model. Foreign keys are how a relational database establishes relationships. Your object model establishes relationships through object references. Thus mapping foreign keys to object fields heavily leaks details of the relational model into the object model, something you really should not do
EDIT
Doctrine does the mapping from your objects to their respective Ids.
What you've done here is a bit redundant.
You've essentially told doctrine the same thing twice.
You've told it that it has a 'user_id' column AND that it also has a User object, which are the same thing. But doctrine can already guess that this relationship will have a user_id column based on the fact that the log class has a user object inside.
You should simply do the following instead
<?php
/**
* #Entity
* #Table(name="log")
* #HasLifecycleCallbacks
*/
class Log
{
/**
* #var type
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
*
* #var type
* #Column(type="datetime")
*/
protected $created;
/**
*
* #var type
* #ManyToOne(targetEntity="User", inversedBy="logs")
*/
protected $user;
/**
*
* #ManyToOne(targetEntity="Type", inversedBy="logs")
*/
protected $type;
public function getId()
{
return $this->id;
}
public function getCreated()
{
return $this->created;
}
public function setCreated($created)
{
$this->created = $created;
}
public function setUser($user)
{
$this->user = $user;
}
public function setType($type)
{
$this->type = $type;
}
/**
* #PrePersist
*/
public function prePersist()
{
$this->setCreated(new DateTime());
}
}
Doctrine will worry about the user_id and type_id on it's own. You don't have to worry about it. This way you get to work with full fledged objects, making it easier to program, instead of having to worry about id's. Doctrine will handle that.
If ALL you have is an id, because that's what you're using on the front end, then just fetch the object associated with that id using the Entitymanager.
$user = $em->getEntity( 'User', $idFromWeb );
$log = new Log();
$log->setUser( $user );
I have created a form that appears to be correct, it has a few text fields and a select box with a list of countries pulled from a table of countries I have. The select box displays correctly using the the correct values for it's 'value' and display text. When I submit the form however I get an exception:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'countryid' cannot be null
If I set the database table (in PHPMyAdmin) to allow a null value for the countryid field it enters the record with no exception but the entry for the countryid is null.
my controller has the following code:
$duck = new \Wfuk\DuckBundle\Entity\Ducks();
$form = $this->createFormBuilder($duck)
->add('city', 'text')
->add('countryid', 'entity', array('class' => 'WfukDuckBundle:Country', 'property' => 'country'))
// cut other fields
->getForm();
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
$errors = $this->get('validator')->validate( $form );
echo $duck->getCountryid();
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($duck);
$em->flush();
return $this->redirect($this->generateUrl('upload_duck_success'));
}
the echo in there returns the __toString function of the country object which seems a bit odd - but it is the full country info for the country chosen in the form.
in the Ducks.php class:
/**
* #var string $countryid
*
* #ORM\Column(name="countryid", type="string", length=2, nullable=false)
*/
private $countryid;
/**
* Set countryid
*
* #param string $countryId
*/
public function setCountryid($countryid)
{
$this->countryid = $countryid;
}
/**
* Get countryid
*
* #return string
*/
public function getCountryid()
{
return $this->countryid;
}
This is my first symfony project, but I've been over the docs several times and think I have everything set up ok...
edit:
I have a join set up as follows:
Ducks.php
/**
* #ORM\ManyToOne(targetEntity="Country", inversedBy="ducks")
* #ORM\JoinColumn(name="countryid", referencedColumnName="id")
*/
private $country;
/**
* Set country
*
* #param string $country
*/
public function setCountry($country)
{
$this->country = $country;
}
/**
* Get country
*
* #return string
*/
public function getCountry()
{
return $this->country;
}
and on the Country.php side:
/**
* #ORM\OneToMany(targetEntity="Ducks", mappedBy="country")
*/
protected $ducks;
public function __construct()
{
$this->ducks = new ArrayCollection();
}
/**
* Get ducks
*
* #return Doctrine\Common\Collections\Collection
*/
public function getDucks()
{
return $this->ducks;
}
What's happening is that the form is sending an actual Country object to ducks. You can confirm this with:
public function setCountryid($countryid)
{
if (is_object($countryid)) die('Yep, got a country object.');
$this->countryid = $countryid;
}
It sounds like you only want to store a 2 char country code? You don't want an actual relation? If so then this might do the trick:
public function setCountryid($countryid)
{
if (is_object($countryid)) $countryid = $countryid->getId();
$this->countryid = $countryid;
}
If you want an actual normal Doctrine managed relation between duck and country then something like:
/**
* #ORM\ManyToOne(targetEntity="Country")
* #ORM\JoinColumn(name="country_id", referencedColumnName="id")
*/
*/
private $country;
And adjust your getter/setters accordingly.
It's a bit strange that you seem to have both yml and annotations. From what I understood, you could use one or the other in a given bundle.