doctrine 2 ODM preventing duplicate record - php

Doctrine NOOB here, trying to figure out how to prevent a duplicate record in an embed many property. I have a EmbededDocment like this:
<?
/**
* #EmbeddedDocument
*/
class Contact {
/**
* #Id
*/
private $id;
/**
* created timestamp
* #Date
*/
private $created;
/**
* modified timestamp
* #Date
*/
private $modified;
/**
* #String
*/
private $name;
/**
* #String
*/
private $name;
/**
* #String
*/
private $address;
}
what I want to happen is when I add a new contact, two contacts can have the same name, two contacts can have the same address, but two contacts can not have the same name and address. When checking for duplicates, doctrine will need to ignore the $id, $created and $modified properties as these will almost always be distinct. It is the combination of all the other fields that must be unique. How can this be accomplished using doctrine? Does this logic belong in a service layer or can doctrine do it for me?
UPDATE:
I do accept that Andrew's answer is the correct way to check for duplication using Mongo, I really want to know if doctrine can do this for me. Therefore, I'm starting a bounty.

You could implement an event listener which will listen to an preUpdate and prePersist event.
http://www.doctrine-project.org/docs/mongodb_odm/1.0/en/reference/events.html
In your event, you can do your own check.

You should validate your document before save it.
For example if user adding Contact with name="Name" and address="Address" you shoud check in mongodb if such Contact exists. And in case if it exists you just showing validation message, otherwise you adding contact to embedded contacts array.
So, suppose you have collection of users that's contains embedded array of contacts. To verify that new contact exists/not exists you can send request like this:
db.users.find({ userId: "userId" ,
contacts.name: "new contact name",
contacts.address: "new contact address"}).count();
If above query will return count >= 1 you no need add new contact, just show validation.

Related

Symfony3.4 - collection reference same entity field lose linked association

Sorry about the title, it's quite hard to describe it with just a few words. Here's the problem:
I have a customer entity which has a OneToMany with Website and customerTrackingIds :
class Customer {
...
/**
* #var ArrayCollection
* #Serializer\Exclude()
* #ORM\OneToMany(targetEntity="AppBundle\Entity\WebSite", mappedBy="customer",cascade={"persist","remove"},orphanRemoval=true)
*/
private $webSites;
/**
* #var ArrayCollection
* #Serializer\Exclude()
* #ORM\OneToMany(targetEntity="AppBundle\Entity\CustomerTrackingId", mappedBy="customer",cascade={"persist","remove"})
*/
private $customerTrackingIds;
...
}
And Website:
class WebSite {
/**
* #var Customer
* #Serializer\MaxDepth(1)
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Customer", inversedBy="webSites")
* #ORM\JoinColumn(nullable=true, onDelete="SET NULL")
*/
private $customer;
/**
* #var ArrayCollection
* #Serializer\Exclude()
* #ORM\OneToMany(targetEntity="AppBundle\Entity\CustomerTrackingId", mappedBy="website")
*/
private $customerTrackingIds;
}
Customer has multiple website and multiple trackingIds. Each tracking ID is associated with one of the customer's website, but more trackingIds can exist for the same Website.
I use https://github.com/ninsuo/symfony-collection to handle the collection of website/trackingIds in the same page but when i go edit the information something really weird (at least for me) happens.
Looking in my debugger session i saw that when the form loads data from submit in my controller:
$formCustomer = $this->createForm( CustomerFormType::class, $customer );
$formCustomer->handleRequest( $request );
if ( $formCustomer->isSubmitted() && $formCustomer->isValid() ) {
$em = $this->getDoctrine()->getManager();
$em->persist( $customer );
$em->flush();
When the request is handled all of the elements of TrackingIds has their website correctly set, but inside the website the customer link gets lost (becomes null).
What happens next is that when all the data get persisted i lost in my database the phisical association between website <-> customer (customer becomes null)
How can i fix this?
If you will ever land here i solved the issue simply double-checking my website collection form type.
Since using https://github.com/ninsuo/symfony-collection i created a new FormTheme to render the collection add/delete stuff in my page with just one field for the website collection which was the website name.
Anyways, inside my website collection formType i had TWO fields, name AND, guess what, CUSTOMER.
Since the FormTheme was rendering just the name field, the other one becomes null automatically, so when it was handled by the controller he was actually doing that right.

Symfony 4 : Doctrine migrate with manyToOne join

I trying Symfony 4 for "fun". And for that I try to rewrite an old website without framework as the moment, with Symfony 4.
For that I configure my app on my database with existing datas. And I'd make login form.
BUT on my User classe, I have some colomn who make me some issues.
See bellow my user's class's annotations:
/**
* Utilisateur
*
* #ORM\Table(name="utilisateur", uniqueConstraints={#ORM\UniqueConstraint(name="mail", columns={"mail"})}, indexes={#ORM\Index(name="FK_UTILISATEUR_idDroit", columns={"idDroit"})})
* #ORM\Entity
*/
and my user's struct :
idutilisateur
nom
prenom
mail
password
dateinscription
datevalidation
token
iddroit
plainPassword
Like you can see, I create before iddroit as foreign key of droit's table.
Doctrine generated the propertie as
/**
* #var Droit
*
* #ORM\ManyToOne(targetEntity="Droit")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="idDroit", referencedColumnName="idDroit")
* })
*/
private $iddroit;
And created the getter as
public function getIddroit(): Droit
{
return $this->iddroit;
}
And after followed the documentation about registration / login, the debugger respond =>
Return value of App\Entity\Utilisateur::getIddroit() must be an
instance of App\Entity\Droit, null returned
So I imagine he want an object and not just an ID, even if in anotation it's making the Join rules. Any ID what's happened ?
And I understand to return an objet, but I have no idea how to return that.
If any suggestion.
Thanks guys ;)
Laurent

Doctrine Results Missing Entity

I've been trying to figure this out for a while now. Let's start with the basic information, I have a client table and a contact table. The client table has a OneToMany and OneToOne relation with contact
class Client
{
/**
* #var int
* #Id
* #Column(type="integer", nullable=false, unique=true, options={"comment":"Auto incrementing client_id of each client"})
* #GeneratedValue
*/
protected $pid;
/**
* #OneToMany(targetEntity="Contact", mappedBy="client")
* #JoinColumn(name="contact_id", referencedColumnName="pid")
* #var Contact[]
*/
protected $contact;
/**
* #OneToOne(targetEntity="Contact")
* #JoinColumn(name="defaultcontact_id", referencedColumnName="pid", nullable=true)
* #var Contact
*/
protected $default_contact;
The contact table has a ManyToOne relation with Client:
class Contact
{
/**
* #var int
* #Id
* #Column(type="integer", nullable=false, unique=true, options={"comment":"Auto incrementing user_id of each user"})
* #GeneratedValue
*/
protected $pid;
/**
* #ManyToOne(targetEntity="Client", inversedBy="contact")
* #JoinColumn(name="client_id", referencedColumnName="pid")
*/
protected $client;
Here's the query that I've been using:
$qb = $entityManager->createQueryBuilder();
$qb->select("cn as contact", "cl as client")
->from('DB\Contact', 'cn')
->innerJoin('cn.client', 'cl')
->where(
$qb->expr()->andX(
$qb->expr()->eq('cl.client_name', '?1'),
$qb->expr()->eq('cn.pid', '?2')
)
)
->setParameter(1, $client)
->setParameter(2, $contact);
try
{
$result = $qb->getQuery()->getOneOrNullResult();
}
I want both the contact and the client. And this is where I'm having problems: array_keys($result) ends up outputting:
Array
(
[0] => contact
)
I wanted something like this:
[0] => contact
[1] => client
In other words, the Client Entity is missing. Flipping the SELECT FROM from the Contact to the Client repository yielded the reverse situation, contact was missing.
I've checked over the previous code, while entityManager was reused from the login step, this is the first time the Client and Contact repository are accessed so I don't believe it's a caching problem.
Here's the SQL statement being executed:
Executing SQL:
SELECT c0_.pid AS pid0, c0_.caller AS caller1, c0_.address_1 AS address_12, c0_.address_2 AS address_23,
c0_.unit AS unit4, c0_.city AS city5, c0_.state AS state6, c0_.zip_code AS zip_code7, c0_.phone AS phone8,
c0_.email AS email9, c0_.is_active AS is_active10, c0_.date_created AS date_created11,
c0_.date_last_modified AS date_last_modified12, c1_.pid AS pid13, c1_.client_name AS client_name14,
c1_.is_active AS is_active15, c1_.date_created AS date_created16, c1_.date_last_modified AS date_last_modified17,
c0_.client_id AS client_id18, c0_.created_by_id AS created_by_id19, c0_.last_modified_by_id AS last_modified_by_id20,
c1_.defaultcontact_id AS defaultcontact_id21, c1_.created_by_id AS created_by_id22,
c1_.last_modified_by_id AS last_modified_by_id23
FROM contacts c0_
INNER JOIN clients c1_ ON c0_.client_id = c1_.pid
WHERE c1_.client_name= ? AND c0_.pid = ?
As a sidenote, if I alter the select so that the missing entity accesses a specific column, I'll get the desired values.
e.g.
$qb->select("cn as contact", "cl.pid as client")
->from('RGAServ\DB\Contact', 'cn')
will have the following array_keys($result):
Array
(
[0] => contact
[1] => client
)
So I can assure you that the client does exist in the database and it should be properly attached to the contact, it's just that under the first select statement where I want the whole entity and not just one column, the entity ends up not being pushed into the result array.
Why is this? Are there too many columns in the Sql statement? Am I forgetting something in the annotations?
First: You can't have different entities in the resulting array: every row in the result must be in the same format.
Second: If you examine your SQL query closely you'll notice that the one row that is returned contains both the contact (c0_) and the client (c1_).
Try executing the SQL query in the database to see the result.
After looking through a bunch of stack overflow questions, I've come to the following conclusion: It looks like this is how doctrine handles "Fetch Joins".
The big clue comes from here:
Doctrine Regular vs Fetch join
With supporting evidence for this behavior's existence coming from:
Doctrine - entities not being fetched
Doctrine join bypass lazy loading
Doctrine2 query with select on multiple entities from different Symfony2 bundles
More specifically, this quote from the doctrine documentation starts to make sense (http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html#joins):
When Doctrine hydrates a query with fetch-join it returns the class in
the FROM clause on the root level of the result array. In the previous
example an array of User instances is returned and the address of each
user is fetched and hydrated into the User#address variable. If you
access the address Doctrine does not need to lazy load the association
with another query.
In layman's terms, during a FETCH JOIN, contact (or client if I'm SELECTing FROM the client repository) is designated as the root entity. Whatever is found for Client will then be pushed into the contact's $client variable to be retrieved afterwards using a getter accessor.
The retrieval itself will not need a follow-up database query to fetch the client entity. I'll need to do a little testing, but it looks like this behavior was for the situation when multiple results are returned during the join. Instead of cluttering up the results, they're organized under an intuitive location.
In other words, I had the wrong expectations and was looking in the wrong spot. The client entity did indeed come back, but it wasn't placed in results. It was filed under contact. Retrieving it separately is, therefore, a given but at least it won't need another database call.
At least now, I believe I know why when I had Client in the from field, I was getting one specific contact instead of all of them when I tried to use the getContact() accessor.

Doctrine2 Check if related entity exists

I'm really tired to figuring out how I can check in Doctrine 2 if related entity record exists in DB. Help me please.
For example I have two entities. One is the order status of certain delivery company. Another one is order.
Order.php
/**
* #ORM\OneToOne(targetEntity="Application\DeliveryBundle\Entity\DpdOrderStatus", mappedBy="order")
* #var DpdOrderStatus
*/
$dpdOrderStatus;
DpdOrderStatus.php
/**
* #ORM\Id
* #ORM\OneToOne(targetEntity="\Application\FrontendBundle\Entity\Order", inversedBy="dpdOrderStatus")
* #ORM\JoinColumn(onDelete="CASCADE")
* #var Order
*/
$order;
Order entity sometimes doesn't have status and I need to check if it has.
AFAIK if I will try to use is_null($order->getDpdOrderStatus()) it will always be false because Doctrine always create Proxy objects for its entities if EAGER mode is not specified.
So what is the most proper way to check if my status entity exists in database?
Add a method that checks if the order has an order status:
Order.php
public function hasOrderStatus(){
return ! is_null($this->dpdOrderStatus);
}
More information: Techniques to check if relationship exists in Doctrine2
This worked for me.
public function hasOrderStatus() {
return !is_null($this->dpdOrderStatus) && (bool) $this->dpdOrderStatus->getId();
}

Symfony2 entity field in a form

I'm trying to create a simple form to add companies and i've a trouble using an entity.
I added a select field using a company type entity :
->add('idtypesociete', 'entity', array('class' => 'PromocastUtilisateurBundle:PcastTypesociete', 'property' => 'nomtypesociete'))
But when i submit the form my idtypesociete field contain an 'PcastTypesociete' object and not just the value of the option selected. So the submission fail.
I made a Many-To-One relation between my company entity and my typeCompany entity like this:
/**
* #var integer $idtypesociete
*
* #ORM\Column(name="IDTYPESOCIETE", type="integer", nullable=false)
* #ORM\ManyToOne(targetEntity="Promocast\UtilisateurBundle\Entity\PcastTypesociete")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="PcastTypesociete_idtypesociete", referencedColumnName="idtypesociete")
* })
*/
private $idtypesociete;
Do you have a solution to get only the id of the company type selected? (if possible without made a simple sql request to list my companies types)
Thanks a lot !
If the relationships are working then Symfony 2 usually does a very good job of building the form fields for you.
I think the issue is the $idtypesociete property. Are you expecting to store an integer here on the hydrated entity?
Doctrine associations use Entity relationships. The annotations you supply determine the behind-the-scenes stuff like the join column:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-one-unidirectional
I suggest backing up or committing your work before doing anything else.
Does changing the entity property to the following help?
/**
* #var PcastTypesociete $typesociete
*
* #ORM\Column(name="IDTYPESOCIETE", type="integer", nullable=false)
* #ORM\ManyToOne(targetEntity="Promocast\UtilisateurBundle\Entity\PcastTypesociete")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="PcastTypesociete_idtypesociete", referencedColumnName="idtypesociete")
* })
*/
private $typesociete;
You may need to update your database schema via doctrine:schema:update using the console if it doesn't work properly the first time. Your Entity will also need to be updated to reflect the new property name.
If that works then your form should only need ->add('typesociete') in the form type and you'll have a functioning entity select field because Symfony is clever enough to know what field type to use.

Categories