filtering making subqueries - laravel

I'm on a Laravel project and I'm trying to do a filter:
I have the following tables: pubs, tapps, pub_tapps(pub_id, tapp_id)
I would like to filter an item (pubs, in this case) by some characteristics that are stored in a separate table (tapps, in this case), and both are related by pub_tapps.
An array of chosen tapps id's is given to me, then I want to query on pub_tapps to know the pub_id's that satisfy the conditions.
For example, I'm trying to filter by WIFI (tapp_id=5) and CreditCard (tapp_id=16). If we're strict the result must be pub_id=57 (tapp_id=5,8), if not pub_id=57 and pub_id=16 (tapp_id=5,7,8).
Any ideas to do something like this?Note that I'm triying to filter with different values of a sema field :S
Thanks a lot for your attention.

as long you An array of chosen tapps $ids
choose tapps that satisfy your conditions
$tapIds = Tapps::whereIn('id',$ids)->where('condition',true)->pluck('id')->toArray();
then get The Ids of the pubs in the connected Table
$pubIds = PubTapps::whereIn('tapp_id',$tapIds)->pluck('pub_id')->toArray();
then get the pubs
$Pubs = Pubs::whereIn('id',$pubIds)->get();

Many to Many
class Pubbs extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tapps()
{
//All but the first argument here are optional so long as you follow Laravel naming conventions
return $this->belongsToMany(Tapps::class, 'pub_tapps', 'pub_id', 'tapp_id', 'id', 'id');
}
}
whereHas
$tappIds = [5, 16];
$strictMode = true;
$pubs = app(App\Models\Pubbs::class)->whereHas('tapps', function (Illuminate\Database\Eloquent\Builder $query) use ($tappIds, $strictMode) {
$tappKey = $query->getModel()->getKeyName();
if (!$strictMode) {
return $query->whereIn($tappKey, $tappIds);
}
foreach ($tappIds as $tappId) {
$query->where($tappKey, $tappId)
}
})->get();

Related

many to many and one to many relationship in Eloquent

I have three tables: "courriers" which is connected with "reponses" by one to-many relationship (1 courrier could have many reponses), and "structures" which is connected with "courriers" by many-to-many relationship
I want to find the courriers which are connected to a certain structure and doesn't have a reponse in table "reponses".
For example, for the structures "DMO" that has 1 as identifiant in "structures", I wish find the courriers that belongs to this structure and doesn't appear in "reponses".
Am using Laravel 8, I want to do this with Eloquent ORM.
Am trying this
public function dmoDG()
{
$structure = Structure::find(1);
$cou = $structure->courriers;
$courr = $cou->where('repondre','=',1)-
>where('dmo_sec','<>',NULL);
$courriers = $courr->doesntHave('reponses')->get();
return view("DG\dmoDG", compact('courriers'));
}
Method Illuminate\Database\Eloquent\Collection::doesntHave does not exist.
When you use $model->relation, it fetches all related records and returns them as a Collection.
If you want to use query builder on a relation, you need to use it as a method: $model->relation()
So, if you access your relation as a property, you got Collection.
But if you access your relation as a method, you got query builder and add your where clauses on it.
In your example;
public function dmoDG()
{
$structure = Structure::find(1);
// $cou = $structure->courriers; // for using without parentheses you got a collection, not a query builder
$cou = $structure->courriers(); // now you will have a query builder and Where clauses will work on this
$courr = $cou->where('repondre', '=', 1)->where('dmo_sec', '<>', NULL);
$courriers = $courr->doesntHave('reponses')->get();
return view("DG\dmoDG", compact('courriers'));
}
Actually you can pipe them to one liner:
public function dmoDG()
{
$structure = Structure::find(1);
$courriers = $structure->courriers()->where('repondre', '=', 1)->where('dmo_sec', '<>', NULL)->doesntHave('reponses')->get();
return view("DG\dmoDG", compact('courriers'));
}
Make sure your relations and column names correctly specified.

Query Builder filter for multi level deep relationship in Laravel

I have a selection of plots which each belong to a development by a hasManyThrough relationship through housetypes. I want to filter these by development on their overview page. Plots has a housetype_id column and housetypes has a development_id column.
public function plots()
{
return $this->hasManyThrough(Plot::class, Housetype::class);
}
When I use my filter it returns the developments ID number as $development, I then need this to only show plots which are linked to that development.
I have looked into using whereHas or Join methods but have been unable to figure this out. Current filter scope is below. Thanks
public function scopeFilterDevelopment($query)
{
$development = request()->input('filter_development');
if ($development == "") {
return;
}
if(!empty($development)){
$query->where('development_id', $development);
}
}
If I can understand it right you wish to assert a condition on other Model, HasMany will load all the objects to the related model once the query is completed. Eloquent then binds the related model objects to each.
Try joins from Laravel instead. I feel this is what you exactly want: https://laravel.com/docs/5.8/queries#joins
I would use whereHas to filter the relationship:
YourModel::whereHas('plots', function($query) {
$query->filterDevelopment();
})->get();
I would also edit the query scope not to rely on the request global function, but instead pass the development of value as a parameter.
you have make a leftjon and then use when, you dont have to use
if(!empty($development)){
$query->where('development_id', $development);
}
this any more, you can use
->when($development=="" ? false : true, function($query) use ($development){
return $query->where('development_id', $development);
})
this is a full example
$queryBuilder = DB::table('facturas')->
leftJoin('clientes','clientes.id','=','facturas.clientes_id')->
select('facturas.estados_id as estado','facturas.numero as
numero',DB::raw('concat(clientes.nombre," ",clientes.apellido) as cliente'))->
when($estados===null ? false: true,function($query) use ($estados){
return $query->whereIn('facturas.estados_id', $estados);
})
It was a whereHas that solved this in the end! (another developer at work walked me through this)
Relationship -
public function housetype()
{
return $this->belongsTo(Housetype::class);
}
Function -
public function scopeFilterDevelopment($query)
{
if (request()->input('filter_development') == "") {
return;
}else{
$query->whereHas('housetype', function($housetype){
$housetype->where('development_id', request()->input('filter_development'));
});
}
}
This then returns any plot where its housetype has a matching development_id for the filter_development from the request.
Thanks for everyone's input

Laravel get result from query in side query by Eloquent in one object

I have two tables:
main_presentations
so here i have "id" and "isEnabled";
child_presentations
And here i have "id" , "isEnabled" and "idParent";
I want to select in one object this is my code:
public function MainSlider(MainPresentation $MainPresentations, ChildPresentation $ChildPresentations)
{
$MainPresentations = MainPresentation::where('isEnabled', true)->get();
foreach ($MainPresentations as $MainPresentation) {
$AnArray[] = ChildPresentation::where([
['idParent', $MainPresentation['id']],
['isEnabled', true]
])->get();
}
return $AnArray;
}
but this is the result:
enter image description here
What you are doing is executing a query per result, which can be ineffective when it starts getting bigger.
You can:
Use querybuilder
As it follows, you just build a query starting on ChildPresentation, set a relation to MainPresentation table by id and get the collection
public function MainSlider()
{
$childPresentations = ChildPresentation::join('main_presentations','main_presentations.id','child_presentations.idParent')
->where('child_presentations.isEnabled', true)->where('main_presentations.isEnabled', true)->get();
return $childPresentations;
}
If you want all the MainPresentations with their respective ChildPresentations, only the enables ones.
You can take advantage of Laravel relationships and eager loading.
https://laravel.com/docs/5.6/eloquent-relationships
First, set the relationships in your MainPresentation model
In MainPresentation.php
public function childPresentation {
return $this->hasMany('App\ChildPresentation', 'idParent', 'id');
}
Your MainSlider function would be:
(Btw, no idea why you're receiving two arguments if you're overriding them but doesn't matter)
public function MainSlider() {
$mainPresentations = MainPresentation::with(['childPresentations' => function ($advancedWith) {
child_presentation.isEnabled is true
$advancedWith->where('isEnabled', true);
}])
->where('isEnabled', true)->get()->toArray();
return $mainPresentations;
}
This will return an array of MainPresentations that contain an array of child_presentations, with all their childs.
This translates to two queries:
Select * from main_presentations where isEnabled = true;
Select * from child_presentations where isEnabled= true and id in (in the first query);
Laravel then does background work to create the structure you desire when you write ->toArray()
Note: If you have a $visible array in your MainPresentation model, be sure to add: 'childPresentation' to it, otherwise the toArray will not agregage the childs to the parent.
Second note: I advise following some standards whenever you're writing code, usually functions are named camelCase and variables are camelCase.

Laravel Relationship with WHERE clause returns nothing

I have areas. Each area have N curses
So, each curse belongsTo only one area.
My class
class Area extends Model
{
public function curses()
{
return $this->hasMany('App\Curse');
}
}
My Controller
public function getCursesByAreaId()
{
$areaId = Input::get('areaId'); //ID of selected Area
//area_id is a field of table CURSE. A FK to Area.
$areas = Area::with('curses')->where('area_id', $areaId)->get();
foreach($areas as $area)
{
var_dump($areas);
}
}
Curse Model
class Curseextends Model
{
protected $fillable =
[
'description',
'area_id'
];
}
Using the laravel's debugbar, I see that the query being executed is this:
select * from "areas" where "area_id" = '2'
Isn't it supose to run the relational query ? (with the joins)
Already checked if I'm receiving the right id and it's ok.
the problem is that it's bringing no data at all.
You need something like the following:
$areas = Area::whereHas('curses', function($query) use ($areaId) {
$query->where('area_id', $areaId);
})
->with('curses')
->get();
The whereHas is required only if you want to get the areas that has matching curses.
But, I think you can do it like this way if you only need the related curses because one curse only belong to a single area so, if have the area then you'll get all the curses attached to it:
$area = Area::with('curses')->find($areaId); // Single area
// Access the curses
$curses = $area->curses; // collection of curses
I think you are using relational area_id which is present in curse table, try retrieving the ID of area by id not by area_id, something like this:
$areas = Area::where('id', $areaId)->with('curses')->get();
Hope you get the solution.
Try this :
$areas = Area::whereHas('curses', function($area_query) use ($area_id) {
$area_query->where('area_id', $area_id)
})
->with('curses')
->get();

Retrieving records from database using eloquent with optional query parameters

i have the following block of code in my Resource Controller:
$travel_company_id = Input::get('travel_company_id');
$transport_type = Input::get('transport_type');
$route_type = Input::get('route_type');
$travelRoutes = TravelRoute::where('travel_company_id', $travel_company_id)
->where('transport_type', $transport_type)
->where('route_type', $route_type)
->get();
Now what this does is it gets travelRoutes based on the parameters supplied. What i want is for it to do is perform a search based on the available parameters, that way if $route_type is empty the search will be performed only on travel_company_id and transport type.
Also if all the parameters are empty then it will simply do a get and return all available records.
I know i can do this with lots of if statements but then if i add a new parameter on the frontend i will have to add it to the backend as well, I was wondering if there was a much simpler and shorter way to do this in laravel.
The where method accepts an array of constraints:
$constraints = array_only(Input::all(), [
'travel_company_id',
'transport_type',
'route_type',
]);
$routes = TravelRoute::where($constraints)->get();
Warning: do not use Input::only() instead of array_only(). They're not the same.
Input::only() fills in any missing items with null, which is not what you want here.
This is pretty hacky and if you spend some time developing a solution I'm sure it could be much nicer. This assumes all the fields in the getSearchFields() function match the input names from the form and database.
/**
* Search fields to retrieve and search the database with. Assumed they match the
* column names in the database
*/
private function getSearchFields()
{
return ['travel_company_id', 'transport_type', 'route_type'];
}
public function search()
{
// Get a new query instance from the model
$query = TravelRoute::query();
// Loop through the fields checking if they've been input, if they have add
// them to the query.
foreach($this->getSearchFields() as $field)
{
if (Input::has($field))
{
$query->where($field, Input::get($field));
}
}
// Finally execute the query
$travelRoutes = $query->get();
}

Resources