Is there a way to handle duplicate enteries? - php

I am using the api-platform framework with a MySQL backend. I am getting errors when API clients use the POST end point to submit data if there is already an entry in the database.
Currently I am using a PRE_WRITE EventSubscriberInterface class to find the original database entry and delete it. However this seems incredable inefficient compared to a simple update action.
I am able to update the existing database entry, but then I'm unable to remove/stop the POST'd an item from being executed.
Is there a way around this? Ether to change the INSERT action to an ...ON DUPLICATE... or to simple stop the data the user post'd from being saved to the database?

You can also use the property "UniqueEntity" to set what's make your entity unique.
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass="App\Repository\ArticleRepository")
* #ORM\HasLifecycleCallbacks()
* #UniqueEntity("slug")
*/
class Article
{
//...
/**
* #ORM\Column(type="string", length=255)
*/
private $slug;
}

Related

Symfony 4 Validation on whole Entity

So, I need a validation for a reservation for a sports club.
A reservation has a start and an end datetime and you can reservate for 1 or more tables.
So the Entity looks like
class Reservation
{
use TimestampAble;
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="reservations")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Table", inversedBy="reservations")
*/
private $tables;
/**
* #ORM\Column(type="datetime")
*/
private $start;
/**
* #ORM\Column(type="datetime")
*/
private $end;
...
}
(Don't worry about the entity "Table" - the db table for it is named "snooker_table" ;))
Now I need to validate, that in the requested time range with the requested tables no other reservation already exist.
And this gives me headaches...
I know I can make it "manually" in the Controller Actions create / update. But I'm also using Symfonys Easy-Admin, so I need to put the code there as well.
I thought about putting the validation as an annotation directly into the entity. But I don't know where... If I put it on "$tables" I just get an ArrayCollection without the needed start and end datetimes. And it's also not an unique entity (as I need to go on a range of datetimes and so on).
So: any ideas how to achieve this in the entity directly? or at least in the form type (and for easy admin i care later)?
Thx in advance.
Ok, I'm going to do it in this way: Symfony 2 UniqueEntity repositoryMethod fails on Update Entity
Creating a repository method (not a constraint) for validation and use it for the UniqueEntity constraint on the whole entity itself. Feels a little bit dirty but ok...
Hope this works in easy admin as well.

Symfony doctrine lazy load properties

I have an entity that stores large files as blobs to the DB.
I would now like to get Symfony to never ever load these blobs unless I specifically request them via the appropriate getter.
In essence I want the same idea as lazy-loading relationships but for a string property.
What I have tried so far is to put all my other properties that hold the file meta data into a trait and then apply that trait to two entities.
namespace App\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
trait DocumentMetaData
{
/**
* #var int|null
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime|null
*
* #ORM\Column(type="datetime")
*/
private $date_uploaded;
}
One entity has nothing to it but the trait...
namespace App\Entity;
use App\Entity\Traits\DocumentMetaData;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="documents")
* #ORM\Entity()
*/
class Document
{
use DocumentMetaData;
}
...the other has the added blob property:
namespace App\Entity;
use App\Entity\Traits\DocumentMetaData;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="documents")
* #ORM\Entity()
*/
class DocumentFile
{
use DocumentMetaData;
/**
* #var string|null
*
* #ORM\Column(type="blob")
*/
private $blob;
}
Now, if I don't want the blob to be loaded, for example for a listing of files, I simply use the entity that doesn't have the blob.
This approach sort of works but causes issues as I need to point both entities at the same table (see the class level ORM annotations).
Specifically, it makes doctrine freak out when running migrations:
The table with name 'myapp.documents' already exists.
That makes perfect sense and really I'm hoping that someone can point me to a nicer solution.
How can I tell doctrine not to load the blob unless its explicitly asked for?
So as per the comments on my question - the way to do this so that migrations do not break is to leverage doctrine's ability to lazy load relationships between tables.
Basically I had to go and create a new entity that only holds my giant blobs and then establish a one to one relationship between the original entity and the blob entity.
I then set that relationship to load EXTRA_LAZY and as a result I can now control when precisely the blobs of giant data should be loaded.
I don't think this is ideal in terms of normalising the DB design but it works a lot better than anything else so happy with that.

Wrong value coming from Doctrine 2 'getClassMetadata()->getIdentifier()', how or why?

I have the following Entity as part of a Symfony 3.2.6 project in Doctrine 2:
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use QuoteBundle\Model\SourceTrait;
/**
* #ORM\Entity
* #ORM\Table(name="quote")
*/
class Quote
{
use SourceTrait;
use TimestampableEntity;
/**
* #ORM\Id
* #ORM\Column(type="integer",unique=true,nullable=false)
* #ORM\GeneratedValue
*/
private $quoteId;
/**
* #return int
*/
public function getQuoteId(): ?int
{
return $this->quoteId;
}
}
In another class dynamically I need to get the identifier of the entities (I wrote just an example of the entities - the one above - but this applies to all of the entities I have all over the application) so the best way I found to achieve this was using getClassMetaData() method from Doctrine.
This is the dynamic code into the class:
$modelMetaData = $em->getClassMetadata($bundleName.':'.$modelName);
Which gets translated for example in:
$modelMetaData = $em->getClassMetadata('QuoteBundle:Quote');
From there I should have all the data related to the entity.
One of the problem is we're two developers and we're having different results regarding getClassMetaData().
I am running a stack in Docker using Docker Compose which is exactly the same and this mean: same PHP version (7.1.2), same Apache version, same MySQL version (this one is in a common server outside the stack) and same Symfony libraries. I have checked, double checked and triple checked and can do it again if it's necessary.
For the examples from now on I'll call myself Dev1 and the second developer will be Dev2.
Dev1 code:
$id = $em->getClassMetadata('QuoteBundle:Quote')->getIdentifier()[0];
dump($id);
quote_id
Dev2 code:
$id = $em->getClassMetadata('QuoteBundle:Quote')->getIdentifier()[0];
dump($id);
quoteId
As you can see the ID obtained is different which is making the code to fails on Dev2 environment and I don't know what else to look since everything seems to be fine for me.
The question is: should getIdentifier() return quote_id (the column name) or quoteId (the mapped name)??
As an addition here is an example of dump($modelMetaData) for Dev1 and here the same example for Dev2.

Symfony2: How to avoid duplicate entities that already exist in the database?

I have the entities "Student" and "Parents" in two forms, one embedded in another are Symfony2. I need to store data from both entities in different tables of the database, when a new student and his parents also adds added. But I need that parents are not duplicated in the database, so before adding have to check that there are none, add only in that case. I do not know how to do this in Symfony2. More information is another question I asked.
look the other question here
I hope someone can help me because I can not find a solution for this problem.
In order to have columns with unique entries you must use UniqueEntity Validation constraint Docs
Validates that a particular field (or fields) in a Doctrine entity is (are) unique. This is commonly used, for example, to prevent a new user to register using an email address that already exists in the system.
add #UniqueEntity("parent") above your entity name and unique=true on the field that you want to be unique
form the docs
// src/AppBundle/Entity/Author.php
namespace AppBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
// DON'T forget this use statement!!!
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #UniqueEntity("email")
*/
class Author
{
/**
* #var string $email
*
* #ORM\Column(name="email", type="string", length=255, unique=true)
* #Assert\Email()
*/
protected $email;
// ...
}

Symfony2: Custom identifier in Sonata entities

I have an entity with a custom id (i.e. UUID) generated on __construct function.
namespace AppBundle\Entity;
use Rhumsaa\Uuid\Uuid;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Person
{
/**
* #ORM\Id
* #ORM\Column(type="string")
*/
private $id;
/**
* #ORM\Column(type="string")
*/
private $name;
public function __construct()
{
$this->id = Uuid::uuid4()->toString();
}
This entity is used in sonata and also in other part of the project. I need this entity to have id before persisting and flushing it, so I can not use a an auto-increment.
So, the problem is sonata don't let me create entities because it takes the create option as and edit on executing because that entity already has an id, but this entity does not exists at this moment, so it fails.
The problem isn't the library for generating UUID, any value for 'id' fails.
Anyone know how to solve it? Another similar approach to solve the problem?
You shouldn't set your id in the constructor, but rather use the prePersist Doctrine event to alter your entity before persisting it for the first time.
You may use annotations to do so, see the Doctrine Documentation on prePersist.
The issue with setting the id in the constructor is that you may override it when you're retrieving it from the database, in which case it will be incorrect.

Categories