How to Add Roles and Permission to Laravel Fortify + Inertia + vue? - laravel

my project use laravel fortify, inertia with vue. i have to add role based permissions (just like spatie permissions package). i'm still a beginner to fortify and inertia. but i have experience in spatie package. i'm stuck with how add roles and permission to fortify. currently i'm planning to create table structure like spatie package have(roles, permssions, roles_has_permissions ,etc). is there per-build package or better way to implement roles and permissions ? and use '#can' in vue files? thank you.
edit
hello guys,this is i'm currently did(im using this now ). it is working but still need some improvements, (any better solution i really appreciate it)
1)installed and configured as usual spatie/laravel-permission
2)added predefined permissions and roles to tables using seeder
created function in user model to get permission array list
// user model function
public function getPermissionArray()
{
return $this->getAllPermissions()->mapWithKeys(function($pr){
return [$pr['name'] => true];
});
}
and added that function to inertia middleware
//App\Http\Middleware\HandleInertiaRequests
public function share(Request $request)
{
return array_merge(parent::share($request), [
'auth'=>['user' => $request->user() ? $request->user()->only('id', 'name', 'email') : null,
'can' =>$request->user() ? $request->user()->getPermissionArray() : []
],
]);
}
now $page.props.auth.can can access globally
added permission check in vue file
<div class="row">
<div class="col-sm-12 col-md-6" v-if="$page.props.auth.can['user_create']">
<inertia-link
class="btn btn-primary"
:href="$route('admin.user.create')"
>Create New
</inertia-link>
</div>
</div>

I resolved issue like below,
first I'm sending permission array to UI.
in user model
<?php
// user model function
public function getPermissionArray()
{
return $this->getAllPermissions()->mapWithKeys(function($pr){
return [$pr['name'] => true];
});
}
in inertia share middleware
<?php
//App\Http\Middleware\HandleInertiaRequests
public function share(Request $request)
{
return array_merge(parent::share($request), [
'auth'=>['user' => $request->user() ? $request->user()->only('id', 'name', 'email') : null,
'can' =>$request->user() ? $request->user()->getPermissionArray() : []
],
]);
}
in app js file, I have added global function to check one or many permssion have user
import Vue from 'vue'
Vue.mixin({
methods: {
hasAnyPermission: function (permissions) {
var allPermissions = this.$page.props.auth.can;
var hasPermission = false;
permissions.forEach(function(item){
if(allPermissions[item]) hasPermission = true;
});
return hasPermission;
},
},
})
in vue components :
<script>
export default {
data() {
return {};
},
mounted: function () {},
methods: {},
};
</script>
<template>
<div>
<li v-if="hasAnyPermission(['testiml_view', 'testiml_edit', 'testiml_create'])">
<inertia-link
:href="$route('admin.testimonial.index')"
class="side-nav-link-a-ref"
>
<i class="fas fa-feather"></i>
<span>Testimonial</span>
</inertia-link>
</li>
</div>
</template>

in the share method on the Inertia Middleware: HandleInertiaRequest.php, I passed the permissions and the roles array to Vue using:
$permissions = $user->getAllPermissions()->pluck('name');
$roles = $user->roles()->pluck('name');
return array_merge(parent::share($request), [
'auth.user' => fn() => $request->user() ?
$request->user()->only('id', 'name', 'email', 'roles')
: null,
'auth.user.permissions' => $permissions,
'auth.user.roles' => $roles
]);

Related

How to redirect from inertia post call to laravel back to inertia with new data

i have laravel+vue+inertia situation;
in my VUE component i'm having Form that sends post request to Laravel backend:
<form #submit.prevent="submit">
<button type="submit">GO</button>
</form>
also:
function submit() {
Inertia.post("/api/myMethodInController")
}
in Laravel controller i fetch some data which i want to send back to page where request came from. My controller function ends with:
return Inertia::render('ComponentWhereRequestIsSentFrom', ['data'=>$myData]);
ok. I'm now getting Laravel data in my VUE component (via props), but my URL stays at POST target:
mydomain.com/api/myMethodInController
what can i do to redirect to initial URL, but with new data?
tnx a lot!
Y
1: Set a redirect back with myVariable data in the controller.
return redirect()->back()->with([
'myVariable' => 'foo',
])
2: Define it in HandleInertiaRequests middleware.
public function share(Request $request)
{
return array_merge(parent::share($request), [
'flash' => [
'myVariable' => fn () => $request->session()->get('myVariable'),
],
]);
}
3: Get it in the component.
<template>
{{ $page.props.flash.myVariable }}
</template>
<script setup>
import { usePage } from '#inertiajs/inertia-vue3'
const myVariable = usePage().props.value.flash.myVariable
</script>

I can't set Session Items in laravel inertia

I am using Laravel with inertia, but I am not able to set session items in laravel.
here is my laravel code:
use Illuminate\Support\Facades\Session;
public function index()
{
Session::put('message', 'showCatergories');
$categories = Category::all();
return Inertia::render('Admin/Categories', ['categories' => $categories]);
}
nothing appears in application -> storage-> sessoin storage
my route:
Route::middleware(['auth', 'web'])->group(function () {
Route::resource('/categories', CategoriesController::class);
});
How to tackle this issue?
Working with session items and flash messages in Inertia.js is done by appending it to the shared method in the HandleInertiaRequests middleware.
class HandleInertiaRequests extends Middleware
{
public function share(Request $request)
{
return array_merge(parent::share($request), [
'flash' => [
'message' => fn () => $request->session()->get('message')
],
]);
}
}
Here we get the message item on the session and append it as a flash prop on the request.
Then we can use it in our frontend of choice. Here is a React.js example:
import { usePage } from '#inertiajs/inertia-react'
export default function Layout({ children }) {
const { flash } = usePage().props
return (
<main>
<header></header>
<content>
{flash.message && (
<div class="alert">{flash.message}</div>
)}
{children}
</content>
<footer></footer>
</main>
)
}
You can read more about flash messages in the documentation.

How to login with google using socialite in laravel 5.8 with different types of users

I want to login with google socialite for two types of user.
1- Company Users.
2- Individuals users
In database users table i add a field sign_up_as if 1 then company users if 2 then individuals. so how i paas 1 and 2 value to signin with google link.
In the login view i add this code
<li>
<i class="fa fa-google"></i>
</li>
Controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Socialite;
use App\Services\SocialGoogleAccountService;
use Illuminate\Routing\UrlGenerator;
class SocialAuthGoogleController extends Controller
{
public function redirect()
{
return Socialite::driver('google')->redirect();
}
public function callback(SocialGoogleAccountService $service)
{
$user = $service->createOrGetUser(Socialite::driver('google')->user());
auth()->login($user);
return redirect()->to('/home');
}
}
createOrGetUser function
public function createOrGetUser(ProviderUser $providerUser)
{
// dd('now here');
$account = SocialGoogleAccount::whereProvider('google')
->whereProviderUserId($providerUser->getId())
->first();
// dd($account);
if ($account) {
return $account->user;
} else {
$account = new SocialGoogleAccount([
'provider_user_id' => $providerUser->getId(),
'provider' => 'google'
]);
$user = User::whereEmail($providerUser->getEmail())->first();
if (!$user) {
$user = User::create([
'email' => $providerUser->getEmail(),
'name' => $providerUser->getName(),
'password' => md5(rand(1, 10000)),
'sign_up_as' => 2, // currently sign_up-as static
]);
}
$account->user()->associate($user);
$account->save();
return $user;
}
}
I got the solution myself rather then passing values simple set the values in the session and get in the controller then save in the database like.
#php
session(['sign_up_as' => '2']);
#endphp
and in the controller
$sign_up_as = session('sign_up_as');

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.

Account balance system with Stripe

For the past two days I've been trying to understand how Stripe works.. What I'm trying to build is a simple system that lets the user to add funds to his account on the site.
I followed a tutorial I found on the internet that uses Laravel Cashier but as I've read on the laravel documentation if I need to perform single charges I should use directly Stripe. The thing is, there are not many tutorials on how this should be done with laravel..
Here's what I have so far:
VIEW:
<form class="app-form" style="margin-bottom: 0px;" action="/add-funds" method="POST">
{{ csrf_field() }}
<select id="funds-options" style="width: 20%; margin-bottom: 20px;" name="add-funds-select">
<option value="30">$30</option>
<option value="50">$50</option>
<option value="100">$100</option>
<option value="200">$200</option>
<option value="300">$300</option>
<option value="500">$500</option>
</select>
<p style="margin-bottom: 0px;">
<script src="https://checkout.stripe.com/checkout.js"></script>
<button id="customButton">Purchase</button>
<script>
var handler = StripeCheckout.configure({
key: '{{ getenv('STRIPE_KEY') }}',
image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
locale: 'auto',
token: function(token) {
// You can access the token ID with `token.id`.
// Get the token ID to your server-side code for use.
}
});
document.getElementById('customButton').addEventListener('click', function(e) {
// Open Checkout with further options:
var userAmount = $("#funds-options").val();
handler.open({
name: 'Demo Site',
description: '2 widgets',
amount: userAmount*100
});
e.preventDefault();
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
</p>
</form>
I have this select tag where the user can select the amount he wants to add to his account. Now, this opens the widget from Stripe but as soon as I hit pay, I'm getting that info: "You did not set a valid publishable key".
I tried this using the publishable key directly and I'm able to pass this but as soon as it gets into the controller it throws pretty much the same error, something like API key was not set.
I set the keys in the env file and I also reference them in the services.php..
ENV:
STRIPE_KEY=pk_test_....
STRIPE_SECRET=sk_test_...
SERVICES:
'stripe' => [
'model' => App\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],
Anyway, even if I pass this "error" I'm still not sure if I'm doing this right.
Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
class WalletController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('user.wallet.index');
}
public function postPayWithStripe(Request $request)
{
return $this->chargeCustomer($request->input('add-funds-select'), $request->input('stripeToken'));
}
public function chargeCustomer($amount, $token)
{
\Stripe\Stripe::setApiKey(getenv('STRIPE_SECRET'));
if (!$this->isStripeCustomer())
{
$customer = $this->createStripeCustomer($token);
}
else
{
$customer = \Stripe\Customer::retrieve(Auth::user()->stripe_id);
}
return $this->createStripeCharge($amount, $customer);
}
public function createStripeCharge($amount, $customer)
{
try {
$charge = \Stripe\Charge::create(array(
"amount" => $amount,
"currency" => "brl",
"customer" => $customer->id,
"description" => "Add funds to your account"
));
} catch(\Stripe\Error\Card $e) {
return redirect()
->route('index')
->with('error', 'Your credit card was been declined. Please try again or contact us.');
}
return $this->postStoreAmount($amount);
}
public function createStripeCustomer($token)
{
\Stripe\Stripe::setApiKey(getenv('STRIPE_SECRET'));
$customer = \Stripe\Customer::create(array(
"description" => Auth::user()->email,
"source" => $token
));
Auth::user()->stripe_id = $customer->id;
Auth::user()->save();
return $customer;
}
/**
* Check if the Stripe customer exists.
*
* #return boolean
*/
public function isStripeCustomer()
{
return Auth::user() && \App\User::where('id', Auth::user()->id)->whereNotNull('stripe_id')->first();
}
public function postStoreAmount($amount)
{
$userBalance = Auth::user()->balance;
$userBalance = $userBalance + $amount;
Auth::user()->save();
session()->flash('message', 'You just added funds to your account.');
return redirect()->route('index');
}
}
I have a field in the users table that holds the user balance.
As I mentioned, I followed a tutorial I found on the internet.. I'm not sure how this should work. Any suggestions?
You will follow this tutorial. I am integrated it in my cart functionality previous week. Its very easy to integrate ...have Fun :)
http://justlaravel.com/integrate-stripe-payment-gateway-laravel/
For anyone else looking for how to retrieve account balance with laravel cashier, I found it like this:
$user = App\User::first();
echo $user->asStripeCustomer()->account_balance;
This returns the account balance in cents.

Resources