I have 2 linked entities: User and Acess. I want my doctrine User entity to have a field that informs me if the user has acesses or not.
I can't do a simple OneToMany relationship between the two tables, because there is thousands of acesses and it would be too costly to get thousands of records from the database once I only need to know if there is any.
What I would want is a field linked to a native query like:
select * from accesses where user = <whatever> limit 1
More specifically, something like:
/**
* USer
*
* #ORM\Table(name="user")
* #ORM\Entity
*/
class User {
/**
* #ORM\Column(name="user_id", type="bigint", nullable=false)
* #ORM\Id
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=300, nullable=false)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\Query="select exists (select id_acesses from accesses where user = "$id" limit 1)"
*/
private $hasAcesses;
}
Is this possible ? is there another way to do this ?
Edit:
based on #Otanaught answer below, I have done some tests:
Using a OneToMany relation with EXTRA_LAZY fetch:
user-getAccesses()->isEmpty() selected the whole collection
user-getAccesses()->count() used count(*) in the database which took 243ms to return
for comparasion my query above who did what I want took 12ms in average with peeks of 2ms or even 1ms.
Maybe the good folks at doctrine could implement this at isEmpty for extra lazy queries ?
Thanx #Otanaught
Doctrine does not provide an annotation that allows you to specify a query for a property of an entity (Annotation reference). You could create a custom method in your repository to implement the check. Did you measure how costly the relation would be? With correct relations and indexes this should be a none issue, because doctrine lazy loads the relation? Check the doctrine documentation about extra lazy collections.
Related
We are developing an online store in Symfony 5 and Doctrine 2 where multiple customers (called participants in this case) can participate in the same order item and share the cost. The following simplified class diagram demonstrates the domain model:
The pure object model works fine in unit tests, but you obviously need to persist the data to a database, which is why we need to introduce IDs.
Order, Product and Participant are entities with their own ID. In an ideal world, OrderItem and OrderItemParticipation would not need their own ID but be identified by the related entities they belong to, meaning their ID would be a composite foreign key.
So, an OrderItem would by identified by the composite key of Order.id and Product.id, which is pretty much exactly the same as given in this example from the Doctrine 2 documentation: https://www.doctrine-project.org/projects/doctrine-orm/en/2.10/tutorials/composite-primary-keys.html#use-case-3-join-table-with-metadata.
Since OrderItemParticipation relates to OrderItem, which uses a composite key itself, it would need to use a nested composite key consisting of Order.id, Product.id and Participant.id.
Unfortunately, Doctrine 2 doesn't seem to be able to work with nested composite keys as ID. I get this error
Column name id referenced for relation from
App\Entity\OrderItemParticipation towards App\Entity\OrderItem does
not exist.
when I try to generate a migration with the following mapping:
/** #ORM\Entity */
class OrderItem {
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=Order::class, inversedBy="items")
*/
private Order $order;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=Product::class)
*/
private Product $product;
// ...
}
/** #ORM\Entity */
class OrderItemParticipation {
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=OrderItem::class, inversedBy="participations")
*/
private OrderItem $orderItem;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=Participant::class)
*/
private Participant $participant;
// ...
}
So it seems that Doctrine is fine with my ID mapping in OrderItem, but it struggles when it gets to OrderItemParticipation. Is there a way to make Doctrine work with the given domain model? Is it maybe just an issue with the auto-generation of the migration, so if I had already manually set up the database, Doctrine might work with the given mapping? Or is the nested composite key ID approach too complicated for Doctrine?
I'm building an application that will be used by some users. Based on the business' logic definition, users will be identified by their email, so no email will be repeated in the whole system. Here es an extract of the UML class diagram:
User class (Sorry, I can't embed images due to my low reputation)
I've been reading how people implement it by using Symfony 4 and the Doctrine ORM, and everybody is letting Doctrine create a numeric ID for being the entity identifier. Based on my diagram, should I do it?
Here is what I have at the moment:
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity(
* fields="email",
* message="error.email_already_registered"
* )
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
* #Assert\NotBlank
* #Assert\Email
* #Assert\Length(min=4,max=180)
*/
private $email;
Should it be similar to the following code?
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* #ORM\Column(type="string", length=180)
* #ORM\Id()
* #Assert\Email
* #Assert\Length(min=4,max=180)
*/
private $email;
"Should it be" depends on your requirements. It is completely fine to use both a unique numeric identifier and setting the e-mail field to be unique (as posted in the first snippet). This helps to build relations between the user entity and other entities, as they have a "simple" field to be used for joining tables then.
If you would use the email address field as the unique identifier for that entity, this value had to be used in all relation tables. I'm not sure whether this would happen automatically, but on changing the address later, you had to propagate this change into all related tables. To avoid trouble, use the numeric identifier - one that should never change and for all time reference that one user entity.
Don't be confuse about UNIQUE and PRIMARY KEY.
UNIQUE value can change but PRIMARY KEY should never change BUT both are INDEX
So, for your case, email can change in your business logic or not?
If yes, don't add it as PRIMARY KEY, should be only UNIQUE INDEX. Otherwise, you can use it as PRIMARY KEY.
Using Doctrine2 and PostgreSQL I need to create foreign key constrains DEFERRABLE and INITIALLY DEFERRED
Found options "deferrable" and "deferred" In Doctrine/DBAL/Platforms/PostgreSqlPlatform.php, but have no idea where to use it inside Entity annotations
<?php
/**
* Class User
*
* #ORM\Table(name="jira_issues_changelogs")
* #ORM\Entity
* #package JiraBundle\Entity\Issue
*/
class Changelog
{
/**
* #var string
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="\JiraBundle\Entity\Issue", inversedBy="changelog")
* #ORM\JoinColumn(name="issue", referencedColumnName="id")
Need this column foreign key to be DEFERRABLE INITIALLY DEFERRED
*/
protected $issue;
I hit this same problem with an app I was working on, and came to the conclusion that the DEFERRABLE support in Doctrine DBAL isn't exposed to Doctrine ORM.
The crux of the problem is that in the ORM's SchemaTool the gatherRelationJoinColumns() method doesn't discover any $fkOptions except for onDelete. In order to support adding 'deferrable' there would need to be extended syntax in the ORM mapping layer for it.
In my case it was easier to just patch SchemaTool to add it than coordinate with upstream to add this properly, as there are very few references to people wanting to use advanced FK options on Google.
I thought I'd dump an answer here to avoid other people having to trace the issue themselves...
If someone wants to file a Doctrine ORM issue about it, be my guest!
I am using symfony2 with the Doctrine entities and I have a problem with the next:
I know I could solve the problem putting an ID to the "club_has_torneo" and turning it into an entity, but to me creating an entity for that looks like something that should not be done. So I wanted to know if there is a way to solve this issue or if I have to do what I think I have to.
Thank you in advance.
I guess I'll submit my own two cents worth.
ORM stands for Object Relational Mapper. The basic idea is to figure out how to map your objects without worrying too much about the database schema. So you have three domain model entities: torneo, club and nadador. Great. Figure out how your application will use these entities. Don't worry about how the relations will end up being stored.
Once you have a working domain model then worry about persistence. The three domain entities clearly map to three doctrine entities. As far as the relations go, I personally am not a big fan of composite primary keys. I think they just complicate things while adding little value. So I would make Doctrine entities for your two has tables and just given them their own primary database id.
Note that these are Doctrine entities not domain entities. Your application code should never need to deal with these relational doctrine entities at all. So in my opinion
creating an entity for that looks like something that should not be
done
does not apply here. It is just a persistence detail.
I think the best solution is indeed to make a entity for your club_has_torneo table. This ClubHasTorneo entity has club_id and torneo_id as composite keys and holds the owning side of a many-to-many relationship between your ClubHasTorneo entity and Nadador entity. This relationship can be done with a join table using the 3 keys. Check the code below on how to do that.
Your database scheme will look exactly like you drew it.
Your ClubHasTorneo entity would look something like this:
<?php
namespace Application\Entity;
use Application\Entity\Club;
use Application\Entity\Torneo;
use Application\Entity\Nadador;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="club_has_torneo")
*/
class ClubHasTorneo
{
/** MANY-TO-ONE BIDIRECTIONAL, OWNING SIDE
* #var Club
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Application\Entity\Club", inversedBy="clubHasTorneos", cascade={"persist"})
* #ORM\JoinColumn(name="club_id", referencedColumnName="id")
*/
protected $club;
/** MANY-TO-ONE BIDIRECTIONAL, OWNING SIDE
* #var Torneo
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Application\Entity\Torneo", inversedBy="clubHasTorneos")
* #ORM\JoinColumn(name="torneo_id", referencedColumnName="id")
*/
protected $torneo;
/** MANY-TO-MANY BIDIRECTIONAL, OWNING SIDE
* #var Collection
* #ORM\ManyToMany(targetEntity="Application\Entity\Nadador", inversedBy="clubHasTorneos")
* #ORM\JoinTable(name="club_has_torneo_has_nadador",
* joinColumns={
* #ORM\JoinColumn(name="club_id", referencedColumnName="club_id"),
* #ORM\JoinColumn(name="torneo_id", referencedColumnName="torneo_id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="nadador_id", referencedColumnName="id")
* }
* )
*/
protected $natadors;
public function __construct()
{
$this->natadors = new ArrayCollection();
}
// setters and getters
}
my 5 cents
If you want your implementation to match the drawn table structure, then (in my opinion) you need create an entity out of the 'club_has_torneo' table (for 'club_has_torneo_has_matador' you don't need to).
The rationale being that if you try to achieve this without creating the entity, you would need to create the entity associations so, that the 'natador' table references the 'club' and 'torneo' directly - in which case the actual database relations wouldn't match with your drawn table relationship anymore (i.e. the natador wouldn't have relationship to the 'club_has_torneo' table).
I'm trying to add a OneToMany bidirectional association on two tables: Course and Certified
There was already a OneToMany relation but Unidirectional.
So I modified both entities to make it bidirectional and added the property on the course side
Here are the two Entities:
/**
* Course
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="ME\ExamsBundle\Entity\CourseRepository")
*/
class Course
{
/**
* #ORM\OneToMany(targetEntity="ME\ExamsBundle\Entity\Certified", mappedBy="course")
* #ORM\JoinColumn(nullable=true)
*/
private $certified;
//getters and setters...
}
/**
* Certified
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="ME\ExamsBundle\Entity\CertifiedRepository")
*/
class Certified
{
/**
* #ORM\ManyToOne(targetEntity="ME\ExamsBundle\Entity\Course", inversedBy="certified")
* #ORM\JoinColumn(nullable=false)
*/
private $course;
// getters and setters...
}
But when i use the command
doctrine:schema:update -- dump-sql
It tells me that there's nothing to update.
I tried to
clear the metadata cach
remove the nullable on both sides
drop the database and recreate it
But the property certified never appeared in the course table in the database.
I'm kind of stuck here, so any help would be greatly appreciated.
Thanks in advance
Tom
A bidirectional relation in Doctrine isn't different from a one-directional one from a SQL-point-of-view: that's because Doctrine can infer the inverse relation reversing the foreign key from the other table. Adding a new colum to your Course table would be a not normalized form, or worse it will have no sense.
The response of the Doctrine's command is all right.
Please study some more on normalized forms of databases: https://en.wikipedia.org/wiki/Database_normalization