On my project I'm tying to generate user titles by users' post, comments, questions and answers count from titles table.
I have titles table which can I add new titles. And each title has own post count. So when a user has greater or equal post count title will be generated from titles table.
Problem is I can't fetch the greater value in the titles table with users post count. When I use <= it shows the title but when I use >= it doesn't return anything.
Ps: There aren't any relation between users and titles table. It returns only equal title data.
My codes are below:
public function title()
{
$commentcount = $this->hasMany('App\Comment')
->whereUserId($this- >id)
->count();
$questioncount = $this->hasMany('App\Question')
->whereUserId($this->id)
->count();
$answercount = $this->hasMany('App\Answer')
->whereUserId($this->id)
->count();
$total = $commentcount + $questioncount + $answercount;
$title = Title::where('postcount', '>=', $total)->first();
if ($title) {
$show = '<span class="badge badge-danger rutbe" style="background:' . $title->color . '">' . $title->text . '</span>';
return $show;
} else {
return false;
}
}
I can't figure out why doesn't return anything when greater or equal count.
I'll sum up my answer based on my comment and give the hints with regards to your queries.
Basically the where conditition in your codes matches multiple title entries. Therefore selecting the first will not always match the correct one. As you want to match the "lowest" matching title you probably want to change
$title = Title::where('postcount', '>=', $total)->first();
to
$title = Title::where('postcount', '>=', $total)->orderBy('postCount', 'ASC')->first();
Some other enhancement proposals
$commentcount = $this->hasMany('App\Comment')
->whereUserId($this- >id)
->count();
Seems to be weird to use in your (probably User?) class. You should refactor this to something like
public function comments()
{
return $this->hasMany('App\Comment');
}
This defines your users realtion to his comments. If you now want to have the amount of the users comments in your title function you can simply do
$this->comments()->count();
When doing this for all 3 of your relations your title method could look like
public function comments()
{
return $this->hasMany('App\Comment');
}
public function questions()
{
return $this->hasMany('App\Question');
}
public function answers()
{
return $this->hasMany('App\Answer');
}
public function title()
{
$total = $this->comments()->count() + $this->questions()->count() + $this->answers()->count();
$title = Title::where('postcount', '>=', $total)->orderBy('postcount', 'ASC')->first();
if ($title)
return '<span class="badge badge-danger rutbe" style="background:' . $title->color . '">' . $title->text . '</span>';
}
return false;
}
This does not only make it look & feel way cleaner - it also helps you with future queries where you handle these relations.
Related
I have Posts and Comments models with hasMany relation:
public function comments()
{
return $this->hasMany(Posts::class, 'posts_id', 'id');
}
In my controller I need to get all published posts (is_published = 1), with all published comments, that have at lease 1 published comment:
$dbRecords = Posts::all()->whereStrict('is_published', 1);
$posts = [];
foreach ($dbRecords as $post) {
if (count($post->comments()) === 0) {
continue;
}
foreach ($post->comments() as $comment) {
if ($comment->is_published === 1) {
$posts[] = $post;
continue(2); // to the next post
}
}
}
But, such solution is ugly. Also I will get all published post, wit published and not published comments, so I will forced to filter comments once again in Resource.
Another solution I've found - to use raw query:
$dbRecords = DB::select("SELECT posts.*
FROM posts
JOIN comments ON posts_id = posts.id
WHERE posts.is_published = 1
AND comments.is_published = 1
HAVING count(posts.id) > 0;");
$users = array_map(function($row) { return (new Posts)->forceFill($row); }, $dbRecords);
But it does not solves the problem with the need of filteration of unpublished comments in Resource.
Use Eager loading to remove n+1 query problem using with in Laravel eloquent.
Use has or whereHas function to querying relationship existence.
In your case it would be like this:
// Retrieve all posts that have at least one comment
$posts = Post::has('comments')->with('comments')->get();
// Retrieve posts with at least one comment and which are published
$callback = function($query) {
$query->where('is_published ', '=', '1');
}
$posts = Post::whereHas('comments', $callback)
->with(['comments' => $callback])
->where('is_published ', '=', '1')
->get();
What about this in eloquent
$posts = Post::query()->where('is_published', 1)->with(['comments' => function ($query) {
$query->where('is_published', 1);
}])->get();
I'm trying to create a search form for my Laravel app. Basically a user can enter some fields (searchable model related, like the title) and sort it in different ways; title, create date, but also vote count or favorite count. The last two are results from a pivot table (many to many relations).
I've been struggling with this but I don't know how to proceed. Here's what I have so far:
public function search(SearchRequest $request)
{
$posts = Post::where('published', 1);
if (strlen($request->title) > 0) {
$posts->where('title', 'like', '%' . $request->title . '%');
}
switch($request->order_by) {
case 'title' : $orderBy = 'title'; break;
case 'createDate' : $orderBy = 'created_at'; break;
case 'favorites' : $orderBy = 'post_favorites.count(*)'; break;
case 'votes' : $orderBy = 'post_votes.count(*)'; break;
}
$posts->orderBy($orderBy, $request->order_by_direction);
$posts = $posts->get();
var_dump($posts);
}
To keep it simple I've rewritten the code to a typical blog post example. Thanks for your suggestions.
going to take a stab on it, but its not tested as got nothing to test it on etc, so bear with me, other might take it further simpler, but hope it helps.
public function search(SearchRequest $request)
{
if(count($request->get('title')) > 0){
$search_text = $request->get('title');
$order_by = $request->get('order_by');
$display_by = $request->get('order_by_direction');
$search_results = Post::where('title', 'like', '%' . $search_text . '%')->get();
if($display_by == 'asc') return this::orderSearchResults($search_results, $order_by);
return this::orderSearchResults($search_results, $order_by)->reverse();
} else {
//do something else here
}
}
private function orderSearchResults($search_results, $order_by)
{
switch ($order_by) {
case 'title' :
return $search_results->sortby('title');
break;
case 'createDate' :
return $search_results->sortby('created_at');
break;
case 'favorites' :
return $search_results->sortby(function($search){
return count($search['post_favorites']);
});
break;
case 'votes' :
return $search_results->sortby(function($search){
return count($search['post_votes']);
});
break;
default :
return $search_results;
break;
}
}
the second part is based on the resulted being returned as a collection etc.
I was in a similar situation where I had to get users with search option on different fields (search in name, email, phone... etc) and order them by number of posts or name or email... and this worked as intended. I tried to rewrite the code to match the example you have provided.
// if the user didn't fill the field, we assign default values
$searchText = $request->input('searchText') ?: null; // the terms we are looking for
$targetField = $request->input('targetField') ?: 'title'; // in which field we want to search
$orderBy = $request->input('orderBy') ?: 'id'; // field used to order the results
$order = $request->input('order') ?: 'DESC'; // order direction
// from here we start assembling our query depending on the request
$query = DB::table('posts')->select('*')->where('published', 1);
// if the user typed something in search bar, we look for those terms
$query->when($searchText, function($query) use ($searchText,$targetField) {
return $query->where($targetField,'like','%'.$searchText.'%');
});
// if the user wants to order by votes
$query->when($orderBy === 'votes', function ($query) use ($order) {
return $query->addSelect(DB::raw('(select count(post_id) from post_votes where post_votes.post_id = posts.id) AS nbvotes'))
->orderBy('nbvotes',$order));
});
// apply same logic for favorites
// if the orderBy is neither favorites or votes, by name or email for example...
$query->when($orderBy != 'favorites' && $orderBy != 'votes', function ($query) use ($orderBy,$order) {
return $query->orderBy($orderBy,$order);
});
// executing the query
$posts = $query->get();
I'm using Magento which is on the zend framework and the following code currently outputs the first row matching the criteria is_read != 1', 'is_remove != 1'. I need to modify this code to output the last 4 table rows that matches said criteria. I tried a few things but none worked. Please Help!
ModuleName/Model/Resource/
public function loadLatestNotice(Mage_AdminNotification_Model_Inbox $object)
{
$adapter = $this->_getReadAdapter();
$select = $adapter->select()
->from($this->getMainTable())
->order($this->getIdFieldName() . ' DESC')
->where('is_read != 1')
->where('is_remove != 1')
->limit(1);
$data = $adapter->fetchRow($select);
if ($data) {
$object->setData($data);
}
$this->_afterLoad($object);
return $this;
}
Here are some other codes that are used...
ModuleName/Model/
public function loadLatestNotice()
{
$this->setData(array());
$this->getResource()->loadLatestNotice($this);
return $this;
}
ModuleName/Block/
public function getLatestNotice()
{
return $this->_getHelper()
->getLatestNotice()->getTitle();
}
Template/
href="<?php echo $latestNoticeUrl ?>" onclick="this.target='_blank';"><?php echo $this->getLatestNotice() ?>
I was able to solve the problem myself, by using the following method.
The first thing i tried to produce is 4 notification table rows instead of 1, is to change ->limit(1); to ->limit(4); and $adapter->fetchRow($select); to $adapter->fetchAll($select);. The issue is, the solution requires more than just changing these 2 values.
ModuleName/Model/Resource/
public function loadLatestNotice(Mage_AdminNotification_Model_Inbox $object)
{
$adapter = $this->_getReadAdapter();
$select = $adapter->select()
->from($this->getMainTable())
->order($this->getIdFieldName() . ' DESC')
->where('is_read != 1')
->where('is_remove != 1')
->limit(4);
$data = $adapter->fetchAll($select);
if ($data) {
$object->setData($data);
}
$this->_afterLoad($object);
return $this;
}
After changing this, the template will stop outputting information, In order for the template to output the new array, you must duplicate some code and remove ->getTitle() line in the block file, then change a few line of codes in the template .phtml file as follows.
ModuleName/Block/
public function getNewFuncName()
{
return $this->_getHelper()
->getLatestNotice();
}
Template/
<?php
$notice = $this->getNewFuncName();
foreach ($notice as $item) {
foreach ($item as $value) {
echo '<div class="notemssg"><p id="notetitle" href='.$value['url'].' >'.$value['title'].'</p><p id="notedate">'.$value['date_added'].'</p></div>';
}
}
?>
Changing the code to properly call and display the array will result it 4 table rows being displayed. the code can be modified to be used and any way you would like to display the info on the fronted.
Hope this helps Someone!
I have created the following for a product catelog/lister:
public function index($type_id = null) {
$filters = $sort = array();
if (isset($type_id)) {
$filters['type'] = $type_id;
} else {
$filters['type'] = Input::get('type');
}
$filters['search'] = Input::get('search');
$filters['brand'] = Input::get('brand');
$sort['sort'] = Input::get('sort');
$sort['sortdir'] = Input::get('dir');
$productsPaginated = $this->fetchProducts($filters, $sort);
return View::make('products.products', array(
'productsList' => $productsPaginated
)
);
}
public function fetchProducts($filters, $sorts, $perpage = 2) {
print_r($filters);
$Product = Product::query();
if (!empty($filters['search']))
$Product->where('name', 'LIKE', '%' . $filters['search'] . '%');
if (isset($filters['type']))
$Product->where('type_id', $filters['type']);
if (isset($filters['brand']))
$Product->where('brand_id', $filters['brand']);
if (isset($sorts['sort']))
$Product->orderBy($sorts['sort'], $sorts['sortdir']);
$Product = $Product->paginate($perpage);
return $Product;
}
Which works well so far.
I am now trying to create some filters so a user can further filter the results.
How can I access and determine distinct rows based on a column in:
$productsPaginated = $this->fetchProducts($filters, $sort);
?
The groupBy method not only exists on the query builder but also on the collection class. (which will be returned when calling paginate)
Take a look at the source on github
So add an argument to your function and use groupBy
public function fetchProducts($filters, $sorts, $perpage = 2, $groupBy = null) {
// code omitted for brevity
$Product = $Product->paginate($perpage);
if($groupBy){
$Product = $Product->groupBy($groupBy);
}
return $Product;
}
Update
Then there's the lists function that works on collections as well as on query builders...
$Product->lists('column-name');
Update 2
I was curious so I did some testing and a found something very weird and I have no idea if its a bug or a feature I don't understand
When calling groupBy the collection returned has actually only one item (index "") and this item contains an array of the "original" items. So to make lists work. I found this workaround
$Product = $Product->groupBy($groupBy);
$Product = new Collection($Product[""]); // \Illuminate\Support\Collection
$Product = $Product->lists('column-name');
Whatsup codeigniters!
I want to display total comments of my blog that I am building with codeigniter.
In my controller I have:
function index() {
$data['query'] = $this->blog_model->get_all_entries();
$this->load->view('blog/index',$data);
}
Function index() gets all posts.
and I have
public function post($id) {
$data['query'] = $this->blog_model->get_post($id);
$data['comments'] = $this->blog_model->get_post_comment($id);
$data['post_id'] = $id;
$data['total_comments'] = $this->blog_model->total_comments($id);
$this->load->view('blog/index',$data,TRUE);
$this->load->helper('form');
$this->load->library(array('form_validation','session'));
//validation rules for post function
$this->form_validation->set_rules('commentor','Name','required');
$this->form_validation->set_rules('email','Your email','required|valid_email');
$this->form_validation->set_rules('comment','Comment','required');
if($this->blog_model->get_post($id))
{
foreach($this->blog_model->get_post($id) as $row)
{
//set page title
$data['title'] = $row->entry_name;
}
if($this->form_validation->run() == FALSE)
{
//if validation runs FALSE
$this->load->view('blog/post',$data);
}
else
{
//if valid
$name = $this->input->post('commentor');
$email = strtolower($this->input->post('email'));
$comment = $this->input->post('comment');
$post_id = $id;
$this->blog_model->add_new_comment($post_id,$name,$email,$comment);
$this->session->set_flashdata('message', '1 new comment added!');
redirect('blog/post/'.$id);
}
}
else
show_404();
}
Basically, post($id) gets a post with id (single post) and display comments. I can print total comments number in single post. But how do i print total comments number in index() function where all posts are listed. Thank you!
Use this active record
$this->db->select("post_id , count(comment) as total_comments");
$this->db->group_by('post_id');
$query = $this->db->get('comments');
This generate this sql query
SELECT
post_id,
count(comment) as total_comments
FROM comments
GROUP BY post_id
Means this selects posts , count of posts for each comment and seperate them by post. For understanding Here is the table Structure
Comments
id count(comment) post_id
Now the query will first fetch all the comments then it will use group by to seperate posts giving you total comments for each post.
Try to do something like this
in Model
public function fetch_all_comment()
{
$query = $this->db->get('comments');
return $query->result();
}
In Controller
$data['all_comments'] = $this->model_name->fetch_all_comment();
$this->load->view('viewname',$data);
In View
For this you have to call model in your view. for example if you want to display post name.
Load Model in view
foreach ($all_comments as $row)
{
echo $row->post;
echo $total_no_post = $this->model_name->fetch_total_no_of_comment_in_post($row->postid);
}
Count no of comment in this function fetch_total_no_of_comment_in_post