Laravel Count rows where column value is 1 - php

Hey so i am trying to do an upvote downvote system and I am only using 1 table, the table columns are
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
$table->integer('voter_id')->unsigned();
$table->foreign('voter_id')->references('id')->on('users');
$table->boolean('vote_type')->default(0);
$table->integer('commentable_id');
$table->string('commentable_type');
$table->string('unique_vote')->unique();
Basically I am trying to count how many votes the comment has but only where the vote_type is == 1 and also the reverse for downvotes where the value is 0
I was thinking about doing this with 2 different tables as it would make counting easier but I also dont want a large database.
i know of {{$comment->votes->count()}} but this returns the total rows regardless of the vote_type value and I am wondering if anyone has a solution or knows of a way while keeping the queries low.

Why you do it like this
public function showCart() {
$votes = Vote::where('vote_type',1)->count();
// do stuff and then return the count with the actual data & view
}
You cant chain like this
$votes = Vote::where('vote_type',1)->where('something',$something)->count();
if you want the result for the logged in user
$votes = Auth::user()->votes->where('vote_type',1)->count();
I hope you get the point here, you dont have to do the count in blade

Too late to answer this question, but in general groupBy on a collection can be a good option for this
$votesInGroups = Vote::all()->groupBy('vote_type');
if you want to refine the data:
$votesInGroups->map(function($group, $key){
// assign keys so that its meaningful
if($key == 1) {
return [
'upvotes' => $group->count();
];
}
// yada yada yada
});

I ended up just creating another relation and then enquing it with the main call. eg
comments class
public function upvotes()
{
return $this->morphMany('App\Models\General\Votes', 'commentable')->whereVoteType(1);
}
public function downvotes()
{
return $this->morphMany('App\Models\General\Votes', 'commentable')->whereVoteType(0);
}
--
public function index($category_slug, $subcategory_slug, $post_slug)
{
return view('pages.forum-post', [
'post' => Post::With('comments','comments.user','comments.votes','comments.upvotes','comments.downvotes','comments.user.UserProfile')->whereSlug($post_slug)->firstOrFail()
]);
}
--
blade
#if ($comment->upvotes)
{{$comment->upvotes->count()}}
#endif
#if ($comment->downvotes)
{{$comment->downvotes->count()}}
#endif
<hr />

Related

What is the best way to select comments with user data and additional with Laravel?

Im trying to make list with comments, including data about user and his car, that are stored in another tables.
Controller contained such query:
Charging::available()
->with('some.ports', 'shares')
->find($id);
And I rewrote it to such:
Charging::available()
->with('some.ports', 'shares')
->with('chargingComments.user')
->with('chargingComments.car')
->find($id);
Thats how ChargingComments model looks like:
public function comments() {
return $this->belongsTo(\App\Models\Charging::class);
}
public function user() {
return $this->belongsTo(\App\Models\User::class);
}
public function car() {
// here 'id' is the row in the table with users cars
// 'car_id' is in the table with comments
return $this->hasOne(\App\Models\UserCar::class, 'id', 'car_id');
}
It returns me data about each comments` user and his car, but btw I have to somehow limit result to 10 rows. I tried to add
'user' => function($query) {
return $query->take(10);
}])
But it didnt work.
Im sure that should be the better way to write this code, but dont know how
try this
Charging::with('some.ports', 'shares')->with('chargingComments.user')->with('chargingComments.car')->find($id)->latest()->take(10)->get();

Laravel - whereHas with where affect other rows

I have an application where I want to fetch parent records based on children conditionals. Current problem is that I have Students, where they have multiple study fields and study fields belong to one faculty. Pivot table students_study_fields has attribute study_status_id.
What I need is, for example, fetch all students and their study fields which belongs to "prf" faculty AND pivot has study_status_id = 1.
So I write a query like this.
return Student::with(['studyfields' => function ($query1) use ($studyStatusId, $facultyAbbreviation) {
$query1->whereHas('pivot', function ($query2) use ($studyStatusId, $facultyAbbreviation) {
$query2->where('study_status_id', $studyStatusId);
});
$query1->whereHas('studyprogram', function ($query4) use ($facultyAbbreviation) {
$query4->whereHas('faculty', function ($query5) use ($facultyAbbreviation) {
$query5->where('abbreviation', $facultyAbbreviation);
});
});
}])->get();
But this query fetch students witch study_status_id = 2 as well because exists record where this same study field (its code) has relation with student, where study_status_id = 1.
So I don't want to include this studyfield if somewhere exists record with status = 1 in pivot but only if has status = 1 for current row
You need to chain the queries...
return Student::with(['studyfields' => function ($query1) use ($studyStatusId, $facultyAbbreviation) {
$query1->whereHas('pivot', function ($query2) use ($studyStatusId, $facultyAbbreviation) {
$query2->where('study_status_id', $studyStatusId);
})->whereHas('studyprogram', function ($query4) use ($facultyAbbreviation) {
$query4->whereHas('faculty', function ($query5) use ($facultyAbbreviation) {
$query5->where('abbreviation', $facultyAbbreviation);
});
});
}])->get();
Otherwise it will re-start the query1 so you won't get AND kind of query, only get the second part
Side Note: However, I want to warn you that whereHas is a slow query if you have many rows as it goes through each value. I personally prefer grabbing the ids with simple ->where queries and utilise ->whereIn approach.
I found solution for my situation
$students = Student::with(['studyfields' => function ($q) use ($studyStatusId) {
$q->whereHas('pivot')->where('study_status_id', $studyStatusId);
}])
->whereHas('studyfields', function ($q) use ($facultyAbbreviation) {
$q->whereHas('studyprogram', function ($q) use ($facultyAbbreviation) {
$q->where('faculty_abbreviation', $facultyAbbreviation);
});
})
->get();
$students = $students->filter(function ($student) {
return count($student->studyfields) > 0;
})->values();
Query above fetch all students from specific faculty and if studyfields array doesn't contains specific study_status, leave empty array so later I can filter collection from empty arrays assuming that each student belongs to at least one studyfield.

Laravel Count Vehicles W/O Images

I am trying to get some help to rewrite some stuff that got thrown at me on a project I am working on to increase speed. I am working in Laravel 5.4. I can trying to count how many vehicles do not have images without looping through each vehicle.
Each vehicle has a vehicle_id that corrects to the vehicle_id col in the vimages table.
I am trying to eliminate having to loop through every single vehicle and make separate SQL calls for each vehicle.
My Function To Count:
'missingDescription' => $inv->where('vehicle_type','=','NEW')->where('description','=', '')->where('description','=', null)->count(),
Original Function To Count:
'images' => $inventories->filter(function($row) {
if ($row->Images()->get()->count() <= 0) {
return true;
}
return false;
})->count(),
'stockphotos' => $inventories->filter(function($row) {
return $row->Images()->get()->filter(function($item) {
return $item->isStockPhoto && !$item->isDeleted;
})->count() > 0 ? true : false;
})->count(),
Images function:
public function images() {
return $this->hasMany('App\Vimage');
}
You could use withCount. so when you get the model originally add withCount('images') which will append images_count to the returned model.
$inventories = Inventory::withCount('images')->get();
'images' => $inventories->where(images_count, 0)->count()
Here's the laravel page for reference Querying Relationship Absence
You need to use a search closure for the description, since you're looking for where it's either an empty string OR null.
'missingDescription' => $inv->where('vehicle_type','=','NEW')
->where(function($query) {
$query->where('description','=', '')
->orWhereNull('description');
})->count(),

Checking if there is only one Database entry in Laravel

I have the table item which each user can add and delete as many as they want, however I want that there is always atleast one entry in item.
There is this dirty way but there sure are better ways.
My solution:
$items = item::all();
foreach($items as item)
{
$a++;
}
if ($a > 1) {
delete_item();
return back();
} else {
return back();
}
There must be a cleaner way to do this.
You could use the count() method from QueryBuilder/Eloquent. It returns the number of items in your table:
$totalOfItems = Items::count();
if ($totalOfItems > 1) {
delete_item();
}
return back();
This way, if the total of items in your table is bigger than 1 your delete_item() operation will do its job, otherwise, it will just return back.
Hope that this helps you.
If you want this to be the default behavior, you can add event listener on deleting event and use count().
Check the documentation for specific details on which laravel version

Laravel get users by id in user model and store them in an array

I'm trying to make a simple "friends" system, by storing each friend id in a single column as a string separated with a comma.
I want to get the users by id and store them in an array:
public function getFriends() {
$friendids = explode(',', $this->friends);
$friends = [];
foreach ($friendids as $id) {
$friends[] = User::findOrFail($id);
}
return $friends;
}
So I can do this in my view:
#foreach ($user->getFriends() as $friend)
<p>Friend ID: {{ $friend->id }}</p>
<p>Friend Username: {{ $friend->username }}</p>
#endforeach
But I think I can't use findOrFail in the user model or what am I doing wrong?
To use the findOrFail($id) method, you should invoke the object type you want to get. Example:
foreach ($friendids as $id) {
$friends[] = User::findOrFail($id);
}
Although there's an existing answer, I wanted to provide an alternative solution that I personally feel is a better long-term solution.
Firstly, saving friends as an comma separated list isn't going to scale very well. It also massively limits the ability for you to do things such as 'friends of friends', and other more complex queries.
Really, you should have two tables, users and friends.
User model
public function acceptedFriends()
{
return $this->hasMany('App\Friend')->where('accepted', true);
}
public function pendingFriends()
{
return $this->hasMany('App\Friend')->where('accepted', false);
}
Now, when a friend request is rejected, you can simply delete the record in the Friends table.
Loading a user with their friends:
$user = User::find($id)->with('acceptedFriends')->firstOrFail();
Loading a user's friend requests:
$users = Friends::where('accepted', false)->where('target_user', $userId)->get();
You might want to also check out https://stackoverflow.com/a/25057320/972370. It's written by someone who is very well known in the Laravel community for advanced Eloquent usage.
I made 2 simple mistakes:
1) I removed User object from findOrFail()when looking for the mistake I made
2) I had specified user id's (friends) that did not exist in the databse, causing an error
I also improved the code a bit, by adding an optional parameter to get only x amount of users:
// App/User.php
public function getFriends($friends_to_get = null) {
$friendids = explode(',', $this->friends);
$friends = [];
$i = 0;
foreach ($friendids as $id) {
if (is_numeric($id)) {
$friends[] = User::findOrFail($id);
}
if (isset($friends_to_get) && $i < $friends_to_get) {
++$i;
}
if (isset($friends_to_get) && $i == $friends_to_get) {
break;
}
}
return $friends;
}
Now you can do something like this in your view:
#foreach ($user->getFriends() as $friend)
<p>Friend ID: {{ $friend->id }}</p>
<p>Friend Username: {{ $friend->username }}
#endforeach
Or if you want to get, for example, 6 friends only, you can do: $user->getFriends(6)
In my case, $user is the user of who's profile I'm currently viewing. If you want, you could also get your friends only, by doing Auth::user()->getFriends()
/Edit: Added is_numeric check in the foreach loop. This way you don't have to worry about first and last element in the column having an extra comma. Since explode adds an extra value to the array, if there's a comma in the end of the friends column. Now you can just add or remove x, from/to the column every time and not worry about fetching an object, that does not exist.

Categories