I have two entities Entity1 and Entity2 with a OneToMany relation, but they live in two MySQL databases.
How can I implement those entities with their relation in Symfony?
Is it possible to create two separated bundles where to implement those entities?
In Doctrine, joining data across databases is not technically “supported” by a designed feature, but you can make it work by tricking Doctrine a little bit.
If you want to build a relationship between entities then they must use the same connection: same database.
The key to getting multiple databases to work is within your entity classes, you need to specify the table name of the entity with a prefix of the name of the database to which the table belongs. Here is an example using annotations:
<?php
namespace Demo\UserBundle\Entity;
use DoctrineORMMapping as ORM;
/**
* Demo\UserBundle\Entity\User
*
* #ORMTable(name="users.User")
*/
class User implements
{
/* ... */
}
and
<?php
namespace Demo\PostBundle\Entity;
use DoctrineORMMapping as ORM;
/**
* Demo\PostBundle\Entity\Post
*
* #ORMTable(name="posts.Post")
*/
class Post implements
{
/* ... */
}
and the relation table:
<?php
namespace Demo\PostBundle\Entity;
use DoctrineORMMapping as ORM;
/**
* Demo\PostBundle\Entity\Post
*
* #ORMTable(name="posts.Post")
*/
class Post implements
{
/**
* #ORM\ManyToOne(targetEntity="\Demo\UserBundle\Entity\User")
**/
private $user;
/* ... */
/**
* Set user
*
* #param \Demo\UserBundle\Entity\Site $site
* #return Post
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \Demo\UserBundle\Entity\Site
*/
public function getUser()
{
return $this->user;
}
}
Here an article about it.
Hope this help
Related
I have entities that make use of Inheritance Mapping Doctrine inheritance. I have a custom identifier that I generate with #ORM\PrePersist(), which is in a trait and this is used in the parent class.
I want to be able to update properties that the child class has, for this reason, I need to run endpoints on the child entity
When I run an item operation, api platform can't find the resource.
PATCH /api/childas/{hash}
NotFoundHttpException
Not Found
api platform, it doesn't recognize hash as identifier. Take the id as your identified, even if it is false and hash is true.
Trait to generate hashes with which I identify the resource
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiProperty;
use Doctrine\ORM\Mapping as ORM;
trait HashableTrait
{
/**
* #ORM\Column(type="string", length=255)
* #ApiProperty(identifier=true)
*/
private $hash;
public function getHash(): ?string
{
return $this->hash;
}
/**
* #ORM\PrePersist()
*/
public function setHash()
{
$this->hash = \sha1(\random_bytes(10));
}
}
Parent class, is the table where the hash will be stored
<?php
namespace App\Entity;
use App\Entity\HashableTrait;
/**
* #ORM\HasLifecycleCallbacks()
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="integer")
* #ORM\DiscriminatorMap({
* 1 = "App\Entity\ChildA",
* 2 = "App\Entity\ChildB"
* })
*/
class Parent
{
use HashableTrait;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #ApiProperty(identifier=false)
*/
private $id;
public function getId(): ?int
{
return $this->id;
}
// Properties, setters, getters
}
Child class, on which I want to perform operations, such as updating some property that belongs to this class
<?php
namespace App\Entity;
class ChildA extends Parent
{
// Custom properties for ChildA
}
Config api platform operations for entity Child
App\Entity\ChildA:
collectionOperations:
post: ~
itemOperations:
post: ~
get: ~
patch: ~
delete: ~
I have thought about using data providers, but I keep getting the error.
The error was because both the hash property in the trait and the id property in the parent entity must be accessible from the entity to use.
Doctrine ORM uses reflection class to get information about attributes and their annotations. ReflectionClass::hasProperty obviously does not allow viewing private properties in the parent class.
I'm facing an issue concerning OneToOne relation between some Entities.
The subtlety is that the owner side can be multiple classes (all children of the same class).
Here is the summary
I've got Process which have several children entity classes (let's take ProcessPassport, ProcessIdentityCard and ProcessCadaster for example)
Some of my children entities need a relation with another entity TaxStamp, but not all of them. So I cannot put this association in the Process main entity.
Instead I created a trait TaxStampTrait, containing my relation mapping.
But this does not work properly :
I cannot define multiple possibilities for the association on the TaxStamp entity
Doctrine tells me there are error in my annotations for the relation, because I refer to the Process (legit since only some entities use the TaxStampTrait)
Question: what should I do to make it all work the right way?
Here is the gist with a summary of the code and classes: https://gist.github.com/bastos71/8e15f69ebecf5e97dc75187d130fe109
<?php
/**
* #ORM\Entity(repositoryClass="ProcessRepository")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
*/
class Process
{
// ...
}
<?php
/**
* #ORM\Entity(repositoryClass="ProcessCadasterRepository")
*/
class ProcessCadaster extends Process implements EntityWithTaxStampInterface
{
use TaxStampTrait;
// ...
}
<?php
/**
* #ORM\Entity(repositoryClass="ProcessIdentityCardRepository")
*/
class ProcessIdentityCard extends Process implements EntityWithTaxStampInterface
{
use TaxStampTrait;
// ...
}
<?php
/**
* #ORM\Entity(repositoryClass="ProcessPassportRepository")
*/
class ProcessPassport extends Process implements EntityWithTaxStampInterface
{
use TaxStampTrait;
// ...
}
<?php
/**
* #ORM\Entity(repositoryClass="TaxStampRepository")
*/
class TaxStamp
{
/**
* #ORM\OneToOne(targetEntity="Advercity\AdminBundle\Entity\Process", mappedBy="taxStamp")
*/
private $process;
// ...
}
<?php
trait TaxStampTrait
{
/**
* #ORM\OneToOne(targetEntity="TaxStamp", cascade={"persist"})
* #ORM\JoinColumn(name="tax_stamp_id", referencedColumnName="id")
*/
private $taxStamp;
// ...
}
I have two tables named jobs and attachments.A job may or may not have one or more than one attachments.I have created one to may relation with job and attachment.But when I trying to persist it gives me an error,
A new entity was found through the relationship 'AppBundle\Entity\JotJobs#attachments' that was not configured to cascade persist operations for entity: AppBundle\Entity\JotJobAttachments#000000004d40cceb00000000fe114bdc. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'AppBundle\Entity\JotJobAttachments#__toString()' to get a clue.
Then I have tried to set cascade persist in jobs entity, after that it always asking for a mandatory attachment for each jobs.Otherwise it will gives an error with job_id can't be null in attachment table.I were trying to correct it for the last few hours.Please help.
My entities are,
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* JotJobs
*
* #ORM\Table(name="jot_jobs")
* #ORM\Entity
*/
class JotJobs
{
/**
* #var \JotJobAttachments
*
* #ORM\OneToMany(targetEntity="JotJobAttachments" ,mappedBy="jotJobs")
* #ORM\JoinColumn(name="ID", referencedColumnName="job_id")
*/
private $attachments;
/**
* Constructor
*/
public function __construct()
{
$this->attachments = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get subTechnologies
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getSubTechnologies()
{
return $this->subTechnologies;
}
/**
* Add attachments
*
* #param \AppBundle\Entity\JotJobAttachments $attachments
* #return JotJobs
*/
public function addAttachment(\AppBundle\Entity\JotJobAttachments $attachments=null)
{
$this->attachments[] = $attachments;
return $this;
}
/**
* Remove attachments
*
* #param \AppBundle\Entity\JotJobAttachments $attachments
*/
public function removeAttachment(\AppBundle\Entity\JotJobAttachments $attachments)
{
$this->attachments->removeElement($attachments);
}
}
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* JotJobAttachments
*
* #ORM\Table(name="jot_job_attachments")
* #ORM\Entity
*/
class JotJobAttachments
{
/**
* #var \JotJobs
*
* #ORM\ManyToOne(targetEntity="JotJobs", inversedBy="attachments")
* #ORM\JoinColumn(name="job_id", referencedColumnName="ID", nullable=true)
*/
private $jotJobs;
/**
* Set jotJobs
*
* #param \AppBundle\Entity\JotJobs $jotJobs
* #return JotJobAttachments
*/
public function setJotJobs(\AppBundle\Entity\JotJobs $jotJobs = null)
{
$this->jotJobs = $jotJobs;
return $this;
}
/**
* Get jotJobs
*
* #return \AppBundle\Entity\JotJobs
*/
public function getJotJobs()
{
return $this->jotJobs;
}
}
In my controller,
$newJob = new JotJobs();
$newJob->setJobName($data->getJobName());
.
.
.
$attachments = $data->getAttachments();
$jobDir = $this->container->getParameter('uploads_directory').'/jobs';
foreach ($attachments as $key => $value) {
if($value->getAttachment()!=null)
{
/** #var Symfony\Component\HttpFoundation\File\UploadedFile $file */
$file = $value->getAttachment();
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($jobDir, $fileName);
$jobAttachment = new JotJobAttachments();
$jobAttachment->setAttachment($fileName);
$jobAttachment->setAttachmentName($file->getClientOriginalName());
$newJob->addAttachment($jobAttachment);
}
}
$entityManager->persist($newJob);
$entityManager->flush();
$lId = $newJob->getId();
You have two things going on here.
The first, as mentioned before, is that you need cascade={"all"} on your OneToMany relation. Use all instead of persist snce if you delete a job you almost certainly want the attachments to be deleted as well.
The second is that you need to set the job reference in your attachment. That is why you getting those null errors.
public function addAttachment(\AppBundle\Entity\JotJobAttachment $attachment=null)
{
$this->attachments[] = $attachment;
$attachment->setJotJob($this); // ADD THIS
return $this;
}
You might also consider changing thing like JotJobAttachments to JotJobAttachment. Makes your code easier to understand.
And don't pay much attention to the down voters. This cross referencing requirement catches many developers and is not easy to search for.
I trying to create CRUD panel from FOSUserBundle but i have some troubles. I mean that i created User entity for FOS and made crud panel for this entity. Now when i trying to add new user i have error like below
Neither the property "expiresAt" nor one of the methods "getExpiresAt()", "isExpiresAt()", "hasExpiresAt()", "_get()" or "_call()" exist and have public access in class "Bn\UserBundle\Entity\User".
It's my first project so please understand when i will ask for simple function, some suggestion ? What is wrong ?
<?php
namespace Bn\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*
* #ORM\Table(name="fos_user")
* #ORM\Entity
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* Get expiresAt
*
* #return \DateTime
*/
public function getExpiresAt()
{
return $this->expiresAt;
}
/**
* Get credentials_expire_at
*
* #return \DateTime
*/
public function getCredentialsExpireAt()
{
return $this->credentialsExpireAt;
}
public function __construct()
{
parent::__construct();
// your own logic
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
Now is working but i don't know why i must declare again function for getter.
I believe this means you need to add public accessors setExpiresAt() and getExpiresAt() to your User entity.
You need only add getExpiresAt to your User.php class. FOSUserBundle\User doesn't have getter for this field, but Sensio generator creates views for all fields.
public function getExpiresAt()
{
return $this->expiresAt;
}
Hi i have that same question as here: Many-to-many self relation with extra fields? but i cant find an answer :/ I tried first ManyToOne and at the other site OneToMany ... but then i could not use something like
public function hasFriend(User $user)
{
return $this->myFriends->contains($user);
}
because there was some this problem:
This function is called, taking a User type $user variable and you then use the contains() function on $this->myFriends.
$this->myFriends is an ArrayCollection of Requests (so different type than User) and from the doctrine documentation about contains():
The comparison of two elements is strict, that means not only the value but also the type must match.
So what is the best way to solve this ManyToMany relationship with extra fields? Or if i would go back and set the onetomany and manytoone relationship how can i modify the hasFriend method? To example check if ID is in array collection of ID's.
EDIT: i have this table... and what i need is:
1. select my friends... and my followers ...check if i am friend with him or not. (because he can be friend with me and i dont have to be with him... like on twitter). I could make manytomany but i need extra fields like: "viewed" "time when he subscribe me" as you can see at my table.
And make query like this and then be able in twig check if (app.user.hasFriend(follower) or something like that)
$qb = $this->createQueryBuilder('r')
->select('u')
->innerJoin('UserBundle:User', 'u')
->Where('r.friend_id=:id')
->setParameter('id', $id)
->orderBy('r.time', 'DESC')
->setMaxResults(50);
return $qb->getQuery()
->getResult();
I was trying to have a many to many relationship with extra fields, and couldn't make it work either... The thing I read in a forum (can't remember where) was:
If you add data to a relationship, then it's not a relationship anymore. It's a new entity.
And it's the right thing to do. Create a new entity with the new fields, and if you need it, create a custom repository to add the methods you need.
A <--- Many to many with field ---> B
would become
A --One to many--> C (with new fields) <-- One to many--B
and of course, C has ManyToOne relationships with both A and B.
I searched everywhere on how to do this, but in the end, it's the right thing to do, if you add data, it's no longer a relationship.
You can also copy what contains usually do, or try to overwrite it in a custom repository, to do whatever you need it to do.
I hope this helps.
I'm adding another answer since it has nothing to do with my original answer. Using the new info you posted, I'm calling the table/entity you posted "Follower". The original entity, "User".
What happens if you create the following associations:
namespace Acme\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\UserBundle\Entity\User
*
* #ORM\Table()
* #ORM\Entity
*/
class User
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Acme\FollowerBundle\Entity\Follower", mappedBy="followeduser")
*/
protected $followers;
/**
* #ORM\OneToMany(targetEntity="Acme\FollowerBundle\Entity\Follower", mappedBy="followeeuser")
*/
protected $followees;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function __construct()
{
$this->followers = new \Doctrine\Common\Collections\ArrayCollection();
$this->followees = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add followers
*
* #param Acme\FollowerBundle\Entity\Follower $follower
*/
public function addFollower(\Acme\FollowerBundle\Entity\Follower $follower)
{
$this->followers[] = $follower;
}
/**
* Add followees
*
* #param Acme\FollowerBundle\Entity\Follower $followee
*/
public function addFollowee(\Acme\FollowerBundle\Entity\Follower $followee)
{
$this->followees[] = $followee;
}
/**
* Get followers
*
* #return Doctrine\Common\Collections\Collection
*/
public function getFollowers()
{
return $this->followers;
}
/**
* Get followees
*
* #return Doctrine\Common\Collections\Collection
*/
public function getFollowees()
{
return $this->followees;
}
}
namespace Acme\FollowerBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\FollowerBundle\Entity\Follower
*
* #ORM\Table()
* #ORM\Entity
*/
class Follower
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="followers")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $followeduser;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="followees")
* #ORM\JoinColumn(name="followee_id", referencedColumnName="id")
*/
protected $followeeuser;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set followeduser
*
* #param Acme\UserBundle\Entity\User $followeduser
*/
public function setFolloweduser(\Acme\UserBundle\Entity\User $followeduser)
{
$this->followeduser = $followeduser;
}
/**
* Get followeduser
*
* #return Acme\UserBundle\Entity\User
*/
public function getFolloweduser()
{
return $this->followeduser;
}
/**
* Set followeeuser
*
* #param Acme\UserBundle\Entity\User $followeeuser
*/
public function setFolloweeuser(\Acme\UserBundle\Entity\User $followeeuser)
{
$this->followeeuser = $followeeuser;
}
/**
* Get followeeuser
*
* #return Acme\UserBundle\Entity\User
*/
public function getFolloweeuser()
{
return $this->followeeuser;
}
}
I'm not sure if this would do the trick, I really don't have much time to test it, but if it doesn't, I thnk that it's on it's way. I'm using two relations, because you don't need a many to many. You need to reference that a user can have a lot of followers, and a follower can follow a lot of users, but since the "user" table is the same one, I did two relations, they have nothing to do with eachother, they just reference the same entity but for different things.
Try that and experiment what happens. You should be able to do things like:
$user->getFollowers();
$follower->getFollowedUser();
and you could then check if a user is being followed by a follower whose user_id equals $userThatIwantToCheck
and you could search in Followers for a Follower whose user = $user and followeduser=$possibleFriend