I have a search query that works perfect (I stripped some unimportant code):
$posts = new Post;
if(Input::has('query')) {
// Search for posts with title or tags matching the given criteria
$posts = $posts
->addSelect(DB::raw("MATCH(posts.title) AGAINST (?) as post_score"))
->addSelect(DB::raw("MATCH(tags.title) AGAINST (?) as tag_score"))
->where(function($query) {
return $query
->whereRaw('MATCH(posts.title) AGAINST (?)')
->orWhereRaw('MATCH(tags.title) AGAINST (?)');
})
->orderBy(DB::raw('post_score+tag_score'), 'desc')
->setBindings([ $input['query'], $input['query'], $input['query'], $input['query'] ]);
}
But as soon as I add this piece of code before the above if() statement:
if(Input::has('filter')) {
$posts = $posts->whereType($input['filter']); //Filter type by either 'article' or 'question'
}
... I get this error:
[2014-11-04 19:28:18] production.ERROR: PDO error: SQLSTATE[HY093]: Invalid parameter number (SQL: select `posts`.*, COALESCE(SUM(post_votes.rating), 0) as rating, MATCH(posts.title) AGAINST (css) as post_score, MATCH(tags.title) AGAINST (css) as tag_score from `posts` left join `post_tags` on `post_tags`.`post_id` = `posts`.`id` left join `tags` on `tags`.`id` = `post_tags`.`tag_id` left join `post_votes` on `post_votes`.`post_id` = `posts`.`id` where `type` = css and (MATCH(posts.title) AGAINST (css) or MATCH(tags.title) AGAINST (?)) group by `posts`.`id` order by post_score+tag_score desc, `views` desc) [] []
I entered css as search query, the type should be filtered on question. As you can see the variables aren't binded right (don't know the right word for this). How could this be? I also tried this which doesn't work:
->where(function($query) use ($input) {
return $query
->whereRaw('MATCH(posts.title) AGAINST (?)', [$input['query']])
->orWhereRaw('MATCH(tags.title) AGAINST (?)', [$input['query']]);
})
Thanks in advance.
This is what you need:
if(Input::has('filter')) {
$posts->whereType($input['filter']); //Filter type by either 'article' or 'question'
}
if(Input::has('query')) {
// no need for this:
// $posts = $posts
// just:
$posts
->addSelect(DB::raw("MATCH(posts.title) AGAINST (?) as post_score"))
->addBinding($input['query'], 'select')
->addSelect(DB::raw("MATCH(tags.title) AGAINST (?) as tag_score"))
->addBinding($input['query'], 'select')
->where(function($query) use ($input) {
$query
->whereRaw('MATCH(posts.title) AGAINST (?)', [$input['query']])
->orWhereRaw('MATCH(tags.title) AGAINST (?)', [$input['query']]);
})
->orderBy(DB::raw('post_score+tag_score'), 'desc')
}
Related
Hello,
I am working on a Posts and Comment Models response API in Code Igniter!
MY Controller:
public function getPosts()
{
if (isset($_POST["getPosts"]))
{
$data = $this->api_model->getPosts();
$json_response2 = array('status' => 'success', 'postList' => $data->result_array());
echo json_encode($json_response2);
}
else
{
$data['status'] = 'error';
echo json_encode($data);
}
}
My Model:
public function get_posts()
{
$this->db->order_by('postID', 'DESC');
$query = $this->db->get('posts');
return $query->result_array();
}
MYSQL Tables:
for Posts---
postID | postTitle | postBody
for Comments---
commentID | postID | commentBody
I want to Get Comments Count for post Array in API response to display list of posts and comments Count for that Post?
ThankYou!
This is the query needed for you to get all those data in single query. It will join both table with left join clause and will give you the comment count as well.
SELECT
p.`postID`,
`postTitle`,
`postBody`,
COUNT(c.commentID) AS comment_cnt
FROM
`Posts` AS p
LEFT JOIN `Comments` AS c
ON p.postID = c.postID
GROUP BY c.postID
ORDER BY p.postID DESC ;
N:B left join is mandatory, otherwise it will not give you zero
commented post.
to convert it to codeigniter you can write it as.
$this->db->from('Posts p');
$this->db->join('Comments c','p.postID = c.postID','left');
$this->db->group_by('c.postID');
$this->db->order_by('p.postID', 'DESC');
$this->db->select('p.*,COUNT(c.commentID) AS comment_cnt');
$query = $this->db->get();
$res = $query->result_array();
Hope this would help you.
You need to join your comments table to get count and group by your postID.
Modify your get_posts() function in model as follows:
$this->db->from('posts');
$this->db->select("posts.*, count(*) as comments_count");
$this->db->join('comments', 'posts.postID = comments.postID');
$this->db->group_by('posts.postID');
$this->db->order_by('postID', 'DESC');
return $this->db->get()->result_array();
Also, you are using result_array() twice which is wrong. Change your controller line
$json_response2 = array('status' => 'success', 'postList' => $data->result_array());
as
$json_response2 = array('status' => 'success', 'postList' => $data);
because you are already getting result from model.
Also, use the correct name for function in controller, $this->api_model->get_posts();
Hope it helps.
I am trying to get collections that are non-empty, i.e. have at least 1 object. Collection entity has OneToMany relationship with Object entity. I am using KNP paginator to paginate result. This is my function:
public function fetchAction(Request $request){
$em = $this->getDoctrine()->getManager();
$page = $request->get('page', 1);
$limit = 10;
$collections = $em->createQueryBuilder()
->select('c')
->add('from', 'CollectionBundle:Collection c LEFT JOIN c.object o')
->having('COUNT(o.id)>0')
->orderBy('c.date', 'DESC')
->getQuery();
$collections = $this->get("knp_paginator")->paginate($collections, $page, $limit);
return $this->render('CollectionBundle:Collection:fetch.html.twig', [
'collections' => $collections
]);
}
Error
I keep getting following error
Cannot count query that uses a HAVING clause. Use the output walkers for pagination
Without 'Having' clause everything works fine, but I must get non-empty collections.
wrap-queries solved this problem
$collections = $this->get("knp_paginator")->paginate($collections, $page, $limit,array('wrap-queries'=>true));
You can implement the Manual counting, as described here in the doc.
As example, you can modify your code as follow:
$count = $em->createQueryBuilder()
->select('COUNT(c)')
->add('from', 'CollectionBundle:Collection c LEFT JOIN c.object o')
->having('COUNT(o.id)>0')
->orderBy('c.date', 'DESC')
getSingleScalarResult();
$collections = $em->createQueryBuilder()
->select('c')
->add('from', 'CollectionBundle:Collection c LEFT JOIN c.object o')
->having('COUNT(o.id)>0')
->orderBy('c.date', 'DESC')
->getQuery();
$collections->setHint('knp_paginator.count', $count);
$collections = $this->get("knp_paginator")->paginate($collections, $page, $limit,array('distinct' => false));
return $this->render('CollectionBundle:Collection:fetch.html.twig', [
'collections' => $collections
]);
Hope this help
My solution is based on #Matteo's solution, since my query was a bit complicated I wanted to share my version also:
$qb = $this->createQueryBuilder('c');
$qb->select('count(c.id)')
->addSelect('COUNT(DISTINCT m.id) AS HIDDEN messageCount')
->addSelect('COUNT(DISTINCT f.id) AS HIDDEN fileCount')
->join('c.user', 'u')
->join('c.status', 's')
->join('c.company', 'comp')
->leftJoin('c.files', 'f')
->leftJoin('c.messages', 'm');
$this->_set_filters($filter, $qb);
$qb->groupBy('c.id');
$countQuery = $qb->getQuery();
/** wrap query with SELECT COUNT(*) FROM ($sql)
* I don't know what exactly does this block but
* I coppied it from Doctrine\ORM\Tools\Pagination\Paginator::getCountQuery()
*/
$platform = $this->getEntityManager()->getConnection()->getDatabasePlatform();
$rsm = new Query\ResultSetMapping();
$rsm->addScalarResult($platform->getSQLResultCasing('dctrn_count'), 'count');
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class);
$countQuery->setResultSetMapping($rsm);
return $countQuery->getSingleScalarResult(); //returns integer
I've been able to get the query result I need using the following raw sql:
select `person`.`id`, `full_name`, count(actions.user_id) as total
from `persons`
left join `actions`
on `actions`.`person_id` = `persons`.`id`
and `actions`.`user_id` = $user
where `type` = 'mp'
group by `persons`.`id`
But I haven't been able to get it working in eloquent yet.
Based on some similar answers, I'd tried functions within ->where() or leftJoin(), but the count of each person's actions isn't yet being filtered by $user. As it stands:
$query = Person::leftJoin('actions', function($q) use ($user)
{
$q->on('actions.person_id', 'persons.id')
->where('actions.user_id', $user);
})
->groupBy('persons.id')
->where('type', 'foo')
//->where('actions.user_id', '=', $user)
->get(['persons.id', 'full_name', DB::raw('count(actions.id) as total')]);
I'm at least heading in roughly the right direction, right...?
If it's relevant, the Persons.php model has two actions relationships:
public function actions()
{
return $this->hasMany('Action');
}
public function actionsUser($id)
{
return $this->hasMany('Action')->where('user_id', $id);
}
So, for reference, I solved it like so:
$query = Person::leftJoin('actions', function($q) use ($user)
{
$q->on('actions.person_id', '=', 'persons.id')
->where('actions.user_id', '=', "$user");
})
->groupBy('persons.id')
->where('type', 'foo')
->get(['persons.id', 'full_name', DB::raw('count(actions.id) as total')]);
The ->where() clause within leftJoin, oddly, needs the speech marks for the variable to be passed through the sql query correctly (likewise, '2' doesn't seem to work while "2" does).
I found that the where doesn't always work on the leftJoin clause
If in the future you get any trouble with it, I'd suggest you using this:
$query = Person::leftJoin('actions', function($q) use ($user)
{
$q->on('actions.person_id', '=', 'persons.id')
->on('actions.user_id', '=', "$user");
})
->groupBy('persons.id')
->where('type', 'foo')
->get(['persons.id', 'full_name', DB::raw('count(actions.id) as total')]);
Hope it helps someone.
When laravel eloquent just start getting complex like this
For more flexibility and readability I'll just use plain sql statement then hydrate the results.
$sql = "
SELECT `person`.`id`,
`full_name`,
count(actions.user_id) AS total
FROM `persons`
LEFT JOIN `actions`
ON `actions`.`person_id` = `persons`.`id`
AND `actions`.`user_id` = $user
WHERE `type` = 'mp'
GROUP by `persons`.`id`
";
$query = Person::hydrate(
DB::select( $sql )
);
public function getInterests($userID) {
$result = $this->tableGateway->select(function (Select $select) use ($userID) {
$select->join('interests', 'users_interests.interest_id = interests.interest_id', array('*'), 'left');
$where = new Where();
$where->equalTo('user_id', $userID);
$select->where($where);
});
return $result;
}
Here is my method. It simply selects all records from users_interests with user_id = $userID and joins the 'interests' table. So far, so good, but when trying to display the fetched results, the fields from the joined table just do not exist. Here is the dump of the $result:
Zend\Db\ResultSet\ResultSet Object
(
[allowedReturnTypes:protected] => Array
(
[0] => arrayobject
[1] => array
)
[arrayObjectPrototype:protected] => Object\Model\UsersInterests Object
(
[settings_id] =>
[user_id] =>
[interest_id] =>
)
[returnType:protected] => arrayobject
[buffer:protected] =>
[count:protected] => 2
[dataSource:protected] => Zend\Db\Adapter\Driver\Pdo\Result Object
(
[statementMode:protected] => forward
[resource:protected] => PDOStatement Object
(
[queryString] => SELECT `users_interests`.*, `interests`.* FROM `users_interests` LEFT JOIN `interests` ON `users_interests`.`interest_id` = `interests`.`interest_id` WHERE `user_id` = :where1
)
[options:protected] =>
[currentComplete:protected] =>
[currentData:protected] =>
[position:protected] => -1
[generatedValue:protected] => 0
[rowCount:protected] => 2
)
[fieldCount:protected] => 6
[position:protected] =>
)
I badly need help on this because I am supposed to finish my project until Sunday. Thanks in advance.
You can use the following to apply left join. $select::JOIN_LEFT instead of 'left'.
public function getInterests($userID) {
$result = $this->tableGateway->select(function (Select $select) use ($userID) {
$select->join('interests', 'users_interests.interest_id = interests.interest_id', array('*'), $select::JOIN_LEFT);
$where = new Where();
$where->equalTo('user_id', $userID);
$select->where($where);
});
return $result;
}
It seems you have a problem in the WHERE clause of the join. This also shows in the error here:
[queryString] => SELECT `users_interests`.*, `interests`.* FROM `users_interests` LEFT JOIN .
`interests` ON `users_interests`.`interest_id` = `interests`.`interest_id`
WHERE `user_id` = :where1
Try this:
$select->from($this->table)
->join('interests', 'users_interests.interest_id = interests.interest_id',
array('*'), 'left');
$where = new Where();
$where->equalTo('user_id', $userID) ;
$select->where($where);
I can not follow your code completely, like here:
$this->tableGateway->select(function (Select $select) use ($userID) {
But, here is a very nice article on this. I think, you can simplify your code a little.
Have you iterated over the resultset? You can see there's two matching rows:
[rowCount:protected] => 2
You have a ResultSet object, but it will not load any of the rows until requested, they are "lazy loaded" when you iterate over the object.
You can force the resultset to get them all for you:
var_dump($resultSet->toArray()); // force load all rows
or iterate over the ResultSet:
foreach($resultset as $row) {
var_dump($row); // each row loaded on request
}
I have written about this before and maybe it will help you as well.
TableGateway with multiple FROM tables
I have search for a long time to get this thing work.
What I want is to know how I user the 'distinct' in a zend db model to make my selection for the followers of a user unique.
My db model to count followers for a user (here I need to add the 'distinct')
public function countFollowers($user_id)
{
$rowset = $this->fetchAll("user_id = $user_id");
$rowCount = count($rowset);
if ($rowCount > 0) {
return $rowCount;
} else {
return $rowCount;
}
}
EDIT: This function is part of 'class Application_Model_DbTable_Followers extends Zend_Db_Table_Abstract'
My table structure
id
article_id // Id of the article who is written by 'user_id'.
user_id // user_id owner of the article
follower_id // member who has following this article
date // date of follow
'user_id' can be written various articles, the follower can follow various articles of the same writer. I want to make a unique follower count. As an example what I want, If a follower is following 8 articles of one writer it has to be compared to '1' in the count.
I hope this will be clear enough to understand what I tried to reach.
With kind regards,
Nicky
Using distinct:
public function countFollowers($user_id)
{
$select = $this->select()
->distinct()
->where('user_id = ?', $user_id);
$rowset = $this->fetchAll($select);
$rowCount = count($rowset);
return $rowCount;
}
EDIT: After edit in question to get count of followers of a user. You actually need to use group NOT distinct. I have tested the following query works to fetch the data to be count()ed,
SELECT * FROM followers WHERE user_id = 1 GROUP BY user_id,
follower_id
I have not tested the code, but something like this should work:
public function countFollowers($user_id)
{
$select = $this->select()
->where('user_id = ?', $user_id)
->group(array('user_id', 'follower_id'));
$rowset = $this->fetchAll($select);
$rowCount = count($rowset);
return $rowCount;
}
You can specify mysql functions in the 'from' function that makes up select query function. To use the from function you need to pass the table name as the first parameter, however passing $this (your table model class) works fine.
public function countFollowers($user_id)
{
$rowset = $this->fetchAll(
$this->select()
->from($this, array('DISTINCT user_id'))
->where('user_id = ?', $user_id)
);
return count($rowset);
}
[edit]
Based on your edit, 'group' may also work for you:
public function countFollowers($user_id)
{
$rowset = $this->fetchAll(
$this->select()
->where('user_id = ?', $user_id)
->group('user_id')
);
return count($rowset);
}
This will group all matching user_id into one record. So if a user is found, it will return 1, else 0.
Retrieving all the rows simply to get a count strikes me as overkill.
You can do a count using something like this:
$select = $db->select();
$select->from('testcount', new Zend_Db_Expr('COUNT(id)'))
->where('user_id = ?', $someUserId);
return $db->fetchOne($select);
don't write that :
public function countFollowers($user_id)
{
$rowset = $this->fetchAll(
$this->select()
->from($this, array('DISTINCT user_id'))
->where('user_id = ?', $user_id)
);
return count($rowset);
}
But that :
public function countFollowers($user_id)
{
$rowset = $this->fetchAll(
$this->select()
->from($this, array('DISTINCT(user_id)'))
->where('user_id = ?', $user_id)
);
return count($rowset);
}
Else you will have an error wich looks like to Mysqli prepare error:
Unknown column 'repertoire.distinct idRepertoireParent' in 'field list'
Also we have one method from the official manual
Just use "distinct"
Build this query: SELECT DISTINCT p."product_name" FROM "products" AS p
$select = $db->select()
->distinct()
->from(array('p' => 'products'), 'product_name');
Today I tried DISTINCT in JOIN LEFT case and it doesn't work. But if you add a Group By to the DISTINCT column, it works fine.