I have difficulty to convert raw SQL query to eloquent model with relations.
This is the raw SQL query I have constructed but I cannot read the count inside the whereHas function of the eloquent.
Raw SQL query:
select *
from `subscriptions` where (`user_id` = 1) and
exists (select count(subscription_infos.id) as rec from `subscription_infos` where `subscriptions`.`id` = `subscription_infos`.`subscription_id` and `status` = 0 HAVING rec > 1)
Attempt Laravel Eloquent Model
$chk = Subscription::where(["user_id" => $userId])
->with(['info' => function($q){
return $q->where("status",0);
}])
->whereHas('info', function($q){
return $q->where("status",0);
})
->has('info','>',5)
->get();
I have setup a relation called "info" in the Subscription Model
public function info(){
return $this->hasMany(SubscriptionInfo::class,"subscription_id","id");
}
I really appreciate for those contributor that help me.
Related
I'm trying to make a complex query using Laravel Eloquent. I know how to do it using raw SQL query, but I don't have any idea how to do it using Eloquent.
Here is my SQL query, and it works perfectly:
select *
from students
where exists(select *
from (select student_movements.id AS sm_id, student_movements.direction, student_movements.deleted_at
from student_movements
inner join student_student_movements
on student_movements.id = student_student_movements.student_movement_id
where students.id = student_student_movements.student_id
and student_movements.deleted_at is null
order by student_movements.id desc
limit 1) as last_sm
where last_sm.direction = 1 AND last_sm.date >= 5-5-2022
);
My models have many-to-many relation using student_student_movements table:
Student
public function studentMovements(): BelongsToMany
{
return $this->belongsToMany(
StudentMovement::class,
'student_student_movements',
);
}
StudentMovement
public function students(): BelongsToMany
{
return $this->belongsToMany(
Student::class,
'student_student_movements'
);
}
My goal is to get all Students, who have the last Movement where direction = 1 and the date of the last Movement >= $someDate.
So, my question is: how to translate the SQL query to Eloquent? I saw many similar questions, but they are not helping me.
Thanks for any advice.
Use the whereHas method, then fine tune the sub query inside the closure to your needs.
You can use the whereHas and orWhereHas methods to define
additional query constraints on your has queries.
There is an example like that in laravel documentation
use Illuminate\Database\Eloquent\Builder;
// Retrieve posts with at least one comment containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();
// Retrieve posts with at least ten comments containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
}, '>=', 10)->get();
check the documentation here
I currently have an Eloquent ORM query.
$uploadedSC = SC::select('id')->with(['cont'=> function ($q) {
return $q->with('crs:id,title')->select('*');
}])->where('act', true)->get();
But I wanna add a ->where('act',true) query to my crs table. However, it gives an error. How can I write this query?
$uploadedSC = SC::select('id')->with(['cont'=> function ($q) {
return $q->with('crs:id,title')->where('act',true)->select('*');
}])->where('act', true)->get();
You could do something like this:
$uploadedSC = SC::select('id')
->with(['cont.crs:id,title' => function ($crs) {
$crs->where('act', true);
}])
->where('act', true)
->get();
Depending on the direction of the relationship you must select the foreign key of cont in crs or you will receive an error. This is the problem of giving bad names to your models and tables, other programmers will not understand at the first time what you are trying to do.
Can I use Eloquents active record to create this query or do I need to use the query builder?
SELECT
*
FROM
chat
inner join chat_member as member1
on member1.chat_id = chat.chat_id
inner join chat_member as member2
on member2.chat_id = chat.chat_id
WHERE
member1.chat_member_id = $member1ID
and member2.chat_member_id = $member2ID
WhereHas() function is to filter from relationship queries.
Make sure you have Chat and Member Models.
And Chat Model has members() relationship.
Then you can achieve your mysql query as follow.
Chat::whereHas('members', function ($query) {
$query->where('chat_member_id', $member1ID);
})
->whereHas('members', function($query) {
$query->where('chat_member_id', $member2ID);
})
->get();
Update, I fixed a syntax issue
I want to create pagination in laravel but it gives error.
here is error "Call to a member function paginate() on array"
this is my query
$blog = DB::select("select blog_post.*,(select img_name from blog_image WHERE blog_image.bid=blog_post.bid limit 0,1) as img_name,
(SELECT count(*) FROM likes WHERE likes.bid =blog_post.bid) AS likes,
(SELECT count(*) FROM comment WHERE comment.bid =blog_post.bid ) as comments ,
(SELECT count(*) FROM blog_share WHERE blog_share.bid =blog_post.bid ) as share
from blog_post WHERE status=1 AND is_draft=0 AND is_publish=1 AND is_delete=0")->paginate(2);
You first need to rewrite your query using Laravel Eloquent. For example:
$blog = DB::table('blog_post')->where([ 'status' => 1, 'is_draft' => 0, 'is_publish' => 1, 'is_delete' = 0 ])->paginate(2);
You don't strictly need to put all the counts / inner-queries into one query. You can use Eloquent Relationships and Mutators to get that information for you. For example, the following Relationship to get the image:
class BlogPost extends Model
{
public function image()
{
return $this->hasOne('BlogImage');
}
}
I have posts table (id, user_id, title) and Post model with this content
class Post extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
}
I want to get some post by id and also the user's information, so I use this query
$post = new Post();
$res = $post->where('id', 1)->select('id', 'title', 'user_id')->with([
'user' => function ($query) {
$query->select('id', 'name', 'email');
}
])->first();
It returns the data as expected, and i can access the post's info like $res->title, or the user's info like $res->user->email, but the problem is it makes 2 queries to the database
I would expect to have one query only
SELECT
`posts`.`id`,
`posts`.`title`,
`posts`.`user_id`,
`users`.`id`,
`users`.`email`,
`users`.`name`
FROM
`posts`
LEFT JOIN `users`
ON `posts`.`user_id` = `users`.`id`
WHERE `posts`.`id` = '1'
LIMIT 1
Please note, this is not the same as N+1 problem
https://laravel.com/docs/5.3/eloquent-relationships#eager-loading
I know I can manually do left join,
$res = $post->where('posts.id', 1)
->select('posts.id', 'posts.title', 'posts.user_id', 'users.email', 'users.name')
->leftJoin('users', 'posts.user_id', '=', 'users.id')
->first();
and it will have the one query as I need, but the problem is in the result all data from related table is in the same array (and besides, what is the point of defining/using relationships if i have to manually make a left join every time)
So, my question is how to get the post data with related tables with one query and result organized according to relations: I am curious what is the best practice in laravel and how experienced Laravel developers are doing this ?
Thanks
Eloquent never uses JOINs to retrieve relationship data, but instead uses seperate queries and links the data together in PHP objects. Therefore, you will always have one extra query for each relationship. Also, Eloquent mostly loads all columns (using *).
To link them together, you have to stop using the query builder and instead use Eloquent directly:
$post = Post::find(1)->load('user');
If you insist on using JOINs, you will have to continue using the query builder.
That is eager loading.
You are using
->with([
'user' => function ($query) {
$query->select('id', 'name', 'email');
}
])
In eager loading, what happens is first run above query and get all the users matching the query.
Then the result is applied to the outer query which is
$post->where('id', 1)->select('id', 'title', 'user_id')->with([
'user' => function ($query) {
$query->select('id', 'name', 'email');
}
])->first();