Access field of entity included in hidden join - php

Two entities, A and B, Bextends A. I'm interested in the field B.isOk.
I have this query that joins A with other entities(and because B extends A, if I look at the pure sql, a join is done between A and B even though it is not specified in the query builder-because of the inheritance, great-).
How can I then access that B.isOk if I only have A my query builder? I tried using INSTANCE OF but to no success.
In my yml for A there's specified :
inheritanceType: joined
discriminatorColumn:
name: type
type: text
discriminatorMap:
internal-b: \C\BBundle\Entity\B
Any other suggestions on how could I use that property? I could add an extra join but what's the point since the join I need is already happening behind the curtains.

One alternative to this would be to give up DQL and write my own SQL query.
since this can not be done because OOP principles, it means something might be wrong with my model so another solution would be to refactor the model and design a correct one that fits the current needs.

Related

JOIN in Symfony Doctrine with QueryBuilder between 2 entities

I am relatively new to Doctrine and am having an absolute nightmare trying to get a join between two entities returned correctly. I don't really understand the DRM that Doctrine uses as much as I would like at the moment so it really is just a case of trial and error at the moment.
I have two entities, one is document and one is document-sow-detail. I need to join the two where the document_id exists in the document-dow-detail table/entity as to get a couple of fields that exist in the document-dow-detail entity returned.
So, within my document repository, I have attempted the following code (with not a lot of understanding of what is happening). Can someone point me as to what is happening here and why it is not working?
$db = $this->createQueryBuilder($this->alias);
$db->addSelect("dsd");
$db->leftJoin("doc_sow_detail",'dsd','ON',"id_document");
When I breakpoint and evaluate the $db->getQuery() function in my IDE this is the _dql that has been generated.
SELECT d, dsd FROM BillingBundle\Entity\Document d LEFT JOIN doc_sow_detail dsd ON id_document
Can anyone give me a clue as to what I am doing wrong here before I chuck my computer out the window?
Thanks!
A simple example of doctrine join which joins product category on category:
$qb = $this->_em->createQueryBuilder();
$qb->select('p')
->from('BRBProductBundle:Product', 'p')
->join('p.category', 'c')
->where('1 = 1');
Hope this may help you.
I thought I'd post my solution.
I had to change my entity structures due to the one-way relationship between the two entities.
I moved the required entity properties onto the document entity which negated my need for a join. This is probably where these properties needed to be in the first place.

how should a data mapper mapping relationships between 2+ dbms tables work?

I have two data domains, Student and Course, and relative data mappers, StudentMapperMySQL and CourseMapperMySQL. Now I want to implement a third data mapper for fetching both data, especially for taking advantage of SQL JOIN (SELECT ... FROM students JOIN courses ON .... WHERE ...). What should the select()/fetch() method return? and how should it work via a repository pattern implementation?
It looks like you are creating new aggregate root just for query purpose, which is totally fine if you would like to use CQRS.
Every AR should have its own repository, so you should make AR StudentCourses and StudentCoursesRepository (something from your ubiqutious language).
You should call that repository to get student course data repo.getCoursesForStudent(student_id) or for all at once repo.getStudentsWithCourses().
Repositories should return only aggregate roots, so in return you would get an aggregate root or list of them.
Such AR would look like that:
class StudentCourses:
studentName Name (VO)
studentId AggregateId (VO)
courseList Course[0..*] (Entity)
class Course:
your course data
What is important here is that you have different models for command and for query, so you can have two different Course model entities in your system. Course in command model is probably an aggregate root, while in query model it's not.
I suppose that this answer might be a bit hard to understand, but I did my best. Anyway I'm not a DDD expert so there might be other ways to do that.

Doctrine 2 find pages having attributes

I need to fill query builder with filter conditions of page attributes. I got Enities "Page", "Attribute" and "Value"
Page: id, name, etc
Attribute: id, name, etc
Value: page_id, date, string, numeric
I need to get all pages having attribute values selected in form.
So i get Query Builder:
$qb->select('p')
->from(Page p)
->leftJoin(p.values);
Can I use "having" or "where" clauses to do like?
$qb->add('having', 'a.id = :attr1_id AND a.value = :attr1_val')
$qb->add('having', 'a.id = :attr2_id AND a.value = :attr2_val')
UPDATE: No i can't. in this way it can get page with attr1_id = attr2_value AND attr2_id = attr1_value and all conditions will be true, but result is wrong.
or I should add custom join for each value?
UPDATE: here's working exapmle:
//attr1.intval=:a1 (or LIKE, BETWEEN, etc compare)
$comp_expression=$aliace.'.'.$value_field.'=:a'.$this->getId();
//INNER JOIN attribute attr1 ON attr1.type=5 AND attr1.intval=:a1
$qb->innerJoin($value_class, $aliace , 'WITH', $aliace.'.type='.$this->getId().' AND '.$comp_expression);
//where attr1.intval IS NOT NULL
$qb->andWhere($aliace.'.'.$value_field.' IS NOT NULL');
$qb->setParameter('a'.$this->getId(),$value);
UPDATE: So the only way to do key+value filtring is to add join for each filter condition? I got 27 attributes now, so need to build query with 27 joins?
Is there another way to this better? Sorry if this is duplicate, cant find keywords to find same questions.
UPDATE: Maybe i shuld get out of sql query limits and create mysql procedure for it?
UPDATE: Can i use somthing like this in doctrine? MySQL optimization on filtering key-value pairs as records
Model
The model you are using here is called Entity-Attribute-Value (EAV). If you weren't aware of that I suggest you read about the pro's and con's first.
One of the con's is that searching in a EAV model is difficult and very inefficient.
AND queries
It seems you want to query for Entities that have a specific Value for a specific Attribute and another specific Value for a specific Attribute. These types of queries are not very efficient in the EAV model.
Let's assume your entities are set up as:
Entity: id, attributes (one-to-many), values (one-to-many)
Attribute: id, name, entity (many-to-one), values (one-to-many)
Value: id, content, entity (many-to-one), attribute (many-to-one)
Let's assume you want to query for all Entities that have an Attribute color with the value blue. A Doctrine query would look like this:
SELECT e FROM Entity e
JOIN e.values v JOIN v.attribute a
WHERE a.name = 'color' AND v.content = 'blue'
(Of course you should never use values in the DQL like this, but bind them as parameters.)
Now let's assume you want to query for all Entities that have an Attribute color with the value blue and an Attribute shape with the value square. The query becomes:
SELECT e FROM Entity e
JOIN e.values v1 JOIN v.attribute a1
JOIN e.values v2 JOIN v.attribute a2
WHERE a1.name = 'color' AND v1.content = 'blue'
AND a2.name = 'shape' AND v2.content = 'square'
Now if you want to query for 3 Attribute/Value pairs, you'll need 3 of those JOIN sets. If you want to query for 4 Attribute/Value pairs, you'll need 4 of those JOIN sets. Etc.
OR queries
Let's assume you want to query for all Entities that have an Attribute color with the value blue or an Attribute shape with the value square. The query becomes:
SELECT e FROM Entity e
JOIN e.values v JOIN v.attribute a
WHERE (a.name = 'color' AND v.content = 'blue')
OR (a.name = 'shape' AND v.content = 'square')
You can see this is more efficient, you won't need those additional JOIN sets for every additional Attribute/Value pair.
Mixing AND OR
I'm going to be short here: this will become a query nightmare. I strongly advise you not to do this.
Alternative
If your application is going to rely heavily on these types of queries (especially AND and mixing AND OR), I suggest you consider a different type of storage engine. Relational Databases are not really suited for this kind of thing.
You'll probably be better of using a Document Oriented Database, something like Elasticsearch, MongoDB, CouchDB, etc.
Hybrid alternative
You can also "copy" the EAV part to a Document Oriented Database and use it only for search functionality. I suggest you set up events for when changes are made to an Entity (create, update, delete). Then create listeners that persist those changes into the Document store.
This way your application can work with the Relational Database for normal operations, and the Document store for searching. In this case I advise Elasticsearch, which is extremely suitable for this kind of thing.
There is no such difference between the two methods, the two methods do exactly the same thing.
By the central point of question, I got the doubt is about the construction of query using like.
The first example you provide is workfull.
The second example you propose is not right.
On the second example, you need to define All the where clauses on andWhere method.
$qb->innerJoin('a1 ON p.id=a1.page_id');
$qb->andWhere('a1.attribute_id=:attr1_id ');
$qb->andWhere('a1.value=:attr1_val');
$qb->andWhere('a1 is not null');
All are you doing is creating a sql by a third api, Doctrine ORM is one of the most successful orm tool for php, knowing how to use it will let you to experience the ORM power, by doctrine implementation all you need to know are there:
Doctrine Create Query Builder -> The same are you using.
Doctrine Query Builder
Doctrine Create Query -> Simplest form
Query Language

Doctrine DQL, class table inheritance and access to subclass fields

I have a problem with a DQL query and entity specialization.
I have an Entity called Auction, which is OneToOne relation with Item. Item is a mappedSuperclass for Film and Book. I need a query that could back a search engine, allowing the user to look for auctions with different properties AND selling items with different properties (it is the AND part that makes it challenging).
The problem is that even though Auction has an association pointing to Item as such, I need to have access to Film- and Book-specific fields. The users will specify the Item type they're looking for, but I don't see any way of using this information other than using INSTANCE OF in my DQL query.
So far, I have tried using a query like:
SELECT a FROM Entities\Auction a
INNER JOIN a.item i
INNER JOIN i.bookTypes b
WHERE i INSTANCE OF Entities\Book
AND b.type = 'Fantasy'
AND ...".
Such a query results in an error saying that:
Class Entities\Item has no field or association named bookTypes
which is false for Book, yet true for Item.
I have also tried
SELECT a FROM Entities\Book i
INNER JOIN i.auction a ...
but I reckon Doctrine requires that I refer to the same Entity in SELECT and FROM statements.
If that's of importance, I am using class table inheritance. Still, I don't think switching to single table inheritance would do the trick.
Any ideas?
As Matt stated, this is an old issue that Doctrine Project won't fix (DDC-16).
The problem is that doctrine's DQL is a statically typed language that comes with a certain amount of complexity in its internals.
We thought about allowing upcasting a couple of times, but the effort to get that working is simply not worth it, and people would simply abuse the syntax doing very dangerous things.
As stated on DDC-16, it is also indeed not possible to understand which class the property belongs to without incurring in nasty problems such as multiple subclasses defining same properties with different column names.
If you want to filter data in subclasses in a CTI or JTI, you may use the technique that I've described at https://stackoverflow.com/a/14854067/347063 . That couples your DQL with all involved subclasses.
The DQL you would need in your case is most probably (assuming that Entities\Book is a subclass of Entities\Item):
SELECT
a
FROM
Entities\Auction a
INNER JOIN
a.item i
INNER JOIN
i.bookTypes b
WHERE
i.id IN (
SELECT
b.id
FROM
Entities\Book b
WHERE
b.type = 'Fantasy'
)
That is the pseudo-code for your problem. It is not nice, but keep in mind that SQL and DQL are very different and follow different rules.
Updated:
I've discovered a solution for this. See my answer for this related question:
Doctrine2: Polymorphic Queries: Searching on properties of subclasses
You can easily solve this by left-joining your base entity with your inheritance class using the id:
SELECT a FROM Entities\Auction a
INNER JOIN a.item i
INNER JOIN Entities\Book b WITH b.id = i.id
INNER JOIN b.bookTypes bt
WHERE bt.type = 'Fantasy'
AND...
or with a queryBuilder:
$queryBuilderb->select('a')
->from('Entities\Auction', 'a')
->innerJoin('a.item', 'i')
->innerJoin('Entities\Book', 'b', 'WITH', 'b.id = i.id')
->innerJoin('b.bookTypes', 'bt')
->where('bt.type = :type')
->andWhere(...
->setParameter('type', 'Fantasy');
This is based on the answer given by Ian Philips in the question here
The Doctrine team has stated that they're not going to add support for this:
https://github.com/doctrine/orm/issues/2237
Pertinent comments from that page:
Thats indeed tricky. That syntax alone can, however, never work,
because there might be several subclasses that have a field named "d",
so Doctrine would not know which field you mean.
I am closing this one.
The requirement of this issue is basically violating OO principles.
If you really need to filter across multiple child-entities in your
inheritance, then try something as following instead:
SELECT
r FROM
Root r WHERE
r.id IN (
SELECT
c.id
FROM
Child c
WHERE
c.field = :value
)
I had the same issue, and didn't find a solution without using separate queries for each subclass and merging them later on the application level.
One thing I'm sure, single table inheritance will not solve this, completely the same thing.
There is another alternative, although being logically dirty.
Define all the fields (the ones you need) in the superclass. If the record logically doesn't have that field it will be empty. Not a pretty sight, but hey, more optimized than 2-3-4-... queries. Also in this scenario single table inheritance is definitely the better way to go

Doctrine2: Polymorphic Queries: Searching on properties of subclasses

I've got a project where I deal with customer orders. Some of those orders are made via Amazon.com. So I've got an Order entity, and an AmazonOrder entity that extends it. One thing added by AmazonOrder is the AmazonOrderId.
I've a requirement to implement a broad search feature. The user can enter some stuff into a text box, and be used in a bunch of expressions in one big where-clause. So, for example, if the user searched for "111", the results include any orders with an ID starting with 111, any order being shipped to zip codes that begin with 111, any order being shipped to "111 Main St", etc.
That stuff is implemented with a query-builder-created query that has a big orX() expression.
Now, I'd like to match against all Orders, but if they're an AmazonOrder, also match against AmazonOrderId.
And I'm stuck -- I suspect it may not be possible
Here's how I'm building up the query:
$qb->select('o,s')->from('PMS\Entity\Order', 'o');
$qb->leftJoin('o.shippingInfo','s');
$qb->andWhere('o.status = :status');
$qb->setParameter('status',$status);
$qb->andWhere(
$qb->expr()->orX(
$qb->expr()->like('o.id',':query')
$qb->expr()->like('s.address',':query')
$qb->expr()->like('s.city',':query')
)
);
$qb->setParameter('query',$userQuery .'%');
$orders = $qb->getQuery()->getResult();
And I can't figure out how to add a condition that says, roughly, "OR (Order is an AmazonOrder AND AmazonOrderId LIKE '$userQuery%')"
Anyone have any insight? Either a way to handle this, or at least a confirmation that it's not doable this way?
Here's another solution that works for me with Doctrine 2.4:
$qb->select('o')
->from('Order', 'o')
->leftJoin('AmazonOrder', 'ao', 'WITH', 'o.id = ao.id')
->andWhere('o.id like :query or ao.amazonOrderId like :query')
->setParameter('query', $someQuery);
You just left-join the entity on the specific subclass of itself. (You can adapt my simple query to your use case.)
I've tried this exactly once, but it seems to work.
Hm, I had similiar problems in my last doctrine project.
One time it was just a single field, so I moved it to the parent class – not the nicest solution, but worked. In some other case there where too many properties so these would have cluttered the parent class. I did a native sql query for searching and fetching me the record ids and then used a WHERE IN (...) dql in order to fetch the entities.
A compromise might be the doctrine ResultSetMapping which can map a native sql query to entities directly, although every time I worked with it I found it quite clumsy to use and the overhead for two queries (fetch ids & fetch entites) as outlined above to be neglectable.
Maybe you could accomplish something with the INSTANCEOF operator in your WHERE clause, although I dont think doctrine would be smart enough to recognize it the way you want.
If you need to join a subclass to get a fields only present into this subclass, you may want to use this syntax :
...->from('From', 'X')->leftJoin('App\Entity\Class\Name', 'Y', \Doctrine\ORM\Query\Expr\Join::WITH, 'Y.id = X.id')
Note that, as it's left join, it comes with a performance hit.
Also note that doctrine will automaticaly join all child entity of a mapped superclass no matter what you're doing.
There's a github issue about this behaviour : https://github.com/doctrine/orm/issues/5980
I would like to add that if you don't need to access any fields of the subclasses, you can just join the superclass and filter with a where INSTANCE OF subclass.
I suggest you adding the discriminator columns and the superclass id into an index as :
* #ORM\Table(indexes={#ORM\Index(name="idx_partition_type", columns={"id", "type"})})
* ...
* #ORM\DiscriminatorColumn(name="type", columnDefinition="CHAR(1) NOT NULL")
abstract class superclass{
...
}
This would be usefull only for non-instantiable, abstract class. Else, if your class isn't abstract, doctrine will leftJoin any related child entity no matter what happens.
I just wanna share all of this because it helped me.

Categories