Laravel - Merge two Queries? - php

I am building a Twitter-like application which has a feed in it. In this feed I need to display shares with this properties:
-Shares from user, who I am following
-Shares from user, which are sorted by the positive rating, but only the top10%
These two queries I need somehow to merge, so it will become an array in the end, which has all the shares which are applying to this criteria, without any duplicates and ordered by ID, desc
My tables are looking like this:
User, Shares, Follows
Shares:
-user_id
-positive
Follows:
-follower_id
-user_id
-level
What I already tried:
$follower_shares = Share::join('follows', 'shares.user_id', '=', 'follows.user_id')
->where('follows.follower_id', Auth::user()->id)
->where('follows.level', 1)
->get(array('shares.*'));
//count = 10% of total shares
$count = Share::count()/10;
$count = round($count);
$top10_shares = Share::orderBy('positive', 'DESC')
->take($count)
->get();
//sorts by id - descending
$top10_shares = $top10_shares->sortBy(function($top)
{
return -($top->id);
});
//merges shares
$shares = $top10_shares->merge($follower_shares);
The problem is now, that I was told that there is a better way to solve this.
Also, $shares is giving me the result which applies to the criteria, but the shares have duplicates (rows, which are applying to both criteria) and arent ordered by id desc in total.
I would be very happy, if you could tell me, how to do this the right way.
Thanks very much!

I found this to be a pretty clean solution:
// Instead of getting the count, we get the lowest rating we want to gather
$lowestRating = Share::orderBy('positive', 'DESC')
->skip(floor(Share::count() * 0.1))
->take(1)->lists('positive');
// Also get all followed ids
$desiredFollow = Auth::user()->follows()->lists('user_id');
// Select where followed or rating higher than minimal
// This lets the database take care of making them distinct
$shares = Share::whereIn('user_id', $desiredFollow)
->orWhere('positive', '>=', $lowestRating[0])
->get();

Related

How to get the count of a specif value from a single column

I was working with a project where I want to get the count of values in a column. I will explain with an example.
I have a table column named name. Where it's values are A,B,C,D,A,D,B,A,C.
Now I want the output as
Count: A-3,B-2,C-2,D-2.
I have tried using group by and distinct. but both don't give me what I want. It all getting the total count of that item. In the code given below is the query I tried. There I want the count of particulars_id and the $public_page_id will be common for all particulars_id I am fetching. There will be a number of public_page_id in the table, and each will have some particulars_id under them.
$output = '';
$this->db->select('COUNT(service_appointment_details.particulars_id)
as count,particulars.particulars_name');
$this->db->from('particulars');
$this->db->group_by('particulars.particulars_id');
$this->db->where('particulars.public_page_id',$public_page_id);
$this->db-
>join('service_appointment_details','particulars.public_page_id =
service_appointment_details.public_page_id','right');
$this->db->where($where_date);
$query = $this->db->get();
Expected Result
Expected Result is (based on the above example)
Count: A-3,B-2,C-2,D-2.
Actual Result
But what I'm getting now is
Count: A-9,B-9,C-9,D-9.
I need to fetch count of each particulars_id under the given public_page_id
You should use count(*)
$this->db->select('COUNT(*) as count,particulars.particulars_name');
........
As pointed out by #scaisEdge, you need to use count(*) instead of count(service_appointment_details.particulars_id). By using count(service_appointment_details.particulars_id), you basically count the number of rows from your select which is not what you want.
Final snippet would be:
$output = '';
$this->db->select('COUNT(*) as count, particulars.particulars_name');
$this->db->from('particulars');
$this->db->group_by('particulars.particulars_name');
$this->db->where('particulars.public_page_id',$public_page_id);
$this->db->join('service_appointment_details','particulars.public_page_id = service_appointment_details.public_page_id','right');
$this->db->where($where_date);
$query = $this->db->get();
Whenever you want to count occurrence of a column values, you'd do
SELECT column_to_count, count(*) FROM table GROUP BY column_to_count

How to group by aggregation of column in foreign table with many-to-many relation in Eloquent?

I am starting to get headaches over this so I thought I just post it here.
I have two tables that are related through a pivot table (as it is a many-to-many relationship). I use Laravel and Eloquent (but general help on how to achieve this with normal SQL queries is also highly appreciated).
I want to order the first table based a column of the second one but the column needs to be "aggregated" for this.
Example with Cars that are shared by many drivers and can have different colors:
Car-Table: [id, color]
Driver-Table: [id, name]
Car.Driver-Table: [car_id, driver_id]
I need a query that gets all drivers that only drive red cars and then all that don't drive red cars.
I have to use a query because I'll maybe do other things (like filtering) on this query afterwards and want to paginate in the end.
I already use queries that get either one of the two groups. They look like this:
In the Driver model:
public function redCars() {
return $this->cars()->where('color', 'red');
}
public function otherColoredCars() {
return $this->cars()->where('color', '<>', 'red');
}
And then in somewhere in a controller:
$driversWithOnlyRedCars = Driver::whereDoesntHave('otherColoredCars')->get();
$driversWithoutRedCars = Driver::whereDoesntHave('redCars')->get();
Is there a way to combine these two?
Maybe I am just thinking completely wrong here.
Update for clarification:
Basically I would need something like this (ot any other way that would lead to the same outcome)
$driversWithOnlyRedCars->addTemporaryColumn('order_column', 0); // Create temporary column with value 0
$driversWithoutRedCars->addTemporaryColumn('order_column', 1);
$combinedQuery = $driversWithOnlyRedCars->combineWith($driversWithoutRedCars); // Somehow combine both queries
$orderedQuery = $combinedQuery->orderBy('order_colum');
$results = $combinedQuery->get();
Update 2
I think, I found out how to get near my goal with raw queries.
Would be something like this:
$a = DB::table(DB::raw("(
SELECT id, 0 as ordering
FROM drivers
WHERE EXISTS (
SELECT * FROM cars
LEFT JOIN driver_car ON car.id = driver_car.car_id
WHERE driver.id = driver_car.driver_id
AND cars.color = 'red'
)
) as only_red_cars"));
$b = DB::table(DB::raw("(
SELECT id, 1 as ordering
FROM drivers
WHERE EXISTS (
SELECT * FROM cars
LEFT JOIN driver_car ON car.id = driver_car.car_id
WHERE driver.id = driver_car.driver_id
AND cars.color <> 'red'
)
) as no_red_cars"));
$orderedQuery = $a->union($b)->orderBy('ordering');
Now the problem is that I need the models ordered like this and paginated in the end so this is not really an answer to my question. I tried to convert this back to models but I didn't succeed yet. What I tried:
$queriedIds = array_column($orderedQuery->get()->toArray(), 'id');
$orderedModels = Driver::orderByRaw('(FIND_IN_SET(drivers.id, "' . implode(',', $queriedIds) . '"))');
But looks like FIND_IN_SET only allows for a column of the table as second parameter. Is there another way to get the Models in the right order out of the ordered union query?
You can use a UNION query:
$driversWithOnlyRedCars = Driver::select('*', DB::raw('0 as ordering'))
->whereDoesntHave('otherColoredCars');
$driversWithoutRedCars = Driver::select('*', DB::raw('1 as ordering'))
->whereDoesntHave('redCars');
$drivers = $driversWithOnlyRedCars->union($driversWithoutRedCars)
->orderBy('ordering')
->orderBy('') // TODO
->paginate();
How do you want drivers with the same ordering to be ordered? You should add a second ORDER BY clause to get a consistent order every time you execute the query.
This is the best I got:
$driversWithOnlyRedCars = Driver::whereHas('cars',function($q){
$q->where('color', 'red');
})->withCount('cars')->get()->where('cars_count',1);

Counting multiple vote choices in Doctrine with createQueryBuilder

I'm working on a voting system, where user will be able to vote either yes, no or not sure to a given question.
These are being stored in a voteOptions table as choice with a value of either 0, 1 and 2 which is attached to a vote table that stores additional information. I struggling to work out how to count up the votes in a single function.
The function below counts a single choice, which I currently call once for each voteOption. I'm counting distinct users as they can appear more than once, but only their last vote counts:
public function countPollVotesByIsCurrentAndChoice(Poll $poll, $choice) {
return $this->createQueryBuilder('v')
->leftJoin('v.voteOptions', 'vo')
->andWhere('v.poll = :poll')
->andWhere('v.isCurrentVote = :isCurrentVote')
->andWhere('vo.choice = :choice')
->setParameters(['isCurrentVote' => true, 'poll' => $poll, 'choice' => $choice])
->select('COUNT(DISTINCT v.user) AS vote_count')
->getQuery()
->getSingleScalarResult();
}
But I'd prefer to make a single call to get all 3 counts. I would really appreciate any advice about how best to do this.
You have to use COUNT() together with GROUP BY which will aggregate the choices to three possible answers and the COUNT function counts the number of records for each answer.
$query = $em->createQuery('SELECT count(vo.choice) AS cnt FROM AppBundle:Vote v JOIN v.voteOptions vo WHERE ... GROUP BY vo.choice');
$result = $query->getResult();
This query will not return an array with Vote objects but just an array with rows that contains columnames and values. print_r() will help you

eloquent query that finds top 5 records

I'm creating a query that gives me the top 5 countries with more visits, but my query isn't working.
My query I can limit, how would I count all record by country, to see the 5 countries more visited.
My query:
$top5 = DB::table('visits') ->select('ip','country', 'browser') ->groupBy('ip') ->get();
output example:
http://pastebin.com/wtu8CnL8
First you need to group your query by country, since that's what you're looking at. Then you need to count your results, order it by that number, and just take what you need.
$top5 = DB::table('visits')
->select('ip','country', 'browser', DB::raw("count(*) as total_visits"))
->groupBy('country')
->orderBy('total_visits','DESC')
->take(5);

Symfony 2 Multiple Selects with counts on the same table?

Ok I have a flag field on one table, open or closed which is boolean. I am trying to build one query that would take that field and count them based on that flag. Then I will need to group them by account ID
Here is what I am working with now,
$GetTest1 = $GetRepo->createQueryBuilder('s') <- I had 'w' in here but all that did was add an index and not a second alias?
->select(' (count(s.open_close)) AS ClosedCount, (count(w.open_close)) AS OpenCount ')
->where('s.open_close = ?1')
//->andWhere('w.open_close = ?2')
->groupBy('s.AccountID')
->setParameter('1', true)
//->setParameter('2', false)
->getQuery();
Is what I want do-able? I know (or at lest think) that I can build a query with multiple table alias? - Please correct me if I am wrong.
All help most welcome.
Thanks
This DQL query will group the rows in table by accountId and for each of them it will give you count for yes (and you can get count for no by substracting that from total).
BTW I found writing straight DQL queries much more straightforward than writing QueryBuilder queries (which i use only when i need to dynamically construct the query)
$results = $this->get("doctrine")->getManager()
->createQuery("
SELECT t.accountId, SUM(t.openClose) as count_yes, COUNT(t.accountId) as total
FROM AppBundle:Table t
GROUP BY t.accountId
")
->getResult();
foreach ($results as $result) {
//echo print_r($result);
//you can get count_no as $result["total"] - $result["count_yes"];
}

Categories