bezhanSalleh / filament-shield

The easiest and most intuitive way to add access management to your Filament Admin Resources, Pages & Widgets through `spatie/laravel-permission`

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature Request: Modular support

WebKenth opened this issue · comments

Greetings!

We love your package, however it falls short whenever there's a third party model which happens to have the same name as ours

This might be an "us" issue, and not something relevant for this package, however i thought i would contribute if anyone else had the same issues:

Context:
We use a Laravel Module package which allows us to seperate our code into modules and keep logic more tightly locked up. a benefit of this is that when it becomes nessecary we can simply turn the entire module into a package and share it to external partners and such. ( Package in question is: https://github.com/InterNACHI/modular )

Problem:

We have 3 Models all names Product
Each are namespaced into their own module

Example:

  • app/Models/Product
  • app-modules/Shopify/Models/Product
  • app-modules/Struct/Models/Product

when using this package it is only possible for us to register / generate a policy for one module as they are all put into the same directory.

So a "ShopifyProduct" would turn into ProductPolicy with the model pointing to the module's model

In the AuthServiceProvider we added each custom model with a corresponding policy

ie:

        'Module\Vendor\Models\Shopify\Product' => 'Module\Vendor\Policies\ProductPolicy',

with the thought that Shield would edit the policy in the module folder, and while we understand why not it was still not possible for Shield to add a sub folder to the app/Policies folder

ideally we wanted something like

  • app/Policies/Shopify/ProductPolicy
  • app/Policies/Struct/ProductPolicy
  • app/Policies/ProductPolicy

however the generate command simple overwrote the same ProductPolicy 3 times and the last one registered won :(

The patch below solves the issue by looking for a folder and then using Sub Folder logic to separate each policy into their own folder

--- src/Commands/Concerns/CanGeneratePolicy.php
+++ src/Commands/Concerns/CanGeneratePolicy.php
@@ -25,6 +25,19 @@
         $policyPath = Str::of(config('filament-shield.generator.policy_directory', 'Policies'))
             ->replace('\\', DIRECTORY_SEPARATOR);
 
+        if (Str::of($path)->contains('app-modules')) {
+            $sub_path = explode('app-modules/', $path);
+            $module_name = explode('/', $sub_path[1])[0];
+            $module_name = Str::studly($module_name);
+            $basePolicyPath = app_path(
+                (string) Str::of($entity['model'])
+                    ->prepend($policyPath->append('\\'))
+                    ->replace('\\', DIRECTORY_SEPARATOR),
+            );
+            $policyPath = str_replace('app/Policies',"app/Policies/{$module_name}","{$basePolicyPath}Policy.php");
+            return $policyPath;
+        }
+
         if (Str::of($path)->contains(['vendor', 'src'])) {
             $basePolicyPath = app_path(
                 (string) Str::of($entity['model'])
@@ -64,6 +77,14 @@
         $stubVariables['namespace'] = Str::of($path)->contains(['vendor', 'src'])
             ? 'App\Policies'
             : Str::of($namespace)->replace('Models', 'Policies'); /** @phpstan-ignore-line */
+
+        if(Str::of($path)->contains('app-modules')) {
+            $sub_path = explode('app-modules/', $path);
+            $module_name = explode('/', $sub_path[1])[0];
+            $module_name = Str::studly($module_name);
+            $stubVariables['namespace'] = "App\\Policies\\{$module_name}";
+        }
+        
         $stubVariables['model_name'] = $entity['model'];
         $stubVariables['model_fqcn'] = $namespace . '\\' . $entity['model'];
         $stubVariables['model_variable'] = Str::of($entity['model'])->camel();
--- src/FilamentShield.php
+++ src/FilamentShield.php
@@ -41,10 +41,14 @@
             if (Str::contains($identifier, '_')) {
                 throw new \InvalidArgumentException("Permission identifier `$identifier` for `$resource` cannot contain underscores.");
             }
-
             return $identifier;
         }
-
+        if (Str::contains($resource, '{{ YOUR MODULE NAMESPACE }}')) {
+            $package = explode('\\', $resource);
+            $vendor = Str::lower($package[0]);
+            $module = Str::lower($package[1]);
+            return "$vendor:$module::{$this->getDefaultPermissionIdentifier($resource)}";
+        }
         return $this->getDefaultPermissionIdentifier($resource);
     }
 

An idea could be to make it an option in the plugin to allow subfolder logic

not planned at the moment but maybe once i get some free time