I'm in a situation where I need to display the last 5 unique commenters information at the top of the comment list as follows screenshot.
comment image
To do this. I did as follows:
Post Model
public function comments()
{
return $this->hasMany(Comment::class);
}
public function commenter_avatars(){
return $this->comments()->distinct('user_id')
->select('id','post_id','user_id','parent_id')
->whereNull('parent_id')
->with('user')->limit(5);
}
My Controller method as follows
public function index() {
$feeds = auth()->user()
->posts()
->with(['user:id,first_name,last_name,username,avatar', 'media', 'commenter_avatars'])
->orderBy('id', 'desc')
->paginate(10);
return PostResource::collection($feeds);
}
I tried to use groupBy and Distinct.. But did't work as expected.
Did I miss something? or Have there any more best way to solve this?
Thank you in advance!
Noted: I am using latest Laravel (8.48ˆ)
I don't know about your joining of post, user and comments table. But i guess, you can do something similar to following.
At first get latest 5 unique user id of one post:
$userIds = Comments::where("post_id", $post_id)->distinct("user_id")->orderBy("id")
->limit(5)->pluck('user_id');
Then, fetch those user information
$users = Users::whereIn("id", $userIds )->get();
Then, you can return those users
UPDATE
You may use map() to fetch and reorder output. Following is an idea for you:
In Controller:
public function index(Request $request) {
$skipNumber = $request->input("skip"); // this is need for offsetting purpose
$userIds = [];
$feeds = Posts::with("comments")->where("comments.user_id", Auth::id())
->skip($skipNumber)->take(10)->orderBy('comments.id', 'desc')
->map(function ($item) use($userIds){
$users = [];
$count = 0;
foreach($item["comments"] as $comment) {
if(!in_array($comment["user_id"], $userIds) && $count < 5){
$count++;
$userIds.push($comment["user_id"])
$user = User::where("id", $comment["user_id"])->first();
$users.push($user);
}
if($count == 5) break;
}
$data = [
"post" => $item,
"latest_users" => $users
];
return $data;
})->get();
return PostResource::collection($feeds);
}
My code syntax may be slightly wrong. Hopefully you will get the idea.
I have solved this issue by using eloquent-eager-limit
https://github.com/staudenmeir/eloquent-eager-limit
I am trying to query two tables in Laravel when the user submits a form. The data is stored in the transaction table and I can also update the Account table by adding the $transactions->amt with $account->total.
I tried doing this;
public function store(Request $request)
{
$account = DB::table('users')
->join('accounts', "users.id", '=', 'accounts.user_id')
->select('users.*', 'accounts.*')
->first();
$transaction = new Transaction();
$data = $this->validate($request, [
'baddress'=>'required',
'package'=>'required',
'amt'=>'required',
]);
$transaction->username = $account->username;
$transaction->baddress = $request->input('baddress');
$transaction->package = $request->input('package');
$transaction->amt = $request->input('amt');
$transaction->fund_option = "Fund";
$transaction->save();
$bal= $transaction->amt + $account->total;
$account->amt_paid = $bal;
$account->total = $bal;
$account->save();
return redirect('/transfer')->with('success', 'Transaction has been made');
}
but I got this error:
Call to undefined method stdClass::save()
How can I find the best method to do this?
That error occur because $account is not collection. method save() is method from model collection.
if you want update you can use this code like.
$accountUpdate = DB::table('accounts')
->where('id', $account->id)
->update(['amt_paid' => $bal, 'total' => $bal]);
but if you want to use method save() then you have to call $account from model.
$account is a row made by your join (users.* and accounts.*) so it's a stdClass, not an Eloquent model. You don't have a save() method
in order to do this you should have a relationship between User model and Account model:
//Account.php
public function user(){
return $this->belongsTo("App\User");
}
........
//User.php
public function account(){
return $this->hasOne("App\Account");
}
then you can retrieve Account from your User:
$user = User::first();
$account = $user->account;
[....]
$account->amt_paid = $bal;
$account->total = $bal;
$account->save();
You have to use like this. This is simple as like as much
$accountModelUpdate = DB::table('accounts')
->where('user_id', $account->id)
->update(['amt_paid' => $bal, 'total' => $bal]);
I have some problem here.
I wanna view all data sort by "kelompok".
*kelompok means group
This is the code :
Controller
public function pengelompokan()
{
$view = DB::table('tb_siswa')->where('id', $kelompok)->get();
return view('pengelompokan')
->with('view', $view);
}
Route
Route::get('kelompok', 'belajarController#kelompok');
You can use the groupBy collection method:
$view = DB::table('tb_siswa')
->where('id', $kelompok)
->get()
->groupBy('kelompok');
Edit
Based on your comments, you could do this:
Route::get('kelompok/{groupId}', 'belajarController#kelompok');
public function pengelompokan($kelompok)
{
$view = DB::table('tb_siswa')
->where('id', $kelompok)
->get()
->groupBy('kelompok');
return view('pengelompokan', compact('view'));
}
Following is the code to resolve this
public function pengelompokan()
{
$view = DB::table('tb_siswa')->where('id', $kelompok)
->groupBy('kelompok')->get();
return view('pengelompokan')->with('view');
}
You can access groupBy data using a variable $view on blade as well.
I am using Routes but you can apply it on your Controller#show
Route::get('tutorial/{id}', function($id){
$tutorial = Tutorial::findOrFail($id);
return view('tutorial.show')->with('tutorial', $tutorial);})->name('show-tutorial');
and Also Check on your show.blade.php
I'm using tabs but same controller, different methods. How to return values to different views with same route?
In /users, get value from db via BuyerSellerController#buyers method for buyer.
Route::get('users','BuyerSellerController#buyers');
In /users as well, get value from db via BuyerSellerController#sellers method for seller.
Route::get('users','BuyerSellerController#sellers');
//BuyerSellerController
public function buyers()
{
$buyerSeller = DB::table('buyerseller')
->where('buyerseller','=','Buyer')
->pluck('id');
$buyers = DB::table('users')
->where('buyerseller','=',$buyerSeller)
->get();
return View::make('pages.users')->with('buyers', $buyers);
}
public function sellers()
{
$buyerSeller = DB::table('buyerseller')
->where('buyerseller','=','Seller')
->pluck('id');
$sellers = DB::table('users')
->where('buyerseller','=',$buyerSeller)
->get();
return View::make('pages.users')->with('sellers', $sellers);
}
//users.blade.php
Then I got this error:
Undefined variable: sellers (View: ...)
compact saved my life! :D
public function index()
{
/* buyers */
$buyerSeller = DB::table('buyerseller')
->where('buyerseller','=','Buyer')
->pluck('id');
$buyers = DB::table('users')
->where('buyerseller','=',$buyerSeller)
->get();
/* sellers */
$buyerSeller = DB::table('buyerseller')
->where('buyerseller','=','Seller')
->pluck('id');
$sellers = DB::table('users')
->where('buyerseller','=',$buyerSeller)
->get();
return View::make('pages.users', compact('buyers', 'sellers'));
}
I am trying to create a page where I can see all the people in my database and create edits on them. I made a form where I fill in the data from the database of certain fields.
I would like to navigate trough them by a Next and Previous button.
For generating the next step I have to take the ID larger than the current one to load the next profile.
For generating the previous step I have to take the ID smaller than the current one to load the previous profile.
My route:
Route::get('users/{id}','UserController#show');
Controller:
public function show($id)
{
$input = User::find($id);
// If a user clicks next this one should be executed.
$input = User::where('id', '>', $id)->firstOrFail();
echo '<pre>';
dd($input);
echo '</pre>';
return View::make('hello')->with('input', $input);
}
View:
The buttons:
Next
What is the best approach to get the current ID and increment it?
Below are your updated controller and view files derived from #ridecar2 link,
Controller:
public function show($id)
{
// get the current user
$user = User::find($id);
// get previous user id
$previous = User::where('id', '<', $user->id)->max('id');
// get next user id
$next = User::where('id', '>', $user->id)->min('id');
return View::make('users.show')->with('previous', $previous)->with('next', $next);
}
View:
Previous
Next
// in your model file
public function next(){
// get next user
return User::where('id', '>', $this->id)->orderBy('id','asc')->first();
}
public function previous(){
// get previous user
return User::where('id', '<', $this->id)->orderBy('id','desc')->first();
}
// in your controller file
$user = User::find(5);
// a clean object that can be used anywhere
$user->next();
$user->previous();
In your App\Models\User.php
...
protected $appends = ['next', 'previous'];
public function getNextAttribute()
{
return $this->where('id', '>', $this->id)->orderBy('id','asc')->first();
}
public function getPreviousAttribute()
{
return $this->where('id', '<', $this->id)->orderBy('id','asc')->first();
}
In your Controller you can simply do this:
public function show(User $user)
{
return View::make('users.show')
->with('user', $user)
->with('previous', $user->previous)
->with('next', $user->next);
}
I understand the approach being taken here by user2581096 but I am not sure it is efficient (by any standards). We are calling the database 3 times for really no good reason. I suggest an alternative that will be way more efficient and scalable.
Do not pass the previous and next IDs to the view. This eliminates 2 unnecessary database calls.
Create the following routes:
users/{id}/next
users/{id}/previous
These routes should be used in the href attributes of the anchor tags
Add methods in the controller to handle each of the new routes you have created. For example:
public function getPrevious(){
// get previous user
$user = User::where('id', '<', $this->id)->orderBy('id','desc')->first();
return $this->show($user->id);
}
This function will only be called when you actually click on the button. Therefore, the database call is only made when you need to actually look up the user.
in-case you want to retrieve the prev/next records along with their data,
you can try
$id = 7; // for example
$prev = DB::table('posts')->where('id', '<', $id)->orderBy('id','desc')->limit(1);
$next = DB::table('posts')->where('id', '>', $id)->limit(1);
$res = DB::table('posts')
->where('id', '=', $id)
->unionAll($prev)
->unionAll($next)
->get();
// now $res is an array of 3 objects
// main, prev, next
dd($res);
1- the query builder is usually much faster than eloquent.
2- with union we are now only hitting the db once instead of 3.
To get next and previous post we can use max and min functions on Model id in laravel. here is an example to get this
https://usingphp.com/post/get-next-and-previous-post-link-in-laravel
The Controller:
public function post($id)
{
$post = Post::find($id);
$previous = Post::where('id', '<', $post->id)->max('id');
$next = Post::where('id', '>', $post->id)->min('id');
return view( 'post', compact( 'post', 'next', 'previous' ));
}
The View:
#if($next)
{{$next->title}}
#endif
#if($previous)
{{$previous->title}}
#endif
Here's a link I found that should help: http://maxoffsky.com/code-blog/laravel-quick-tip-get-previous-next-records/
It looks like for next you want to use: $next = User::where('id', '>', $id)->min('id'); and have the view as: Next
Also don't forget to pass $next to the view.
Simplest approach
// User.php
public static function findNext($id)
{
return static::where('id', '>', $id)->first();
}
// UserController.php
$nextUser = User::findNext($id);
// view
Next
Lazy approach :
// view
Next
// routes.php (should be optimized, this is just to show the idea)
Route::get('users/{user}/next', function($id) {
$nextUser = User::findNext($id);
return Redirect::to('user/' . $id);
});
// yourModel.php
public function previous()
{
return $this->find(--$this->id);
}
public function next()
{
return $this->find(++$this->id);
}
Works like magic, you can chain it:
$prevprev = Model::find($id)->previous()->previous();
$nextnext = Model::find($id)->next()->next();
First, get a record out of the database.
$post = Post::where('slug', $slug)->first();
With a database record, we can get the previous record where the record id is less than the id stored inside $post order by the id in descending order and use first() to get a single record back.
$previous = Post::where('id', '<', $post->id)->orderBy('id','desc')->first();
To get the next record it's almost the same query, this time get the record where the id is more than the id stored in $post.
$next = Post::where('id', '>', $post->id)->orderBy('id')->first();
Controller:
public function show($id)
{
// get the current user
$user = User::find($id);
// get previous user id
$previous = User::offset($user->id-2)->first();
// get next user id
$next = User::offset($user->id)->first();
return View::make('users.show')->with('previous', $previous)->with('next', $next);
}
i developed the code.
it work all times, even if we don't have any next or prev post
public function nextPost($table, $id)
{
$next = DB::table($table)->where('id', '>', $id)->orderBy('id','asc')->first();
if(!$next)
$next = DB::table($table)->orderBy('id','asc')->first();
return $next;
}
public function prevPost($table, $id)
{
$prev = DB::table($table)->where('id', '<', $id)->orderBy('id','desc')->first();
if(!$prev)
$prev = DB::table($table)->orderBy('id','desc')->first();
return $prev;
}