Lost column definitions with joins, in twig - symfony

I've got a query set up in doctrine that I'm confident does what it's supposed to...it joins several tables so we can skip a bunch of extra queries to get data through foreign keys:
$posts = $this->getDoctrine()->getManager()
->createQuery('
SELECT a AS article, COUNT(c) AS comments
FROM ArticleBundle:Article a
LEFT JOIN CommentsBundle:Comment c WITH (c.article = a.id)
LEFT JOIN ImageBundle:Image i WITH (i.id = a.image)
GROUP BY a.id
ORDER BY a.timestamp DESC
')
->getResult();
I'm able to access the data quite succesfully, like so:
{% for post in posts %}
<div id="news-story">
<div class="title">{{ post.article.title }}</div>
<div class="comments">{{ post.comments }}</div>
...
{% endfor %}
The issue is, as soon as I add a second column ("i AS image") to the field list, it fails. I get the following message...
Item "article" for "Array" does not exist in ...
...and I can't quite figure out why. I would hope that I would be able to access the variables like {{ post.article.title }} and {{ post.image.path }} (which does exist, by the way)...but I can't.
Any thoughts would be greatly appreciated.
Thanks!

I think your confusion arises from the fact that you have created a query that returns a scalar result: COUNT(c)... along with objects.
This means that instead of returning an array of Article entities, as you expect (you are trying to use post.image) you actually get an associative array, where each member contains the count - post.comments and the article entity post.article.
n.b. If you want to access images associated with the article, you will need to use post.article.image.
I'll try and illustrate:
Your expecting an array of article objects that you can iterate over:
array(
[0] => ArticleEntity,
[1] => ArticleEntity,
[2] => ArticleEntity
)
What you actually get from your query
array(
[0] => array(
'article' => ArticleEntity,
'comments'=> 10
),
[1] => array(
'article' => ArticleEntity,
'comments'=> 3
),
[2] => array(
'article' => ArticleEntity,
'comments'=> 0
)
)
If you remove the scalar part of your query COUNT(c) AS comments then you will get the first example, leave it in and you will get the second example.
The Doctrine documentation on pure/mixed results is some good reading/explanation on this.
As a side note is there any reason you are writing raw SQL. For a start you can re-write the query using DQL to leverage some of the power Doctrine offers.
$qb = $this->getDoctrine()->getManager()->createQueryBuilder();
$qb
->select('article', 'image', 'COUNT(comments) as comment_count')
->from('ArticleBundle:Article', 'article')
->leftJoin('article.comment', 'comments')
->leftJoin('article.image', 'image')
->groupBy('article.id')
->orderBy('article.timestamp', 'DESC')
;
$posts = $qb->getQuery()->getResult();

Related

getResult() method Doctrine

What is the format of the array returned using the getResult() method in the following example, using Doctrine and Symfony2:
$query = $this->_em->createQuery('SELECT p.id, p.nameProduct FROM ArkiglassProductBundle:Product p');
return $query->getResult();
And I would like to know how to access each case and print every row.
<?php
$query = $em->createQuery('SELECT u.username, u.name FROM CmsUser u');
$users = $query->getResults(); // array of CmsUser username and name values
echo $users[0]['username'];
taken from the doctrine documentation
Are you asking about "0" in $users[0]?
I hope I am not misunderstanding your question.
getResults() returns an array of database results. Once you've given your array a name you can access each element using the index.
Of course if you want to loop over it you will probably use a foreach loop so you won't have to use the index:
$products = $query->getResults();
foreach ($products as $product){
echo $product->id.' '.$product->nameProduct;
}
This said... this pseudo code is here for the sake of explanation. If you are using Symfony you will have to display your results in a view file, probably using twig like Manuszep did in his example.
In his case you will have to use a for in loop like he did.
The query returns an array of Users:
array(
0 => array(username => 'aaa', name => 'bbb'),
1 => array(username => 'ccc', name => 'ddd')
)
So the $users[0] element means the first user in the list.
You can use a for loop to iterate:
{% for user in users %}
{{ user.username }} - {{ user.name }}
{% endfor %}

Symfony2 KnpPaginator bundle, cannot do sorting with 2 paginated tables in my view

I have 1 controller that passes 2 paginated set of results to a twig (as 2 arrays representing tables) using KnpPaginator Bundle.
While both tables show up and are paginated, I am not able to sort any of them.
Edit: When I change page of table 1, table 2's page also changes to that page number.
Trying to sort either of them returns: There is no component aliased by [t] in the given Query or There is no component aliased by [r] in the given Query.
The controller:
$em = $this->getDoctrine()->getEntityManager();
$pageSize = $this->container->getParameter('page_size');
$paginator = $this->get('knp_paginator');
/* Queries */
$queryTest = $em
->createQuery('
SELECT t FROM PanasonicTestEtAvisBundle:Test t
JOIN t.product p
WHERE p.id = :id
AND t.isDeleted = :isdeleted
ORDER BY t.creationDate DESC'
)->setParameter('id', $id)
->setParameter('isdeleted', '0');
$queryReview = $em
->createQuery('
SELECT r FROM PanasonicTestEtAvisBundle:Review r
JOIN r.product p
WHERE p.id = :id
AND r.isDeleted = :isdeleted
ORDER BY r.creationDate DESC'
)->setParameter('id', $id)
->setParameter('isdeleted', '0');
/* paginated results */
$paginationTest = $paginator->paginate($queryTest, $this->get('request')->query->get('page', 1), $pageSize);
// compact('paginationTest');
$paginationReview = $paginator->paginate($queryReview, $this->get('request')->query->get('page', 1), $pageSize);
// compact('paginationReview');
// compact('pagination');
return $this->render('PanasonicTestEtAvisBundle:Product:show.html.twig', array(
'paginationTest' => $paginationTest,
'paginationReview' => $paginationReview
));
The error shows up only when I pass both pagination (paginationTest & paginationReview), if I pass only one it works flawlessly.
Anyone can tell me how I can solve this problem?
Your example didn't work, because you use the same page variable "page" for both of pagination sets.
If you want to use two or more pagination sets in the same place, you must do this:
Your Controller:
...
$pagename1 = 'page1'; // Set custom page variable name
$page1 = this->get('request')->query->get($pagename1, 1); // Get custom page variable
$paginationReview = $paginator->paginate(
$queryReview,
$page1,
$pageSize,
array('pageParameterName' => $pagename1)
);
$pagename2 = 'page2'; // Set another custom page variable name
$page2 = this->get('request')->query->get($pagename2, 1); // Get another custom page variable
$paginationReview = $paginator->paginate(
$queryReview,
$page2,
$pageSize,
array('pageParameterName' => $pagename2)
);
return $this->render('PanasonicTestEtAvisBundle:Product:show.html.twig', array(
'paginationTest' => $paginationTest,
'paginationReview' => $paginationReview
));
Your view:
// PanasonicTestEtAvisBundle:Product:show.html.twig
{# First set #}
{% for test in paginationTest %}
<tr>
<td>{{ test.id }}</td>
</tr>
{% endfor %}
{{ paginationTest.render()|raw }}
{# Second set #}
{% for review in paginationReview %}
<tr>
<td>{{ review.id }}</td>
</tr>
{% endfor %}
{{ paginationReview.render()|raw }}
Enjoy!
It's 2017 and I got into similar problem. Just for reference, I'm using:
"knplabs/knp-paginator-bundle": "^2.5",
I was trying to run several functional tests with Symfony's crawler. Some of them were using Knp Paginator with such options:
[
'defaultSortFieldName' => 'voucher.id',
'defaultSortDirection' => 'DESC',
'wrap-queries' => true,
'defaultFilterFields' => ['voucherText.title']
]
while others with those options:
[
'defaultSortFieldName' => 'event.id',
'defaultSortDirection' => 'DESC',
'wrap-queries' => true,
'defaultFilterFields' => ['event.name', 'eventText.description']
]
Problem appeared with sorting parameter name, because both Paginators were using defaul 'sort' key, taking it from global $_GET array - you can check it yourself: Knp\Component\Pager\Event\Subscriber\Sortable\Doctrine\ORM\QuerySubscriber.
However the enigmatic part was the error being thrown:
UnexpectedValueException: There is no component aliased by [event] in the given Query
In order to avoid this weirdos/crappiness, I found a 'sortFieldParameterName' option, which you can add to all of your Paginators' options:
[
'sortFieldParameterName' => 'sort_event',
'defaultSortFieldName' => 'event.id',
'defaultSortDirection' => 'DESC',
'wrap-queries' => true,
]

drupal table update query not working

I have extended the menu_custom table in drupal to add role1, role2 and role3 fields and now when I try to update the values using the following query. The fields are not getting updated and the drupal shows no error in executing the query. I think there is a problem with the condition but I could not figure out where the problem is. Kindly help me to resolve this problem.
db_update('menu_custom')
->fields(array('role1','role2','role3'))
->values(array(
'role1' => $form_state['values']['role1'],
'role2' => $form_state['values']['role2'],
'role3' => $form_state['values']['role3'],
))
->condition('title',$form_state['values']['title'])
->execute();
}
I'm not sure why your code doesn't work but I always use an associative array for the fields and it works fine:
$fields = array(
'role1' => $form_state['values']['role1'],
'role2' => $form_state['values']['role2'],
'role3' => $form_state['values']['role3'],
);
db_update('menu_custom')
->fields($fields)
->condition('title', $form_state['values']['title'])
->execute();

how to populate text area with database value before form load in drupal 7?

I have used hook_form_FORM_ID_alter( ) function to alter a menu_edit_menu form to add few fields. I ve stored the values from those fields in a table when submit action is performed. what I need now is when the particular form entry is loaded again for editing. The fields should be populated with the data in the database before form loads. Can anyone tell me how this could be done.
I have tried using hook_validate ( ) function but the function is called when the submit action is performed which is of no use in this regard. I have to perform the database query before the form gets rendered to populate the text fields with data from tables. Kindly provide me some insight into how this could be accomplished.
I have a problem with the sql select query as well/
$query = db_select('menu_custom');
$query
->fields(array('menu_name','role1','role2','role3'))
->condition('check',1,'=')
->conditon('title',$form_state['values']['title'])
->execute();
foreach($query as $record)
{
$mname = $result ->menu_name;
$rl1 = $result -> role1;
$rl2 = $result -> role2;
$rl3 = $result -> role3;
dpm($mname." ".$rl1);
}
I am getting error that my field specification is wrong but I cant figure out the problem there.
This is a bit too long for a comment so I'll put it here:
The only error you've got in your query is that the first argument passed to the fields() function needs to be the name/alias of the table from which the fields are coming. So your query should probably look something like this:
$query = db_select('menu_custom')
->fields('menu_custom', array('menu_name','role1','role2','role3'))
->condition('check',1,'=')
->conditon('title',$form_state['values']['title'])
->execute();
You get the data from your database in your form function, and put them as default_value
$name = db_query(YOUR_SQL);
$form['first_name'] = array(
'#title' => t('First Name'),
'#type' => 'textfield',
'#default_value' => $name,
);
is this what you meant?

Add Table Join, Where, and Order By to Views Query in views_query_alter()

I am trying to modify the query for Views in Drupal (Views version 3, Drupal version 7).
What I want to do is change the query prior to running such that it LEFT JOINs a table in which I have weights assigned to the nodes.
If I was to write the query I want in SQL, it would look like this:
SELECT a.nid, a.title, a.description
FROM node a
LEFT OUTER JOIN node_weights b
ON a.nid = b.nid
WHERE b.uid = $uid
ORDER BY b.weight DESC
This query works like a champ when I run it in the query analyzer. So, now I need to make it work in my module.
I've seen multiple approaches detailed on various blogs for different ways to modify View queries, but they seem to be addressing different versions of Views. So it is very confusing to try to determine whether anything I'm looking at could even possibly work for my application.
It seems that I need to use a MODULE_NAME_views_tables() function to tell Views what the relationship is between the table I want to join and the node table.
I've added the following functions to MODULE_NAME.views.inc:
function MODULE_NAME_views_tables() {
$tables['node_weights'] = array(
"name" => "node_weights",
"join" => array(
"left" => array(
"table" => "node",
"field" => "nid"
),
"right" => array(
"field" => "nid"
),
),
);
return $table;
}
This does seem to be working because when I use Krumo to look at the query array, I see my "node_weights" table in the "table_queue" element.
In the views_query_alter() function, I'd like it to work something like this:
function MODULE_NAME_views_query_alter(&$view, &$query) {
$uid = $_COOKIE['uid'];
$view->query->add_relationship('node_weights', new views_join('node_weights', 'nid', 'node', 'nid','LEFT'));
$view->query->add_where('node_weights', "node_weights.uid", $uid);
krumo($query);
}
This function barfs pretty badly. Although my join table is appearing in the $view object, the add_relationship method is throwing an error for a 3rd argument, but I don't see any examples online that have 3 arguments so I don't know what it's missing.
Also, I'm pretty sure my add_where method isn't correct, but I don't know what the inputs should actually be. This is just a blind guess.
The bottom line is that I want to join the node table to my node_weights table, and then make sure my weights are used in the query to sort the results in a descending fashion where the user id = the user id in my table, and the tables are joined on the nid field.
Thanks in advance.
WHEREs are pretty easy to add once you've got the JOIN in. You can both in a query alter (Drupal 7).
function MODULE_NAME_views_query_alter(&$view, &$query){
// Only alter the view you mean to.
if($view->name == 'VIEW NAME' && $view->current_display == 'DISPLAY'){
// Create the join.
$join = new views_join();
$join->table = 'table_name';
$join->field = 'entity_id';
$join->left_table = 'node';
$join->left_field = 'nid';
$join->type = 'left';
// Add the join the the view query.
$view->query->add_relationship('table_name', $join, 'node');
// Add the where.
$view->query->where[1]['conditions'][] = array(
'field' => 'table_name.collumn_name',
'value' => 'value',
'operator' => '='
);
}}
I found the OP's comments helpful in creating a join in the hook_views_query_alter function, so I wanted to put the parts I found useful in a more digestible answer.
I was using Views 2x on Drupal 6x, but I assume it would be very similar to use on D7 Views 2.
The OP mentions describing the relationship of the join in hook_views_table. This wasn't necessary for me, as I was not linking to a custom table, but one that existed in core.
The join creation in the HOOK_views_query_alter() function was very helpful though:
$join = new views_join;
$join->construct('table_name',
'node', // left table
'nid', // left field
'nid', // field
)
See views_join::construct documentation for more information. In particular, I didn't need to use the 'extra' parameter that the OP used. Perhaps this is necessary with a custom table.
Finally, add the join to the query, and whatever other elements are needed from it:
// Add join to query; 'node' is the left table name
$view->query->add_relationship('table_name',$join,'node');
// Add fields from table (or where clause, or whatever)
$view->query->add_field('table_name','field_name');
...
You already have the $query in parameters, so you can just do:
myhook_views_query_alter(&$view, &$query) {
if ($view->name = ....) {
$join = new views_join();
$join->construct(
...
);
$query
->add_relationship(...)
->add_where(...)
->add_orderby(...)
...
;
}
No need to use $view->query->...

Resources