I have a question about symfony2.
I have a project and I am using databases with it. I use for the most part Doctrine2 and entity classes. I like the entity class object database stuff, very handy etc.
My question is, is there a way to perform normal SQL in symfony? I always get an exception when I try to use standard SQL. I am having trouble with joins in doctrine2, so i would rather use normal SQL for that.
My join would look like this in SQL:
SELECT DISTINCT Document . *
FROM Document
INNER JOIN DocumentGruppe ON Document.id = DocumentGruppe.dokId
INNER JOIN UserGruppe ON DocumentGruppe.gruppenId = UserGruppe.gruppenId
WHERE UserGruppe.userId =9
The where clause at the end is just for testing. If I use doctrine with it's DQL it always says that there is an exception: The Variable DocumentGruppe was not defined before.
Here is my DQL query:
$test = $em->createQuery(
'SELECT DISTINCT d
FROM AcmeDocumentBundle:Document d
INNER JOIN DocumentGruppe dg ON d.id = dg.dokId
INNER JOIN UserGruppe ug ON dg.gruppenId = ug.gruppenId
WHERE ug.userId =9
'
);
Does anyone know a workaround or a way to use this doctrine2 stuff to work with joins?
Every JOINED tables must be declared as associations in mapping... How is your entity defined ? Show us your mapping file (Document.php if annotation, or Resources/config/doctrine/document;xml or yml if XMl or YAML).
Your request will be something like that :
$test = $em->createQuery(
'SELECT DISTINCT d
FROM AcmeDocumentBundle:Document d
INNER JOIN d.documentGruppen dg
INNER JOIN d.userGruppen ug
WHERE ug.userId =9
'
);
Related
I have a problem with EntityGraph on CriteriaApi. Here is what I am doing:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
EntityGraph<LabelFamily> entityGraph = entityManager.createEntityGraph(LabelFamily.class);
entityGraph.addAttributeNodes("childs");
CriteriaQuery<LabelFamily> criteria = builder.createQuery(LabelFamily.class);
Root<LabelFamily> fromPost = criteria.from(LabelFamily.class);
criteria.select(fromPost);
criteria.where(specification.toPredicate(fromPost, criteria, builder));
return entityManager.createQuery(criteria)
.setHint("javax.persistence.fetchgraph", entityGraph)
.getResultList();
I have some specifications in my criteria that defines an inner join on the "childs" (It's legacy code). Then i want to load every child entity (defined by an OneToMany) in one request, therefore i use EntityGraph.
My problem is this: EntityGraph use the inner join declared in my specifications but it is used to filter the result of the request.
Here is the sql request to better understand the underlying problem:
select distinct labelfamil0_.*,
childs1_.*
from label_family labelfamil0_
inner join label_family childs1_ on labelfamil0_.id = childs1_.family_parent_id
inner join flow flows2_ on childs1_.id = flows2_.label_family_project_id
cross join project project3_
where X
But as we can see the relation childs1_ is filtered through an other inner join.
The result I want is something like this:
select distinct labelfamil0_.*,
childs2_.*
from label_family labelfamil0_
inner join label_family childs2_ on labelfamil0_.id = childs2_.family_parent_id
inner join label_family childs1_ on labelfamil0_.id = childs1_.family_parent_id
inner join flow flows2_ on childs1_.id = flows2_.label_family_project_id
cross join project project3_
where X
I want to filter my "parent" entity using a filter on its child, but then i want all the childs of my resulting parents.
I have the correct result if I do not use an entityGraph and I simply EAGER (or LAZY) fetch the relation.
So if I simply do that:
parent.getChilds()
Without using EntityGraph, I get all childs as I wanted
I am using MySQL 5.7, i do not know if that is of any importance.
Version of Spring: 2.5.8 (I use Hibernate imported from the parent pom)
Example we have Table A and B.
In table A we have data field with some json data.
How to be build Active Record relation using JSON_VALUE condition?
In plain sql it would look like
SELECT * FROM A
LEFT JOIN B ON B.id = JSON_VALUE(A.data, '$.paramName')
You could use a findBySql method
$sql = "SELECT * FROM A
LEFT JOIN B ON 'B.id = JSON_VALUE(A.data, " . $paramName . ")";
$model = YourModel::findBySql($sql)->all();
As far as i could find framework it self dosnot support sql function execution result as join relation. For now best way for me is to execute subqueries and use populateRelation method. ofcouse if youll find better way i would be glad to know.
How to write this query in codeigniter
SELECT U.username,U.user_id
FROM storylikes S, user U
WHERE U.user_id=S.user_id_fk AND S.user_id_fk='$id'
try this :
$this->db->select('u.username, u.user_id');
db->where('u. user_id = s.user_id_fk');
$this->db->where('s.user_id_fk = '.$id);
$query = $this->db->get('storylikes s, user u');
use $your_variable = $query->result(); for the result
you should use joins instead of this query
$this->db->select('username,user_id');
$this->db->from('user');
$this->db->join('storylike','storylike.user_id_fk = user.user_id');
$this->db->where('storylike.user_id','$id');
as long as the db helper is loaded... You dont need to do anything special
$query = $this->db->query('SELECT U.username,U.user_id FROM storylikes S, user U WHERE U.user_id=S.user_id_fk AND S.user_id_fk=$id);
Using a cartesian (cross join) by doing FROM with 2 tables can cause some unruly results if not used 'correctly'
I suggest that if you are trying to just join tables together your SQL should be
SELECT U.username,U.user_id
FROM storylikes S, user U
INNER JOIN user U ON S.user_id = U.user_id_fk
WHERE S.user_id_fk=$id
CI querybuilder for this would be:
$query = $this->db->select('U.username,U.user_id')
->join('user U', 'S.user_id = U.user_id_fk', 'inner')
->where('S.user_id', $id)
->get('user U');
Using the correct join for the correct requirements is key;
INNER JOIN to ensure both FROM and the JOIN table match 1 for 1...
LEFT JOIN if you want to ensure you have all data from your FROM table and any without results in the JOIN table show up as NULL
RIGHT JOIN (opposite of left), to grab all data from the JOIN table and only matching data from the FROM table.
CROSS (CARTESIAN) JOIN when you want to ... frankly... mash the data together... A CROSS JOIN will also function like an INNER JOIN when you stipulate criteria in the WHERE statement (like you did) but still, use the correct JOIN for the correct usage-case.
There are other available joins but those are the basics.
Can you use Doctrine QueryBuilder to INNER JOIN a temporary table from a full SELECT statement that includes a GROUP BY?
The ultimate goal is to select the best version of a record. I have a viewVersion table that has multiple versions with the same viewId value but different timeMod. I want to find the version with the latest timeMod (and do a lot of other complex joins and filters on the query).
Initially people assume you can do a GROUP BY viewId and then ORDER BY timeMod, but ORDER BY has no effect on GROUP BY, and MySQL will return random results. There are a ton of answers out there (e.g. here) that explain the problem with using GROUP and offer a solution, but I am having trouble interpreting the Doctrine docs to find a way to implement the SQL with Doctrine QueryBuilder (if it's even possible). Why don't I just use DQL? I may have to, but I have a lot of dynamic filters and joins that are much easier to do with QueryBuilder, so I wanted to see if that's possible.
Sample MySQL to Reproduce in Doctrine QueryBuilder
SELECT vv.*
FROM view_version vv
#inner join only returns where the result sets overlap, i.e. one record
INNER JOIN (
SELECT MAX(timeMod) maxTimeMod, viewId
FROM view_version
GROUP BY viewId
) version ON version.viewId = vv.viewId AND vv.timeMod = version.maxTimeMod
#join other tables for filter, etc
INNER JOIN view v ON v.id = vv.viewId
INNER JOIN content_type c ON c.id = v.contentTypeId
WHERE vv.siteId=1
AND v.contentTypeId IN (2)
ORDER BY vv.title ASC;
Theoretical Solution via Query Builder (not working)
I am thinking that the JOIN needs to inject a DQL statement, e.g.
$em = $this->getDoctrine()->getManager();
$viewVersionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryMax = $viewVersionRepo->createQueryBuilder()
->addSelect('MAX(timeMod) AS timeModMax')
->addSelect('viewId')
->groupBy('viewId');
$queryBuilder = $viewVersionRepo->createQueryBuilder('vv')
// I tried putting the query in a parenthesis, to no avail
->join('('.$queryMax->getDQL().')', 'version', 'WITH', 'vv.viewId = version.viewId AND vv.timeMod = version.timeModMax')
// Join other Entities
->join('e.view', 'view')
->addSelect('view')
->join('view.contentType', 'contentType')
->addSelect('contentType')
// Perform random filters
->andWhere('vv.siteId = :siteId')->setParameter('siteId', 1)
->andWhere('view.contentTypeId IN(:contentTypeId)')->setParameter('contentTypeId', $contentTypeIds)
->addOrderBy('e.title', 'ASC');
$query = $queryBuilder->getQuery();
$results = $query->getResult();
My code (which may not match the above example perfectly) outputs:
SELECT e, view, contentType
FROM Gutensite\CmsBundle\Entity\View\ViewVersion e
INNER JOIN (
SELECT MAX(v.timeMod) AS timeModMax, v.viewId
FROM Gutensite\CmsBundle\Entity\View\ViewVersion v
GROUP BY v.viewId
) version WITH vv.viewId = version.viewId AND vv.timeMod = version.timeModMax
INNER JOIN e.view view
INNER JOIN view.contentType contentType
WHERE e.siteId = :siteId
AND view.contentTypeId IN (:contentTypeId)
ORDER BY e.title ASC
This Answer seems to indicate that it's possible in other contexts like IN statements, but when I try the above method in the JOIN, I get the error:
[Semantical Error] line 0, col 90 near '(SELECT MAX(v.timeMod)': Error: Class '(' is not defined.
A big thanks to #AdrienCarniero for his alternative query structure for sorting the highest version with a simple JOIN where the entity's timeMod is less than the joined table timeMod.
Alternative Query
SELECT view_version.*
FROM view_version
#inner join to get the best version
LEFT JOIN view_version AS best_version ON best_version.viewId = view_version.viewId AND best_version.timeMod > view_version.timeMod
#join other tables for filter, etc
INNER JOIN view ON view.id = view_version.viewId
INNER JOIN content_type ON content_type.id = view.contentTypeId
WHERE view_version.siteId=1
# LIMIT Best Version
AND best_version.timeMod IS NULL
AND view.contentTypeId IN (2)
ORDER BY view_version.title ASC;
Using Doctrine QueryBuilder
$em = $this->getDoctrine()->getManager();
$viewVersionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryBuilder = $viewVersionRepo->createQueryBuilder('vv')
// Join Best Version
->leftJoin('GutensiteCmsBundle:View\ViewVersion', 'bestVersion', 'WITH', 'bestVersion.viewId = e.viewId AND bestVersion.timeMod > e.timeMod')
// Join other Entities
->join('e.view', 'view')
->addSelect('view')
->join('view.contentType', 'contentType')
->addSelect('contentType')
// Perform random filters
->andWhere('vv.siteId = :siteId')->setParameter('siteId', 1)
// LIMIT Joined Best Version
->andWhere('bestVersion.timeMod IS NULL')
->andWhere('view.contentTypeId IN(:contentTypeId)')->setParameter('contentTypeId', $contentTypeIds)
->addOrderBy('e.title', 'ASC');
$query = $queryBuilder->getQuery();
$results = $query->getResult();
In terms of performance, it really depends on the dataset. See this discussion for details.
TIP: The table should include indexes on both these values (viewId and timeMod) to speed up results. I don't know if it would also benefit from a single index on both fields.
A native SQL query using the original JOIN method may be better in some cases, but compiling the query over an extended range of code that dynamically creates it, and getting the mappings correct is a pain. So this is at least an alternative solution that I hope helps others.
The Doctrine2 DQL allows for the following SQL:
$query = $em->createQuery('SELECT u.id FROM CmsUser u WHERE :groupId MEMBER OF u.groups');
$query->setParameter('groupId', $group);
$ids = $query->getResult();
Is the MEMBER OF clause supported by a MySQL database?
Moreover, focusing on the previous example, is $group the id of the entity "Group" or is an instance of the "Group" entity itself?
MEMBER OF is a pure ORM clause and has nothing to with the DBAL therefor it should work with any vendors.
MEMBER OF is supposed to accept an Entity but may accept an identifier too.
I checked SQL generated with MEMBER OF clause:
SELECT *fields*
FROM Page p0_
WHERE
EXISTS (
SELECT 1
FROM post_rubric p4_
INNER JOIN Page p3_
ON p4_.rubric_id = p3_.id
WHERE p4_.post_id = p0_.id AND p3_.id = ?
)
Thats a way Doctrine translates MEMBER OF clause into SQL