Two actions on a function in Laravel, possible? - laravel

I have 2 users roles in my application, admin and former.
The admin can create several formers...
If, I connect me with the ID 1 ? I retrieve the information of the former.
So, my function index() allows to retrieve id of the user
public function index()
{
if($has_role = auth()->user()->hasRole('admin')){
$formers = Former::first()->paginate(5);
return view('admin.formers.index', compact('formers'));
} else{
$formers = Former::where('email', Auth::user()->email)->paginate(5);
return view('admin.formers.index', compact('formers'));
}
}
Well, for the user admin, I would like to create a search bar...
I had created before a function index() and which worked
public function index(Request $req)
{
if ($req->search == "") {
$formers = Former::paginate(5);
return view('admin.formers.index', compact('formers'));
} else {
$validated = $req->validate([
'search' => 'alpha',
]);
$formers = Former::where('nom', 'LIKE', '%' . $validated['search'] . '%')->paginate(5);
$formers->appends($req->only('search'));
return view('admin.formers.index', compact('formers'));
}
}
Now, I would like to adapte my 2 actions in a function, is it possible according you?
Do you think that I can get the user_id and make a search bar in the same function?
Thank you

What I would do is the following:
Add one action which serves both roles with data.
Display the search only to admins, but ignore this fact on the server-side as it doesn't matter from a security perspective whether non-admins can search or not. They are limited to their result anyway.
Basically, this is achievable in the following way:
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
public function index(Request $request)
{
$user = $request->user();
$formers = Former::query()
->when($user->hasRole('admin') !== true, function (Builder $query) use ($user) {
$query->where('email', $user->email);
})
->when($request->has('s'), function (Builder $query) use ($request) {
$query->where('nom', 'like', '%'.$request->input('s').'%');
})
->paginate(5);
return view('admin.formers.index', compact('formers'))
->with('display_search', $user->hasRole('admin'));
}
You can then in your view simply use the $display_search variable to decide whether or not you want to display the search:
#if($display_search)
<form method="post" action="...">
<input type="text" name="s" placeholder="Type to search...">
</form>
#endif

I would create a policy with a search method:
public function search($user)
{
if ($user->isAdmin()) {
return true;
}
}
now you may just edit your blade
#can('search')
<form method="post" action="/search">
<input type="text" name="search" placeholder="Recherche...">
</form>
#endcan
If you want to give search access to other users as well, you only have to modify the policy.

Related

How to re-render a table with Livewire after an event emitted from another component

I'm new to Livewire and I am stuck with this problem.
I've created a table.blade.php component with livewire, and another searchbar.blade.php component, which is not a child of the table component. Every time a search for a term, the table should rerender with the seached parameter.
All is right, and the search query gives the correct result (clients with pagination), but somehow the table does not rerender the html.
Any ideas what I'm doing wrong? Thanks
<div>
<input type="text" wire:model="query" autofocus>
</div>
class SearchBar extends Component
{
public $query;
public function updatedQuery()
{
$this->emit('searchForQuotes', $this->query);
}
public function render()
{
return view('livewire.clients.searchbar');
}
}
<div>
<table>
<tbody>
#foreach($clients as $client)
#livewire('clients.row', ['client' => $client], key($client->id))
#endforeach
</tbody>
</table>
</div>
class Table extends Component
{
use WithPagination;
public $query;
protected $listeners = [
'searchForQuotes' => 'render'
];
public function mount()
{
$this->resetData();
}
public function resetData()
{
$this->query = null;
}
public function render($query = null)
{
$q = Client::query();
if ($query) {
$q->whereRaw("CONCAT(surname, ' ', name) LIKE '%" . $query . "%'");
}
$clients = $q->latest()->paginate(20);
return view('livewire.clients.inc.table', [
'clients' => $clients, 'query' => $query
]);
}
}
You can make your child components reactive by making your key() unique every render of the parent:
#livewire('clients.row', ['client' => $client], key($client->id . "-" . Str::random()))
By adding a Str::random(), the key is different every time the parent updates, which forces the children to update as well. This also works with now(), but only as long as you have a prefix. It is important to note that this causes more requests and thus can make your table slower.
Try something like this :
class Table extends Component
{
use WithPagination;
public $query;
protected $listeners = ['searchForQuotes'];
public function mount()
{
$this->resetData();
}
public function searchForQuotes($query)
{
$this->query = $query;
// Do something
$this->render();
}
public function resetData()
{
$this->query = null;
}
public function render()
{
$q = Client::query();
if ($this->query) {
$q->whereRaw("CONCAT(surname, ' ', name) LIKE '%" . $query . "%'");
}
$clients = $q->latest()->paginate(20);
return view('livewire.clients.inc.table', [
'clients' => $clients, 'query' => $this->query
]);
}
}
I think I found the problem, but don't know how to solve it.
I the table.blade.php component I've got this code.
#foreach($clients as $client)
#livewire('clients.row', ['client' => $client], key($client->id))
#endforeach
It seems like the nested component are not rendering after firing the event.

I am trying to update data without page reload using live wire

Hi I am making a Private chat Application In Livewire but the Thing is When i Insert a message don't show Unless I reload the Page please help me how can i resolved that ? thank u.
I am using Livewire Full-Page Components here is the Code
Note :- I am using this render function inside viewMessage function but data is not updated without page relaod.
this will load the all the conservation message
$this->render();
app\Http\Livewire\Messaging.php
class Messaging extends Component
{
public $body;
public $searchTerm;
public $selectedUser;
public function mount(){
$this->selectedUser =User::where('id','!=',Auth::user()->id)
->first();
}
public function render()
{
$searchTerm = '%'.$this->searchTerm.'%';
if($searchTerm){
$user= User::where('id', '!=', Auth::user()->id)
->where('user_type',1)
->where('email', 'like', $searchTerm)
->with('messages')
->get();
}
$conservation = Message::query()
->where('sender_id', Auth::user()->id)
->where('receiver_id', $this->selectedUser->id)
->orWhere('receiver_id', Auth::user()->id)
->where('sender_id', $this->selectedUser->id)
->with('sender')
->with('receiver')
->get();
return view('livewire.messaging',[
'users' => $user,
'conservation' =>$conservation
]);
}
public function viewMessages($userId){
$this->selectedUser = User::findorFail($userId);
$senderExist = Message::where('sender_id',$this->selectedUser->id)->exists();
if($senderExist){
$message = Message::where('sender_id',$this->selectedUser->id)->orderBy('receiver_id','Desc')->get();
foreach($message as $value){
$value->notification = "0";
$value->save();
}
}
}
public function sendMessages(){
Message::create([
'receiver_id' => $this->selectedUser->id,
'sender_id' => Auth::user()->id,
'body' => $this->body,
]);
$this->reset('body');
$this->viewMessages($this->selectedUser->id);
$this->render(); //this will load the all the conservation message
}
}
resources\views\livewire\messaging.blade.php
<form wire:submit.prevent="sendMessages" action="#" >
<div class="position-absolute bottom-0 col-md-12 pe-3">
<div class="input-group comment-box p-3">
<input wire:model.defer="body" type="text" class="form-control"
placeholder="Type Message ..." aria-label="Aa" aria-describedby="button-addon2" required>
<button class="btn btn-outline-secondary py-0 px-3" type="submit" id="button-addon2"><i class='bx bx-send fs-4'></i></button>
</div>
</div>
</form>
I personally would not suggest loading in data on every single render. Livewire is able to hydrate previously fetched models and collections, which takes away the strain of reloading it from your database each render. I personally would write your component as such:
use Illuminate\Support\Collection;
class Messaging extends Component
{
public $body = '';
public $searchTerm = '';
public $selectedUser;
// Assuming you're using PHP 7.4 or above, else remove typehint
public Collection $users;
public Collection $conversation;
// Ensure we validate the data passed
protected $rules = [
'body' => ['required', 'string'],
];
public function mount()
{
$this->selectedUser = User::where('id','!=',Auth::user()->id)->first();
$this->getUsers();
$this->getConversation();
}
public function render()
{
return view('livewire.messaging');
}
public function updated($field)
{
// Only update the users if the search term has changed
if ($field === 'searchTerm') {
$this->getUsers():
}
}
public function viewMessages($userId)
{
$this->selectedUser = User::findorFail($userId);
$senderExist = Message::where('sender_id',$this->selectedUser->id)->exists();
if($senderExist) {
$messages = Message::where('sender_id',$this->selectedUser->id)->orderBy('receiver_id','desc')->get();
foreach($messages as $message){
$message->notification = "0";
$message->save();
}
// Are you saving the messages one by one to trigger model events? If not, use below query to instantly update all of them at once in the database:
// Message::where('sender_id',$this->selectedUser->id)->orderBy('receiver_id','desc')->update([
// 'notification' => 0,
// ]);
}
}
public function sendMessages()
{
// Validate the data as per given rules
$this->validate();
Message::create([
'receiver_id' => $this->selectedUser->id,
'sender_id' => Auth::user()->id,
'body' => $this->body,
]);
$this->reset('body');
$this->viewMessages($this->selectedUser->id);
// Refresh conversation
$this->getConversation();
}
public function getConversation()
{
$this->converstaion = Message::query()
->where('sender_id', Auth::user()->id)
->where('receiver_id', $this->selectedUser->id)
->orWhere('receiver_id', Auth::user()->id)
->where('sender_id', $this->selectedUser->id)
->with('sender')
->with('receiver')
->get();
}
public function getUsers()
{
$query = User::where('id', '!=', Auth::user()->id)
->where('user_type', 1)
->with('messages');
if (! empty($this->searchTerm)) {
$searchTerm = '%'.$this->searchTerm.'%';
$query->where('email', 'like', $searchTerm);
}
$this->users = $query->get();
}
}
The render method gets called by Livewire itself each time something happens, such as a value change of a variable, or after a function was called. You should not call it yourself.
P.S. For Livewire to fully keep track of what is what, I suggest adding wire:key to data rows. For example, on a div that is holding the data to your conversation messages, add wire:key="message-{{$message->id}}". This way, if a data change happens, Livewire knows exactly what date should be changed. Read more about it here
You should use the livewire emite event
https://laravel-livewire.com/docs/2.x/events

Prevent textbox sending request on page load in Laravel Livewire

I want to prevent a request sent by Livewire when my page is loaded. I am working with a search operation using Livewire.
View
<div class="col-md-4 my-3">
<input wire:model.debounce.500ms="search"
type="text"
placeholder="Search Permissions...."
class="form-control">
</div>
Component
class ViewPermisson extends Component
{
use WithPagination;
public $search;
protected $queryString = ['search'];
protected $listeners = ['refreshPermissions' => '$refresh'];
public function updatingSearch()
{
$this->resetPage();
}
public function render()
{
$permissions = Permission::where('guard_name', 'admin')
->where('name', 'like', '%' . $this->search . '%')
->simplePaginate(10);
return view('livewire.admin.access.view-permisson',
['permissions' => $permissions]);
}
}
The issue is that when I load my page, Livewire input sends the request with an empty query string. I want to prevent this request on page load.
127.0.0.1:8000/admin/permissions?
Here's a URL with an empty query string added when I am requesting a page.
The problem is with while you rendering the page initially it return with the permission query. You should query the permission while the search field has been updated.
public $permissions;
public function updatedSearch()
{
if($this->search == '') return;
$this->permissions = Permission::where('guard_name','admin')->where('name', 'like', '%'.$this->search.'%')->simplePaginate(10);
}
public function render()
{
return view('livewire.admin.access.view-permisson');
}
Or you simply bypass the query by checking that the search variable is empty or not. like:
public function mount(){
$this->search = '';
}
public function render()
{
if($this->search != ''){
$permissions = Permission::where('guard_name','admin')->where('name', 'like', '%'.$this->search.'%')->simplePaginate(10);
return view('livewire.admin.access.view-permisson',['permissions'=>$permissions]);
}else{
return view('livewire.admin.access.view-permisson',['permissions'=>null]);
}
}

Why does the old() method not work in Laravel Blade?

My environment is Laravel 6.0 with PHP 7.3. I want to show the old search value in the text field. However, the old() method is not working. After searching, the old value of the search disappeared. Why isn't the old value displayed? I researched that in most cases, you can use redirect()->withInput() but I don't want to use redirect(). I would prefer to use the view(). method
Controller
class ClientController extends Controller
{
public function index()
{
$clients = Client::orderBy('id', 'asc')->paginate(Client::PAGINATE_NUMBER);
return view('auth.client.index', compact('clients'));
}
public function search()
{
$clientID = $request->input('clientID');
$status = $request->input('status');
$nameKana = $request->input('nameKana');
$registerStartDate = $request->input('registerStartDate');
$registerEndDate = $request->input('registerEndDate');
$query = Client::query();
if (isset($clientID)) {
$query->where('id', $clientID);
}
if ($status != "default") {
$query->where('status', (int) $status);
}
if (isset($nameKana)) {
$query->where('nameKana', 'LIKE', '%'.$nameKana.'%');
}
if (isset($registerStartDate)) {
$query->whereDate('registerDate', '>=', $registerStartDate);
}
if (isset($registerEndDate)) {
$query->whereDate('registerDate', '<=', $registerEndDate);
}
$clients = $query->paginate(Client::PAGINATE_NUMBER);
return view('auth.client.index', compact('clients'));
}
}
Routes
Route::get('/', 'ClientController#index')->name('client.index');
Route::get('/search', 'ClientController#search')->name('client.search');
You just need to pass the variables back to the view:
In Controller:
public function search(Request $request){
$clientID = $request->input('clientID');
$status = $request->input('status');
$nameKana = $request->input('nameKana');
$registerStartDate = $request->input('registerStartDate');
$registerEndDate = $request->input('registerEndDate');
...
return view('auth.client.index', compact('clients', 'clientID', 'status', 'nameKana', 'registerStartDate', 'registerEndDate'));
}
Then, in your index, just do an isset() check on the variables:
In index.blade.php:
<input name="clientID" value="{{ isset($clientID) ? $clientID : '' }}"/>
<input name="status" value="{{ isset($status) ? $status : '' }}"/>
<input name="nameKana" value="{{ isset($nameKana) ? $nameKana : '' }}"/>
...
Since you're returning the same view in both functions, but only passing the variables on one of them, you need to use isset() to ensure the variables exist before trying to use them as the value() attribute on your inputs.
Also, make sure you have Request $request in your method, public function search(Request $request){ ... } (see above) so that $request->input() is accessible.
Change the way you load your view and pass in the array as argument.
// Example:
// Create a newarray with new and old data
$dataSet = array (
'clients' => $query->paginate(Client::PAGINATE_NUMBER),
// OLD DATA
'clientID' => $clientID,
'status' => $status,
'nameKana' => $nameKana,
'registerStartDate' => $registerStartDate,
'registerEndDate' => $registerEndDate
);
// sent dataset
return view('auth.client.index', $dataSet);
Then you can access them in your view as variables $registerStartDate but better to check if it exists first using the isset() method.
example <input type='text' value='#if(isset($registerStartDate)) {{registerStartDate}} #endif />

Laravel Resource controller for filtering

I have a resource Controller for an API that I'm developing for displaying records that can be filtered by Type and Customer and I have the following methods that can get this data:
index
show -> requires an parameter (id)
Can I therefore put a request inside the index method for filtering all of the entries back or is it bad practise for doing this? My code looks like the following:
public function index()
{
$entries = \App\Ent::where(function($en) {
$request = app()->make('request');
if($request->has('type')) {
$en->where('type', '=', $request->get('type'));
}
if($request->has('customer')) {
$en->where('customer', '=', $request->get('customer'));
}
})->get();
dd($entries);
}
Filtering in Laravel is very simple and you don't need to do this. In your Ent model, define the following:
public function scopeFilter($query, $filters)
{
if( isset($filters['type']) ){
$query->where('type', '=', $filters['type']);
}
// keep going for all of your filters
}
And on your Controller index method you can:
public function index()
{
$entries = \App\Ent::filter(['type', 'customer'])->get();
dd($entries);
}
EDIT
To help you make more sense of this, let's filter Ent on the type column in the database.
Route:
Route::get('/ent', 'EntController#index');
Ent Model:
class Ent extends Model
{
public function scopeFilter($query, $filters)
{
if( isset($filters['type']) ){
$query->where('type', '=', $filters['type']);
}
}
}
Ent Controller:
class EntController extends Controller {
index()
{
$entries = \App\Ent::filter(['type'])->get();
return view('ent.index', compact('entries'));
}
}
Let's say for the sake of this example we are just going to put a form on the same blade template we are outputting our list:
#foreach( $entries as $entry )
<p>{{ $entry->type }}</p>
#endforeach
<form method="GET" action="/ent">
{{ csrf_field() }}
<input type="text" name="type" />
<input type="submit" value="Filter" />
</form>
So now, if I were to go into that form and type 'Foo Bar' and hit submit, you would get what would look something like this in SQL
SELECT * FROM Ent WHERE type='foo bar'
Or in other words, all Ent with the type column = 'foo bar'.
When I give a user the ability to type raw text in to filter, I like to give them the benefit of the doubt and use LIKE instead of =. So for example we would just change our scopeFilter method:
if( isset($filters['type']) ){
$query->where('type', 'LIKE', '%' . $filters['type'] . '%');
}
Another thing to note here, the filters[name] is the name of the <input> field, NOT the name of the column in your database. You target the column in the $query extension, NOT in the $filters array. See below example:
if( isset($filters["ent_type"] ){
$query->where('type', '=', $filters["ent_type"]);
}
On my form that would be
<input name="ent_type" type="text" />

Resources