Hi I have table that contain two foreign key as below.
symptom_diseases table
disease_id symptom_id
1 1
1 2
2 1
2 3
I would like to query with symptom_id array and return disease_id array. For example
symptom_id array = [1] then return disease_id [1,2]
symptom_id array = [1,2] then return disease_id [1]
symptom_id array = [1,3] then return disease_id [2]
symptom_id array = [2,3] then return null
I try to query using
whereIn
$diseaseArr = SymptomDisease::whereIn('symptom_id',$request->symptom)->pluck('disease_id')->toArray();
but I got wrong result. Any advice or guidance on this would be greatly appreciated, Thanks.
Firstly the whereIn query will get all models which match any of the entries in the array. You seem to want models which match all of them.
A naïve approach to do this would be something like:
$diseaseArr = SymptomDisease::whereIn('symptom_id',$request->symptom)
->select('disease_id')
->groupBy('disease_id')
->having(\DB::raw('COUNT(*)'),'=',count($request->symptom))
->pluck('disease_id')->toArray();
This will get all disease ids that occur the exact number of times as the number of symptoms you are looking for.
However you have made a pivot table into a model. That is usually a bad approach. I suggest you go about this via your Disease model (assuming one exists and has the relationships correctly set-up).
In which case you could do:
$disease = Disease::whereHas('symptoms',function ($query) use ($request) {
$query->whereIn('id', $request->symptom);
}, count($request->symptom))->get();
This should get all diseases which have all the symptoms specified in the $request->symptom array.
Related
i have 2 tables User and subadmin consider user have 3 columns and subadmin has 2 column i want to get 3+2 (5 column) data into a single veriable array
the technique i want to use is that in user table i have id which is same in subadmin table with sub_admin_id(column) how i can use eloquent model to first link id with sub_admin_id and then into a single query get 5 column in single veriable array
here i am using to get data
$subadmindata = User::find($id)->get(); // [column1 ,column2 ,column3 ]
$subadmindata1 = SubAdmin::find($id)->get(); // [column1 ,column2 ]
output should be
$data = // [column1 ,column2 ,column3 , column4 ,column5 ]
note i dont want to use array merge or combine method i want to use eloquent model for my learning
you could use concat like this
$subadmindata = User::find($id)->get();
$subadmindata1 = SubAdmin::find($id)->get(); // it will return array of collections
$data = $subadmindata->concat($subadmindata1);
Notice when you use get after find it stop it's jobs so there is no need to find here
get() method will give you a collection not array, so you can merge two collection as follows.
$subadmindata = User::find($id)->get();
$subadmindata1 = SubAdmin::find($id)->get();
$data = $subadmindata->merge($subadmindata1);
You can't use find with get(Assuming that you need a one result not all of the users). Try this. But looks like you need build the relationships correctly first. Anyway, quick answer is below.
$userCols = User::select('column1','col2','col3')->find($id);
$subAdminCols = SubAdmin::select('col4','col5')->find($id);
$cols = array_merge($userCols->toArray(), $subAdminCols->toArray());
try this
in user model
public function subadmin(){
return $this->hasOne(SubAdmin::class,'sub_admin_id');
}
in controller
$sub_admins = User::find($id);
dd($sub_admins->subadmin->sub_admin_id)
you can use php ... operator to to push data
example like this
$subadmindata = User::find($id)->get()->toArray(); // [column1 ,column2 ,column3 ]
$subadmindata1 = SubAdmin::find($id)->get()->toArray(); // [column1 ,column2 ]
array_push($subadmindata, ...$subadmindata1);
return $subadmindata
ref link https://wiki.php.net/rfc/spread_operator_for_array
I use Laravel 6.12, I have this request :
$queryJob = DB::table('jobs as j')->join('job_translations as jt', 'j.id', 'jt.job_id')
->whereNull('j.deleted_at')
->whereNull('jt.deleted_at')
->select('j.id', 'j.short_name', 'j.status', DB::raw("case when j.short_name = '{$request->short_name}' then 0 else 1 end"))
->distinct();
$jobs = $queryJob->paginate($qtyItemsPerPage);
The results displays an error for the total :
The total = 3, but as you can see the data contains only 2 elements.
I read here that when using a distinct, I must be clear on which column the total must be calculated: distinct() with pagination() in laravel 5.2 not working
So I modified my query like that:
$jobs = $queryJob->paginate($qtyItemsPerPage, ['j.*']);
But without success, the total is still wrong.
Hoping that I don't misunderstand your DB and relations structure and purpose of your query perhaps this will avoid using distinct or groupBy altogether?
$shortname = $request->input('short_name');
$queryJob = Job::with('job_translations')->select('id','short_name',
'status', DB::raw("case when short_name = '" . $shortname . "'
then 0 else 1 end")
->paginate($qtyItemsPerPage);
Pagination can be easily manually added with skip and take in case you need to use groupBy
$queryJob->skip(($page - 1) * $qtyItemsPerPage)->take($qtyItemsPerPage)->get();
The solution for me was to pass a field name to the distinct() method.
With your example:
$queryJob = DB::table('jobs as j')
// joins, where and other chained methods go here
->distinct('j.id')
Solution taken from https://stackoverflow.com/a/69073801/3503615
I am trying to get sum of amounts group by time_ids. I am passing the following time_ids as an array.
Eloquent query is as follows
$time_ids = [1, 2, 3, 4];
$amount = Data::selectRaw('ifnull(sum(amount),0) as amount')
->whereIn('time_ids', $time_ids)
->groupBy('time_ids')
->pluck('amount')
->toArray();
As a result I get an array of only 3 values.
In my database, time_ids = 1 will not have any value, but I want the query to return 0 if it could not find any value.
Not enough reputation so adding it as an answer. Your query looks fine to me. Try querying the database directly and see what it is returning. Use this to generate the raw sql
$amount = Data::selectRaw('ifnull(sum(amount),0) as amount')->whereIn('time_ids',$time_ids)->groupBy('time_ids')->toSql();
$projects = Project::find(collect(request()->get('projects'))->pluck('id')); // collect(...)->pluck('id') is [2, 1]
$projects->pluck('id'); // [1, 2]
I want the result to be in the original order. How do I achieve this?
Try $projects->order_by("updated_at")->pluck("id"); or "created_at" if that's the column you need them ordered by.
Referencing MySQL order by field in Eloquent and MySQL - SELECT ... WHERE id IN (..) - correct order You can pretty much get the result and have it order using the following:
$projects_ids = request()->get('projects'); //assuming this is an array
$projects = Project::orderByRaw("FIELD(id, ".implode(',', projects_ids).")")
->find(projects_ids)
->pluck('id'));
#Jonas raised my awareness to a potential sql injection vulnerability, so I suggest an alternative:
$projects_ids = request()->get('projects');
$items = collect($projects_ids);
$fields = $items->map(function ($ids){
return '?';
})->implode(',');
$projects = Project::orderbyRaw("FIELD (id, ".$fields.")", $items->prepend('id'))
->find($projects_ids);
The explanation to the above is this:
Create a comma separated placeholder '?', for the number of items in the array to serve as named binding (including the column 'id').
I solve this by querying the data one by one instead mass query.
$ids = collect(request()->get('projects'))->pluck('id');
foreach($ids as $id){
$projects[] = Project::find($id);
}
$projects = collect($projects);
$projects->pluck('id');
I have to do this manually because laravel collection maps all the element sorted by using ids.
I am trying to select some records using LINQ for Entities (EF4 Code First).
I have a table called Monitoring with a field called AnimalType which has values such as
"Lion,Tiger,Goat"
"Snake,Lion,Horse"
"Rattlesnake"
"Mountain Lion"
I want to pass in some values in a string array (animalValues) and have the rows returned from the Monitorings table where one or more values in the field AnimalType match the one or more values from the animalValues. The following code ALMOST works as I wanted but I've discovered a major flaw with the approach I've taken.
public IQueryable<Monitoring> GetMonitoringList(string[] animalValues)
{
var result = from m in db.Monitorings
where animalValues.Any(c => m.AnimalType.Contains(c))
select m;
return result;
}
To explain the problem, if I pass in animalValues = { "Lion", "Tiger" } I find that three rows are selected due to the fact that the 4th record "Mountain Lion" contains the word "Lion" which it regards as a match.
This isn't what I wanted to happen. I need "Lion" to only match "Lion" and not "Mountain Lion".
Another example is if I pass in "Snake" I get rows which include "Rattlesnake". I'm hoping somebody has a better bit of LINQ code that will allow for matches that match the exact comma delimited value and not just a part of it as in "Snake" matching "Rattlesnake".
This is a kind of hack that will do the work:
public IQueryable<Monitoring> GetMonitoringList(string[] animalValues)
{
var values = animalValues.Select(x => "," + x + ",");
var result = from m in db.Monitorings
where values.Any(c => ("," + m.AnimalType + ",").Contains(c))
select m;
return result;
}
This way, you will have
",Lion,Tiger,Goat,"
",Snake,Lion,Horse,"
",Rattlesnake,"
",Mountain Lion,"
And check for ",Lion," and "Mountain Lion" won't match.
It's dirty, I know.
Because the data in your field is comma delimited you really need to break those entries up individually. Since SQL doesn't really support a way to split strings, the option that I've come up with is to execute two queries.
The first query uses the code you started with to at least get you in the ballpark and minimize the amount of data you're retrieving. It converts it to a List<> to actually execute the query and bring the results into memory which will allow access to more extension methods like Split().
The second query uses the subset of data in memory and joins it with your database table to then pull out the exact matches:
public IQueryable<Monitoring> GetMonitoringList(string[] animalValues)
{
// execute a query that is greedy in its matches, but at least
// it's still only a subset of data. The ToList()
// brings the data into memory, so to speak
var subsetData = (from m in db.Monitorings
where animalValues.Any(c => m.AnimalType.Contains(c))
select m).ToList();
// given that subset of data in the List<>, join it against the DB again
// and get the exact matches this time
var result = from data in subsetData
join m in db.Monitorings on data.ID equals m.ID
where data.AnimalType.Split(',').Intersect(animalValues).Any ()
select m;
return result;
}