1

I'm trying to work out how I can achieve this with Laravel route and controller. I'm having 3 tables as below:

user

  • id
  • name

company

  • id
  • name

company_user

  • id
  • user_id
  • company_id

So as you might have guessed I have the following models:

class User extends Authenticatable implements MustVerifyEmail
{
    use HasApiTokens, HasFactory, Notifiable;

    public function companies(): BelongsToMany
    {
        return $this->belongsToMany(Company::class);
    }
}
class Company extends Model
{
    use HasFactory, SoftDeletes;

    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class);
    }
}

The relationship is ManyToMany and it works all fine.

So I'm trying to achieve the following route:

Route::apiResource('company/user', CompanyUserController::class);

I know I can achieve such this by the following route easily:

Route::apiResource('company.user', CompanyUserController::class);

However what I need in my route is not to require to pass the company id as route parameter.

I want the company_id to be set automatically through payload from JWT token which I already have. So imagine we have company_id as 1 in JWT token payload and I want the user to go to this link: GET /company/user and controller would then list all users who belong to the company_id 1 which is grabbed from logged in user JWT token payload. I want the nested resource functionality without needing to pass the parent parameter in the route and instead grabbing it automatically from logged in user token.

I don't want the route to be like /company/1/user.

I hope someone could help me achieve such these. This also needs to apply to all other controller methods automatically as in show, store, update etc.

Thanks guys.

Mo Baqeri
  • 333
  • 2
  • 16
  • I dont know how you implement your JWT Token to include the company_id since there is no code shown. But **if** it is already in the JWT Token, you can decode them just like this answer: https://stackoverflow.com/a/48309695 – Mr. Kenneth Aug 10 '23 at 02:30
  • Once you decode the jwt, you can then do your eloquent things to fetch your users – Mr. Kenneth Aug 10 '23 at 02:30
  • Thanks @Mr.Kenneth However this does not answer my question. My question is not how to get company_id from JWT token. I know all that bit. I need to find a way that laravel route predefine the parameter in the resource route rather than requiring passing the parameter. A resource route like this `/company/1/user` works fine but I want this route `/company/user` to work the same and always assume company_id is ` without requiring to pass that 1 every time in the url. I hope this makes sense. – Mo Baqeri Aug 10 '23 at 03:53
  • If you really dont want to use JWT decode, use `/company/user` without the company_id in the route, then you can change the method to a `POST REQUEST` to the route. Create a `hidden form` with hidden input. Use javascript or jquery to addEventListener to button click, channge the value of hidden input to the company_id you want then do form.submit(). – Mr. Kenneth Aug 10 '23 at 07:53
  • Dont know if it works but you can use cookie like this: https://stackoverflow.com/a/45207639 – Mr. Kenneth Aug 10 '23 at 07:55
  • if the only company that user can see in `/company/user` is the company that the user joined, then you can just fetch the company id from the model.. – Mr. Kenneth Aug 10 '23 at 07:57

2 Answers2

1

Avoid using apiResources for both company and user routes, define a custom route group instead:

Route::group(['prefix' => 'company'], function(){
    Route::apiResource('user', CompanyUserController::class);
    ... Other company routes if you need ...
});

Now, in CompanyUserController you can read the correct company id from JWT Token and do your stuffs.

The php artisan route:list result:

  GET|HEAD        company/user
  POST            company/user
  GET|HEAD        company/user/{user}
  PUT|PATCH       company/user/{user}
  DELETE          company/user/{user}
Legoraccio
  • 36
  • 3
  • That means I need to check and make sure the user belongs to the company manually on every method of controller or in the construct of the controller. I was hoping I could rely on Laravel bind methods to do this for me instead of me doing it manually. Thanks for your solution though @Legoraccio – Mo Baqeri Aug 10 '23 at 22:51
0

Just to answer my own question, I ended up doing this route:

Route::apiResource('company/user', CompanyUserController::class); As @Legoraccio suggested.

Then I did the following in my CompanyUserController constructor to make sure I'm sanitising data before hitting the methods.

public function __construct()
    {
        $payload = auth()->payload();
        $companyId = $payload->get('companyId');
        // making sure the selected company exists
        try {
            $this->company = Company::where('id', $companyId)->firstOrFail();
        } catch (ModelNotFoundException $exception) {
            abort(Response::HTTP_NOT_FOUND, "Unable to find company");
        }

        // making sure the logged in user is part of the selected company
        try {
            $this->company->users()->where('users.id', Auth::user()->id)->firstOrFail();
        } catch (ModelNotFoundException $exception) {
            abort(Response::HTTP_NOT_FOUND, "You don't belong to this company.");
        }
    }
Mo Baqeri
  • 333
  • 2
  • 16