How to automatically update data in laravel view? - laravel

I have a view which will show the list important tasks from database. I have a function in controller which returns the collection of data to the view.
My controller function is
public function list()
{
$tasks= Task::where('category','1')->get();
//category is 1 when the task is important
return view('important', compact('tasks'));
}
My view is like
<ul>
#foreach ($tasks as $task)
<li> {{$task->body}}</li>
#endforeach
</ul>
What I want to essentially do is to call the list function whenever a new important task is added into the database. How can I do that?

in your web.php
Route::get('/tasks','TasksController#list')->name('get_tasks');
inside your controller :
use Illuminate\Http\Request;
public function list(Request $request)
{
$tasks= Task::where('category','1')->get();
if($request->ajax()){
return response()->json(array('tasks'=>$tasks));
}
return view('important', compact('tasks'));
}
inside your blade view :
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$(document).ready(function(){
setInterval(function(){
$.ajax({
url:'/tasks',
type:'GET',
dataType:'json',
success:function(response){
if(response.tasks.length>0){
var tasks ='';
for(var i=0;i<response.tasks.length;i++){
tasks=tasks+'<li>'+response.tasks[i]['body']+'</li>';
}
$('#tasklist').empty();
$('#tasklist').append(tasks);
}
},error:function(err){
}
})
}, 5000);
});
</script>
<ul id="tasklist">
#foreach ($tasks as $task)
<li> {{$task->body}}</li>
#endforeach
</ul>

To achieve this kind of setup you may use Pusher or any other similar provider, once you signup on pusher you can send 200k notifications per day for free, you can check the limits after login to pusher. Before we proceed please install pusher's official php package
composer require pusher/pusher-php-server
From your pusher dashboard obtain app_id, key, secret and cluster now in your controller/model where you are inserting the data in database add the following code
//You will get cluster name from pusher.com replace it below
$options = ['cluster' => 'mt1', 'encrypted' => true];
//Replace your key, app_id and secret in the following lines
$pusher = new Pusher(
'key',
'secret',
'app_id',
$options
);
//this could be a single line of message or a json encoded array, in your case you want to pass some data to display in table I assume you have an array
$message= json_encode(['name' => 'John doe', 'age' => 42, 'etc' => 'etc']);
//Send a message to users channel with an event name of users-list. Please mind this channel name and event name could be anything but it should match that with your view
$pusher->trigger('users', 'users-list', $message);
Now in your view before the </body> tag paste the following code
<!-- Incldue Pusher Js -->
<script src="https://js.pusher.com/4.2/pusher.min.js"></script>
<script>
//Remember to replace key and cluster with the credentials that you have got from pusher.
var pusher = new Pusher('key', {
cluster: 'mt1',
encrypted: true
});
//In case you have decided to use a different channel and event name in your controller then change it here to match with the one that you have used
var channel = pusher.subscribe('users');
channel.bind('users-list', function(message) {
//if you will console.log(message) at this point you will see the data
//that was sent from your controller is available here please consume as you may like
alert(message);
});
</script>

Related

On Form Submit Inertia is not redirecting to particular page

I'm using Inertia (Vue3 & Laravel 9). I have a form in "Register.vue" Component.
On submitting that form I'm calling a controller to process the request. Once the controller process the request I want the controller to redirect to an other component i.e. regComplete (where I want to show data which I received as a prop from controller).
Now the thing is the Controller is redirecting me to the desired page (Although I'm unable to get the prop data but I'm getting the other data successfully) but the URL is still same as it was on form submit.
"Register.vue"
<template>
<form #submit.prevent="submit">Here are the form fields i.e. email & password </form>
</template>
<script setup>
let form = reactive({
email: "",
password: "",
});
let submit = () =>{
Inertia.post('users',form);
}
</script>
Route.php
//Route on submitting the form
Route::post('users',[userController::class,'register']);
Controller = userController
public function register(Request $request){
// $email = $request->input('email');
// $password = $request->input('password');
// return "User with".$email." and with password ".$password." is created";
return Inertia::render('regComplete');}
Now my question is How to redirect to the settings page with desired props ?
for example return Inertia::render('regComplete',['msg'=>'User registerd']);
After successfully creating a new user, you return the Component rather than returning or redirecting to another route where the component (regComplete) is being loaded.
What you can do is add more routes that deal with the (regComplete) component.
On routes.php, add the new route /registration/complete
Route::post('users',[UserController::class,'register']);
Route::get('/registration/complete',[UserController::class,'regComplete']);
On UserController.php, add new function regComplete() and update register
// add the this function
public function regComplete () {
return Inertia::render('regComplete',[
'users' => User::all() // make sure you have defineProps()
]);
}
// update your register function
public function register(Request $request){
// creation of user process here
if(successfully created) {
return redirect('/registration/complete');
}
return back()->with('error','You error message here');
}
It is possible that it will not solve your problem. However, hopefully it will assist you in determining where the problem may be occurring.

How do I pass a variable from blade file to controller in laravel?

I have ProjectController that fetches data from the database and passes it to a blade file. One of the data items is the project_id. I want to pass the project _id from the blade file to another controller BidController.
ProjectController.php
public function show($id)
{
$project = Project::find($id);
return view('project.show',['project'=>$project]);
}
show.blade.php
div class="card-header">PROJECT <p>{!! $project->id !!}</p></div>
BidController.php
public function store(Request $request)
{
$bid = new Bid;
$bid->project_id = $project_id;
dd($project_id);
}
The dd(); does not output the project_id. I need help in passing the project_id from the blade file to the BidController method.
You can't directly set a model's id like you're doing in the line $bid->id = $project_id;. Are you trying to set up a relationship? That should be more like $bid->project_id = $request->project_id;.
Blade templates can't really pass things back to controllers, once they're in the browser your app is sort-of finished running. You need to create an HTML link/request on the page (like a form post request) that'll request the next thing from your app when the user clicks it.
If you want to create a button that creates a new bid for an existing project, you could do something like set up a form with a hidden field of 'project_id' that posts back to '/bids' which goes to the route 'bids.store'. You'll find 'project_id' under $request->project-id'.
You can send an AJAX request from Javascript:
View
<script type="text/javascript">
var project_id= {!! json_encode($project->id) !!}
$.ajax({
type: 'POST',
url: url, //Your bidController route
data: {project_id: project_id},
error: function (jqXHR, textStatus, errorThrown) {
console.log(errorThrown)
},
success: function()
{
console.log('successful')
}
});
</script>
This will sent the data to the controller asynchronously so the user experience doesn't get affected.
One extra point: In your Bid controller, as project_id is coming from the request, you'll have to use:
$bid->id = $request->project_id;
I hope it helps!
PS: I'm using JQuery for this, so you'll have to include it if you don't already have.
I think this will solve your problem :
ProjectController.php
public function show($id)
{
$project = Project::findOrFail($id);
return view('project.show',compact('project');
}
web.php
Route::post('/bids/store/{id}' , 'BidController#store')->name('bids.store');
show.blade.php
div class="card-header">PROJECT <p>{{$project->id}}</p></div>
<form action="{{route('bids.store', $project->id)}}" method="post">
BidController.php
public function store(Request $request, $id)
{
$bid = new Bid;
$bid->id = $id;
$bid->save();
dd($id);
}

Vue.js with Laravel Permission

I am in the process of integrating Laravel Permission API with Vue.JS frontend. I am using https://github.com/spatie/laravel-permission library for Laravel Permission. I am not understanding how can I check permission in the Vue JS front End (In Laravel blade I am using #Can to check the permission).
I will do a ajax call to check for permissions instead , something like this, but of cours eyou need to modify it to cater your needs.
Routes:
Route::get('/permission/{permissionName}', 'PermissionController#check');
Controller:
function check($permissionName) {
if (! Auth::user()->hasPermissionTo($permissionName)) {
abort(403);
}
return response('', 204);
}
Vue: (if you wanna do this synchronously), this is a simple example (Vue global mixin), you can turn this into Vue directive or component.
Vue.mixin("can", (permissionName) => {
let hasAccess;
axios.get(`/permission/${permissionName}`)
.then(()=> {
hasAccess = true;
}
.catch(()=> {
hasAccess = false;
};
return hasAccess;
});
And then everytime you wanna check permission, you can just do
<el-input v-if="can('write-stuff')"> </el-input>
I'm literally working on this exact same thing. I'm thinking of adding a custom Vue directive that would check against the Laravel.permissions array.
It might even be as simple as
Vue.directive('can', function (el, binding) {
return Laravel.permissions.indexOf(binding) !== -1;
})
I haven't tested this code. Just brainstorming here.
<button v-can="editStuff">You can edit this thing</button>
I can hold permissions this way:
window.Laravel = <?php echo json_encode([
'csrfToken' => csrf_token(),
'userId' => Auth::user()->id,
'permissions' => Auth::user()->permissions()->pluck('name')->toArray()
]); ?>
Just stumbled upon this problem and I would like to share what I found and implemented.
Add an accessor on the User model the spatie/laravel-permission is using
public function getAllPermissionsAttribute() {
$permissions = [];
foreach (Permission::all() as $permission) {
if (Auth::user()->can($permission->name)) {
$permissions[] = $permission->name;
}
}
return $permissions;
}
On your global page or layout page pass the permission from the accessor to the javascript.
<script type="text/javascript">
#auth
window.Permissions = {!! json_encode(Auth::user()->allPermissions, true) !!};
#else
window.Permissions = [];
#endauth
</script>
Create a global directive on resources/js/app.js
Vue.directive('can', function (el, binding, vnode) {
if(Permissions.indexOf(binding.value) !== -1){
return vnode.elm.hidden = false;
}else{
return vnode.elm.hidden = true;
}
})
Here you are checking if the permission you supplied on the directive is on the permission array from laravel.
If found then it will hide the element else show, this function is like a v-if.
Use it like this on your component - "add_items" is your permission
<button type="button" v-can="'add_items'"></button>
This solution is from this but instead of a mixin, I use a directive.
Got the idea of directive from #Ismoil Shifoev comment above.
You can use this format in Vuejs for Laravel Permission:
<div v-if="can('edit post')">
<!-- Edit post form -->
</div>
<div v-if="is('super-admin')">
<!-- Show admin tools -->
</div>
add function to User Model to get all user permissions&roles like this:
class User extends Authenticatable
{
// ...
public function jsPermissions()
{
return json_encode([
'roles' => $this->getRoleNames(),
'permissions' => $this->getAllPermissions()->pluck('name'),
]);
}
}
pass this data to JavaScript in HTML header:
<script type="text/javascript">
window.Laravel = {
csrfToken: "{{ csrf_token() }}",
jsPermissions: {!! auth()->check()?auth()->user()->jsPermissions():null !!}
}
</script>
in app.js file add global Vuejs can function to check user permissions and is function to check user roles:
Vue.prototype.can = function(value){
return window.Laravel.jsPermissions.permissions.includes(value);
}
Vue.prototype.is = function(value){
return window.Laravel.jsPermissions.roles.includes(value);
}
https://github.com/ahmedsaoud31/laravel-permission-to-vuejs
I would go with Ralph solution. But I find myself better using. This function to fetch the Permissions.
public function getAllPermissionsAttribute() {
return Auth::user()->getAllPermissions()->pluck('name');
}
Just a bit cleaner, and since I tend to use Roles instead of particular permissions for each User, this solution should work as well.

Load cities from state laravel

I am working with laravel, right now, i am making an user registry Form, i am relating the state and its cities, so, i need to change a select field values according the state that user chooses.
I have something in the form:
{{ Form::select('city', $city, array('id', 'city')}}
If i use the {{Form::select}} fields in conventional way it charges all the cities from one state, so, when the user select a state, it must change the list of cities in the select field.
I searched but i didn't find any. How can i do that?
thanks.
You can use ajax with jQuery.
In your view set an event when the state change, like this:
$(document).on('change', '#state_id', function (e) {
// empty the select with previous cities if we have.
$('#cities').empty();
$.ajax({
type: "POST",
dataType: "json",
// actions is a controller
// cities is a method of actions controller
url : "{{ URL::to('actions/cities') }}",
//here we set the data for the post based in our form
data : $('#MyFormID').serialize(),
success:function(data){
if(data.error === 0 ){ // all was ok
for (var i = 0; i < data.cities.length; i++) {
$('#cities').append("<option value='"+data.cities[i].id+"'>"+data.cities[i].city_name+"</option>")
}
}else{
alert(data);
}
},
timeout:10000
});
});
actions/cities controller
//remember, this is a post method
public function postCities(){
// validate
$validator = Validator::make(Input::all(),
array(
'state_id' => 'required|integer'
));
if ($validator->fails()) {
return Response::json(array('error' => 1, 'message' => 'State is required'));
}
//City is your model, I assumes that you pkey is ID and the city name is city_name and your fkey is state_id
$cities = City::where('state_id', '=', Input::get('state_id'))->get();
return Response::json(array('error' => 0, 'cities' => $cities));
}
public function getCities($province_id)
{
$cities = Cities::where('province_id', '=', $province_id)->get(['id', 'name']);
return Response::json(array('error' => 0, 'cities' => $cities));
}
You might want to check a sample vue component that ships with my package Laravel Cities that performs exactly what you are trying to build.
This is a simple package that allows you seed localy all the cities of any country on the world (provided by geonames.org) and perform any query with the provided Eloquent model. It exposes an HTTP API and a vue component that allows you to select any city through a series of steps.
You can insert it in your forms like any other input field:
<form action="post-url" method="POST">
<geo-select></geo-select>
<!-- Add more form fields here... -->
<input type="submit">
</form>
With the provided Eloquent model You can perform queries like this:
// Get the States of USA in aplhabetic order
Geo::getCountry('US')
->children()
->orderBy('name')
->get();
Sorry, no demo yet, but you can check some sceenshots on the github page...

Laravel 4 route

I've got a problem with using URL::route. There is a public function in my controller called AuthController called delete_character, here's how it looks:
public function delete_character()
{
$player->delete();
return View::make('index')->with('danger', 'You have successfully deleted your character!');
}
Also, I've created a named route:
Route::post('delete_character', array(
'as' => 'delete_character',
'uses' => 'AuthController#delete_character'
));
All I want to do is to execute the $player->delete. I don't want it to be a site, just when I click a button it's gonna delete the player.
I've also done the button:
<td><a class="btn btn-mini btn-danger" href="{{ URL::route('delete_character') }}"><i class="icon-trash icon-white"></i> Delete</a></td>
But I constantly get MethodNotAllowedHttpException. Any hints?
In my example, I am using GET request method (POST is used when form is being submited, for instance) to capture this action.
I pass ID of client I wish to delete in the reqeust URL, which results into URL in this form: http://localhost:8888/k/public/admin/client/delete/1 (You should post it from form, according to your example/request).
Not posting whole solution for you to force you to learn! My answer is not 100% identical to your situation, but will help, for sure.
// routes.php
Route::group(['prefix' => 'admin'], function(){
Route::get('client/delete/{id}', 'Admin\\ClientController#delete');
});
// ClientController.php
<?php
namespace Admin;
use Client;
class ClientController extends BaseController
{
...
public function delete($clientId)
{
$client = Client::findOrFail($clientId);
// $client->delete();
// return Redirect::back();
}
...
}
// view file, here you generate link to 'delete' action
delete

Resources