I have 3 tables:
Posts
--id
--post
Points
--id
--user_id
--post_id
--points
User(disregard)
--id
--username
My models are like this.
Class Posts extends Eloquent {
function points(){
return $this->hasMany('points', 'post_id');
}
}
Class Points extends Eloquent {
function posts() {
return $this->belongsTo('posts', 'post_id');
}
How can I order it so that the returning results would be ordered by highest sum of points.I also need to know how I can get the sum of points per post.
Post_id | Post | Points<-- SumPoints
5 |Post1 | 100
3 |Post2 | 51
1 |Post3 | 44
4 |Post4 | 32
Here is my Code:
$homePosts = $posts->with("filters")
->with(array("points" => function($query) {
$query->select()->sum("points");
}))->groupBy('id')
->orderByRaw('SUM(points) DESC')
->paginate(8);
May I know how it would be solved by using query builder and/or model relationships
Eloquent way:
$posts = Post::leftJoin('points', 'points.post_id', '=', 'posts.id')
->selectRaw('posts.*, sum(points.points) as points_sum')
->orderBy('points_sum', 'desc')
->paginate(8);
The Query\Builder way is exactly the same, only the result won't be Eloquent models.
I think the following query-builder should get you started..
DB::table('posts')
->join('points', 'posts.id', '=', 'points.post_id')
->orderBy('sum(points)')
->groupBy('points.post_id')
->select('points.post_id', 'posts.post', 'sum(points.points) as points')
->paginate(8)
->get();
Related
I have tables,
ec_products : id | brand_id (id from ec_brands) | store_id (id from mp_stores)
ec_brands: id
mp_stores: id
I am calculating total products belong to each brand and store using relations and withCount of Laravel, Like,
Brand model,
public function products()
{
return $this->hasMany(Product::class, 'brand_id')->where('is_variation', 0);
}
Stores model
public function products()
{
return $this->hasMany(Product::class, 'store_id')->where('is_variation', 0);
}
And in each model,
$data = $data->withCount(‘products’);
Now I introduced categories, One product belongs to multiple categories.
So used separate table to link category and product.
ec_product_category_product: id | category_id (from category table) | product_id (from our products table)
So final question I have, How to join all,
I want to list each of these (brand,store, category) and count products of each based on request parameters.
Like
if any brands selected, then count store and category related to that brand.
if stores selected, then count brands and category related to that store.
if any category selected, then count store and brand related to that category.
structure of UI
Suggestion or solution.
Thanks
You should always start you Eloquent query with the Object you want to manipulate, here, it's Product and THEN constraint your query,
so in your controller :
$product_count = Product::when($request->category_id, function($query) use ($request){
$query->where('category_id', $request->category_id);
})
[...] //do that for each of your constraints
->count();
if you want to do a search on the name of the category for example, use a whereHas :
$product_count = Product::when($request->search, function($query) use ($request){
$query->whereHas('category', function($query) use ($request){
$query->where('name', 'like', "%$request->search%");
});
})
[...] //do that for each of your constraints
->count();
I have a channel_members table containing channel_id, user_id. I need to get the channel_id if there are multiple rows using that same channel_id which contains multiple user_id that I will provide.
Example:
If there are 5 rows in the table
CHANNEL_ID | USER_ID
2 | 2
2 | 3
2 | 4
3 | 2
3 | 4
I need to get the channel_id which are being used by user_id 2, 3, and 4. So based in the table above that I provided. I should get channel_id 2.
I assume you have already defined your relations uisng models, As per your provided description, channel has many to many relation with user
class Channel extends Model
{
public function members()
{
return $this->belongsToMany('App\User', 'channel_members', 'channel_id', 'user_id');
}
}
Using eloquent realtions you can check existance of related models as
$channels = App\Channel::whereHas('members', function ($query) {
$query->where('id', '=', 2);
})->whereHas('members', function ($query) {
$query->where('id', '=', 3);
})->whereHas('members', function ($query) {
$query->where('id', '=', 4);
})->get();
Above will return you only channels who have associations with all 3 users based on supplied id
Querying Relationship Existence
SELECT CHANNEL_ID
FROM channel_members
WHERE USER_ID in (2,3,4)
GROUP BY CHANNEL_ID
HAVING COUNT(CHANNEL_ID) > 0
I am pulling my hair out over this one, and I feel like I have tried every method!
Here is my problem.
I have 2 tables
USERS
ID | FIRSTNAME | EMAIL_ADDRESS
1 | Joe Bloggs | joe#bloggs.com
STATUS
ID | USER_ID | STATUS | DATE
1 | 1 | 'In' | 2018-06-04 09:01:00
2 | 1 | 'Out' | 2018-06-04 09:00:00
As you can see by the tables above, each user can have many status', but each user has to have 1 most recent status, which I am doing like this (please tell me if I am doing it wrong)
public function statusCurrent(){
return $this->hasOne('App\Status', 'user_id', 'id')->orderBy('date', 'desc')->limit(1);
}
I then a form on in my view, which passes filters to the controller via a $request.
I need to be able to use the filters, and apply them to the 1 most recent status. For example, if someone searches for the date 2018-06-04 09:00:00 and a user id 1, I need it to show NO RESULTS, because the 1 most recent record for that user does not match that date, but at the moment, it will just jump over the one most recent if it doesn't match, and get the next record that does.
I have tried what seems like every method, I have tried like this
$users = Users::with(['StatusCurrent' => function($query){
$query->where('status.status', 'In');
}])->get();
Which gets the correct most recent row, but then if i try status.status, 'out' instead, it just jumps over and gets record number 2 where the status is out.
I've also tried like this
$users = Users::has('StatusCurrent')->paginate(10);
if(!empty($request->statusIn)){
$users = $users->filter(function ($item){
$item = $item->statusCurrent->status == 'in';
return $item;
});
}
return $users;
Which works great but then the pagination breaks when trying to append any GET parameters for the filters.
Plain English
I need to be able to get the most recent status for the user, then once I have it, I need to be able to apply where statements/filters/arguments to it, and if they don't match, completely ignore that user.
You have to combine a JOIN with a subquery:
$users = User::select('users.*')
->join('status', 'users.id', 'status.user_id')
->where('status.status', 'in')
->where('status.id', function($query) {
$query->select('id')
->from('status')
->whereColumn('user_id', 'users.id')
->orderByDesc('date')
->limit(1);
})
->get();
You can get the ID first, then do your query with filters:
$status_id = Users::find($user_id)->statusCurrent()->id;
Now do the actual query, using $status_id in whereHas clause:
$users = Users::with('statusCurrent')
->whereHas('statusCurrent', function($query) use($status_id) {
$query->where('status.status', 'In')
->where('id',$status_id);
})->get();
The relationship should be like:
public function statusCurrent(){
return $this->hasOne('App\Status', 'user_id', 'id')->latestOfMany();}
I have two tables with a pivot table
Table tours
id | name | country_id | featured
Table countries
id | name
Pivot Table country_tour
id | country_id | tour_id
I want to to find the tour that has featured column of tours table set to 1 and country_id of country_tour table set to 1.
UPDATED:
You can do it like this using Laravel's query Builder method - whereHas():
Your models should look like this (Many to Many Relationships):
Tour Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tour extends Model
{
public function countries() {
return $this->belongsToMany('App\Country');
}
}
and Country Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
public function tours() {
return $this->belongsToMany('App\Tour');
}
}
and now you can fetch the desired results by using the below query:
Tour::where('featured', 1)
->whereHas('countries', function($q) {
$q->where('id', 1);
})
->get();
This will get you the collection of tours with featured = 1 and having country with id = 1.
Hope this helps!
With reference to Saumya Rastogi answer, change the id to countries.id to avoid "column 'id' in where clause is ambiguous" error.
i.e.
From:
Tour::where('featured', 1)
->whereHas('countries', function($q) {
$q->where('id', 1);
})->get()
To:
Tour::where('featured', 1)
->whereHas('countries', function($q) {
$q->where('countries.id', 1);
})->get();
I am using Laravels Eloquent ORM and i ran into a little problem with a special relationship. Lets assume i have the following table:
Recipe:
id | ... | ingredient1 | ingredient2 | ingredient3 | ingredient4
Every recipe has exactly 4 ingredients and i get the data from an external source in this specific format, thats why i have the ingredients as columns and not as a normal many-to-many relation.
I could set these up as 4 one-to-many relations, but i want to be able to write $ingredient->usedInRecipes()->get().
With 4 one-to-many relations i would have to write $ingredient->usedInRecipesAsIngredient1()->get(), [...], $ingredient->usedInRecipesAsIngredient4()->get() and merge them after afterwards, which would result in 4 queries.
If you know a good way to join these before querying the database or how to make a 4-to-many relation work please answer!
From the question I can't tell if you have already attempted this or not although as I see it you just need to use a single many-to-many relationship.
Each ingredient presumably has a common set of properties that can all be handled in one table ingredients.
id name created_at updated_at
1 Paprika 01/01/1970 00:00:00 01/01/1970 00:00:00
1 Rosemary 01/01/1970 00:00:00 01/01/1970 00:00:00
1 Basil 01/01/1970 00:00:00 01/01/1970 00:00:00
1 Oregano 01/01/1970 00:00:00 01/01/1970 00:00:00
Then your recipes table
id name created_at updated_at
1 Herb Soup 01/01/1970 00:00:00 01/01/1970 00:00:00
To hold the relationships, a pivot table ingredient_recipe
id recipe_id ingredient_id
1 1 1
2 1 2
3 1 3
4 1 4
Now all you require is a belongsToMany relationship on both your Recipe and Ingredient model.
You can code safeguards to make sure one recipe only ever has 4 relationships with ingredient if you wish but to keep it simple:
Recipe::with('ingredients')->get();
Would retrieve all the ingredients along with the recipe.
You can read more about this relationship in the documentation here.
Without Pivots
If you kept the columns ingredient_1, ingredient_2 and so on in the recipes table you could add something like this to your Recipe model.
public function scopeGetWithIngredients($query)
{
$query->leftJoin('ingredients i1', 'recipes.ingredient_1', '=', 'i1.id')
->leftJoin('ingredients i2', 'recipes.ingredient_2', '=', 'i2.id')
->leftJoin('ingredients i3', 'recipes.ingredient_3', '=', 'i3.id')
->leftJoin('ingredients i4', 'recipes.ingredient_4', '=', 'i4.id')
->select('recipes.name', 'i1.name AS ing_1', 'i2.name AS ing_2');
}
You can then just get the ingredients in your model with
Recipe::getWithIngredients();
I found a solution that seems to work in all my use cases.
In the Recipe model i defined the 4 ingredients as One-to-Many relations and made two helper scope functions.
class Recipe extends Eloquent {
public function ingredient1()
{ return $this->belongsTo('Ingredient', 'ingredient1'); }
public function ingredient2()
{ return $this->belongsTo('Ingredient', 'ingredient2'); }
public function ingredient3()
{ return $this->belongsTo('Ingredient', 'ingredient3'); }
public function ingredient4()
{ return $this->belongsTo('Ingredient', 'ingredient4'); }
public function scopeHasIngredient( $query, Ingredient $ingredient ) {
return $query-> where( 'ingredient1', '=', $ingredient->id )
->orWhere( 'ingredient2', '=', $ingredient->id )
->orWhere( 'ingredient3', '=', $ingredient->id )
->orWhere( 'ingredient4', '=', $ingredient->id );
}
public function scopeWithIngredients( $query ) {
return $query->with('ingredient1', 'ingredient2',
'ingredient3', 'ingredient4');
}
}
class Ingredient extends Eloquent {
public function ingredientForRecipes() {
return Recipe::hasIngredient( $this )->withIngredients();
}
}
To get all recipes for an Ingredient i can now call $ingredient->ingredientForRecipes()->get() and use the ingredients without extra queries.