Filter relations in laravel - php

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();

Related

How to put where in relation to method?

Below is my Eloquent query.
$cat = Category::with('subcategory.items.products')
->where('id',$discates)->first();
I want to put a where on products with status = 1.
where('status',1)
You should spend some time reading Laravel docs. They are full of examples and a very good learning resource for the basics.
https://laravel.com/docs/8.x/eloquent-relationships#constraining-eager-loads
$cat = Category::with(['subcategory.items.products' => function($query){
$query->where('status', 1);
}])->where('id',$discates)->first();
Try this
$query = Category::query();
$query->whereHas('products', function ($q) {
$q->where('status', 1);
});
$cat = $query->with('subcategory.items.products')->find($discates);
or
$cat = $query->with('subcategory.items.products')>where('id',$discates)->first();

Helping querying relations in eloquent

I have this query in my codebase,
$listings = Tag::has('listings')->with(['listings' => function ($query) use ($request) {
$query->where('moderated', 1)
->where('active', 1);
if($request->query('free') == true) {
$query->where('cost', '0.00');
}
if($request->query('type') != "") {
$query->with(['types' => function($q) use ($request) {
$q->whereIn('id', explode(",", $request->query('type')));
}]);
}
$query->with('primaryImage');
}])
->paginate(3);
What I am trying to do is add parts of the query based on what is in the GET request (this bit works), what isnt working is the query on a relation.
Here I am querying Tags that can have many listings, each listing can have many types and I want to only return tags that have listing that match the filter parameters, i.e only show listings that cost "0.00" and then only tags that have listings that match the types in the get request.
So if a user sends type=1,2,3 in the GET request I want to return tags that have listings where the types relationship contains 1 of those IDs, is this possible?
The types relationship on a listing looks like this,
public function types() {
return $this->belongsToMany('App\Type');
}
and the relation from type to listing looks like this,
public function listings() {
return $this->belongsToMany('App\Listing');
}
Instead of using has and with maybe you can use whereHas
$listings = Tag::whereHas('listings', function($query){
$query->where('moderated', 1)
->where('active', 1);
if($request->query('free') == true) {
$query->where('cost', '0.00');
}
if($request->query('type') != "") {
$query->whereHas('types', function($q) use($request){
$q->whereIn('id', explode(",", $request->query('type')));
});
}
$query->with('primaryImage');
});
As you said, "each listing can have many types"
Change this in your Listing::class
public function types() {
return $this->hasMany('App\Type');
}

How count a column from a tableB where tableA.y = TableB.y laravel

I would like to count number of courses and questions of each modules.
When I try it with this code below, it displays the counting of all questions
//$data = DB::table('module')->orderBy('idmodule', 'desc')->paginate(5);
$data = DB::table('module')
->leftJoin('cours', 'module.idmodule', '=', 'cours.id_module')
->leftJoin('question', 'cours.id_cours', '=', 'question.id_cours')
->select('module.*', 'cours.*',DB::raw("count(cours.id_module) as cours"),DB::raw("count(question.id_cours) as quest"))
->groupBy('module.idmodule')
->get();
$id = Auth::id();
return view('admin.modules',compact('data'))>with('profile',profile::find($id));
If you declare the relation between Module->Cour and Module->Question, you can simply do
$modules = Modules::withCount(['cours', 'questions'])->get();
Module.php :
public function cours()
{
return $this->hasMany(Cour::class, 'id_module', 'idmodule');
}
public function questions()
{
return $this->hasManyThrough(Question::class, Cour::class, 'id_module', 'id_cours', 'idmodule', 'id_cours');
}

Codeigniter: Pass variable to the View

I have to table article and table comment. In the home page I want to see how many comment for earch article.
Model
function show_latest_article($start,$display,$cate)
{
$query= $this->db->get_where('news_news', array('News_Cate_ID'=>$cate),$start,$display);
if($cate==0)
{
$query= $this->db->get_where('news_news',$start,$display);
}
return $query->result();
}
function count_comment($id)
{
$query = $this->db->get_where('comment',array('comment_article_id'=>$id) );
return $query->num_rows();
}
Controller
function index() {
$this->load->model('article');
$data=array('article'=>$this->article->show_latest_article(0,8,1),
'sidebar'=>$this->article->side_bar(9),
'count_comment'=> $this->article->count_comment($id),
);
$this->load->view('page/index',$data);
}
In the view I have this
foreach($article as $art)
{
echo $art->title."<br>";
$id= $art->id;
// I want to echo number of comment here.
// Or I want to call function count_comment($id)
}
$this->load->model('article');
$data['article'] = $this->article->show_latest_article(0, 8, 1);
$data['sidebar'] => $this->article->side_bar(9);
$data['count_comment'] => $this->article->count_comment($id);
$this->load->view('page/index', $data);
And it should work.
It's not so clear, where the $id variable come from, but I suggest to join the comments table to your article. It's not a best practice to make queries within loops.
In your model:
public function show_latest_article($ids = array())
{
return $this->db
->select('articles.*, COUNT(comments.id) as comment_count')
->where_in('articles.id', $ids) // You can skip this
->join('comments', 'comments.article_id = articles.id', 'left')
->group_by('articles.id')
->get('articles')
->result();
}
In your controller:
public function index()
{
$data['articles'] = $this->article->show_latest_article(array(0, 8, 1));
$this->load->view('page/index', $data);
}
Just change the field name according to database columns. Also you can skip the where_in condition. (I'm not sure what the three number stands for).
Then in your view, you can simply access the field:
$art->comment_count
EDIT: According to your comment:
$this->db
->select('a.*, (SELECT COUNT(*) FROM comment c WHERE c.comment_article_id = a.News_News_ID ) as counta')
->get('news_news')
->result();
I am writing this answer as you provided code. It could be more simpler if you give more database details. Try below code
function index() {
$this->load->model('article');
$articles = $this->article->show_latest_article(0,8,1);
foreach($articles as $article)
{
$article->comment_count = $this->article->count_comment($article->id);
$all_article[] = $article;
}
$data=array('article'=>$all_article,
'sidebar'=>$this->article->side_bar(9)
);
$this->load->view('page/index',$data);
}
On view
foreach($article as $art)
{
echo $art->title."<br>";
$id= $art->id;
echo $art->comment_count;
}
Controller :
function index() {
$this->load->model('article');
$articles = $this->article->show_latest_article(0,8,1);
$count_comments = Array();
for($i=0;$i<count($articles);$i++){
$count_comments[$i] = $this->article->count_comment($articles->$id);
}
$count_comments =
$data=array('article'=>$articles,
'sidebar'=>$this->article->side_bar(9),
'count_comment'=> $count_comments);
$this->load->view('page/index',$data);
}
In the View :
$i=0;
foreach($article as $art)
{
echo $art->title."<br>";
$id= $art->id;
echo $count_comment[$i];
$i++;
}
Data is passed from the controller to the view by way of an array or an object in the second parameter of the view loading function. Here is an example using an array:
$data = array(
'title' => 'My Title',
'heading' => 'My Heading',
'message' => 'My Message'
);
$this->load->view('blogview', $data);
Also below is the example for object
$data = new Someclass();
$this->load->view('blogview', $data);
PLEASE NOTE: Note: If you use an object, the class variables will be turned into array elements.
You can find out more from below ref URL
Ref: https://ellislab.com/codeigniter/user-guide/general/views.html
First of all in your controller you get count_comment only for specific article ID and not for all articles so you cant do foreach to display comment count of each article.
You need to setup you model better and in model function show_latest_article to use JOIN comments table and do count comment in query.
I will help you with query if you provide me more info about database table of article and comments.
SELECT
article.*,
COUNT(comment.id),0) AS numberOfCommments
FROM article
LEFT JOIN comment
ON comment.article_id = article.id
GROUP BY article.id

how to implement nested comments 2 levels deep in codeigniter

I am trying to implement a commenting system on my website that is only two levels deep for example you will have the main comment and replies to that comment but it does not go any further:
main comment 1
(sub_comment1)
(sub_comment2)
main comment 2
(sub_comment1)
(sub_comment2)
(sub_comment2)
etc...
Make sense?
I am creating the site in codeigniter but i think a basic php solution will do.
each row in my database table has an id and a parent_id, if the parent id is 0 then its a main comment and if its a sub-comment it will have the id of its parent comment in parent_id.
how to I feed a two dimensional array with the parent and child comments in the right order.
My current code is like so: The controller:
function status_comments($id){
$this->load->model('status_model');//load the status model
$this->load->model('comment_model');//load the comment model
$status = $this->status_model->get_entry_and_category($id);
$comments = $this->comment_model->get_comments($id);
if($status !== false) {
if($comments !== false) {
foreach($comments as $comment){
if($comment->reply_id == 0){
$comment =
}
}
$content_data['comments'] = $comments;
}
$content_data['status'] = $status;
$data['content'] = $this->load->view('status_view', $content_data, TRUE);
$data['title'] = $status->title.' - High Value Status';
$data['page_title'] = $status->title;//The page H1 tag
$this->load->view('home', $data);
}
else
{
$this->session->set_flashdata('invalid', '<p class="rejectionalert"><span>The status you tried to view does not exist.</span></p>');
redirect('home');
}
}
The model function:
//Gets comments associated with an individual status
function get_comments($status_id, $offset=null, $limit=null)
{
$this->db->select('id, comment, nickname, created, reply_id');
$this->db->from('comments');
$this->db->where('active', 1);
$this->db->where('status_id', $status_id);
$query = $this->db->get();
if ($query->num_rows() > 0) {
return $query->result();
}
return false;
}
This works but its using more than one query, the model:
function get_comments($status_id, $limit=NULL, $offset=NULL)
{
$this->db->where(array('status_id' => $status_id, 'reply_id' => 0));
$query = $this->db->get('comments', $limit, $offset);
$parents = $query->result_array();
$comments = array();
foreach($parents as $key => $comment)
{
array_push($comments, $comment);
$this->db->order_by('created', 'ASC');
$this->db->where(array('status_id' => $status_id, 'reply_id' => $comment['id']));
$comments = array_merge($comments, $this->db->get('comments')->result_array());
}
return $comments;
}
I thought it would be more efficent to make one query of all comments, index them into an array by their id, and iterate over them again to find their children. I still dont know how to implement that?

Categories