How can I join two tables without association? - symfony

I try to combine two arrays in Symfony that do not have an association field. In documents the field "uuid" is actually the "documentId" in data. Here my approach:
$builder = $this->em->createQueryBuilder();
$result = $builder->select('documents')
->from('App:Documents', 'documents')
->leftJoin('data', 'data')
->andWhere('documents.uuid = data.documentId')
->andWhere('data.fields = :id')
->setParameter('id', 2)
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
But I get the error message that documents has no association data.

You are quite close, but lack a few things.
You need to be more precise with your join and specify which entity is targeted (if there is no relation or inheritance).
// If your entity name is called Data
->leftJoin(Data::class, 'data', 'WITH','data.documentId = documents.uuid')
Let's say you are creating your method from the Documents repository (which by the way should be in singular instead of plural).
Your method would look like this:
$result = $this->createQueryBuilder('documents')
->select('documents as document, data as myData')
->leftJoin(Data::class, 'data', \Doctrine\ORM\Query\Expr\Join::WITH,'data.documentId = documents.uuid')
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
But I think there may be an issue with your schema if there is no relation between two entities that are clearly related.

Related

Drupal entityQuery multiple node types and join

Looking to join multiple nodes via a query and join. Let's say one node has a matching ID of another field in another node. I would like to join them into an array so that I can output other fields with them indexed together on the same ID. Example: node1.nid and node2.field.target_id
How do I do this?
$nids = \Drupal::entityQuery('node')->accessCheck(FALSE)
->join('node2', 'n2', 'n.nid = n2.field.target_id');
->condition('type', 'node1', 'node2')
->sort('title')->execute();
$nodes = \Drupal\node\Entity\Node::loadMultiple($nids);
$response = array();
This is definitely doable and it looks like you're almost there, but it looks like you're combining the Database API with the Entity API, specifically trying to run Database joins on EntityQuery fetches.
Now, if you want to continue the Database query route, which may make it a little easier to join the values of multiple entities and output them, this is what I would recommend:
The first thing that I notice is that you're attempting to chain the join. Unfortunately, according to the Database API Docs
Joins cannot be chained, so they have to be called separately (see Chaining).
I have done similar to this with a join between two custom content types. Here is the code I used:
$connection = Database::getConnection();
if ($connection->schema()->tableExists('companies') && $connection->schema()->tableExists('users')) {
$query = $connection->select('companies', 'c');
$query->join('users', 'u', 'c.sourceid1 = u.sourceid1');
$results = $query->fields('u', ['destid1', 'sourceid1'])
->fields('c', ['destid1'])
->execute()
->fetchAll();
return $results;
}
Now this assumes that you've brought in Drupal\Core\Database\Database, but let's walk through the process.
$connection = Database::getConnection();
This is fetching the database connection for your site
if ($connection->schema()->tableExists('companies') && $connection->schema()->tableExists('users')) {
This is a check I always do to make sure the tables I'm working with exist.
$query = $connection->select('companies', 'c');
One of your tables needs to be the "base" table. In this case, I chose companies.
$query->join('users', 'u', 'c.sourceid1 = u.sourceid1');
This line is where the join actually happens. Notice that it's not chained, but it's own command. We're joining these tables, users and companies, on this sourceid1.
$results = $query->fields('u', ['destid1', 'sourceid1'])
->fields('c', ['destid1'])
->execute()
->fetchAll();
This is where I'm pulling whichever fields I want from both entities. In this case I want destid1 and sourceid1 from my user table and destid1 from my company table.
The ->execute() call actually runs the query, and ->fetchAll() returns an array of all matching results. This will get you to being able to JSONify all the things.
However, if you want to stick with the entityQuery route, that's also viable. My personal preference is to use a regular query for more complex things and an entityQuery if I just need to get a value from a single field or return a single entity.
For an entityQuery, well, that's a bit different. I wouldn't really recommend using an entityQuery for a join here. Instead, using entityTypeManager if you know or can get the matching value from both tables.
For example, let's assume that field_content_type_a_id on content type A matches field_related_type_a_id on content type B.
You could load one, let's go with type A for now:
// Get the current NID somehow... that's up to you
$entity_a = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
// Get the value of the field you want to match to the other content type
$matching_value = $entity_a->get('field_content_type_a_id')->getValue();
// Get the entities with matching type A values
$entity_b_array = \Drupal::entityTypeManager()->getStorage('node')
->loadByProperties(['field_related_type_a_id' => $matching_value]);
// Do something with $entity_b_array.

How to do a WHERE when the column is an array?

I have and Entity : Company, which has a column: Type.
Type can either be a string, or an array of strings.
Ex: Type can be "Foo", "Bar", or array("Foo", "Bar").
I can't figure how to get all the Companies where the type contains "Bar".
I tried
$qb = $this->companyRepository()->createQueryBuilder("c");
$companies = $qb
->select("c")
->Where( $qb->expr()->in('c.type', array($qb->expr()->literal('Bar'))))
->getQuery()
->getResult();
Which only fetch the Companies where the type is "Bar", and not the ones where it is array("Foo", "Bar").
I tried $qb->expr()->like(...) instead of $qb->expr()->in(..) with the same results.
How can I get Companies where the type contains "Bar"?
(Assuming type has more than just the 3 values I gave as an example)
As I wrote in the comments you can't query against single array values when using doctrines array column. But as those columns require you not to use commas inside the array values, it is possible to write a query that utilizes this requirement.
Instead of regex (which would require an doctrine extension), you could also write a LIKE query, like so:
$query = 'Bar';
$qb = $this->companyRepository()->createQueryBuilder("c");
$companies = $qb
->where('c.type LIKE :only OR c.type LIKE :first OR c.type LIKE :last OR c.type LIKE :middle')
->setParameter('only', $query)
->setParameter('first', sprintf('%s%%,', $query))
->setParameter('last', sprintf('%%,%s', $query))
->setParameter('middle', sprintf('%%,%s,%%', $query))
->getQuery()
->getResult()
;
dump($companies);
Here is a similar question with an answer for you.
Symfony2 Doctrine querybuilder where IN
However, I would suggest using DQL if it's an option or just using Doctrine with PDO instead of QueryBuilder.

Doctrine DQL Delete from relation table

Using Doctrine 2 and Symfony 2.0.
I have two Doctrine entities (let's suppose EntityA and EntityB).
I have a ManyToMany relation between them. So a EntityA_EntityB table has been created in database.
Using DQL or QueryBuilder, how can I delete from that relation table EntityA_EntityB?
Docrtine offers something like this to perform something similar:
->delete()
->from('EntityA a')
->where('a.id', '?', $id);
But I don't really get how to perform the deletion of row from the relation table.
$em = ...; // instance of `EntityManager`
// fetch both objects if ID is known
$a = $em->getRepository("YourProjectNamespace:EntityA")->find($id_of_A);
$b = $em->getRepository("YourProjectNamespace:EntityB")->find($id_of_B);
// suppose you have `EntityA::getObjectsOfTypeB` which retrieves all of linked objects of type `EntityB`.
// This method return instacne of ArrayCollection
$a->getObjectsOfTypeB()->removeElement($b);
$em->flush();
Something like this?
Basically, you need to remove related object from collection rather than delete relation itself. you want to remove relation directly you can always use pure SQL, but in DQL that is not possible.
Raw DELETE SQL statement via DBAL Connection object
$conn = $this->getDoctrine()->getManager()->getConnection();
$stmt = $conn->prepare("DELETE FROM EntityAEntityB WHERE id_b IN (:ids_of_b)");
$stmt->bindParam('ids_of_b', $to_delete_ids); // BEWARE: this array has to have at least one element
$stmt->executeUpdate();

Symfony2 Select one column in doctrine

I'm trying to refine the query trying to select fewer possible values ​​..
For example I have an entity "Anagrafic" that contains your name, address, city, etc.,
and a form where I want to change only one of these fields, such as address.
I have created this query:
//AnagraficRepository
public function findAddress($Id)
{
$qb = $this->createQueryBuilder('r')
->select('r.address')
->where('r.id = :id')
->setParameter('id', $Id)
->getQuery();
return $qb->getResult();
}
there is something wrong with this query because I do not return any value, but if I do the query normally:
//Controller
$entity = $em->getRepository('MyBusinessBundle:Anagrafic')->find($id);
Return the right value.
How do I do a query selecting only one column?
Since you are requesting single column of each record you are bound to expect an array. That being said you should replace getResult with getArrayResult() because you can't enforce object hydration:
$data = $qb->getArrayResult();
Now, you have structure:
$data[0]['address']
$data[1]['address']
....
Hope this helps.
As for the discussion about performance in comments I generally agree with you for not wanting all 30 column fetch every time. However, in that case, you should consider writing named queries in order to minimize impact if you database ever gets altered.
You can use partial objects to only hydrate one field and still return a object.
This worked for me:
$qb = $repository->createQueryBuilder('i')
->select('i.name')
->...
Use partial objects like this to select fields
$qb = $this->createQueryBuilder('r')
->select(array('partial r.{id,address}'))
...
Put your field names between the brackets

doctrine querybuilder limit and offset

i'm a symfony beginner and i want to make a blog with the framework. i use repository to get home articles with this method :
public function getHomeArticles($offset = null, $limit = null)
{
$qb = $this->createQueryBuilder('a')
->leftJoin('a.comments', 'c')
->addSelect('c')
->addOrderBy('a.created', 'DESC');
if (false === is_null($offset))
$qb->setFirstResult($offset);
if (false === is_null($limit))
$qb->setMaxResults($limit);
return $qb->getQuery()
->getResult();
}
so in my database i have 10 articles. In my BlogController i use :
$blog = $em->getRepository('TestBlogBundle:Article')
->getHomeArticles(3,4);
With this i want 4 articles. But in return i also have one article.
What is the problem?
This is a know issue where setFirstResult() and setMaxResults() need to be use with care if your query contains a fetch-joined collection.
As stated about First and Max Result Items:
If your query contains a fetch-joined collection specifying the result
limit methods are not working as you would expect. Set Max Results
restricts the number of database result rows, however in the case of
fetch-joined collections one root entity might appear in many rows,
effectively hydrating less than the specified number of results.
Instead, you can:
Lazy load
use the Paginator (as stated by #Marco here)
Use Doctrine\Common\Collections\Collection::slice()

Resources