Symfony2, Doctrine2, left join (dql) and its result - symfony

So we have such dql
$oQuery = $this->createQueryBuilder('assets')
->addSelect('flags')
->addSelect('types')
->addSelect('groups')
->leftJoin('Site\MainBundle\Entity\123\invFlags', 'flags', Join::WITH, 'assets.flag = flags.flagID')
->leftJoin('Site\MainBundle\Entity\123\invTypes', 'types', Join::WITH, 'assets.typeID = types.typeID')
->leftJoin('Site\MainBundle\Entity\123\invGroups', 'groups', Join::WITH, 'types.groupID = groups.groupID');
if (!empty($aFilter)) {
$bFirst = true;
foreach ($aFilter as $sKey => $sValue) {
if ($bFirst) {
$oQuery->where($oQuery->expr()->eq('assets.' . $sKey, $sValue));
$bFirst = false;
continue;
} else {
if ($sValue === null) {
$oQuery->andWhere($oQuery->expr()->isNull('assets.' . $sKey));
}
}
}
}
return $oQuery->getQuery()->getResult();
We call it from repository and have result... BUT. In sql query ($oQuery->getQuery()->getSQL()) I see all columns and proper left join I need, but NOT in result. Why it happens? When I return '->getScalarResult()' I receive all needed data but like array and with prefix of tables
array(38) {
["assets_id"]=>string(4) "4558"
["assets_characterID"]=>string(8) "90206263"
["assets_parentItemID"]=>NULL
Can I anyhow get simple 'combined' object' with all get methods?
ADD:
Forgot to say that there are no relations in entities.

Related

Symfony - How to use LIKE with COALESCE in a Doctrine request?

I have some trouble with LIKE and COALESCE in a Doctrine request (not 100% sure that the trouble is there).
I would like to search inside a database with a filter that match only if exist, and only with a part of the value (for example find 'abc' with the filter set to 'ab').
This request works fine :
public function findUsers($entreprise, $filtres)
{
$filtre_name = $filtres['name'];
return $this->createQueryBuilder('users')
->where('users.entreprise = :entreprise')
->andWhere('users.name = COALESCE(:filtre_name, users.name)')
->setParameter('entreprise', $entreprise)
->setParameter('filtre_name', $filtre_name)
->orderBy('users.name', 'ASC')
->getQuery()
->getResult();
}
It return all the users of the company "entreprise" where "filtre_name" match (if not null) with "name" in the database. (If "filtre_name" is null, then the where match for all the database thanks to COALESCE).
I would like now to do the same thing but with "LIKE" instead of "=" because for now the name has to match perfectly and I would like a match for "abc" with only "ab" inside the filter for example.
public function findUsers($entreprise, $filtres)
{
$filtre_name = $filtres['name'];
return $this->createQueryBuilder('users')
->where('users.entreprise = :entreprise')
->andWhere('users.name LIKE COALESCE(:filtre_name, users.name)')
->setParameter('entreprise', $entreprise)
->setParameter('filtre_name', '%'.$filtre_name.'%')
->orderBy('users.name', 'ASC')
->getQuery()
->getResult();
}
The result is an error : "Warning: Undefined property: Doctrine\ORM\Query\AST\CoalesceExpression::$type".
I have find a solution. I build my queryBuilder with parameter only if they are not Null. So I remove COALESCE and now I can use LIKE.
public function findUsers($entreprise, $filtres)
{
$filtre_nom = $filtres['nom'];
$filtre_gestionnaire = $filtres['gestionnaire'];
$qb = $this ->createQueryBuilder('users');
$qb ->where('users.entreprise = :entreprise')
->setParameter('entreprise', $entreprise);
if ($filtre_nom != Null) {
$qb ->andWhere('users.nom LIKE :filtre_nom')
->setParameter('filtre_nom', '%'.$filtre_nom.'%');
}
if ($filtre_gestionnaire != Null) {
$qb ->andWhere('users.gestionnaire LIKE :filtre_gestionnaire')
->setParameter('filtre_gestionnaire', '%'.$filtre_gestionnaire.'%');
}
$qb->addorderBy('users.nom', 'ASC');
return $qb ->getQuery()
->getResult();
}

Get custom getter property into QueryBuilder object?

I have the following QueryBuilder snippet working as expected:
$restaurants = $qb->select('r.id, r.name, r.addressGpsLat, r.addressGpsLng, r.addressZip, r.addressCity')
->from('BackendBundle:Restaurant', 'r')
->orderBy('r.name', 'ASC');
In our Restaurant entity we calculate ratings based on a relation, which looks like this:
public function getRating()
{
$rating = 0;
$votes = count($this->votes);
foreach ($this->votes as $vote) {
$rating += $vote->getRating();
}
if ($votes == 0) {
return null;
}
return number_format(($rating / $votes), 2, ',', '.');
}
So my question: is there a way I get the result of the getRating() property into my QueryBuilder object? We need it to be a seperate attribute in our JSON response. E.g. { …, "rating":2.2, …}
Any advice on this? Thanks!

symfony 2 doctrine $query->getArrayResult() how to remove selected key->values from result

As I don't want id values from a select with createQuery, but the select command doesn't allow omitting id (primary key) from the actual query (using "partial") I need to remove the id's from the result from getArrayResult()
I made this small recursive key remover static class:
class arrayTool
{
public static function cleanup($array, $deleteKeys)
{
foreach($array as $key => $value )
{
if(is_array( $value))
{
$array[$key] = self::cleanup($array[$key], $deleteKeys);
} else {
if (in_array($key, $deleteKeys)) unset($array[$key]);
}
}
return $array;
}
}
Which is called by an array containing one or more keys to be removed from the result, of any array depth:
$array = arrayTool::cleanup($array, array('id', 'id2'));

Symfony2 / Typecasting query results to simpeler object

I am using Stof's DoctrineExtension bundle to retrieve my Tree, now I want to convert that tree to an array (which will then in turn get converted to json).
The format of NestedTreeRepository->childrenHierarchy() is not in the correct format though, I want to modify the output so only the node "title" property and the "id" property is returned, and put any children in a "children" subarray. In compliance with this format (JSON):
{
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
},
{
label: 'node2',
children: [
{ label: 'child3' }
]
}
}
I have tried to following code, this returns the same as childrenHierarchy() but would allow me to modify the query.
$query = $em
->createQueryBuilder()
->select('node')
->from('MyBundle:Page', 'node')
->orderBy('node.root, node.lft', 'ASC')
->getQuery()
;
$nodes = $query->getArrayResult();
[Do magic here]
$tree = $pagerepo->buildTree($nodes);
Is it possible to typecast every node into a much simpler object containing only the following property's:
id
title
a few other ints used for positioning
if I would then run that through json_encode() I would have exactly what I needed.
Any other solutions are of course welcome.
my code for this purpose (just made this a few hours ago)
it's a remake of stof's buildTreeArray function
in the controller (I'm writing this for symfony2):
function gettreeAction {
$query = .... // do your query
$tree = $this->buildTree($query->getArrayResult());
$response = new Response(json_encode($tree));
return $response;
}
private function buildTree($nodes)
{
$nestedTree = array();
$l = 0;
if (count($nodes) > 0) {
// Node Stack. Used to help building the hierarchy
$stack = array();
foreach ($nodes as $child) {
$item = array();
$item['name'] = $child['title'];
$item['id'] = 'page_'.$child['id'];
$item['level'] = $child['level'];
$item['children'] = array();
// Number of stack items
$l = count($stack);
// Check if we're dealing with different levels
while($l > 0 && $stack[$l - 1]['level'] >= $item['level']) {
array_pop($stack);
$l--;
}
// Stack is empty (we are inspecting the root)
if ($l == 0) {
// Assigning the root child
$i = count($nestedTree);
$nestedTree[$i] = $item;
$stack[] = &$nestedTree[$i];
} else {
// Add child to parent
$i = count($stack[$l - 1]['children']);
$stack[$l - 1]['children'][$i] = $item;
$stack[] = &$stack[$l - 1]['children'][$i];
}
}
}
return $nestedTree;
}
works perfectly with jqTree...
I have solved it as following:
public function getPageTreeAction() {
$pagerepo = $this->getDoctrine()->getRepository('MyBundle:Page');
$em = $this->getDoctrine()->getEntityManager();
$query = $em
->createQueryBuilder()
->select('node')
->from('MyCorpBundle:Page', 'node')
->orderBy('node.root, node.lft', 'ASC')
->getQuery();
$flatnodearray = $query->getArrayResult();
$flatsimplenodearray = array();
foreach ($flatnodearray as $currentNode) {
$currentSimpleNode = array();
$currentSimpleNode['id'] = $currentNode['id'];
$currentSimpleNode['lft'] =$currentNode['lft'];
$currentSimpleNode['rgt'] = $currentNode['rgt'];
$currentSimpleNode['lvl'] = $currentNode['lvl'];
$currentSimpleNode['title'] = $currentNode['title'];
$flatsimplenodearray[] = $currentSimpleNode;
}
$tree = $pagerepo->buildTree($flatsimplenodearray);
$response = new Response(json_encode($tree));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
I would use the Stofs Repository function to get the nodes in an hierarchical array:
$repo = $em->getRepository('MyBundle:Page');
$arrayTree = $repo->childrenHierarchy();
And I think there is no other solution than modify that array manually. After you have removed some properties that you dont need, you can json_encode the array and return it.

unique slugs with multi entities in symfony2

I have a small symfony2 application where a user can create pages. Each page should be accessible via the route /{user_slug}/{page_slug}. I have the entities user and page and I use the sluggable behavior for both entities. In order to find the correct page the combination of user_slug and page_slug has to be unique.
What is the best way to check that the combination of user_slug and page_slug is uniqe?
Try this in your prepository:
public function findByUsernameAndSlug($username, $slug)
{
$em = $this->getEntityManager();
$query = $em->createQuery("
SELECT g
FROM Acme\PagesBundle\Entity\Page p
JOIN p.owner u
WHERE u.username = :username
AND p.slug = :slug
")
->setParameter('username', $username)
->setParameter('slug', $slug);
foreach ($query->getResult() as $goal) {
return $goal;
}
return null;
}
Before you persist the entity in your service-layer check whether given combination of user and page slug are unique, if not modify the page slug (append -2 or something like that) or throw an exception:
public function persistPage(Page $page) {
$userSlug = $page->getUser()->getSlug();
$pageSlug = $page->getSlug();
if ($this->pagesRepository->findOneBySlugs($userSlug, $pageSlug) != null) {
// given combination already exists
throw new NonUniqueNameException(..);
// or modify the slug
$page->setSlug($page->getSlug() . '-2');
return $this->persistPage($page);
}
return $this->em->persist($page);
}
// PagesRepository::findOneBySlugs($userSlug, $pageSlug)
public function findOneBySlugs($userSlug, $pageSlug) {
$query = $this->em->createQueryBuilder('p')
->addSelect('u')
->join('p.user', 'u')
->where('p.slug = :pageSlug')
->where('u.slug = :userSlug;)
->getQuery();
$query->setParameters(combine('userSlug', 'pageSlug'));
return $query->getSingleResult();
}

Resources