Laravel GroupBy with Multiple Joins - php

I'm having problems with getting correct data in a three-way join.
EDIT: Sample table structure
Jobs Table
id | name | country | country_slug | city
1 | Job1 | Canada | canada | Ontario
2 | Job2 | South Africa | south-africa | Durban
Cheers Table
id | rep | jobs_id
1 | 14 | 2
2 | 9 | 1
3 | 12 | 2
4 | 23 | 1
Categories Table
id | name
1 | PHP
2 | Laravel
3 | Javascript
4 | Go
category_job pivot Table
id | category_id | job_id
1 | 2 | 1
2 | 2 | 2
3 | 1 | 1
4 | 3 | 2
5 | 4 | 1
Here is an example of what my models look like:
Job
public function categories(){
$this->belongsToMany(Category::class)
}
public function cheers(){
$this->hasMany(Cheer::class)
}
Category
public function jobs() {
$this->belongsToMany(Job::class)
}
Cheer
public function job() {
return $this->belongsTo(Job::class);
}
This is what I'm trying to achieve:
Group jobs by country
Get count of how many jobs are in each country (job_count)
Get count how many cities (distinct) in each group (city_count)
Sum (rep) in cheers relation table for each group
Get the most popular category for each country group via Categories relationship
This is my code so far:
$result = Job::
selectRaw(
"jobs.country,
jobs.country_slug,
COUNT('jobs') as job_count,
COUNT(DISTINCT city) as city_count,
SUM(rep) as cheer_rep",
)
->join('cheers', 'cheers.jobs_id', '=', 'jobs.id')
->orderByDesc('cheer_rep')
->groupBy('jobs.country', 'jobs.country_slug')
->get();
Here are my problems;
The job count is wrong because the join clause adds more results due to the hasMany relationship.
I can't seem to wrap my head around grouping the categories and getting the most occurring (popular) category.
Sample result
[
{
"country": "Canada",
"country_slug": "canada",
"job_count": 23,
"cities": 5,
"cheer_rep": "35000"
},
{
"country": "South Africa",
"country_slug": "south-africa",
"job_count": 9,
"cities": 2,
"cheer_rep": "700"
},
]
Any help would be highly appreciated. Also, if there's an eloquent way of achieving this, I'd appreciate that too.

Eloquent way:
Job::with('cheers')
->select(DB::raw('count(country) as job_count, country'),
DB::raw('count(city) as city_count, city')
)
->orderByDesc('cheers.cheer_rep')
->groupBy('country', 'country_slug')
->get();
Note: Not tested, as you did not provided DB schema.

Related

Laravel add calculate the sum where the value is in another table

Basically i have two tables. answers and choices . In my choices table, I got a column choices.value which is has values 0-4. My current query like this
$answers = \DB::table('answers')
->join('choices','answers.choice_id','=','choices.choice_id')
->select('answers.user_id','choices.choice_id','choices.choice','choices.value')
->where('answers.user_id',\Auth::user()->id)
//->groupBy('answers.user_id')
->get();
My current response is like this
"user_id": 2,
"choice_id": 2,
"choice": "I feel discourated about the future",
"value": 1
},
{
"user_id": 2,
"choice_id": 2,
"choice": "I don't enjoy things the way I used to",
"value": 1
},
{
"user_id": 2,
"choice_id": 2,
"choice": "I feel guilty a good part of time",
"value": 1
how do i add the values so that my result will be like this
"user_id":2,
"total_score":3
I tried doing DB::raw(SUM(choices.values)as score) but i get a large amount. I guess its adding all the choices values from the choices table and not in the answers.
My answers db which i only select the answers of the user = 2. I limit to 5
+---------+-------------+-----------+
| user_id | question_id | choice_id |
+---------+-------------+-----------+
| 2 | 1 | 2 |
| 2 | 2 | 2 |
| 2 | 3 | 2 |
| 2 | 4 | 2 |
| 2 | 5 | 2 |
+---------+-------------+-----------+
My choices table, I only select questions 1 and 2 and their choices.
+-----------+-------------+--------------------------------------------------------------+-------+
| choice_id | question_id | choice | value |
+-----------+-------------+--------------------------------------------------------------+-------+
| 1 | 1 | I do not feel sad | 0 |
| 2 | 1 | I feel sad | 1 |
| 3 | 1 | I am sad all the time and I can't snap out of it | 2 |
| 4 | 1 | I am so sad and unhappy that I can't stand it | 3 |
| 1 | 2 | I am not particularly discouraged about the future | 0 |
| 2 | 2 | I feel discourated about the future | 1 |
| 3 | 2 | I feel I have nothing to look forward to | 2 |
| 4 | 2 | I feel the future is hopeless and that things cannot improve | 3 |
+-----------+-------------+--------------------------------------------------------------+-------+
Also i want to make a new table named scores and the columns will be the result of what i want. I want to add the choices.values in the answer in every answers.user_id so that when i view the scores table, it will display the total score in every user or when the user finished answering all 21 questions because i only got 21 items it will automatically add in the scores table. Could I possibly do that?. Is it okay to add a value in the answers table based in the choice_id? That is what im thinking but I think its redundant since the choice_id is there already. Thanks in advance.
PS: Tried writing these queries but always get 441 which is the total value of all the choices in the choices table
SELECT answers.user_id,choices.choice_id,choices.value,COALESCE(sum(choices.value),0) as score FROM
`answers` JOIN `choices` ON choices.choice_id = answers.choice_id where
answers.user_id = 2
SELECT answers.user_id,choices.choice_id,choices.value,SUM(choices.value),0 as score FROM `answers`
join choices on choices.choice_id = answers.choice_id
where answers.user_id = 2
SELECT answers.user_id,choices.choice_id, sum(choices.value) from answers
JOIN `choices` ON choices.choice_id = answers.choice_id
group by answers.user_id
Currently you are only matching to a choice but you need to match the choice as well as the question.
I found this after seeng each choices.question_id had choices 1, 2, 3 and 4 in a slack conversation. I didn't know until I actually saw a screenshot of the choices table.
$answers = \DB::table('answers')
->join('choices', function ($q) {
$q->on('answers.choice_id','=','choices.choice_id')
->on('answers.question_id', '=', 'choices.question_id');
})
->select('answers.user_id',\DB::raw('sum(choices.value) as total'))
->groupBy('answers.user_id')
->get();
use group by maybe it will help
$answers = \DB::table('answers')
->join('choices','answers.choice_id','=','choices.choice_id')
->select('answers.user_id',DB::raw('count(*) as total'))
->where('answers.user_id',\Auth::user()->id)
->groupBy('answers.user_id')
->get();

How to fetch record from three table in laravel eloquent

I have four tables
jobposts:
id | user_id | cat_id | job_title
1 | 1 | 1 | job 1
2 | 1 | 2 | job 2
3 | 2 | 3 | job 3
4 | 1 | 3 | job 4
categorymasters:
id | category_name
1 | cat1
2 | cat2
3 | cat3
4 | cat4
lastsubcategoryselectedbycompanies:
id | jobposts_id | lastsubcategorymasters_id
1 | 1 | 1
2 | 1 | 2
3 | 2 | 3
4 | 1 | 3
lastsubcategorymasters:
id | LastSubCategoryName
1 | lastsubcat1
2 | lastsubcat2
3 | lastsubcat3
4 | lastsubcat4
jobposts have unique rows.
lastsubcategoryselectedbycompanies is a mapping of jobposts and lastsubcategorymasters.
Now assume some user is logged in with their credentials (EX: take user_id 1 in jobposts). Now I need to show LastSubCategoryName in a comma separated list from the lastsubcategorymasters table, grouped by the jobposts, lastsubcategoryselectedbycompanies and lastsubcategorymasters tables.
allpostedjob.blade.php is:
#foreach($jobposteddetails as $jobposteddetail)
<tr>
<td>{{ $jobposteddetail->job_title }}</td>
</tr>
#endforeach
cotroller is:
public function index()
{
$user = Auth::user();
$jobposteddetails = jobpost::with('categorymaster')->where('user_id', '=', $user->id)->get();
return view('jobprovider.allpostedjob', compact('user','jobposteddetails'));
}
jobpost.php model is:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class jobpost extends Model
{
function categorymaster()
{
return $this->belongsTo(categorymaster::class, 'cat_id');
}
}
It is working proper.
But I also need to show LastSubCategoryName grouped by the tables jobposts, lastsubcategoryselectedbycompanies and lastsubcategorymasters.
function lastsubcategory()
{
return $this->belongsTo(lastsubcategoryselectedbycompanies::class);
}
It is not working. How can I fetch my result?
I am not very skilled at applying complex queries with eloquent, I prefer to use DB query builder with the join method
https://laravel.com/docs/5.8/queries

Laravel Eloquent multiple relationship in a football game schedule

I am new to Laravel and Eloquent and i am trying to create a football game plan.
For now i have 4 tables (with some example entries):
teams (all teams)
+---------+-----------+
| team_id | team_name |
+---------+-----------+
| 1 | Arsenal |
| 2 | Chelsea |
+---------+-----------+
competition (all competitions)
+----------------+------------------+
| competition_id | competition_name |
+----------------+------------------+
| 1 | Premier League |
+----------------+------------------+
schedule (schedule to the competitions)
+----+----------------+----------+--------------+--------------+-----------+-----------+
| id | competition_id | matchday | home_team_id | away_team_id | home_goal | away_goal |
+----+----------------+----------+--------------+--------------+-----------+-----------+
| 1 | 1 | 1 | 1 | 2 | 3 | 2 |
| 2 | 1 | 2 | 2 | 1 | 0 | 3 |
+----+----------------+----------+--------------+--------------+-----------+-----------+
schedule_teams (matches the schedule teamid with the teams id over the competition_id and the schedule_team_id)
+----+------------------+----------------+----------+
| id | schedule_team_id | competition_id | teams_id |
+----+------------------+----------------+----------+
| 1 | 1 | 1 | 1 |
| 2 | 2 | 1 | 2 |
+----+------------------+----------------+----------+
And here are my current classes:
Schedule.php
public function competition()
{
return $this->belongsTo(Competition::class, 'competition_id', 'competition_id');
}
Competition.php
public function schedule()
{
return $this->hasMany(Schedule::class, 'competition_id', 'competition_id');
}
With
$id = \request('competition_id');
$schedule = Schedule::where('competition_id', $id)->with('competition')->get();
i get the schedule with the home and away id's from schedule.
The question now is, how can i get the entries from the teams table over the schedule_teams table to a specifiy home and away id, also for example home_team_id = 1:
home_team_id (=1) -> schedule_team_id (=1) and competition_id (=1) -> teams (Arsenal)
I want the data from schedule and the associated teams in a collection to output in a blade.
can anyone help or give me improvement tips for a football database?
You should make use of the hasManyThrough relationship.
If you create say Schedule\Team, and then have that like the following.
public function schedule() {
$this->belongsTo(Schedule::class, 'schedule_id');
}
public function team() {
$this->belongsTo(Team::class, 'team_id');
}
Now in your Schedule class, you can have the following.
public function teams() {
$this->hasManyThrough(Team::class, Schedule\Team::class, 'schedule_id');
}
It should also be noted, that you don't need competition_id in your schedule team. Since a team belongs to a schedule, which belongs to competition, you can get it like that.
If you also want your Team to know about its schedules, you can add this to Team.
public function schedules() {
return $this->hasManyThrough(Schedule::class, Schedule\Team::class);
}
You Schedule\Team class becomes essentially, a glorified representation of a pivot table, but having it as a model, allows you to expand upon it in the future. It also helps keep everything neat.
Hope that makes sense.

How to have multiple layers of hasMany() relation for Laravel 5?

Let say I have tables like the following:
order
order_id | customer_name | delivery_address
1 | David | ABC Road, DEF district
...........Some other record ..............
order_dish
order_id | dish_id | dish_name
1 | 1 | chicken wing
1 | 2 | meat pie
1 | 3 | onion ring
...........Some other record ..............
dish_ingredient
dish_id | ingredient
1 | chicken wing
1 | salt
1 | oil
2 | pork meat
2 | flour
3 | onion
...........Some other record ..............
In Laravel 5, if I want to get all dishes of an order, I can do:
$order = Order::hasMany('App\OrderDish', 'order_id')->get();
Is there a way I can get all ingredients needed for an order in one line?
Yes. The "has-many-through" relationship provides a convenient shortcut for accessing distant relations via an intermediate relation. You define it using hasManyThrough() method of your model.
First, define the relation in your Order model:
class Order extends Model {
public function ingredients() {
return $this->hasManyThrough('App\DishIngredient', 'App\OrderDish', 'order_id', 'dish_id');
}
}
With such a relation defined, you'll be able to get ingredients for given order with:
$ingredients = $order->ingredients;

count with doctrine

I have two table:
Name
id | name | city_id
1 | aaa | 1
2 | vvv | 2
3 | ddd | 2
4 | sss | 3
5 | dds | 1
etc
City:
id | name
1 | London
2 | NY
3 | Boston
etc
how can i get City and count:
name_city | count
London | 2
NY | 2
Boston | 1
In City table:
$q = $this->createQuery('a')
->leftJoin('a.Name n')
->select('a.name_city as name_city, sum(n.city_id) as sum');
return $q->execute();
but this is wrong.
You should use count() instead of sum(), and plus, you need a group by.
You do not appear to have a FROM clause, the object type is not specified for the a entity.
Also, read the aggregate values section in the documentation.
this post was kinda helpful but I though that I would add a little more details for anyone looking to join 2 tables and with an aggregate count.
e.g. this post (http://stackoverflow.com/questions/7837671/mysql-join-tables-and-count-instances) but in doctrine.
Using the example above the query would be (doctrine 2.0):
$q = $this->em->createQueryBuilder('a')
->select('a.name_city as name_city, count(n.city_id) as sum');
->from('city','a')
->leftJoin('a.Name n')
->groupBy('n.id')
->orderBy('sum')
$query = $qb->getQuery();
echo $qb->getDql(); // if you want to see the dql created
$result = $query->getResult();

Categories