Retrieve 5 most commented posts in seven last day with Laravel - php

I have articles with comments. I would like to post 5 popular items on my home page based on reviews received over a 7 day period. Is it possible to do this with Laravel? I do not know how to deal with the query builder at all.
I have an article_id in each comment.
$mostPopular = Article::published()->whereHas('comments', function ($query){
$query->count();
})->orderBy($query, 'ASC');
thank you !

Untested, but should satisfy your conditions. Let me know if you have any issues.
$popularArticles = Article::published()
->whereHas('comments')
->withCount('comments')
->where('created_at', '>', \Carbon\Carbon::now()->subWeek())
->orderBy('comments_count', 'DESC')
->take(5)
->get();

Related

How to create filtering using SQL statement PHP Laravel?

I have one table named as Package. Currently, I want to filter the table actually. Let say the user insert values into variable, for example, from = 2/3/2020 and to = 10/3/2020. Then it will be calculated inside my coding and get the duration of days, resulting noOfdays = 8 days. So, from the duration, it will determined, which packages it belongs within the duration of 8 days.
calculation of days :
$today = Carbon::now();
$dt1 = Carbon::createFromFormat('d/m/Y',$departure);
$dt2 = Carbon::createFromFormat('d/m/Y',$arrival);
$noOfDays = $dt1->diffInDays($dt2);
The calculation have no error, which when dd($noOfDays), it will result = 8 days.
SQL statement :
$packages = Package::where([
['id', '=', $plan],
['from', '<=', $noOfDays, 'AND', 'to', '>=', $noOfDays],
])
->get();
Package Table :
The error part is, when I filtering, it will get Package 1 and package 2. It supposed to get only the package 2. I think it have something wrong somewhere around the SQL statement. Anyone whoever in this situation before? because logically I think, the SQL part is true already. But it why it filter and get the package 1?
You have to separate your two where clause to get what you wanted like this.
$packages = Package::where([
['id', '=', $plan],
['from', '<=', $noOfDays],
['to', '>=', $noOfDays]
])
->get();
To be more readable, its good if your code goes like this
$packages = Package::where('id', $plan)
->where('from', '<=', $noOfDays)
->where('to', '>=', $noOfDays)
->get();
Change your second where statement. Where clause is always using an ADN unlelss you use orWhere or nest it in a function.
['id', '=', $plan],
['from', '<=', $noOfDays],
['to', '>=', $noOfDays]
The 4th parameter, that you are passing as an AND is not used like you are expecting it to work.
Which results to package1 to be included in your output.

Apply where condition only to one occurrence

I'm building search functionality for my app. To simplify things:
there are two tables: shops and subscriptions
Each shop can have multiple subscription records, subscription has field expires_at. Now, I assume that shop has active subscription if subscription exsists and at least one of shop's subscripion expires_at date is bigger than now().
It is one of the conditions to the whole query. Here is code:
$shops = Shop::when($subscription, function($query, $subscription) {
$query->doesntHave('subscriptions')->orWhereHas('subscriptions', function($q) use ($subscription, $query) {
$query->where('expires_at', '<', now());
});
});
It doesn't work as expected because if i.e. shop has three related subscriptions and at least one of them is expired – it assumes that shop has no active subscription (even though it has).
I would need to implement some nested function inside or whereHas, I guess, to sort by expires_at desc and then limit to one and only then pass where expires_at clause, however I've no idea how.
And I rather need to stick with Eloquent Query Builder rather than DB facade or raw sql.
Basically, it is the same problem what wasn't answered here:
https://laracasts.com/discuss/channels/eloquent/latest-record-from-relationship-in-wherehas?page=1
Try this:
$shops = Shop::doesntHave('subscriptions')->orWhereHas('subscriptions', function ($query) {
$query->whereDate('expires_at', '<', now());
})->get();
try this :
$shops = Shop::WhereHas('subscriptions')->withCount('subscriptions as
active_subscriptions_count' => function ($query) {
$query->where('expires_at', '<', now());
}])->having('active_subscriptions_count', '>=', 3)->get();
Ok, so after some tries in raw sql I figured it out:
$query->select('shops.*')
->leftJoin('subscriptions', 'shops.id', 'subscriptions.shop_id')
->whereNull('subscriptions.id')
->orWhere('subscriptions.expires_at', '<', now())
->where('subscriptions.id', function($q) {
$q->select('id')
->from('subscriptions')
->whereColumn('shop_id', 'shops.id')
->latest('expires_at')
->limit(1);
});
It is not only faster than where exists clause but also gives me what I needed – only the "highest" subscription for given shop is under consideration.

Retrieve 5 most commented posts in seven last day

I have articles with comments. I would like to post 5 popular items on my home page based on reviews received over a 7 day period. I tried this :
$popularArticles = Article::published()
->whereHas('comments')
->withCount('comments')
->where('created_at', '>', \Carbon\Carbon::now()->subWeek())
->orderBy('comments_count', 'DESC')
->take(5)
->get();
But with this method, articles that were created more than 7 days ago are "ignored". What I would like is that it is the comments of the last 7 days that define whether an article is popular or not.
Thank you very much
You need to filter counted comments like this:
$popularArticles = Article::published()
->has('comments')
->withCount(['comments' => function ($q) {
$q->where('created_at', '>', Carbon\Carbon::now()->subWeek());
}])
->latest('comments_count')
->take(5)
->get();

Laravel merge 2 collection with paginator

I need to do paging with two different collection. Have priority listing. Money that is placed in the priority list.
Why do not you do a single query, you can say the priority list. Reason, when using filtering; city, county, category I choose. However, the site owner, having priority listing in the city, county, or even in one of the priority areas listed in the category you want.
So in a query keywords when using 3 criteria, other criteria will be enough in the first. Each page lists 20 records, if the primary listing of the 30 first listing, the second collection of records in the other I would like to be listed thereafter.
According to the criteria normally be with you now I share my code. Waiting for your suggestions. Thank you in advance.
I'm sorry for my bad english.
DB::table('UserAds')
->join('Ads', 'Ads.id', '=', 'UserAds.ad_id')
->join('Users', 'Users.id', '=', 'UserAds.user_id')
->join('AdCategory', 'AdCategory.ad_id', '=', 'Ads.id')
->join('AdEducationPrices', 'AdEducationPrices.ad_id', '=', 'Ads.id')
->join('City', 'City.id', '=', 'Ads.city_id')
->join('Town', 'Town.id', '=', 'Ads.town_id')
->leftJoin('AdMedias', 'AdMedias.ad_id', '=', 'Ads.id')
->select('Ads.*', 'AdEducationPrices.*', 'City.name as city_name', 'City.id as city_id',
'Town.name as town_name', 'Town.id as town_id', 'AdMedias.url')
->where('Ads.is_publish', 1)
->whereIn('AdCategory.category_id', $new_category_array)
->where('Ads.city_id', $city_id)
->where('Ads.town_id', $town_id)
->where('UserAds.is_purchased', true)
->where('UserAds.started_at', '<', date('Y-m-d H:i:s'))
->where('UserAds.finished_at', '>=', date('Y-m-d H:i:s'))
->where('AdMedias.type', 'picture')
->groupby('UserAds.ad_id')->orderby('finished_at', 'DESC')
->orderby('started_at', 'DESC')->paginate(20);
I used array_merge for 2 queries. And filtered records for paginator. Ty for reading my question. I hope to help someone
$new_collection = array_merge($Priority, $Ads);
$new_collection_count = count($new_collection);
$perPage = 20;
$currentPage = Input::get('page', 1) - 1;
$new_collection = array_slice($new_collection, $currentPage * $perPage, $perPage);
$Ads = Paginator::make($new_collection, $new_collection_count, $perPage);

Laravel Eloquent get results grouped by days

I currently have a table of page_views that records one row for each time a visitor accesses a page, recording the user's ip/id and the id of the page itself. I should add that the created_at column is of type: timestamp, so it includes the hours/minutes/seconds. When I try groupBy queries, it does not group same days together because of the seconds difference.
created_at page_id user_id
========== ======= =======
10-11-2013 3 1
10-12 2013 5 5
10-13 2013 5 2
10-13 2013 3 4
... ... ...
I'd like to get results based on views/day, so I can get something like:
date views
==== =====
10-11-2013 15
10-12 2013 45
... ...
I'm thinking I'll need to dig into DB::raw() queries to achieve this, but any insight would help greatly, thanks
Edit: Added clarification of created_at format.
I believe I have found a solution to this, the key is the DATE() function in mysql, which converts a DateTime into just Date:
DB::table('page_views')
->select(DB::raw('DATE(created_at) as date'), DB::raw('count(*) as views'))
->groupBy('date')
->get();
However, this is not really an Laravel Eloquent solution, since this is a raw query.The following is what I came up with in Eloquent-ish syntax. The first where clause uses carbon dates to compare.
$visitorTraffic = PageView::where('created_at', '>=', \Carbon\Carbon::now->subMonth())
->groupBy('date')
->orderBy('date', 'DESC')
->get(array(
DB::raw('Date(created_at) as date'),
DB::raw('COUNT(*) as "views"')
));
You can use Carbon (integrated in Laravel)
// Carbon
use Carbon\Carbon;
$visitorTraffic = PageView::select('id', 'title', 'created_at')
->get()
->groupBy(function($date) {
return Carbon::parse($date->created_at)->format('Y'); // grouping by years
//return Carbon::parse($date->created_at)->format('m'); // grouping by months
});
Here is how I do it. A short example, but made my query much more manageable
$visitorTraffic = PageView::where('created_at', '>=', \Carbon\Carbon::now->subMonth())
->groupBy(DB::raw('Date(created_at)'))
->orderBy('created_at', 'DESC')->get();
Like most database problems, they should be solved by using the database.
Storing the data you want to group by and using indexes you can achieve an efficient and clear method to solve this problem.
Create the migration
$table->tinyInteger('activity_year')->unsigned()->index();
$table->smallInteger('activity_day_of_year')->unsigned()->index();
Update the Model
<?php
namespace App\Models;
use DB;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
class PageView extends Model
{
public function scopePerDay($query){
$query->groupBy('activity_year');
$query->groupBy('activity_day_of_year');
return $query;
}
public function setUpdatedAt($value)
{
$date = Carbon::now();
$this->activity_year = (int)$date->format('y');
$this->activity_day_of_year = $date->dayOfYear;
return parent::setUpdatedAt($value);
}
Usage
$viewsPerDay = PageView::perDay()->get();
You can filter the results based on formatted date using mysql (See here for Mysql/Mariadb help) and use something like this in laravel-5.4:
Model::selectRaw("COUNT(*) views, DATE_FORMAT(created_at, '%Y %m %e') date")
->groupBy('date')
->get();
I had same problem, I'm currently using Laravel 5.3.
I use DATE_FORMAT()
->groupBy(DB::raw("DATE_FORMAT(created_at, '%Y-%m-%d')"))
Hopefully this will help you.
PageView::select('id','title', DB::raw('DATE(created_at) as date'))
->get()
->groupBy('date');
To group data according to DATE instead of DATETIME, you can use CAST function.
$visitorTraffic = PageView::select('id', 'title', 'created_at')
->get()
->groupBy(DB::raw('CAST(created_at AS DATE)'));
Warning: untested code.
$dailyData = DB::table('page_views')
->select('created_at', DB::raw('count(*) as views'))
->groupBy('created_at')
->get();
I know this is an OLD Question and there are multiple answers. How ever according to the docs and my experience on laravel below is the good "Eloquent way" of handling things
In your model, add a mutator/Getter like this
public function getCreatedAtTimeAttribute()
{
return $this->created_at->toDateString();
}
Another way is to cast the columns
in your model, populate the $cast array
$casts = [
'created_at' => 'string'
]
The catch here is that you won't be able to use the Carbon on this model again since Eloquent will always cast the column into string
Hope it helps :)
Using Laravel 4.2 without Carbon
Here's how I grab the recent ten days and count each row with same day created_at timestamp.
$q = Spins::orderBy('created_at', 'desc')
->groupBy(DB::raw("DATE_FORMAT(created_at, '%Y-%m-%d')"))
->take(10)
->get(array(
DB::raw('Date(created_at) as date'),
DB::raw('COUNT(*) as "views"')
));
foreach ($q as $day) {
echo $day->date. " Views: " . $day->views.'<br>';
}
Hope this helps
You could also solve this problem in following way:
$totalView = View::select(DB::raw('Date(read_at) as date'), DB::raw('count(*) as Views'))
->groupBy(DB::raw('Date(read_at)'))
->orderBy(DB::raw('Date(read_at)'))
->get();
this way work properly and I used it in many projects!
for example I get data of views the last 30 days:
$viewsData = DB::table('page_views')
->where('page_id', $page->id)
->whereDate('created_at', '>=', now()->subDays(30))
->select(DB::raw('DATE(created_at) as data'), DB::raw('count(*) as views'))
->groupBy('date')
->get();
If you want to get the number of views based on different IPs, you can use the DISTINCT like below :
$viewsData = DB::table('page_views')
->where('page_id', $page->id)
->whereDate('created_at', '>=', now()->subDays(30))
->select(DB::raw('DATE(created_at) as data'), DB::raw('count(DISTINCT user_ip) as visitors'))
->groupBy('date')
->get();
You can easily customize it by manipulating the columns name
you can use the following code to group them by the date, since you have to parse both in the selection query and in the groupBy method:
$counts = DB::table('page_views')->select(DB::raw('DATE(created_at) as created_at'), DB::raw('COUNT(*) as views'))->
groupBy(DB::raw('DATE(created_at)'))->
get();
in mysql you can add MONTH keyword having the timestamp as a parameter
in laravel you can do it like this
Payement::groupBy(DB::raw('MONTH(created_at)'))->get();
I built a laravel package for making statistics : https://github.com/Ifnot/statistics
It is based on eloquent, carbon and indicators so it is really easy to use. It may be usefull for extracting date grouped indicators.
$statistics = Statistics::of(MyModel::query());
$statistics->date('validated_at');
$statistics->interval(Interval::$DAILY, Carbon::createFromFormat('Y-m-d', '2016-01-01'), Carbon::now())
$statistics->indicator('total', function($row) {
return $row->counter;
});
$data = $statistics->make();
echo $data['2016-01-01']->total;
```
Get pages views (or whatever), group my year-month-day and count for each date:
PageView::get()
->groupBy(fn($pv) => $pv->created_at->format('Y-m-d'))
->map(fn($date) => count($date));

Categories