#Laravel Gates

228 messages · Page 1 of 1 (latest)

regal drift
#

@ocean bramble

#
        $validator = Validator::make([$attribute => $value], [
            $attribute => 'unique:' . $this->databaseTable . ',' . $this->column
        ]);```
ocean bramble
#

hi Krayvok

regal drift
#

Hi 🙂

ocean bramble
#

So let's take the project back to basics, and help me understand it more fully

You have a website (we'll call it a project herein)

The project has multiple Customers (e.g. Companies)

Each Company's data should only be avaialble to their "users"

#

We'll call a Company's users "company staff" to keep it all simple

regal drift
#

Correct.

#

A user signs up their company, and they have many users

ocean bramble
#

All data is stored in a Single DB

regal drift
#

Many users can do as they please within the application.

#

Yes, All data in a single db -- There are boot methods associated to most if not all models that require the company_id to be appended to all calls to limit them into their respective company data for a given feature.

ocean bramble
#

Can we assume that the application is realistically "SaaS" / Software as a Service"

regal drift
#

This will stop a user from company A accessing company B.

#

Yes.

ocean bramble
#

Before we continue - is there a reason you discounted the tenancy approaches?

#

There are a few packages out there for SaaS applications that allow you to split the data more smoothly, I'm familiar with one package that actually splits each customer into their own physical database for example

#

If you've discounted them, then that's not a total blocker, just wanted to ask the question

regal drift
#

Yeah, Ive developed for about 10 years, but over the last 6 years I have grown expontentionally as a developer -- Originally when I started this application I was afraid of complexity things like multiple databases and felt the single was the best design approach for my wheel house.

ocean bramble
#

For example, I use the "Stancl" - "Tenancy" package, and each one of my "tenants" (Companies) has its own database, which are all populated etc from the "core" migrations

#

then they all use the same codebase

regal drift
#

Yeah, something like that would be awesome

#

and solve a lot of issues for me when it comes to raw sql queries etc

ocean bramble
#

And you can determine the tenancy by a number of methods (e.g domain/subdomain/request parameter)

regal drift
#

because I have to remember to put in these boot methods when I cannot handle it through eloquent natively.

#

I am curious how a Gate would stop a user from company A alterting a user 2 from company A by injecting the id during form submission and that form request validation check the validity of the ID before its allowed to 'pass'

jolly sky
#

It’s possible to use a single database, too. I have an application that does this. I use a Website model as my “tenant” model. Because I use a route group, the Website model instance that gets injected into each controller, and I can query models through it as an almost aggregate root:

Route::group('{website:domain}')->group(function () {
    Route::get('articles', [ArticleController, 'index');
});
namespace App\Http\Controllers\Website;

class ArticleController extends Controller
{
    public function index(Website $website)
    {
        return view('website.articles.index')->with([
            'articles' => $website->articles()->paginate(),
        ]);
    }
}
ocean bramble
#

Effectively the way that it works is:
You create your core website etc
In Tenancy, you specify that you want individual databases per-tenant

You add the relevant migrations to the "tenant" folder in your migrations, that should be run whenever a new tenant is created.

When you create a new "Tenant" (Company), a new database is created, and the "tenant" folder migrations are run

EVERYTHING is then run in the tenant context

#

Yep, definitely possible to use a single database for it too, however, I know that 90%+ of "Customers" with mature security approaches LOVE it when there's separation of data at a physical level

#

I've written some improved segregation approaches for v4 Stancl Tenancy, just waiting for him to review them and then either he'll add them as documents, or I'll end up writing some blog posts about it

regal drift
ocean bramble
#

Typical Example:
"Customer" will be used in place of "Tenant"
Each Customer uses the same codebase
Each Customer has their own database (which is determined automatically by the Package at runtime)

A user with "Full Admin" in Tenancy A, will NEVER be able to see the data in Tenancy B

regal drift
#

So im missing Gates/Services/Repositories eh?

ocean bramble
#

You can put gates in-line in services if you so choose!

#

or in providers/AppServiceProvider

#

its up to you where you define them

regal drift
#

I dont neccesary plan on different ORM, however I do wish to get on the ios/android wagon with the application so I could see repositories helping with the ORM attachments

ocean bramble
#

I'd really, really, strongly advise for your use case that you take a more tenant approach to the data, at least plan on that for the future

#

in the meantime, how are you authenticating your Android/iOS user?

regal drift
#

Im not but I wish to get eventually get to that point.

#

I originally asked martin if I should split my controllers over to API only

#

and he pointed me towards services

#

which got me on gates

#

because services from my new understanding can handle both

ocean bramble
#

So at the moment, an API call just doesn't know who the use is?

#

user is*

regal drift
#

it does through form requests

#

and it checks if the user exsits in the db

#

but it doesnt validate that its not a bad actor injecting a valid id

#

under that company

#

idk how open you are to goign to a pm and doing a screenshare

jolly sky
ocean bramble
#

Okay, so you shared your route of:

Route::get('dispatch/employee/event/create/{id}', [DispatchCalendarEmployeeEventController::class,'create'])
    ->name('user.dispatch.calendar.employee.event.create');

So let's say I submit a request to:
YourDomain/dispatch/employee/event/create/12345

What validation are you doing here as to who/what I am?

regal drift
ocean bramble
#

What Martin's saying is, if you use either services or traits, you can re-use methods across both API and Request, where it makes sense to do so

regal drift
#

oh the logic isnt in the controllers

#

derp

#

theyre just a middle man?

ocean bramble
#

the Controller will access methods within traits/services

regal drift
#

the service holds the logic? and its applied to the api controller and mvc controller?

jolly sky
#

Yeah. So using your example, you may have an EventService. It’d be a class with methods for retrieving and persisting events. So the class might have a createForEmployee method. So you could use that class and method in both a web controller and an API controller, and even a console command, because the service doesn’t care how it’s used; it just exists to encapsulate some bit of logic, and then your application uses that logic where it needs to.

ocean bramble
#

Exactly that, you can do similar with Traits if you want to re-use basic logic across Controllers/Components etc

regal drift
#

Yeah

#

I wrote all my models and learned about traits long ago

ocean bramble
#

Yep, but you can use traits in your Controllers too

regal drift
#

and went through all my models and removed duplicated relationships, or methods that are applicable to other models and wrote traits around it

#

I wasnt aware you could use traits in your controller

#

would make life simple for simple crud actions lol

#

I end up just C&P that stuff sadly

ocean bramble
#

Traits aren't Laravel specific, they're a core PHP thing

#

So you can use them in Laravel, core PHP, Livewire Components etc

regal drift
#

Theyre helper methods essentially is my take?

#

Isolated functions

ocean bramble
#

slightly different to a helper method, a helper method you can define as a core include

#

and reuse everywhere

#

a Trait, is just a collection of methods/attributes, that you want to use in a Class

#

so for example, I have a collection of Traits that relate to Models, which contain things relating to Relationships, or core Methods that I want to call on a Model (e.g. a scope)

regal drift
#

got it

#

So back to Gates

#

how does a gate check that a user pushing the form request is the user and not injecting a valid id?

ocean bramble
#

That's where you go back to - how have you authenticated the user on the API Controller

regal drift
#

So I have no API atm.

#

So everythings MVC

#
    {
        $attemptResult = Auth::guard('user')->attempt(['username' => $request->username, 'password' => $request->password], $request->remember);
        if ($attemptResult)
        {

            $user = User::where('id', Auth::guard('user')->user()->id)->first();

            $user->update(['updated_at' => carbon::now()]);

            if ($user->isAccountOwner()) {

                if ($user->hasValidSubscription())
                {
                    return redirect()->intended(route('user.dashboard'));
                }

                return redirect()->route('user.company.subscription.index');

            }

            if ($user->account_type === 1) {
                if (!$user->onboard_complete) {
                    return redirect()->intended(route('user.onboard'));
                }
                return redirect()->route('user.company.subscription.index');
            }

            if ($user->account_type === 4)
            {
                if ($user->isValidPiggybackSubscription())
                {
                    return redirect()->intended(route('user.dashboard'));
                }

                return redirect()->route('user.company.subscription.notice');
            }

            $request->session()->flush();
            $request->session()->regenerate();
            Session::flash(AlertsEnum::ERROR, 'Your account has been disabled. Please contact your account manager.');
            return redirect()->back();
        }

        Session::flash(AlertsEnum::ERROR, 'We could not verify your account. Please check your username and password then try your request again.');
        return redirect()->back()->withInput($request->only('username', 'remember'));

    }```
#

Is my trashy user login 😛

ocean bramble
#

So are you looking at
Using the API purely internally, never allowing any 3rd party access
or
Allowing some 3rd party access potentially in the future

regal drift
#

Allowing potentionally some aspects of it, but would like to get the ability to get an ios/android app up and going so my understanding is ill need a mix of both?

ocean bramble
#

Okay, you should read up on Fortify vs Sanctum, in terms of using a key based authentication mechanism, both have their pros/cons

jolly sky
#

My preference is Passport, as it’s OAuth-based, which is an actual standardised protocol, unlike Sanctum.

ocean bramble
#

Or that!

#

As you'll need to use a 3rd party auth mechanism for authenticating the API, unless you want to re-invent the wheel

#

Get that working as a basic thing, put a basic Controller in your API namespace, and add the relevant route, test it using postman or similar.

#

Once you have that working smoothly, you can then integrate that into the Google & Apple apps, there are docs/tutorials around for this

regal drift
#

yeah i just played with laravel api starter app

ocean bramble
#

What this will mean, is that a user of the app is "authenticated" with your app

regal drift
#

that does oauth2

#

was good experience playin around

#

what kidna elad me to the checking if I should start refactoring things to be api driven

#

ive been refactoring things away from seperate blade views for each action; like edit,update, etc and using a lot of javascript to do that without page loads

#

for a better client exp

#

short cutting the 2-3 page loads to update a simple boolean type thing

#

QoL fixes

#

imo

ocean bramble
#

If you're doing that

#

Are you using Vue at the moment?

regal drift
#

no

#

native javsacript

#

I was tlaking to someone about react on the portability

#

since I was comfortable writing javascript

ocean bramble
#

So you're using
Controller -> View/Blade (with native JS)

regal drift
#

yes

#

some pages dont even load the controller data

#

page loads, and ajax calls happen to give page loads instantly

ocean bramble
#

I mean, for what you're doing, I'd really consider Livewire

regal drift
#

rather than 1-3 second page loads compiling the data

ocean bramble
#

which is a Laravel package

#

and basically JS wrapper

#

allowing things to be "live"

#

It cuts out a lot of the pain you're probably going through in terms of duplicating your code

regal drift
#

yeah

#

So originally id duplicate

#

then I figured out ways to eliminate that

#

Now ive been duplicating javascript, now Im about to go through every page and start moving them to javascript classes, or external files to be brought into each page applicable.

#

Ive been doing a UI update and migrating to current bootstrap 5, and newer UI components

#

was on bootstrap 3 and im near finished on that

#

probably another 2-3 weeks before im fully finished

ocean bramble
#

Bs3 is wholly unsupported now btw

regal drift
#

hundreds and hundreds of blades

#

yeah

#

lol

#

thats why im doing ui update

ocean bramble
#

I'm not sure how you've ended up with so many blades to be honest

regal drift
#

I own a construction company, and I wrote this software specifically for my construction company

#

so I am the only developer

ocean bramble
#

or why it's so difficult to update them all

#

are you using any blade components for example?

regal drift
#

Id love to screenshare my turd

#

let you see why/what im doing

ocean bramble
#

Don't have time today for a screenshare, plus the missus will undoubtedly kick me in the head if I start speaking, but if you have a GH repository, or can put a sample into there, then I can give you some pointers

#

I suspect the hole you've fallen into is not using blade components at all

regal drift
#
    <div id="layout-wrapper">
        @include('user.v2.layouts.topbar')
        @include('user.v2.layouts.sidebar')
        <!-- ============================================================== -->
        <!-- Start right Content here -->
        <!-- ============================================================== -->
        <div class="main-content">
            <div class="page-content">
                <div class="container-fluid">
                    @yield('content')
                </div>
                <!-- container-fluid -->
            </div>
            <!-- End Page-content -->
            @include('user.v2.layouts.footer')
        </div>
        <!-- end main content-->
    </div>
    <!-- END layout-wrapper -->

    <!-- Right Sidebar -->
    @include('user.v2.layouts.right-sidebar')
    <!-- /Right-bar -->

    <!-- JAVASCRIPT -->
    @include('user.v2.layouts.vendor-scripts')```
#

is out of my master

#

so I am usin sections?>

ocean bramble
#

using sections is fine

regal drift
#

ah yeah components

ocean bramble
#

Now let's say, I want toi see your "user edit" blade

regal drift
#

yeah

#

I m not

#
@section('content')
    <div class="page animsition">
        <div class="page-header">
            <h1 class="page-title">Edit User</h1>
            <ol class="breadcrumb">
                <li><a href="{{ route('user.dashboard') }}">Dashboard</a></li>
                <li><a href="#">Administrative</a></li>
                <li><a href="{{ route('user.administrative.management.index') }}">User Management</a></li>
                <li class="active">Edit User</li>
            </ol>
        </div>
        <div class="page-content">
            <div class="row">
                <div class="col-lg-4 col-lg-push-4">
                    <div class="panel">
                        <form method="POST" action="{{ route('user.administrative.management.update') }}">
                        @csrf
                        <div class="panel-body">
                            <div class="form-group col-sm-12 col-md-12 col-lg-12">
                                @if ($errors->has('name'))
                                    <p class="alert alert-danger">{{ $errors->first('name') }}</p>
                                @endif
                                <label class="control-label">Full Name</label>
                                <input type="text" class="form-control" name="name" value="{{ old('name', $user->name) }}"
                                       autocomplete="off">
                            </div>
                            <div class="form-group col-sm-12 col-md-12 col-lg-12">
                                @if ($errors->has('email'))
                                    <p class="alert alert-danger">{{ $errors->first('email') }}</p>
                                @endif
                                <label class="control-label">Email Address</label>
                                <input type="text" class="form-control" name="email"
                                       value="{{ old('email', $user->email) }}" autocomplete="off">
                            </div>
                        </div>
                            <div class="panel-footer">
                                <input type="hidden" name="user_id" value="{{ $user->id }}">
                                <button type="submit" class="btn btn-block btn-success">Save Changes</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection
@section('extra_css')
@endsection
@section('extra_js')
@endsection
ocean bramble
#

And that is where you're creating more work for yourself than you ever possibly want to

regal drift
#

I have to go to each blade and edit stuff

ocean bramble
#

Yep, which I'm sure you're finding totally painful?

regal drift
#

very

#

lmao

#

between hundreds of controllers

#

and hundreds of blades

#

making changes, or refactoring is a task

ocean bramble
#

So using a blade component, what you do is pass in the relevant items, and the rest is taken care of by the blade

regal drift
#

ie a widget

ocean bramble
#

Not a widget, just a blade component

regal drift
#

Like you have a compoonent

#

of a widget

#

that shows a user count

#

you feed the number

#

on 10 pages

#

same code, but its not c&P?

#

ie @bladeshowcomponent(usercount, 5)

#

or w/e

ocean bramble
#

Let's say you have a "text entry" field

regal drift
#

I understand that

#

I am not doing that

#

I know there are several areas where I am falling on my face

#

blades, javascript, and some of thsoe controllers that have create/{id}

#

and gates/validatng a user can do an action

ocean bramble
#

Yep, I think you're missing a massive trick in terms of your blade components

#

Remember you don't have to create the actual View Component PHP for it

#

it can just be a blade

#

Now this won't work off-the-bat as I've stolen the code out of one of my projects which is Livewire oriented
views/components/text-input.blade.php

@props(['errors', 'name', 'value', 'label'])
<div class="form-group col-sm-12 col-md-12 col-lg-12">
    @if ($errors->has($name))
        <p class="alert alert-danger">{{ $errors->first({{ $name }} ) }}</p>
    @endif
    <label class="control-label">{{ $label }}</label>
    <input type="text" class="form-control" name="email" value="{{ $value }}" autocomplete="off">
</div>

Then I call

<x-text-input :$errors name="'email'" :$value label="'User Email'" />
#

when I want to update what a text input looks like, I'm doing it in one place only. You can start to add in things like merged classes/attributes etc

#

For example, I have around 150 forms in one of my projects, I can:

  1. Use the standard "text input" on any text input in any of them
  2. Use a "wide" text input on any text input in any of them
  3. Override the default classes/styles on any text input in any of them
#

In reality, mine looks a tad different to that as I use Livewire, so I've had to quickly swap out a few items there to make it make more sense for you

#

Then - I do the same for the form itself

So my "form" blade looks like

<x-default-form>
  <x-text-input :$errors name="'username'" value="{{ $user_name}}" label="'User Name'" />
  <x-text-input :$errors name="'email'" value="{{ $user_email}} " label="'User Email'" />
</x-default-form>
#

And my default-form has no PHP code/view component, it's just a default-form.blade.php file

#

This bit will save you ENORMOUS time in terms of updating

#

You can chunk of massive amounts of the code you're using into separate blades too

#

e.g

this:

  <div class="page-header">
      <h1 class="page-title">Edit User</h1>
      <ol class="breadcrumb">
          <li><a href="{{ route('user.dashboard') }}">Dashboard</a></li>
          <li><a href="#">Administrative</a></li>
          <li><a href="{{ route('user.administrative.management.index') }}">User Management</a></li>
          <li class="active">Edit User</li>
      </ol>
  </div>

Can go into a
views/components/page-header.blade.php

Then replace with something like the below, noting that you could further extrapolate it out into "page header breadcrumt items"

  <div class="page-header">
      <h1 class="page-title">Edit User</h1>
      <ol class="breadcrumb">

        <li @class([
                'active' => url()->current() == route('user.dashboard') 
            ]>
              <a href="{{ route('user.dashboard') }}"Dashboard</a>
        </li>

        <li @class([
                'active' => url()->current() == route('user.administrative.management.index') 
            ]>
              <a href="{{ route('user.administrative.management.index') }}">User Management</a>
        </li>

      </ol>
  </div>
regal drift
#

ah

ocean bramble
#

Effectively, 95% of my classes are contained within blade components

regal drift
#

yeah

ocean bramble
#

Hardly any of them have corresponding PHP code

regal drift
#

I dont have that dynamic feature of showing current url page

#

and throwing active/not on it

#

without actually watning that and doing it manually

#

lol

#

yeah I need to clean up my blades

#

figured if I could get it to bootstrap 5, and get the UI portion atleast up to current speed

#

then it would be advantageous to do that

ocean bramble
#

For the blade issue, you absolutely should take a step back, and look at commonalities

regal drift
#

yeah

ocean bramble
#

What are you re-using more than half a dozen times

regal drift
#

like 90% of my app is common

#

just little variations being showed

#

ie showing customer/property/job data

ocean bramble
#

If I want to change the background of 100% of my forms, I need to change 3 blades

regal drift
#

gonna have similar widgets, or ui components

#

but will maybe show different context

#

yeah id have to change 200-300 blades lmao

ocean bramble
#

The other advantage is, once you componentise everything, you can start to use the $attributes->merge approach

#

I mean, I contribute to a very popular (free!) Livewire Table Component. What that allows you to do is display a Model (with relations that you incldue) as a table, with filters/search/sorting with a few lines of code.

You can then, just add attributes to different fields/rows dynamically.

Using a similar approach, you'd be able to allow your customers to specify the background colours etc.

If you keep down the route you're on of not using components, you will have oh-so-much pain, when you want to change something

regal drift
#

yeah I wish

ocean bramble
#

I'd suggest that you pick two Models (User is normally more complex, so pick an easier target)

Compare the two side by side

Look at where you can extract the code into a blade component, and create the blade components (don't worry about creating a App/View/Components etc, just create the component in resources/views/components)

regal drift
#

For example I track

#

customer Payments, Credits, Refunds, or Write offs

#

They all share same controller code

#

bascially duplicated between 4 controllers

#

The models are near mirror

#

outside of the 'name'

ocean bramble
#

Ignoring the controller/PHP element of it, and focusing just on your blades, I bet all of those have a lot of the same structure in each blade

regal drift
#

Yeah

ocean bramble
#

This is where using a blade component will come into its own, massively

regal drift
#

Makes editing things UI wise miserable I know that

ocean bramble
#

How disgusted by Livewire are you... ha

regal drift
#

not at all?

#

never used it