Related
I want to order a query in laravel ways depending on certain factors. One of which would be a related table.
So right now my query goes like this:
$products = Product::where("price", "<=", $maxBudget)
and I know that I can add a function to my where clause like so
->where(function ($q) use($mature) {
if ($mature == "1") {
$q->where('mature', 1)->orWhere('mature', 0);
} else {
$q->where('mature', 0);
}
})
however I want to make a function for my order as well. Something like this (I know this is wrong, this is just an example):
->orderBy(function ($q) use($orderBy) {
if ($orderBy == "price_low") {
$q->orderBy('price', 'desc');
} elseif ($orderBy == "price_high") {
$q->orderBy('price', 'asc');
} elseif ($orderBy == "rating") {
$q->orderBy( $product->user->getAvgStarRating(), 'desc')
} else {
$q->orderBy('created_at', 'desc');
}
})
The $q->orderBy( $product->user->getAvgStarRating(), 'desc') is obviously wrong, $product isn't even defined, but you get the idea. In this scenario, I'd like to order my query based off the average rating of the creator of the product.
So my questions are: How do I make it so that I can add a function of some kind to my ordering, and how can I order a query based off related tables?
Define a user relationship (if haven't already):
public function user() {
return $this->belongsTo(User::class);
}
Then you can use a modified withCount():
Product::withCount(['user' => function($query) {
$query->select('avgStarRating');
}])->orderBy('user_count', 'desc');
You can also use a simple JOIN:
Product::select('products.*')
->join('users', 'users.id', 'products.user_id')
->orderBy('users.avgStarRating', 'desc');
can you try this
$products = Product::where("price", "<=", $maxBudget)
->where(function ($q) use($mature) {
if ($mature == "1") {
$q->where('mature', 1)->orWhere('mature', 0);
} else {
$q->where('mature', 0);
}
})
->orderBy(function ($q) use($orderBy) {
if ($orderBy == "price_low") {
$q->orderBy('price', 'desc');
} elseif ($orderBy == "price_high") {
$q->orderBy('price', 'asc');
} elseif ($orderBy == "rating") {
$q->join('users', 'users.id', '=','products.user_id')->orderBy('users.avgStarRating', 'desc')
} else {
$q->orderBy('created_at', 'desc');
}
})->get();
I'm trying to implement a function in my controller to filter users of the application by their roles
My controller ProfileController.php has the following function:
public function membrevis() {
$filter = isset($_GET['filter']) ? $_GET['filter'] : null;
$users = DB::table('users')
->join('user_role', 'user.id', '=', 'user_role.user_id')
->join('roles', 'users_roles.role_id', '=', 'roles.id')
->where('users.valid','=',0)
->select('users.*','roles.description');
if ($filter != null) {
$users->where('users.name','like','%'.$filter.'%')
->orWhere('roles.description','like','%'.$filter.'%')
}
$users->get();
return view('member2',['users'=> $users]);
}
My view membre2.blade.php has the following HTML code:
<form action="/membre2" method="get">
<input type="text" name="filter" >
<button type="submit">filter</button>
</form>
The error i'm getting is:
Parse error: syntax error, unexpected '}'
Any help would be appreciated!
You are missing a ; in the if statement
public function membrevis() {
$filter = isset($_GET['filter']) ? $_GET['filter'] : null;
$users = DB::table('users')
->join('user_role', 'user.id', '=', 'user_role.user_id')
->join('roles', 'users_roles.role_id', '=', 'roles.id')
->where('users.valid','=',0)
->select('users.*','roles.description');
if($filter != null) {
$users->where('users.name','like','%'.$filter.'%')
->orWhere('roles.description','like','%'.$filter.'%');
}
$users->get();
return view('member2',['users'=> $users]);
}
Missing ; after ->orWhere('roles.description','like','%'.$filter.'%')
Code with Updated Indent
public function membrevis()
{
$filter = isset($_GET['filter']) ? $_GET['filter'] : null;
$users = DB::table('users')
->join('user_role', 'user.id', '=', 'user_role.user_id')
->join('roles', 'users_roles.role_id', '=', 'roles.id')
->where('users.valid','=',0)
->select('users.*','roles.description');
if($filter != null)
{
$users->where('users.name','like','%'.$filter.'%')
->orWhere('roles.description','like','%'.$filter.'%');
}
$users->get();
return view('member2',['users'=> $users]);
}
Indent make your code more readable. There are different styles of indention. You can understand them better via this
You should take a look on Conditional Clauses
public function membrevis() {
$filter = isset($_GET['filter']) ? $_GET['filter'] : null;
$users = DB::table('users')
->join('user_role', 'user.id', '=', 'user_role.user_id')
->join('roles', 'users_roles.role_id', '=', 'roles.id')
->where('users.valid','=',0)
->when($filter, function ($query) use ($filter) {
return $query->where('users.name','like','%' . $filter . '%')
->orWhere('roles.description','like','%' . $filter . '%');
})
->select('users.*','roles.description')
->get();
return view('member2',['users'=> $users]);
}
The instructions in the closure given to when() will only be applied if the first argument is evaluated to true, that way you'll be able to write complex conditional instructions without having to break the query chain.
You should provide more information about your erorr if it will not help.
CODES IN CONTROLLER
public function getAthleteProperties(Request $request)
{
$getAthlete = DB::table('students')
->join('sports', 'students.id', '=', 'sports.athlete');
->select('students.*', 'sports.*')
->get();
if($request->input('gender') == 1)
{
//add this line of queries to $getAthlete queries
->where('gender', "m");
}
else
->where('gender', "f");
if($request->input('active') == 1)
{
//add this line of queries to $getAthlete queries
->where('active', "y");
}
else
->where('active', "n");
return view('admin', compact('getAthlete'));
}
Is it possible to append queries in laravel? For example I have codes as shown on above codes, if the condition for gender is 1 and active is 1 then in the end of the $getAthlete queries will become like this. Is it possible? How?
$getAthlete = DB::table('students')
->join('sports', 'students.id', '=', 'sports.athlete');
->select('students.*', 'sports.*')
->where('gender', "m") //added because condition is true
->where('active', "y") //added because condition is true
->get();
You don't have to use the "get" method at first, since it executes the select statement.
public function getAthleteProperties(Request $request)
{
$getAthlete = DB::table('students')
->join('sports', 'students.id', '=', 'sports.athlete')
->select('students.*', 'sports.*');
if($request->input('gender') == 1) {
//add this line of queries to $getAthlete queries
$getAthlete->where('gender', 'm');
} else {
$getAthlete->where('gender', 'f');
}
if($request->input('active') == 1) {
//add this line of queries to $getAthlete queries
$getAthlete->where('active', 'y');
} else {
$getAthlete->where('active', 'n');
}
$getAthlete = $getAthlete->get();
return view('admin', compact('getAthlete'));
}
UPDATE:
With the latest versions of laravel a conditional clause method was introduced. So we can do it this way:
$getAthlete = DB::table('students')
->join('sports', 'students.id', '=', 'sports.athlete')
->select('students.*', 'sports.*')
->when($request->input('gender') == 1, function ($query) {
return $query->where('gender', 'm');
}, function ($query) {
return $query->where('gender', 'f');
})
->when($request->input('active') == 1, function ($query) {
return $query->where('active', 'y');
}, function ($query) {
return $query->where('active', 'n');
})
->get();
This eloquent query filter:
return $this->games()
->where(function ($query) {
$query->where('active_player_id', '=', $this->id)
->where('stage_name', '<>', 'setup');
})
->orWhere(function ($query) {
$query->where('active_player_id', '<>', $this->id)
->where('stage_name', '=', 'setup');
});
Builds into SQL like this:
where `games_players`.`player_id` = '1'
and (`active_player_id` = '1' and `stage_name` <> 'setup')
or (`active_player_id` <> '1' and `stage_name` = 'setup')
How do I change the eloquent code to build this query (brackets around the OR):
where `games_players`.`player_id` = '1'
and (
(`active_player_id` = '1' and `stage_name` <> 'setup')
or (`active_player_id` <> '1' and `stage_name` = 'setup')
)
You can achieve that by doing this:
->where('active_player_id',1)
->where(function($q){
$q->where([ ['active_player_id', 1],['stage_name','!=', 'setup'] ])
->orWhere([ ['active_player_id','!=', 1],['stage_name', 'setup'] ]
})->get()
I don't have an instance of Eloquent to hand to test against just now but would it now be something like this?
return $this->games()
->where(function ($query) {
$query->where(function ($query) {
$query->where('active_player_id', '=', $this->id)
->where('stage_name', '<>', 'setup');
})
->orWhere(function ($query) {
$query->where('active_player_id', '<>', $this->id)
->where('stage_name', '=', 'setup');
})
})
;
That is, you put the conditions to be grouped together into an anonymous function on where?
Try moving your orWhere condition into the first anonymous function like this,
return $this->games()
->where(function ($query) {
$query->where('active_player_id', '=', $this->id)
->where('stage_name', '<>', 'setup')
->orWhere(function ($query) {
$query->where('active_player_id', '<>', $this->id)
->where('stage_name', '=', 'setup');
});
})
I've got a Laravel query working the way I'd like with min() and groupBy():
$certificates = CertificateRecord::whereHas('learner', function($query) use($user) { $query->where('user_id', '=', $user->id); })
->selectRaw('*,min(type) as type')
->groupBy('certificate_template_id')
->with('issuer','certificateType')
->paginate(10);
But when I add this to the mix...
->when($request->type, function ($query) use ($request) {
return $query->where('type', '=', $request->type);
})
The previously grouped results are somehow ignored while performing this where() and it returns all ungrouped rows.
Edit: The original query with the when condition placement here seems fine to me. This should filter the type field without affecting the groupBy. One thing i want to tell you is that when the $request->type is set, there would be only one type value in the result set, therefore the minimum value would be the same without any change.
$certificates = CertificateRecord::whereHas('learner', function($query) use($user) {
$query->where('user_id', '=', $user->id);
})
->with('issuer', 'certificateType')
->when($request->type, function ($query) use ($request) {
return $query->where('type', $request->type);
})
->selectRaw('*, min(type) as type')
->groupBy('certificate_template_id')
->paginate(10);
$certificates = CertificateRecord::whereHas('learner', function($query) use($user) {
$query->where('user_id', '=', $user->id);
})
->with('issuer', 'certificateType')
->selectRaw('*, min(type) as type')
->groupBy('certificate_template_id')
->when($request->type, function ($query) use ($request) {
return $query->having('type', '=', $request->type);
})
->paginate(10);
If that doesn't work, switch having to havingRaw.
return $query->havingRaw('min(type) = ' . $request->type);
Just don't complete the query immediately:
$query = $certificates = CertificateRecord::whereHas('learner', function($query) use($user) { $query->where('user_id', '=', $user->id); })
->selectRaw('*,min(type) as type')
->groupBy('certificate_template_id')
->orderBy('type')
->with('issuer','certificateType')
if ($request->status){ $query->where('status', '=', $request->status); }
$query->paginate(10);