Symfony 4 OneToOne relationship not pulling from database - doctrine-orm

This issue is so weird!
Example structure of entities relating to each other:
Property
-> InstructionToSell (OneToOne)
-> acceptedOffer (OneToOne)
If I do something like:
$property = $repo->findOneBy([‘id’=> 1]);
It works, all the relationships come back ok.
But now if I create a custom method in the property repo to search postcode with a LIKE query the acceptedOffer relationship is null
It’s confusing me as to why this is happening? It’s the same entity, same relationships, I’m just loading it via a like instead of directly by Id.
Even if I use fetchAll it works fine.
I hope I explained this well enough it’s difficult to explain what I’m seeing.

Seems like "lazy loading" is at work here.
Did you explicitely write the join query in your custom one ? (because maybe you should)

It is because you might have created custom query and when you create them you need to define your joins and select them.
For example;
$queryBuilder
->addSelect('p', 'i', 'o')
->join('p.instructionToSell', 'i')
->join('i.acceptedOffer', 'o')
->setParameter('name', $name)
->andWhere(
$queryBuilder->expr()->eq('p.name', ':name')
);

Related

How to use Doctrine 2 DQL ON to override default JOIN condition

For starters, it is NOT a duplicate of Doctrine 2 JOIN ON error. I am indeed getting Expected end of string, got 'ON' but using WITH won't solve my case.
The problem there is similar but different from mine. I don't need to add a condition to my JOIN, I need to substitute the default condition with a different one.
Let's say, we have 2 hypthetical tables: album and artist, with an artist_id FK pointing from album to artist. In my case, I don't want to join artists with their albums. I want to list artists joined with unrelated albums using some arbitrary condition. So each artist will be joined with the exact same small set of albums. Believe me, in my case it does make sense - I don't want to describe it fully because it is too complex and out of the scope of my question.
SELECT * FROM artist
LEFT JOIN album ON album.some_unrelated_property = 'foo'
The example above is raw SQL (I'm using PostgreSQL) and works perfectly fine in this form.
In my code I'm using query builder (hard to avoid it, because my query is way more complex and built step by step by a series of functions). The line that causes an error is this:
$qb->leftJoin('artist.albums', 'aa', Join::ON, 'aa.someUnrelatedProperty = "foo"');
In Doctrine I'm getting the dreaded Expected end of string, got 'ON'. When I use WITH instead of ON it works, but as expected, it appends the standard join condition by artist_id which I do not want.
What's even more confusing for me, in this post: What is the difference between JOIN ON and JOIN WITH in Doctrine2? which explains a difference between ON and WITH in DQL, somebody uses an example equivalent to mine as a correct use of ON in DQL:
Case Two
DQL
FROM Album a LEFT JOIN a.Track t ON t.status = 1
Will translate in SQL
FROM Album a LEFT JOIN Track t ON t.status = 1
What am I missing here? Is it possible to achieve what I want at all, using DQL? If not, what the hell is the reason for ON to exist in DQL when there's also WITH which works in more standard cases?

Doctrine ORM "Multiple non-persisted new entities were found through the given association graph:"

$added_obj = [];
foreach ($something as $data) {
$obj = $this->class->function($data, $par2);
if (null !== $obj && !(array_key_exists
(->getVal1(), $added_obj[$obj->getVal1()] === $$obj->getVal2())) {
$this->persister->persist($obj);
$added_bank_account[$obj->getVal1()] = $obj->getVal2();
} else {
}
}
What the code does: It calls an function which returns an entity or null. If an entity was created there is an check if 2 values already exists in an array, if not, than persist and add the 2 values to an array as key/val pair.
But, when an entity is created and it already exists in the array i don't want it to be persisted, but I want to do nothing with it.
But, when I do absolutely zero with it I got the error:
```Multiple non-persisted new entities were found through the given association graph:
A new entity was found through the relationship 'MyCompany\Client\Entity\Client#something' that was not configured to cascade persist operations for entity:
which makes sense because doctrine doesn't know what to do with the created entity. How can I "destroy" the created entity so that the problem is solved.
When there is only 1 object created everything works fine.
In your case you can simply merge or clear the entity from EntityManager
ex :
$em->merge($obj);
OR
$em->clear($obj);
I was facing the same issue because it was trying to insert a duplicated registry in a particular table, when in fact I was just trying to update it.
I was doing a persist with a flush right after.
So I found that (obvious for many, but certainly helpful):
->merge
will duplicate the registry in many cases, if the id is not set properly.
If you are trying to update an entity, this is not a good idea.
->persist
In the same way, if you are trying to update an entity, you may not use it. It's used to add a new entity to the db. To update a record, you may just use flush as you can see in this example from the docs.
I had the same problem and googling this error did not give me much results, but it appeared that in case of only one entity (not multiple) doctrine gives different error message, which has a solution that worked for multiple entities too. So, let me leave here a link for it: Doctrine - A new entity was found through the relationship
This problem occurs if you got the related entity in another object manager. For example, if there was previously a check for duplication through an exception and resetting the manager.

Doctrine2 DQL join with unrelated tables to fetch both entities

My DQL query returns only the FROM object, which is nice if the other object were related, but it isn't.
My Query:
$query = $this->em->createQuery('SELECT c, s FROM MyBundle:Person c, MyBundle:Spot s
JOIN s.geo_data g JOIN g.features f WHERE f.active = true AND
ST_Distance(f.location, c.location) < :distance GROUP BY c, s');
This works perfectly in SQL, giving me all the spots and all the persons within :distance of them. But in DQL, it only returns the person object, and since on the database level they are not related, I have no way to fetch the correct spot.
My database setup is correct, I'm using a PostGIS backend and spots and persons are not related in any way. They just happen to be on the same map and I'm querying for spatial relationships.
According to documentation, it's intended behaviour, from what I read, s is being hydrated, but not returned anywhere at all, good job!
How can I teach DQL to please return me what I told it in SELECT? Where's the "I mean what I say, stop being a smartass" switch?
Doctrine cannot give you both entities if they are not related because if the were related you would get the first entity c where you could get s through the relation.
What you can try is selecting all fields of both entities like
SELECT c.location, ..., s.geo_data, ...
This will give you an array for each column that contains all fields from both entities.
Maybe you can use result set mapping to get the entities if desired.
If you want to stuck with Doctrine, you HAVE TO define a OneToMany relation between places and people. In this way, you could set up the PeopleRepository and set up a method like getPeopleByLocationAndMaxDistance(Location $location, $distance)
SELECT p
FROM People AS p
LEFT JOIN Places AS pl
WHERE ST_Distance(p.location, pl.location) < :distance

Loading data from associated model in same query in rails

Basically, I have a Listing model, where each listing has a country id. I need the country name in my search results view. I know I can do #listing.country.name, but this performs an extra query for each listing in my search results. I'm using Thinking Sphinx, and in my controller I have
#listings = Listing.search(#ts_params).page(page_num).per(limit)
I have tried adding .includes(:countries) and variations thereof but no luck.
What's the best way to go about this? I want the country data to be fetched in the same query as the listings.
I have exactly the same issue with listing images - it is performing an extra query for every listing to find the image, when surely it can be done in one with joins.
Are you trying to eager load the associated model (to avoid an N + 1 query problem), or are you trying to load the associated model into fields on the parent model?
If it's the former, you're probably better off forgetting about :select and instead of :joins using:
ts_params[:sql][:include] = :countries, :listing_images
Now you should be able to call listing.countries and listing.listing_images to access child models, as normal.
Thinking Sphinx provides functionality to eager load associated entities, so for eager loading we don't need to add [:sql]. Following is the way to do this.
For eager loading associated entities using sphinx.
ts_params[:include] = [:country, :listing_image]
I managed to solve this using the :sql hash provided by Thinking Sphinx. I now have the following:
#ts_params[:sql][:joins] = "INNER JOIN countries ON countries.id = listings.country_id INNER JOIN listing_images ON listing_images.listing_id = listings.id"
#ts_params[:sql][:select] = "listings.*, countries.name as country_name, listing_images.image as image_name"
This is correctly retrieving the country name and image name, but I still have a bit of work to do in making it work with the images - I think that will deserve its own question!
Current v4 syntax is:
Article.search :sql => {:include => :user}
Ref: https://freelancing-gods.com/thinking-sphinx/v4/searching.html

doctrine: QueryBuilder vs createQuery?

In Doctrine you can create DQL in 2 ways:
EntityManager::createQuery:
$query = $em->createQuery('SELECT u FROM MyProject\Model\User u WHERE u.id = ?1');
QueryBuilder:
$qb->add('select', 'u')
->add('from', 'User u')
->add('where', 'u.id = ?1')
->add('orderBy', 'u.name ASC');
I wonder what the difference is and which should I use?
DQL is easier to read as it is very similar to SQL. If you don't need to change the query depending on a set of parameters this is probably the best choice.
Query Builder is an api to construct queries, so it's easier if you need to build a query dynamically like iterating over a set of parameters or filters. You don't need to do any string operations to build your query like join, split or whatever.
Query builder is just, lets say, interface to create query... It should be more comfortable to use, it does not have just add() method, but also methods like where(), andWhere(), from(), etc. But in the end, it just composes query like the one you use in the createQuery() method.
Example of more advanced use of query builder:
$em->createQueryBuilder()
->from('Project\Entities\Item', 'i')
->select("i, e")
->join("i.entity", 'e')
->where("i.lang = :lang AND e.album = :album")
->setParameter('lang', $lang)
->setParameter('album', $album);
They have different purposes:
DQL is easier to use when you know your full query.
Query builder is smarter when you have to build your query based on some conditions, loops etc.
The main difference is the overhead of calling the methods. Your first code sample (createQuery) just for simplicity makes one method call, while the the queryBuilder makes 4. At the end of everything, they come down to a string that has to be executed, first example you are giving it the string, and the other you are building it with multiple chained method calls.
If you are looking for a reason to use one over the other, that is a question of style, and what looks more readable. For me, I like the queryBuider most of the time, it provides well defined sections for the query. Also, in the past it makes it easier to add in conditional logic when you need it.
It might be easier to unit test when using the query builder. Let's say you have a repository that queries for some data basing on the complicated list of conditions. And you want to assure that if a particular condition is passed into the repository, some other conditions are added into the query. In case of DQL you have two options:
1) To use fixtures and test the real interaction with DB. Which I find somewhat troublesome and ununitestish.
2) To check the generated DQL code. Which can make your test too fragile.
With QueryBuilder, you can substitute it with mock and verify that "andWhere" method with needed parameter is called. Of course such considerations are not applicable if your query is simple and not depended on any parameters.