I'm newer to Laravel and trying to use ORM to filter my results. So in my database I have table one (Developers) and table two (Programs):
Developers
-------------------
ID. | Name |
------|------------
1 | Bluegreen |
2 | Dancer |
3 | Martin |
------|------------
Programs
-------------------------------------
id | developer_id | name |
------|------------------------------
1 | 12 | Program Test |
2 | 3 | Capital |
3 | 2 | Asus |
4 | 2 | Rich |
5 | 5 | Huna |
------|------------------------------
I'm trying to filter by the Program name and my code is filtering by Developer name. I can easily do this in raw sql but I'm trying to learn ORM, for some reason it's like super difficult for me to learn.
Here's my code:
$query = $request->get('search');
$developers = Developer::where('name', 'LIKE', '%'.$query.'%')->with('programs')->get();
I think this code will work.
DB::table('developers')
->join('programs','developers.id','=','programs.developer_id')
->where('programs.name', 'like', '%'.'Capital'.'%')
->select('programs.*','developers.name')
->get();
You can simply do this in a closure function in laravel with relationships. This will filter your record on program name and also developer name.
$developers = Developer::where('name', 'LIKE', '%'.$request->search.'%')
->with(['programs' => function($filter) use ($request) {
$filter->where('name','LIKE','%'.$request->search.'%');
}])->get();
If you want to only filter by program name then you can do it in this way
$developers = Developer::with(['programs' => function($filter) use ($request) {
$filter->where('name','LIKE','%'.$request->search.'%');
}])->get();
You can also write a condition if $request->search is empty then return all data. If it is not empty then only return the filtered data. I am just simply doing it but you can optimize the query later.
if($request->search != '')
$developers = Developer::with(['programs' => function($filter) use ($request) {
$filter->where('name','LIKE','%'.$request->search.'%');
}])->get();
else
$developers = Developer::with('programs')->get();
Related
table 1
| ID | val1 |
| 1 | a |
| 2 | b |
| 3 | c |
table 2
| ID | val1 |
| 1 | a |
| 2 | b |
I need to get the table1 data, which is not on table 2
$users_without_info = DB::table('users')->rightJoin('user_infos', 'users.email', '=', 'user_infos.email')
->select('users.*')
->latest()
->get();
I tried this command, doesn't work.
use inner join
$users_without_info = DB::table('users')
->join('user_infos', 'users.email', '!=', 'user_infos.email')
->select('users.*')
->latest()
->get();
if you have models then use relationships with the foreign key then in user model add relation
public function userInfo(){
return $this->hasOne(UserInfo::class);
}
and the query will be
$users_without_info=\App\Models\User::doesntHave('userInfo')->get();
if you don't have foreign key and want to use email as unique identifier then
public function userInfo(){
return $this->hasOne(UserInfo::class,'email','email');
}
Here is the solution,
The issue is in the join statement.
You can try this way to get the table-1(users) data, which is not on table-2(user_infos),
$users_without_info = DB::table('users')
->leftJoin('user_infos', 'users.email', '=', 'user_infos.email')
->whereNull('user_infos.email')
->get();
Hope you will fix it..
I have two related tables:
projects:
| id | name |
| 1 | Test |
| 2 | Example |
project_dates:
| id | project_id | date |
| 1 | 1 | 2020-02-01 |
| 2 | 1 | 2020-02-10 |
| 3 | 1 | 2020-01-25 |
| 4 | 2 | 2020-01-10 |
| 5 | 2 | 2019-12-15 |
Now for example I would like to get all projects where the first date of project_dates is equal to 2020-01-25. So I tried the following:
$projects = Project::whereHas('dates', function($query) {
$query->whereRaw("MIN(date) = '2020-01-25'");
});
But this returns the following error:
SQLSTATE[HY000]: General error: 1111 Invalid use of group function
which revers to MIN(date). So my question remains how can I get all projects with the first date being equal to 2020-01-25 or any other specific date. From the example above I would expect to get the project with id 1 since the first date of 2 is 2019-12-15
I know that is possible to use a subqueryjoin however I feel like doing 2 queries shouldn't be required.
$firstDates = ProjectDate::groupBy('project_id')
->selectRaw('project_id, MIN(date) as first_date');
Project::joinSub($firstDates, 'first_dates', function($join){
$join->on('first_dates.project_id', '=', 'project.id')
->where('first_date', '=','2020-01-25');
})->get();
You cannot use where aggregate function with groupBy, try to use havingRaw instead:
$projects = Project::whereHas('dates', function($query) {
$query->havingRaw("MIN(date) = ?", ['2020-01-25']);
});
// Or
$projects = Project::whereHas('dates', function($query) {
$query->having(DB::raw("MIN(date)"), '2020-01-25');
});
$projects = Project::whereHas('dates', function($query) {
$query->having(DB::raw("MIN(date)"), '2020-01-25')
->groupBy( 'project_dates.id' ); // For sql_mode=only_full_group_by
});
I have two related tables:
publications:
| id | name |
| 1 | Test |
| 2 | Example |
publication_dates:
| id | publication_id | date |
| 1 | 1 | 2020-02-01 |
| 2 | 1 | 2020-02-10 |
| 3 | 1 | 2020-01-25 |
| 4 | 2 | 2020-01-10 |
| 5 | 2 | 2019-12-15 |
Now for example I would like to get all publications where the first date of publication_dates is after 2020-01-01. So I tried the following:
$publications = Publication::whereHas('dates', function($query) {
$query->whereRaw("MIN(date) > '2020-01-10'");
});
But this returns the following error:
SQLSTATE[HY000]: General error: 1111 Invalid use of group function
which revers to MIN(date). So my question remains how can I get all publications with the first date being after 2020-01-01 or any other specific date. From the example above I would expect to get the publication with id 1 since the first date of 2 is 2019-12-15
You should reverse querying logic and then you can use relationship absence.
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
$date = Carbon::create('2020-01-10');
$publications = Publication::whereDoesntHave('dates', function(Builder $query) use ($date) {
$query->where('date', '<', $date->toDateString());
});
You can also go further and make local scope method for query builder in model
// Publication model
/**
* Scope a query to only include publications that has date newer than $date
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #param string|object Carbon $date
* #return \Illuminate\Database\Eloquent\Builder
*/
public function scopeDatesNewerThan($query, Carbon $date)
{
return $query->whereDoesntHave('dates', function(Builder $query) use ($date) {
$query->where('date', '<', $date->toDateString());// or set '<=' if more appropriate
});
}
Then somewhere in code i.e. newer than last 30 days
// $publications = Publication::datesNewerThan(Carbon::now()->subDays(30))->get();
I assume the error is because you are using an aggregate function without a proper group by clause, You could use a join to tackle this. but here is a slightly different approach using the subquery join.
$firstDates = PublicationDate::groupBy('publication_id')
->selectRaw('publication_id, MIN(date) as first_date'); // assuming you have a model called PublicationDate for the publication_dates table
Publication::joinSub($firstDates, 'first_dates', function($join){
$join->on('first_dates.publication_id', '=', 'publications.id')
->where('first_date', '>','2020-01-10');
})->get();
I am asked to load data from a single table like the following:
=================================
|id |dept | response | created_at |
=================================
|1 |FO | GOOD | 2018-05-20 |
|2 |FO | GOOD | 2018-05-20 |
|3 |IT | GOOD | 2018-05-20 |
|4 |FO | BAD | 2018-05-20 |
|5 |IT | GOOD | 2018-05-20 |
|6 |LO | BAD | 2018-05-20 |
|7 |IT | GOOD | 2018-05-20 |
|8 |IT | GOOD | 2018-05-21 |
|9 |LO | GOOD | 2018-05-21 |
=================================
they want me to display only records created_at 2018-05-20. and the desired output display in blade is like this:
FO
GOOD 2
BAD 1
IT
GOOD 3
BAD 0
LO
GOOD 0
BAD 1
when I use select distinct, it displays only one row of response data (only GOOD). The same thing happens when I use groupBy
here are my current codes:
Controller:
$date = new Carbon('2018-05-20');
$result = Result::select('dept', 'response')->where('created_at', '=', $date->toDateString())->groupby('dept')->get();
blade:
#foreach($result as $result)
{{$result->dept}}</BR>
{{$result->response}}</br></br>
#endforeach
The output I get:
FO
GOOD
IT
GOOD
LO
GOOD
I don't do the count yet because I still get this undesired result.
Is there a way to achieve the desired request with that single table?
EDIT I:
Based on Yrv16's answer, my controller goes like this:
namespace App\Http\Controllers;
use App\Result;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
public function index()
{
$date = new Carbon('2018-05-20');
$results = Result::select('dept', 'response', \DB::raw('COUNT(*) as count'))
->where('created_at', '=',$date->toDateString())
->groupby('dept','response')
->get();
$results = $results->groupBy('dept');
return view('result.index', compact ('results'));
}
and my blade:
#foreach($results as $result)
{{$result->dept}}</BR>
{{$result->response}}</br></br>
#endforeach
this gives me an empty page. but when i change this where('created_at', '=', $date->toDateString()) to where('created_at', '>=', $date->toDateString()) i get this error Property [dept] does not exist on this collection instance.
Use groupBy for two columns dept, response:
$date = new Carbon('2018-05-20');
$results = Result::select('dept', 'response', \DB::raw('COUNT(*) as count'))
->where('created_at', '=',$date->toDateString())
->groupby('dept','response')
->get();
Also result collection you can groupBy('dept') in order to display it like you want:
$results = $results->groupBy('dept');
Blade:
#foreach($results as $dept => $result)
{{$dept}}</BR>
#foreach($result as $item)
{{$item->response}} {{ $item->count }}
#endforeach
#endforeach
You can use collection manipulation for that:
$results = Result::where('created_at', '2018-05-20')
->get()
->groupBy('dept')
->map(function ($item) {
return $item->groupBy('response')->map(function ($responses) {
return $responses->count();
});
});
It will give you the following results:
print_r($results->toArray());
Array (
[FO] => Array (
[GOOD] => 2
[BAD] => 1
)
[IT] => Array (
[GOOD] => 3
)
[LO] => Array (
[BAD] => 1
)
)
I got a problem, When try to use whereHas in this case
Table users.
----------------
| id | name |
----------------
| 1 | AAA |
| 2 | BBB |
| 3 | CCC |
----------------
Table subjects.
------------------
| id | title |
------------------
| 1 | Subject 1 |
| 2 | Subject 2 |
------------------
Table subject_user.
------------------------
| user_id | subject_id |
------------------------
| 1 | 1 |
| 2 | 1 |
| 1 | 2 |
| 3 | 2 |
------------------------
in Subject Model
...
public function User()
{
return $this->belongsToMany(User::class, 'subject_user');
}
...
When I want to find subject by user_id with this query.
In this case Auth::id() == 1 and $request->user_id == 3
$subject = Subject::whereHas('User', function ($query) use ($request) {
$query->whereIn('user_id', [Auth::id(), $request->user_id]);
})->get();
With this query, I got subjects 1 and 2. That was a wrong result. That must got only subject 2.
Then I try this.
$subject = Subject::whereHas('User', function ($query) use ($request) {
$query->where('user_id', Auth::id())->where('user_id', $request->user_id);
})->get();
It would not get any subjects.
What query do I use in this case to get only subject 2.
#Lloople from your answer, I got an idea.
$subject = Auth::user()->Subject()->whereHas('User', function ($query) use ($request) {
$query->where('user_id', $request->id);
})->first();
Why not doing it backwards? You already have the logged in user, so if you define the relationship in the User model you can do
$subjects = auth()->user()->subjects;
Anyway, you don't need to check double the Auth::id()and $request->user_id. In fact, I'm not sure you can do this last one.
Edit after comments
$subjects = Subject::whereBetween(auth()->user()->id, $request->user_id)->get();
You will need to change the order from the inputs, in case $request->user_id is less than auth()->user()->idor it wouldn't work.