Laravel Count and Group By Child Relationship - laravel

I have two tables, one called companies and one called leads. Using Laravel's Eloquent I'd like to get a count of leads for each company. Which is pretty straight forward, but I also need to group these by the Leads status.
e.g.
Company A has
3 hot leads
2 cold leads
Company B has
6 cold leads
0 hot leads
Companies table has the following structure:
id
name
Leads table has the following structure
id
company_id
status (hot, cold)
I have tried the following, which gets the count of leads out for each company, but this needs to be further grouped by the status of the leads
DB::table('companies')
->selectRaw('companies.id, companies.name, COUNT(*) as count')
->join('leads', 'leads.company_id', '=', 'companies.id')
->groupBy('id')
->get();

When using aggregates such as count, you need to make sure to group by all selected non-aggregate columns.
MySQL will usually let you get away with out doing this but the query results can come out being non-sensical.
$companies = DB::table('companies')
->select(['companies.name', 'leads.status', \DB::raw('COUNT(*) as count')])
->join('leads', 'leads.company_id', '=', 'companies.id')
->groupBy('companies.name', 'leads.status')
->get();
If you have multiple records in the companies table with the same name (but different ids), this will group all the counts together for each duplicate company name. If that's the case, you should also select and group by companies.id as well.
If you want to key this by the company, and then by the status, the following should work...
$companies = $companies->groupBy('name')->map(function ($company) {
return $company->groupBy('status')->map(function ($company) {
return $company->first()['count'];
});
});
Then you can use it like...
echo $companies['testCompanyName']['hot'];
echo $companies['testCompanyName']['cold'];

Related

Laravel - Get records between two dates from second table

I have something like this:
Table 1: Training Name, created_at, user_id (Plan_Treninga)
Table 2: user_id, created_at, expire_at (InvoiceUser)
I want to pull all from Table 1 where created_at is between Table 2 created_at and expire_at.
This is something what i am trying to..
$plan = Plan_Treninga::whereBetween(function($q) use ($id){
$inv = InvoiceUser::where([
["user_id",$id],
["status","paid"],
])->latest("id")->first();
})
I haven't finished it yet, but my brain stopped working so I have to ask here.
If I understand what you want clearly is. you want to query all from table 1 which created exist between table 2 created and expire_at right? if so you can use where exist query to achieve this.
// assume your table name is plan_treningas & invoice_users
Plan_Treninga::whereExists(function ($query) {
$query->select(DB::raw(1))
->from('invoice_users')
->whereRaw('plan_treningas.created_at BETWEEN invoice_users.created_at AND invoice_users.expire_at'); // add more query depend your logic
})->get();
for more you can take a look at docs
or if you want to use raw query
SELECT
*
FROM plan_treningas
WHERE EXISTS (
SELECT 1 FROM invoice_users WHERE plan_treningas.created_at BETWEEN invoice_users.created_at AND invoice_users.expire_at
)
Take a look at joins https://laravel.com/docs/7.x/queries#joins
I am not saying this is the exact solution but I have something similar that I have changed to point you in the right direction.
With joins you can do lots of things.
$results = DB::table('table1')
->join('table2', function ($join) {
$join->on('table1.user_id', '=', 'table2.user_id')
->where('table2.status', '=', 'paid')
->where('table2.created_at', '>', 'table1.created_at');
})
->get();
Also look at relationships. There is some good answers for setting up many to many relationships.
https://laravel.com/docs/7.x/eloquent-relationships#many-to-many

Laravel - How to paginate united Many To Many(Polymorphic) collection?

Trying to figure out how to fetch two related models(obviously united) of my Many To Many(Polymorphic) relationship.
What we have:
3 models: Bucket, Template and DesignPack.
Bucket has Many-To-Many(Polymorphic) relationship with Template and DesignPack(It means we have pivot table bucketables).In essence Bucket can have(be related with) both: Template and DesignPack.
Laravel 6.*
What I want to get:
I want to get a Bucket templates and design packs united in one collection and paginated!
Please check one of the solutions I've tried:
$templates = Bucket::find($bucket_id)->templates()->select(['id', 'file_name as name', 'size', 'preview']);
$design_packs = Bucket::find($bucket_id)->dps()->select(['id', 'name', 'size', 'preview']);
$all = $templates ->union($design_packs )->paginate(10);
Unfortunately that solution throws me the error(thought I checked what each request returns and it returns the same fields, not different):
"SQLSTATE[21000]: Cardinality violation: 1222 The used SELECT statements have a different number of columns (SQL: (select `id`, `size`, `preview`, `bucketables`.`bucket_id` as `pivot_bucket_id`, `bucketables`.`bucketable_id` as `pivot_bucketable_id`, `bucketables`.`bucketable_type` as `pivot_bucketable_type` from `design_packs` inner join `bucketables` on `design_packs`.`id` = `bucketables`.`bucketable_id` where `bucketables`.`bucket_id` = 3 and `bucketables`.`bucketable_type` = App\DesignPack and `design_packs`.`deleted_at` is null) union (select `id`, `size`, `preview` from `templates` inner join `bucketables` on `templates`.`id` = `bucketables`.`bucketable_id` where `bucketables`.`bucket_id` = 3 and `bucketables`.`bucketable_type` = App\Template and `templates`.`deleted_at` is null))"
Are there any different way to get what I want?
May be examples, documentation links or any helpful ideas?
Will be so grateful guys for any help!
Thank you!
You can pass closure to queries:
$templates = Bucket::whereHas('templates', function($query) use $bucket_id {
$query->where('bucket_id', $bucket_id);
})->get();
$designPacks = Bucket::whereHas('dps', function($query) use $bucket_id {
$query->where('bucket_id', $bucket_id);
})->get();
then merge 2 eloquent collections:
$mergedCollections = $templates->merge($designPacks);
now you have a collection of both results, you can select specific fields, limit the results or etc. you may want take a look at Laravel collection helpers.
also if you insist to use the union, you may want to take a look at this treat:
The used SELECT statements have a different number of columns (REDUX!!)

Search query from joined table in Laravel 5.3

I have a books table that contains many subject on my subjects table (one-to-many relationship).
I tried to join my tables like this:
$book = Book::latest()
->leftjoin('subjects', 'books.id', '=', 'subjects.book_id')
->select('books.*', 'subjects.subject')
->where('subject', 'like', '%' .$search. '%')
->paginate(20);
I want a search query that will display the books having subjects matched form the $search variable. However, it keeps displaying a book redundantly depending on how many subjects of a book that matched on the $search variable since a book has many subjects.
I only want to display a book once, regardless of how many subjects the book matched.
This image below was the output of the search query I made, the value of the $search= ""
On the second image notice that I search "a" on the search box:
The book entitled "Special Education assessment: Issues strategies affecting today's classrooms" (see it on the first image; it was being redundant 6 times since the subjects of that book was 6)
To display a book only once you have to group by book id (or any other unique column)
->groupBy('books.id');
Mind you, as mentioned in the MySQL doc here
SQL92 and earlier does not permit queries for which the select list, HAVING condition, or ORDER BY list refer to nonaggregated columns that are not named in the GROUP BY clause.
Hence the error message 'bisu_ccc_library.books.ISBN' isn't in GROUP BY
To bypass this, turn off strict in Laravel and everything will work nicely.
Go to config/database.php and in the mysql configuration array, change strict => true to strict => false
I think you want to use distinct for your select
$book = Book::latest()
->leftjoin('subjects', 'books.id', '=', 'subjects.book_id')
->select('books.*', 'subjects.subject')
->distinct()
->where('subject', 'like', '%' .$search. '%')
->paginate(20);
Just like in regular SQL (which it will translate to) it will "Force the query to only return distinct results." (from laravel api docs)
The SELECT DISTINCT statement is used to return only distinct
(different) values.
Inside a table, a column often contains many duplicate values; and
sometimes you only want to list the different (distinct) values.
The SELECT DISTINCT statement is used to return only distinct
(different) values. - W3Schools

Eloquent query if value in relationship isn't same as value in other relationship

I have four models: Item, Car, ItemLocation and Branch.
An Item has a Car and a ItemLocation via a person_id and a item_location_id field in the DB.
A Car has a branch_id which links to Branch, and an ItemLocation also has a branch_id in the same way.
What I want to do is to select all Items where their Cars's branch is not equal to their ItemLocation's branch.
I tried this statement, though I knew that it wouldn't work:
Item::with('car','item_location')
->where('car.branch_id', '<>', 'item_location.branch_id')
->get();
I'm aware of querying on relationships, but don't understand how to do that across relationships like this.
Any ideas?
Querying relationships won't help you, since you want to compare values from separate tables. You need joins:
$items = Item::select('items.*')
->join('item_locations as il', 'il.id', '=', 'items.item_location_id')
->join('cars', function ($j) {
$j->on('cars.person_id', '=', 'items.id')
->on('cars.branch_id', '<>', 'il.branch_id');
})
->get()
This will fetch all the items having both cars and item_location and matching your criteria. If you want to include also ones that don't have either of the relations, then use leftJoins instead and whereNull('cars.id')
ps. It's hard to read your question. Instead of describing these relationships, better simply show the tables with relevant fields.

"Order by" when querying relation

I have a Project table and a pivot table "like_project" which has 2 columns : user_id and project_id, so users can like Projects
I'm trying to list all the Projects and order them by number of likes, by using the "has" method, like this :
$projects = Project::has('likes')->paginate(10);
the issue is that I don't know how to order by number of likes, I have a function on my Project model to count the number of likes for a project :
public function getTotalLikes(){
return sizeof($this->likes()->getRelatedIds()); //I could use $this->likes()->count()
}
Unless you want to write out a long SQL query to run an GROUP/ORDER BY command I'd just run the eloquent collection's sort method once you get the projects back.
$projects->sort(function($project) {
return $project->likes->count();
});
It depends on how your likes are stored exactly, but most straightforward way is simple join (suppose MySQL):
$projects = Project::join('like_project as lp', 'lp.project_id', '=', 'projects.id')
->groupBy('projects.id')
->orderByRaw('count(lp.project_id) desc')
->select('projects.*') // and if needed: DB::raw('count(lp.project_id) as likesCount')
->paginate(10);

Resources