Laravel query scope combination - laravel

I'm using Laravel 4.2 Query Scopes but encountered a problem.
My Model:
class SomeModel extends Eloquent {
public function scopeS1($query) {
return $query->where('field1', '=', 'S1');
}
public function scopeS2($query) {
return $query->where('field2', '=', 'S2');
}
}
Now when I do SomeModel::s1()->s2()->get(); it returns all results and doesn't filter by S1 AND S2. Note also that I have no problem when I do
SomeModel::where('field1', '=', 'S1')->where('field2', '=', 'S2')->get()
So why is query scoping and doing anything here??

Since your real scopes contain OR conditions you should use a nested where to make sure they get interpreted correctly. Laravel will wrap parentheses around.
public function scopeS1($query) {
return $query->where(function($q){
$q->where('field1', '=', 'S1')
->orWhere('foo', '=', 'bar');
});
}
// and the same for scopeS2...

Related

Eloquent `with()` with filtering based on relation

I have this tables.
And this model relations, this relations works fine.
class Item extends Model
{
public function translations()
{
return $this->hasMany(ItemTranslations::class);
}
}
class ItemTranslation extends Model
{
public function language()
{
return $this->belongsTo(Language::class);
}
}
I need to return a list of items with the translations, but only the translations related to a specific language.
I can't have this query working, im getting all translations of each item, not only the one filtered with this query. The language related to the translation is not needed on the result.
$query = Item::query();
$query->with('translations')->when('language',function($query) use ($ISOlanguage) {
return $query->where('languages.ISO_code', '=', $ISOlanguage);
});
return $query->paginate();
Any idea who i can have this working? Thanks!
So what you want to do is constraining eager loading
Item::with(["translations" => function ($query) use ($ISOlanguage) {
$query->where('language.ISO_code', $ISOlanguage);
}])->get();
https://laravel.com/docs/5.8/eloquent-relationships#constraining-eager-loads
I finally have it working
Item::with(['translations' => function($query) use ($ISOlanguage) {
$query->whereHas('language', function($query) use ($ISOlanguage) {
$query->where('ISO_code', '=', $ISOlanguage);
});
}])->get();
Thanks #julian-s for your help!

Joining 2 scopes laravel

I've been trying to solve this for quite a while now. I want to join these two scopes from my Match Model:
public function scopeMainMatches($query)
{
return $query->where('type', 'main');
}
public function scopeDotaMatches($query)
{
return $query->join('leagues', function ($join) {
$join->on('matches.league_id', '=', 'leagues.id')
->select('matches.*')
->where('leagues.type', '=', 'dota2')
->where('matches.type', '=', 'main');
});
}
so basically, when I put in into join eloquent relationship it will be the same like this:
$query = DB::table('matches')
->join('leagues', 'leagues.id', '=', 'matches.league_id')
->select('matches.*')
->where('leagues.type', '=', 'dota2')
->get();
it works fine during the terminal check. but I need to connect 2 scopes for the Controller which looks like this:
$_matches = \App\Match::mainMatches()
->get()
->load('teamA', 'teamB')
->sortByDesc('schedule');
so when I try to connect mainMatches and dotaMatches, it doesn't show up on the matches. although when i run php artisan tinker, it returns the correct output, but it won't show up on the matches table.
$_matches = \App\Match::mainMatches()
->dotaMatches()
->get()
->load('teamA', 'teamB')
->sortByDesc('schedule');
any Ideas how to work on this? TYIA!
I've managed to join two tables in just one scope here is the code:
public function scopeMainMatches($query) {
return $query->join('leagues','leagues.id','=','matches.league_id')->select('matches.*')->where('matches.type', 'main');
}

How to add subqueries in eloquent db of laravel 5?

I am building an api in laravel 5.3 using eloquent if I use /api/location/event_name/event_status then I am getting results.
But when I use /api/location/event_name or /api/location/ nothing comes.
How to write the query so that all my link show result?
class events_api extends Controller
{
public function events($location = "",$event_name="",$event_status="")
{
$events = DB::table('event_table')
->join('event_details', 'event_table.event_id', '=', 'event_details.event_id')
->join('venue', 'event_details.venue_id', '=', 'venue.venue_id')
->where('venue.venue_city','=',$location)
->where('event_table.event_name','=','$event_name')
->where('event_table.event_status','=','$event_status')
->where('event_table.event_post_status','=','publish')
->select('event_table.event_title','event_details.event_start_ts','event_details.event_views','venue.venue_name','venue.venue_city','venue.venue_location')
->get();
echo $events;
}
}``
If you would like to make a subQuery try using toSql method:
class events_api extends Controller
{
public function events($location = "",$event_name="",$event_status="")
{
$subQuery = DB::table('event_table')
->join('event_details', 'event_table.event_id', '=', 'event_details.event_id')
->join('venue', 'event_details.venue_id', '=', 'venue.venue_id')
->where('venue.venue_city','=',$location)
->where('event_table.event_name','=','$event_name')
->where('event_table.event_status','=','$event_status')
->where('event_table.event_post_status','=','publish')
->select('event_table.event_title','event_details.event_start_ts','event_details.event_views','venue.venue_name','venue.venue_city','venue.venue_location');
DB::table(DB::raw("{$subQuery->toSql()} as main_query"))
->mergeBindings($subQuery->getBindings())
// build your query here
->get()
}
}
You'll also need to mergeBindings if you use any bindings in subquery

Splitting Eloquent methods between model and controller?

I have this code in a Controller:
public function getStock(Request $request){
$laptops = new Laptop;
$laptops->addJoins();
$laptops->filterResults($request);
return $laptops->get();
}
In the laptops Model I have:
public function addJoins() {
$this->Join('locations', 'locations.id', '=', 'laptops.location_id');
$this->Join('models', 'models.id', '=', 'laptops.model_id');
$this->Join('types', 'types.id', '=', 'models.type');
$this->leftJoin('statuses', 'status.id', '=', 'laptops.status');
$this->leftJoin('earmarks', 'earmarks.laptop', '=', 'laptop.id');
}
public function filterResults($request) {
if ($request->stock) {
$this->where('locations.stock', 0);
}
if ($request->loc) {
$this->where('laptops.location_id', $request->loc);
}
}
The problem is I receive all laptop records, and the filtering from URL parameters doesn't appear to be working at all. Am I barking up the wrong tree with this structure (I'm very new to Laravel)?
The filtering isn't working because running your queries on $this will result in unwanted behavior, this is what local scopes are for. See the documentation for them here:
https://laravel.com/docs/master/eloquent#local-scopes
In your case it would be:
Your Laptop model:
public function scopeAddJoins($query)
{
return $query->join('locations', 'locations.id', '=', 'laptops.location_id')
->join('models', 'models.id', '=', 'laptops.model_id')
->join('types', 'types.id', '=', 'models.type')
->leftJoin('statuses', 'status.id', '=', 'laptops.status')
->leftJoin('earmarks', 'earmarks.laptop', '=', 'laptop.id');
}
public function scopeFilterResults($query, $request)
{
if ($request->has('stock')) {
$query->where('locations.stock', 0);
}
if ($request->has('loc')) {
$query->where('laptops.location_id', $request->input('loc'));
}
return $query;
}
Then you can do this in your Controller:
public function getStock(Request $request)
{
return Laptop::addJoins()->filterResults($request)->get();
}
Instead of reinventing the wheel I'd recommend you to change structure. Skinny controller, fat model. The good practice is to keep controllers as thin as possible, so my controller would look like this:
public function getStock(Request $request)
{
return view('laptop.index', [
'laptops' => Laptop::getAllLaptops($request->all())
]);
}
Also, use Eloquent and scopes for filtering etc. All data related logic would be in model:
public function getAllLaptops($data)
{
return $this->with('locations', 'models', 'types', 'statuses', 'earmarks')->search($data)->get();
}
My first thought is that $request->stock and $request->loc does not have any value in your URL parameters. If you are calling the route this way:
http://www.yoursite.com/yourpath?stock&loc
Instead, try to use it this way:
http://www.yoursite.com/yourpath?stock=1&loc=1
The point is to give them some value.

Eloquent conditional where filter

If I have a variable that is optional, and I only want to have it search on it if it is defined, I know that I can do this, but is there a way to define the function elsewhere, so I don't need to rewrite the same anonymous function over and over again? Or are the any other good alternatives to going about solving this problem?
.......->where(function($query) use ($gender){
if ($gender) {
$query->where('gender', '=', $gender);
}
})->get();
Since Laravel 5.2.27, you can use conditional clauses
For example:
->when($gender, function($query) use ($gender) {
return $query->where('gender', '=', $gender);
})
->get();
You could use a dynamic scope
class User extends Eloquent {
public function scopeGender($query, $gender)
{
if ($gender) {
return $query->whereGender($gender);
}
return $query;
}
}
Then throughout your application
...->gender($gender)->get();

Resources