Laravel 5.2 - Selecting only articles that has comments - php

I built a commenting system, and I'm working on a page that shows all the comments that are waiting for approval.
The relationship:
Article.php
public function comments()
{
return $this->hasMany('App\ArticleComment');
}
ArticleComment.php
public function article()
{
return $this->belongsTo('App\Article');
}
Now, I want to select only the articles that have comments that are waiting for approval (status column on article_comments table equals 0).
Any easy way of doing it? (Of course I can get all articles and check on each one if it has comments)

The other answer will work but you asked for an easy (also re-usable) approach to use so I would suggest to create a scope method in your ArticleComment model using something like the following:
In your Article model:
use App\ArticleComment;
use Illuminate\Database\Eloquent\Model;
class Article extends Model {
// Relation for comments
public function comments()
{
return $this->hasMany(ArticleComment::class);
}
// Relation for pending comments
public function pendingComments()
{
return $this->comments()->pending();
}
}
In your ArticleComment model:
// Query scope for pending comments
public function scopePending($query)
{
$query->whereStatus(0);
}
So, you can use something like this:
$posts = Post::has('pendingComments')->get();
Also, you may chain with like:
$posts = Post::has('pendingComments')->with('pendingComments')->get();

$articles = Article::whereHas('comments', function($query) {
$query->where('status', 0);
})->get();

Related

withSum in deep nested relationship in laravel eloquent

I could not find this in the laravel docs on aggregate relationships
I was able to do something like this
private function refreshUsers()
{
$this->users = User::withSum(['taskTimeSessions'=> function ($query) {
$query->whereMonth('created_at',$this->month)
->where('is_reconciled',1);
}],'session_duration_in_seconds')
->get();
}
But now I am trying to query what is the total time a Sprint has or at the very least what the individual tasks inside a sprint have so that I can just sum the total of those somehow.
Sprint has many SprintTasks (pivot table)
SprintTask belongs to one Task
Task has many TaskTimeSessions
So I am trying to go find the total time of the TaskTimeSessions
Sprint::with([
'sprintTasks.task'=> function ($query) {
$query->withSum('taskTimeSessions','session_duration_in_seconds');
}])
->get();
I am not getting any errors, but not finding the result anywhere when dd
I thought i would get lucky and have something like this work
->withSum('sprintTasks.task.taskTimeSessions', 'session_duration_in_seconds')
But I am getting this error
Call to undefined method App\Models\Sprint::sprintTasks.task()
If anyone can help me out with some guidance on how to go about this, even if it doesn't include withSum it would be much appreciated.
As requested, these are the models.
// Sprint
public function sprintTasks()
{
return $this->hasMany(SprintTask::class, 'sprint_id');
}
// SprintTask
protected $fillable = [
'sprint_id',
'task_id',
'is_completed'
];
public function task()
{
return $this->belongsTo(Task::class,'task_id');
}
public function sprint()
{
return $this->belongsTo(Task::class,'sprint_id');
}
// Task
public function taskTimeSessions()
{
return $this->hasMany(TaskTimeSession::class, 'task_id');
}
// TaskTimeSessions
protected $fillable = [
'task_id',
'session_duration_in_seconds'
];
public function task()
{
return $this->belongsTo(Task::class,'task_id');
}
Is it possible to abstract this into the model as like
public function totalTaskTime() {
// using the relationship stuff to figure out the math and return it?
}
Looking for any advice on what the best approach is to do this.
Right now I am literally doing this in the blade and seems very bad
#php
$timeTracked = 0;
foreach ($sprint->sprintTasks as $sprintTask) {
$timeTracked += $sprintTask->task->time_tracked_in_seconds;
}
#endphp
You have a many to many relation between sprint and task
For that you can setup a direct relation belongsToMany with sprint_tasks as the pivot table
// Sprint
public function sprintTasks()
{
return $this->hasMany(SprintTask::class, 'sprint_id');
}
public function tasks()
{
return $this->belongsToMany(Task::class, 'sprint_tasks', 'sprint_id', 'task_id')->withPivot('is_completed');
}
Now you can use that relation to query your needs
Sprint::with(['tasks'=> function ($query) {
$query->withSum('taskTimeSessions','session_duration_in_seconds');
}])
->get();
There is a good package for Laravel for complex relationships - eloquent-has-many-deep. You can use it to build relationships through an unlimited number of tables.
composer require staudenmeir/eloquent-has-many-deep:"^1.7"
Sprint.php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Staudenmeir\EloquentHasManyDeep\HasManyDeep;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
class Sprint extends Model
{
use HasRelationships;
public function tasks(): BelongsToMany
{
return $this->belongsToMany(Task::class, 'sprint_tasks');
}
public function taskTimeSessions(): HasManyDeep
{
return $this->hasManyDeepFromRelations($this->tasks(), (new Task())->taskTimeSessions());
}
}
Task.php
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Task extends Model
{
use HasFactory;
public function taskTimeSessions(): HasMany
{
return $this->hasMany(TaskTimeSession::class);
}
}
Result:
$sprints = Sprint::withSum('taskTimeSessions', 'session_duration_in_seconds')->get();

Laravel: Sorting a collection with a many to many relationship

I have two tables: assessments and benchmarks. benchmarks has a field called content. There is a many to many relationship between them: assessment_benchmark. I want to sort a collection of records from the assessment_benchmark table by the content attribute of the corresponding benchmark. I have tried:
$sorted = AssessmentBenchmark::all()->sortBy(function($assessmentBenchmark){
return $assessmentBenchmark->benchmark->content;
});
But this just does not work (it just returns the original order). However, when I return $assessmentBenchmark->comment for example, it does work (comment is a field in assessment_benchmark).
The models look like this:
class AssessmentBenchmark extends Model
{
public function benchmark()
{
return $this->belongsTo(Benchmark::class);
}
public function assessment()
{
return $this->belongsTo(Assessment::class);
}
}
class Benchmark extends Model
{
public function assessments()
{
return $this->belongsToMany(Assessment::class);
}
}
class Assessment extends Model
{
public function benchmarks()
{
return $this->belongsToMany(Benchmark::class);
}
}
Well, you can use below query for sorting, I'm gonna use Assessment model, because, I'm never use pivot modal before. Actually, I never had pivot model..
$assessments = Assessment::with(["benchmarks"=>function($query){
$query->orderBy("content","DESC");
}])
With method aşso provide you eagerloading, so when you put $assessments in iteration , you won't make new query for each relation
From chat discussion, it found that you have pivot field and for that you can change your belongsToMany relationship like this
class Benchmark extends Model
{
public function assessments()
{
return $this->belongsToMany(Assessment::class)->withPivot('comment','score')->withTimestamps();
}
}
class Assessment extends Model
{
public function benchmarks()
{
return $this->belongsToMany(Benchmark::class)->withPivot('comment','score')->withTimestamps();
}
}
Now fetch data
$assessment = Assessment::with(['benchmarks' => function($query){
$query->orderBy('content', 'desc');
}])->find($assessmentId);
In view you can render it like this
#foreach($assessment->benchmarks as $benchmark)
<tr>
<td>{{$benchmark->id}}</td>
<td>{{$benchmark->name}}</td>
<td>{{$benchmark->pivot->score}}</td>
<td>{{$benchmark->pivot->comment}}</td>
</tr>
#endforeach
For update you can use updateExistingPivot
For details check ManyToMany relationship https://laravel.com/docs/5.6/eloquent-relationships#many-to-many

get limited records in eloquent

First , i'm sorry for my english.
I have two tables: Users
class Users extends Model {
public function posts(){
return $this->hasMany('App\posts','user_id');
}
}
and posts
class posts extends Model{
public function users(){
return $this->belongsTo('App\Users','user_id');
}
}
Each users has many posts.
I want take only 4 posts of each users.
I tested this code, but it does not work.
Users::with(['posts'=>function($query){
return $query->skip(0)->take(4);
}])->get();
You can't limit results on eager loads
However, you can run it like this:
return Users::with('posts')->get()->map(function ($users) {
$users->posts = $users->setRelation('posts', $users->posts->take(4));
return $users;
});

laravel 4 how to order and join by eloquent

Im new in Laravel 4, and right now im coding for small project, i use laravel as framework to build my website, but my code i always wonder it's optimize or not because in my model i just wrote:
Category Model
public function parents()
{
return $this->belongsTo('Category', 'cat_father');
}
public function children()
{
return $this->hasMany('Category', 'cat_father');
}
}
Post Model:
<?php
class Post extends BaseModel{
public $table = "post";
protected $primaryKey = 'idpost';
public function Category()
{
return $this->belongsTo('Category', 'cat_id');
}
}
because i didn't know how to join 2 tables in laravel 4, i have a condition is find all post from my categories, which it hadn't belong to category name "Reunion", but i didn't know how to do that, therefore i wrote 2 lines code for that purpose (im not sure wrote code in controller is best way but i didn't know how to call method from Model to controller and get return value)
My method from controller for select all post, it hasn't belong to category name "Reunion"
public function getAllPostView()
{
$getCat = Category::where('cat_name','=', 'Reunion')->firstOrFail();
$post = Post::where('cat_id', '!=', $getCat->idcategory)->get();
return View::make('layouts.post')->with('post',$post);
}
My question, my code is optimize when i wrote it in controller? and how to wrote it in model and get parameter for passing it to controller and use it to view.
second question is how to order "POST" because some cases post need to be ordered from new to old
This is how you do it:
$exclude = 'Reunion';
$posts = Post::select('posts.*')->join('categories', function ($j) use ($exclude) {
$j->on('posts.cat_id', '=', 'categories.idcategory')
->where('categories.name', '<>', $exclude);
})->get();
could just use simple joins
public function getAllPostView()
{
$getCat = Category::where('cat_name','=', 'Reunion')
->join('post','post.cat_id', '!=','Category.idcategory')->get();
return View::make('layouts.post')->with('post',$post);
}
Look out for same field names in both the tables if so can use select
$getCat = Category::select('Category.idcategory as cat_id','Category.cat_id as pos_id','many other fields')
// 'as cat_id' not required for unique field names
->join('post','post.cat_id', '!=','Category.idcategory')
->where('cat_name','=', 'Reunion')
->get();

Laravel querying model relations

I'm still struggeling with the laravel Models. At first I tried doing it all using the tables, but thats not smart, I'll miss out on lots of the laravel functions.
I have the following setup
ProjectTwitterStatus links the projects and the twitter statuses.
TwitterStatus has all the details of a twitter status and has a unique ID ('posted at' datetime of tweet is among the details)
TwitterRetweets has the ID of the TwitterStatus - the actual retweet - and the tweet ID of the retweeted status
TwitterReplies has the ID of the TwitterStatus - that is the actual reply - and/or the user ID if not a reply to a status but to a user.
What I want? To get for each date (DATE(datetime)) the count of the statuses, retweets and replies, using the laravel model relations.
These are the models.
class ProjectTwitterStatus extends Eloquent {
protected $table = 'project_twitter_statuses';
protected $softDelete = true;
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
public function project() {
return $this->belongsTo('Project');
}
}
class TwitterStatus extends Eloquent {
protected $table = 'twitter_statuses';
public function twitterRetweet() {
return $this->hasMany('TwitterRetweet');
}
public function twitterReply() {
return $this->hasMany('TwitterReply');
}
public function twitterUser() {
return $this->belongsTo('TwitterUser');
}
public function projectTwitterStatus() {
return $this->hasMany('ProjectTwitterStatus');
}
}
class TwitterRetweet extends Eloquent {
protected $table = 'twitter_retweets';
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
}
class TwitterReply extends Eloquent {
protected $table = 'twitter_replies';
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
}
I got the count of the twitterStatuses using this:
$twitterStatuses = TwitterStatus::has('projectTwitterStatus')
->groupBy(DB::raw('DATE(datetime)'))
->get(array(DB::raw('COUNT(id) AS tweets'),DB::raw('DATE(datetime) AS date')));
I tried for example this to get the retweet count added but that has no effect (a reference to the model apears in the object -> array().
$twitterStatuses = TwitterStatus::has('projectTwitterStatus')
->with(array('twitterRetweet' => function($query)
{
$query->count();
}))
->groupBy(DB::raw('DATE(datetime)'))
->take(10)
->get(array(DB::raw('COUNT(id) AS tweets'),DB::raw('DATE(datetime) AS date')));
Can anyone point me in the right direction?
Not 100% sure how your intended solution is to be used - Assuming you simply want a count of the number of retweets related to twitterStatus?
$count = $twitterStatus->twitterRetweet()->count();
where $twitterStatus is an already retrieved model - not a collection.
if $twitterStatus is a collection to iterate through you can also eager load the related model using either with() or load()
Then you can iterate through each model in the collection - depends on how you wanted to use the results

Categories