hide confidential data for multiple users - php

I have an user named eric which has like user role Former.
I would like the user Eric sees only his confidential information but not those of others.
Would you have any idea how I could make this?
Thank you for your help.
Edit Controller Former
public function index(Request $req)
{
if ($req->search == "") {
$formers = Former::paginate(5);
$formerIdsDown = Cours::where('date_seance', "<=" , Carbon::now())->pluck('fk_former')->toArray();
return view('admin.formers.index', compact('formers', 'formerIdsDown'));
} else {
$validated = $req->validate([
'search' => 'alpha',
]);
$formers = Former::where('nom', 'LIKE', '%' . $validated['search'] . '%')->paginate(5);
$formers->appends($req->only('search'));
$bikeIdsDown = Cours::where('date_seance', "<=" , Carbon::now())->pluck('fk_former')->toArray();
return view('admin.formers.index', compact('formers', 'formerIdsDown'));
}
$user = User::when($has_role, function ($query) {
return $query->where('id', auth()->id());
})
->get();
}

You could handle this in two ways.
Limiting the data from the server
This should be your approach. Here you could add a condition in your query to know if the user has the restricted role in order to limit the data:
# InSomeController.php
$has_role = auth()->user()->hasRole('former'); // checking if the user has the role
$user = User
::when($has_role, function ($query) {
return $query->where('a_column', 'a_value'); // The limitation you want to apply
})
->get(); // executing the query
Limiting in the view
In case you are using blade templating engine.. you can make use of some Blade directives:
# in_some_view.blade.php
#if (auth()->user()->hasRole('former'))
/** Your limited data goes here.. */
#else
/** The default data goes here.. */
#endif
Obs: In both cases I used the hasRole('role') method, replace this with the logic to validate if the user has a given role.

Related

Search in data from cache

My question is, if there is data in the cache, I created a condition by saying fetch, and if not, I bring it from the database and refresh the cache for a certain period of time. Normally, if I don't use the cache, I can search the page, but I cannot search by id or nickname with the code below.So when using cache::get can I add a search condition?
public function index(Request $request)
{
$submit = $request->get('submit');
$id = $request->get('id');
$nick_name = $request->get('nick_name');
if (Cache::has('users')) {
$users = Cache::get('users');
return view('admin.users.index', compact('users'));
}
$users = User::when(!empty($id), function ($query) use ($id) {
$query->where('id', $id);
})->when(!empty($nick_name), function ($query) use ($nick_name) {
$query->where('nick_name', $nick_name);
})->orderBy('created_at', 'asc')->get();
Cache::put('users', $users, now()->addMinutes(60));
return view('admin.users.index', compact('users'));
}
Update: Based on the examples on the page below, the problem was solved
https://www.honeybadger.io/blog/caching-in-laravel/
You should add search criteria in your cache key. E.g.
public function index(Request $request) {
$submit = $request->get('submit');
$id = $request->get('id');
$nick_name = $request->get('nick_name');
$cacheKey = User::class . md5(json_encode([$submit, $id, $nick_name]));
if (Cache::has($cacheKey)) {
/* ... */
}
because now your cache writes only first search result and ignores everything else.
Or write to cache all users. Then run that array through array_filter applying filters. But that can be waste of developer time if search is very dynamic.

Laravel 8 gates not working with Auth::guard

Hey I am developing a project where I have roles and permissions for users and I am trying to protect my routes with the help of middleware by defining Gates but it's showing me 403| Not Authorized. I can't understand what the actual problem is?
Into Category Controller
public function addcategory(AdminsRole $adminsroles){
return view('add-category');
}
Into Routes
Route::get('/add-category', [CategoryController::class, 'addcategory'])->middleware('can:add-category')->name('addcategory');
Into AuthServiceProvider.php
$admin = Auth::guard('admin');
Gate::define('add-category', function ($admin, AdminsRole $adminsroles) {
if($admin->user()->u_type != 'superadmin'){
$adminRolescount = $adminsroles::where([
['admin_id', '=', $admin->user()->id],
['module', '=', 'categories'],
['add_access', '=', '1'],
])->count();
return $adminRolescount;
}else{
return $adminRolescount = 1;
}
});
I think what you're looking for can be simplified. The code in your controller and routes file does not need to be adjusted. I would however change your gate definition to the following:
Gate::define('add-category', function ($user = null) {
// Fetch user from custom guard
$user = Auth::guard('admin')->user();
// Check if a valid user instance was passed
if (is_null($user)) {
return false;
}
// Allow super admins to add categories regardless of AdminsRole existence
if ($user->u_type === 'superadmin') {
return true;
}
// Check if current user has a matching AdminsRole row with add_access permission
return AdminsRole::where([
['admin_id', '=', $user->id],
['module', '=', 'categories'],
['add_access', '=', '1'],
])->exists();
});
Note that a gate always receives a user instance as their first parameter if there is a logged in user, you needn't supply this yourself. Additionally, you can query for the AdminsRole existence directly via the model, using the id of the user instance that is being checked and automatically supplied to the gate.

How Can I get Latest 5 Commentors user information By Laravel Relational Eloquent eager loading

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

Laravel Redirect URL Controller

I want redirect url from /topic/{title} to /topic/{category}/{title}.
So I try to registered this in routes:
Route::get('/topic/{title}',function(){
return redirect()->action('/topic/{category}/{title}','DetailController#index');
});
But I got error
Action App\Http\Controllers/topic/{category}/{title} not defined.
Anyone can help me about the routes?
Thanks for advance.
This is my Controller
class DetailController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index($section=0,$title = 0)
{
$detail = DB::table('t_artikel')
->join('t_section', 't_section.id_section', '=', 't_artikel.id_section')
//->join('t_kolom', 't_kolom.id', '=', 't_artikel.penalar')
->where('publish', '=', 'Y')
->where('parent_id', '=', 0)
->where('tgl_pub', '<=', date('Y-m-d H:i:s'))
->where('t_artikel.urltitle', '=', $title)
->select('t_artikel.*', 't_section.*', 't_artikel.urltitle as urlartikel')
->first();
$kategori = DB::table('t_section')
->where('id_supsection', '=', 0)
->where('status', '=', 1)
->orderBy('position', 'asc')
->whereNotIn('id_section', [34])
->select('t_section.*')
->get();
$page = 'Detail';
return view ('detail.detail',
['detail' => $detail,
'kategori' => $kategori,
'page' => $page
]);
}
As per the documentation,
If your controller route requires parameters, you may pass them as the second argument to the action method
So, you need to pass the parameters like this:
return redirect()->action(
'DetailController#index', ['category' => <enter_value>, 'title' => <enter_value>]
);
I would suggest you to use 2 routes and 2 controllers. One handles the details of the topic and the other one handles the redirect of the "old" url.
For example: the user will visit "/topic/title" that is handled by a controller that will recognize topic and category, then will use the
public function handleTopic($title){
// the code here will recognize topic and category
// and will just provide the stuff to show the right page
// in the end will redirect to that controller
return redirect()->action('DetailController#index', ['category' => $category, 'title' => $title]);
}
public function index($stuffYouNeed){
// retrieve the rest of data you need to show the page
// you already know the title and category (cause of previous controller)
// in the end return the view with data
return view ('detail.detail',['data' => $data]);
}
In your routes you'll have to add one route and edit the existing one like:
Route::get('topic/{title}', 'DetailController#handleTopic')->name('handleTopic');
Route::get('topic/{category}/{title}', 'DetailController#index')->name('showTopic');
It's not tested cause atm i don't have a laravel env set up in local. But i think it should work. Let me know
Edit: I forgot to explain why you see the error
Action App\Http\Controllers/topic/{category}/{title} not defined.
You are using the redirect incorrectly
Route::get('/topic/{title}',function(){
return redirect()->action('/topic/{category}/{title}','DetailController#index');
});
You can only provide an action controller, not a route. And the destination route have to exist. So the right use is:
return redirect()->action('Controller#action');
Anyway, you should split logic from routes. You have controllers for that... even for really short blocks of code. It will keep everything in order and clear. In your route file you should have ONLY routes.
Solved, I found my way after modified. and this is the code:
Route::get('/topik/{title}',function($title){
$article = DB::table('t_artikel')
->join('t_section', 't_section.id_section', '=', 't_artikel.id_section')
->where('publish', '=', 'Y')
->where('parent_id', '=', 0)
->where('tgl_pub', '<=', date('Y-m-d H:i:s'))
->where('t_artikel.urltitle', '=', $title)
->select('t_artikel.*', 't_section.urltitle as urltitlesec', 't_artikel.urltitle as urlartikel')
->first();
$kategori = $article->urltitlesec;
return redirect("/topik/{$kategori}/{$title}");
});
If you want to redirect to a URI, then don’t use redirect()->action(); use redirect()->to() instead:
Route::get('/topic/{title}', function ($title) {
$category = ''; // Get category some how
return redirect()->to("/topic/{$category}/{$title}");
});

Laravel previous and next records

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;
}

Categories