Laravel eloquent - 如何在自定义关系中使用 DB raw

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33042983/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 12:28:42  来源:igfitidea点击:

Laravel eloquent - how to use DB raw inside custom relationship

mysqllaravel

提问by user804293

I have laravel models : Post and Comment. As normal, post will have many comment tables as bellow:

我有 Laravel 模型:发布和评论。正常情况下,帖子会有很多评论表,如下所示:

Posts table
--------------------------------------
|id   |title   |                     |
--------------------------------------
|1    |test    |

comments table: 
--------------------------------------
|id   |post_id |   created_at       |
--------------------------------------
|1    |1          |2015-09-12 09:01:02 |    
--------------------------------------
|2    |1          |2015-09-12 09:03:02 |

now I want to query for the post with the most number of comments from current date back to last 7 days. Here is my code in Laravel Controller:

现在我想查询从当前日期到过去 7 天评论数量最多的帖子。这是我在 Laravel 控制器中的代码:

$posts= Post::whereHas(
         'comments', function ($q) {
             $q->select(DB::raw("DATE_FORMAT(created_at,'%Y-%m-%d %H:%i:%s')"),'post_id',DB::raw('count(*) as cmt_count'))
             ->where(DB::raw('`comments`.`created_at` BETWEEN NOW()-INTERVAL 8 DAY AND NOW()'))
                 ->groupBy('post_id');

         })
         ->take(5)->get();

got the ERROR:

得到错误:

QueryException in Connection.php line 651: SQLSTATE[21000]: Cardinality violation: 1241 Operand should contain 3 column(s) (SQL: select * from `posts` where (select DATE_FORMAT(created_at,'%Y-%m-%d %H:%i:%s'), `post_id`, count(*) as cmt_count from `comments` where `comments`.`post_id` = `posts`.`id` and `comments`.`created_at` BETWEEN NOW()-INTERVAL 8 DAY AND NOW() is null group by `post_id`) >= 1 limit 5)

Please help to point me what am I doing wrong ?

请帮助指出我做错了什么?

回答by user804293

OK. after some digging with the suggestions above, I solve the problem myself. Post this answer here for anyone who having same problem could find it useful. bellow is the query (assumed you already setup the relationship )

好的。在对上述建议进行了一些挖掘之后,我自己解决了问题。在这里发布此答案,供遇到相同问题的任何人使用。波纹管是查询(假设您已经设置了关系)

$posts = Post::select(DB::raw('posts.id,posts.title,count(*) as cmnt_count'))
->join('comments',function($q){
  $q->on('posts.id','=','comments.post_id')
     ->where(DB::raw("DATE_FORMAT(comments.created_at,'%Y-%m-%d %H:%i:%s')),'>=',Carbon::now()->subDays(7));
})->groupBy('posts.id')
->orderBy('cmnt_count')->get();

now I can get what I want: retrieve all posts which have comments within 7 days and sort by number of comments.

现在我可以得到我想要的:检索所有在 7 天内有评论的帖子并按评论数量排序。

回答by MaGnetas

I'm not sure about the whole logic of your SQL. But the problem is with the following part:

我不确定你的 SQL 的整个逻辑。但问题在于以下部分:

`comments`.`created_at` BETWEEN NOW()-INTERVAL 8 DAY AND NOW() is null

Now the part of your query builder responsive for this piece is:

现在,您的查询构建器响应这一部分的部分是:

where(DB::raw('`comments`.`created_at` BETWEEN NOW()-INTERVAL 8 DAY AND NOW()'))

This is quite clear - you want to use a raw where expression. but where method usually takes 2 or three params.

这很清楚 - 您想使用原始 where 表达式。但是 where 方法通常需要 2 或 3 个参数。

Why don't you try replacing it with whereRaw?

你为什么不尝试用 whereRaw 替换它?

Your builder would look like this:

您的构建器将如下所示:

$posts= Post::whereHas(
         'comments', function ($q) {
             $q->select(DB::raw("DATE_FORMAT(created_at,'%Y-%m-%d %H:%i:%s')"),'post_id',DB::raw('count(*) as cmt_count'))
             ->whereRaw('`comments`.`created_at` BETWEEN NOW()-INTERVAL 8 DAY AND NOW()')
                 ->groupBy('post_id');

         })
         ->take(5)->get();

This should use the whereRaw argument as a raw piece of the final SQL.

这应该使用 whereRaw 参数作为最终 SQL 的原始部分。

Let me know if this solves your issue.

如果这能解决您的问题,请告诉我。

Take care

小心

回答by Crystalwing Bakaboe

Don't use $q->select(), whereHas()expecting the query to return a number (typically count(*)). The whereHas()is likely to select parent records where the count of its relative records is more than 1.

不要使用$q->select()whereHas()期望查询返回一个数字(通常是 count(*))。在whereHas()可能选择父记录其中的相对的记录数超过1。

Try ->toSql()instead of ->take(5)->get()to see your raw SQL query.

尝试->toSql()而不是->take(5)->get()查看您的原始 SQL 查询。

====== EDIT ========

====== 编辑 ========

I think you could do something like this.

我认为你可以做这样的事情。

$posts= Post::whereHas(
     'comments', function ($q) {
         $q->where(DB::raw('`comments`.`created_at` BETWEEN NOW()-INTERVAL 8 DAY AND NOW()'))
             ->groupBy('post_id');

     })
     ->with('comment_counts') 
     ->take(5)->get();

Or if you want to use Carbon:

或者,如果您想使用Carbon

$q->where('created_at', '>=', Carbon::now()->addWeek(-1))

In your model:

在您的模型中:

class Post extends Model {
//...
     public function comment_counts() {
         return $this->hasMany('App\Comment')->select([ DB::raw("DATE_FORMAT(created_at,'%Y-%m-%d %H:%i:%s')"),'post_id',DB::raw('count(*) as cmt_count') ]);
     }
}

回答by Kiran Subedi

Try this query, I haven't try but it must work

试试这个查询,我还没试过,但它一定能用

$posts= Post::whereHas(
         'comments', function ($q) {
             $q->select(DB::raw("DATE_FORMAT(created_at,'%Y-%m-%d %H:%i:%s')"),'post_id',DB::raw('count(*) as cmt_count'))
             ->groupBy('post_id')
             ->where('created_at','>',DB::raw('date_sub(CURDATE(), INTERVAL 1 WEEK)'))
             ->where(DB::raw('DATE(created_at)'),'<=',DB::raw('CURDATE()'));

         })
         ->take(5)->get();